Skip to content

Commit 66f4296

Browse files
authored
Merge pull request #1114 from Haehnchen/feature/1035-inline-twig-vars
improve Twig types support for inline type declaration {# @var Class variable #} and {# @var variable Class #} #1035
2 parents bd1fcee + eb87b94 commit 66f4296

File tree

8 files changed

+96
-12
lines changed

8 files changed

+96
-12
lines changed

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigPattern.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
2020
import org.jetbrains.annotations.NotNull;
2121

22+
import java.util.ArrayList;
23+
import java.util.Collection;
24+
2225
/**
2326
* @author Daniel Espendiller <daniel@espendiller.net>
2427
*/
@@ -872,10 +875,13 @@ public static ElementPattern<PsiElement> getTypeCompletionPattern() {
872875
}
873876

874877
public static ElementPattern<PsiComment> getTwigTypeDocBlockPattern() {
875-
return PlatformPatterns.or(
876-
PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(TwigTypeResolveUtil.DEPRECATED_DOC_TYPE_PATTERN)).withLanguage(TwigLanguage.INSTANCE),
877-
PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE)).withLanguage(TwigLanguage.INSTANCE)
878-
);
878+
Collection<ElementPattern> patterns = new ArrayList<>();
879+
880+
for (String s : TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE) {
881+
patterns.add(PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(s)).withLanguage(TwigLanguage.INSTANCE));
882+
}
883+
884+
return PlatformPatterns.or(patterns.toArray(new ElementPattern[patterns.size()]));
879885
}
880886

881887
/**

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateGoToDeclarationHandler.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ private Collection<PsiElement> getConstantGoto(@NotNull PsiElement psiElement) {
344344
return targetPsiElements;
345345
}
346346

347+
/**
348+
* Extract class from inline variables
349+
*
350+
* {# @var \AppBundle\Entity\Foo variable #}
351+
* {# @var variable \AppBundle\Entity\Foo #}
352+
*/
347353
@NotNull
348354
private Collection<PhpClass> getVarClassGoto(@NotNull PsiElement psiElement) {
349355
String comment = psiElement.getText();
@@ -352,10 +358,10 @@ private Collection<PhpClass> getVarClassGoto(@NotNull PsiElement psiElement) {
352358
return Collections.emptyList();
353359
}
354360

355-
for(String pattern: new String[] {TwigTypeResolveUtil.DEPRECATED_DOC_TYPE_PATTERN, TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE}) {
361+
for(String pattern: TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE) {
356362
Matcher matcher = Pattern.compile(pattern).matcher(comment);
357363
if (matcher.find()) {
358-
String className = matcher.group(2);
364+
String className = matcher.group("class");
359365
if(StringUtils.isNotBlank(className)) {
360366
return PhpElementsUtil.getClassesInterface(psiElement.getProject(), className);
361367
}

src/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java

+24-5
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,35 @@
4343
*/
4444
public class TwigTypeResolveUtil {
4545

46-
public static final String DEPRECATED_DOC_TYPE_PATTERN = "\\{#[\\s]+([\\w]+)[\\s]+([\\w\\\\\\[\\]]+)[\\s]+#}";
47-
public static final String DOC_TYPE_PATTERN = "@var[\\s]+([\\w]+)[\\s]+([\\w\\\\\\[\\]]+)[\\s]*";
46+
/**
47+
* {# variable \AppBundle\Entity\Foo[] #}
48+
*/
49+
public static final String DEPRECATED_DOC_TYPE_PATTERN = "\\{#[\\s]+(?<var>[\\w]+)[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]+#}";
50+
51+
/**
52+
* {# @var variable \AppBundle\Entity\Foo[] #}
53+
* {# @var variable \AppBundle\Entity\Foo #}
54+
*/
55+
private static final String DOC_TYPE_PATTERN_CLASS_SECOND = "@var[\\s]+(?<var>[\\w]+)[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]*";
56+
57+
/**
58+
* {# @var \AppBundle\Entity\Foo[] variable #}
59+
* {# @var \AppBundle\Entity\Foo variable #}
60+
*/
61+
private static final String DOC_TYPE_PATTERN_CLASS_FIRST = "@var[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]+(?<var>[\\w]+)[\\s]*";
4862

4963
public static final Pattern[] INLINE_DOC_REGEX = {
50-
Pattern.compile(DOC_TYPE_PATTERN, Pattern.MULTILINE),
64+
Pattern.compile(DOC_TYPE_PATTERN_CLASS_SECOND, Pattern.MULTILINE),
65+
Pattern.compile(DOC_TYPE_PATTERN_CLASS_FIRST, Pattern.MULTILINE),
5166
Pattern.compile(DEPRECATED_DOC_TYPE_PATTERN),
5267
};
5368

5469
// for supporting completion and navigation of one line element
55-
public static final String DOC_TYPE_PATTERN_SINGLE = "\\{#[\\s]+@var[\\s]+([\\w]+)[\\s]+([\\w\\\\\\[\\]]+)[\\s]+#}";
70+
public static final String[] DOC_TYPE_PATTERN_SINGLE = new String[] {
71+
"\\{#[\\s]+(?<var>[\\w]+)[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]+#}",
72+
"\\{#[\\s]+"+ DOC_TYPE_PATTERN_CLASS_SECOND + "[\\s]+#}",
73+
"\\{#[\\s]+"+ DOC_TYPE_PATTERN_CLASS_FIRST + "[\\s]+#}",
74+
};
5675

5776
private static String[] PROPERTY_SHORTCUTS = new String[] {"get", "is", "has"};
5877

@@ -192,7 +211,7 @@ private static Map<String, String> getInlineCommentDocsVars(@NotNull PsiElement
192211
for (Pattern pattern : INLINE_DOC_REGEX) {
193212
Matcher matcher = pattern.matcher(text);
194213
while (matcher.find()) {
195-
variables.put(matcher.group(1), matcher.group(2));
214+
variables.put(matcher.group("var"), matcher.group("class"));
196215
}
197216
}
198217
}

src/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -2694,13 +2694,14 @@ private static void visitTemplateVariables(@NotNull PsiElement scope, @NotNull C
26942694
}
26952695
} else if (psiElement instanceof PsiComment) {
26962696
// {# @var foobar Class #}
2697+
// {# @var Class foobar #}
26972698
String text = psiElement.getText();
26982699

26992700
if(StringUtils.isNotBlank(text)) {
27002701
for (Pattern pattern : TwigTypeResolveUtil.INLINE_DOC_REGEX) {
27012702
Matcher matcher = pattern.matcher(text);
27022703
while (matcher.find()) {
2703-
consumer.consume(Pair.create(matcher.group(1), psiElement));
2704+
consumer.consume(Pair.create(matcher.group("var"), psiElement));
27042705
}
27052706
}
27062707
}

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/TwigTemplateGoToDeclarationHandlerTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public void testSimpleTestNavigationToExtension() {
136136

137137
public void testGetVarClassGoto() {
138138
assertNavigationMatch(TwigFileType.INSTANCE, "{# @var bar \\Date<caret>Time #}", PlatformPatterns.psiElement(PhpClass.class));
139+
assertNavigationMatch(TwigFileType.INSTANCE, "{# @var \\Date<caret>Time bar #}", PlatformPatterns.psiElement(PhpClass.class));
139140
}
140141

141142
public void testGetVarClassGotoDeprecated() {

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigTypeResolveUtilTest.java

+49
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package fr.adrienbrault.idea.symfony2plugin.tests.templating.util;
22

3+
import com.intellij.psi.PsiFile;
4+
import com.intellij.psi.PsiFileFactory;
35
import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
46
import com.jetbrains.php.lang.psi.elements.Method;
7+
import com.jetbrains.twig.TwigFile;
8+
import com.jetbrains.twig.TwigLanguage;
59
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil;
610
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
711
import org.jetbrains.annotations.NotNull;
812

13+
import java.util.Map;
14+
915
/**
1016
* @author Daniel Espendiller <daniel@espendiller.net>
1117
*/
@@ -17,6 +23,49 @@ public void testThatTwigGetAttributeSupportShortcuts() {
1723
assertEquals("foo", TwigTypeResolveUtil.getPropertyShortcutMethodName(createMethod("isFoo")));
1824
}
1925

26+
/**
27+
* @see TwigTypeResolveUtil#findFileVariableDocBlock
28+
*/
29+
public void testFindFileVariableDocBlock() {
30+
PsiFile fileFromText = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE, "" +
31+
"{# @var foo_1 \\AppBundle\\Entity\\MeterValueDTO #}\n" +
32+
"{# @var foo_2 \\AppBundle\\Entity\\MeterValueDTO[] #}\n" +
33+
"{# @var \\AppBundle\\Entity\\MeterValueDTO foo_3 #}\n" +
34+
"{# @var \\AppBundle\\Entity\\MeterValueDTO[] foo_4 #}\n" +
35+
"" +
36+
"{#\n" +
37+
"@var \\AppBundle\\Entity\\MeterValueDTO foo_5\n" +
38+
"@var foo_6 \\AppBundle\\Entity\\MeterValueDTO\n" +
39+
"#}\n"
40+
);
41+
42+
Map<String, String> fileVariableDocBlock = TwigTypeResolveUtil.findFileVariableDocBlock((TwigFile) fileFromText);
43+
44+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_1"));
45+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO[]", fileVariableDocBlock.get("foo_2"));
46+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_3"));
47+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO[]", fileVariableDocBlock.get("foo_4"));
48+
49+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_5"));
50+
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_6"));
51+
}
52+
53+
public void testReqExForInlineDocVariables() {
54+
assertMatches("{# @var foo_1 \\AppBundle\\Entity\\MeterValueDTO #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
55+
assertMatches("{# @var \\AppBundle\\Entity\\MeterValueDTO foo_1 #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
56+
assertMatches("{# foo_1 \\AppBundle\\Entity\\MeterValueDTO #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
57+
}
58+
59+
private void assertMatches(@NotNull String content, @NotNull String... regularExpressions) {
60+
for (String regularExpression : regularExpressions) {
61+
if(content.matches(regularExpression)) {
62+
return;
63+
}
64+
}
65+
66+
fail("invalid regular expression: " + content);
67+
}
68+
2069
@NotNull
2170
private Method createMethod(@NotNull String method) {
2271
return PhpPsiElementFactory.createFromText(

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigUtilTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ public void testVisitTemplateVariables() {
491491

492492
// file doc
493493
assertContainsElements(elementMap.keySet(), "inline_foo");
494+
assertContainsElements(elementMap.keySet(), "inline_foo_2");
494495

495496
// if
496497
assertContainsElements(elementMap.keySet(), "if_foo");

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/fixtures/variables.html.twig

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{% for foo in for_bar %}{% endfor %}
1818

1919
{# @var inline_foo Class #}
20+
{# @var Class inline_foo_2 #}
2021

2122
{% block my_block %}
2223
{% set my_block_set_foo = '' %}

0 commit comments

Comments
 (0)