Skip to content

improve Twig types support for inline type declaration {# @var Class variable #} and {# @var variable Class #} #1035 #1114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
Expand Down Expand Up @@ -872,10 +875,13 @@ public static ElementPattern<PsiElement> getTypeCompletionPattern() {
}

public static ElementPattern<PsiComment> getTwigTypeDocBlockPattern() {
return PlatformPatterns.or(
PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(TwigTypeResolveUtil.DEPRECATED_DOC_TYPE_PATTERN)).withLanguage(TwigLanguage.INSTANCE),
PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE)).withLanguage(TwigLanguage.INSTANCE)
);
Collection<ElementPattern> patterns = new ArrayList<>();

for (String s : TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE) {
patterns.add(PlatformPatterns.psiComment().withText(PlatformPatterns.string().matches(s)).withLanguage(TwigLanguage.INSTANCE));
}

return PlatformPatterns.or(patterns.toArray(new ElementPattern[patterns.size()]));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,12 @@ private Collection<PsiElement> getConstantGoto(@NotNull PsiElement psiElement) {
return targetPsiElements;
}

/**
* Extract class from inline variables
*
* {# @var \AppBundle\Entity\Foo variable #}
* {# @var variable \AppBundle\Entity\Foo #}
*/
@NotNull
private Collection<PhpClass> getVarClassGoto(@NotNull PsiElement psiElement) {
String comment = psiElement.getText();
Expand All @@ -352,10 +358,10 @@ private Collection<PhpClass> getVarClassGoto(@NotNull PsiElement psiElement) {
return Collections.emptyList();
}

for(String pattern: new String[] {TwigTypeResolveUtil.DEPRECATED_DOC_TYPE_PATTERN, TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE}) {
for(String pattern: TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE) {
Matcher matcher = Pattern.compile(pattern).matcher(comment);
if (matcher.find()) {
String className = matcher.group(2);
String className = matcher.group("class");
if(StringUtils.isNotBlank(className)) {
return PhpElementsUtil.getClassesInterface(psiElement.getProject(), className);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,35 @@
*/
public class TwigTypeResolveUtil {

public static final String DEPRECATED_DOC_TYPE_PATTERN = "\\{#[\\s]+([\\w]+)[\\s]+([\\w\\\\\\[\\]]+)[\\s]+#}";
public static final String DOC_TYPE_PATTERN = "@var[\\s]+([\\w]+)[\\s]+([\\w\\\\\\[\\]]+)[\\s]*";
/**
* {# variable \AppBundle\Entity\Foo[] #}
*/
public static final String DEPRECATED_DOC_TYPE_PATTERN = "\\{#[\\s]+(?<var>[\\w]+)[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]+#}";

/**
* {# @var variable \AppBundle\Entity\Foo[] #}
* {# @var variable \AppBundle\Entity\Foo #}
*/
private static final String DOC_TYPE_PATTERN_CLASS_SECOND = "@var[\\s]+(?<var>[\\w]+)[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]*";

/**
* {# @var \AppBundle\Entity\Foo[] variable #}
* {# @var \AppBundle\Entity\Foo variable #}
*/
private static final String DOC_TYPE_PATTERN_CLASS_FIRST = "@var[\\s]+(?<class>[\\w\\\\\\[\\]]+)[\\s]+(?<var>[\\w]+)[\\s]*";

public static final Pattern[] INLINE_DOC_REGEX = {
Pattern.compile(DOC_TYPE_PATTERN, Pattern.MULTILINE),
Pattern.compile(DOC_TYPE_PATTERN_CLASS_SECOND, Pattern.MULTILINE),
Pattern.compile(DOC_TYPE_PATTERN_CLASS_FIRST, Pattern.MULTILINE),
Pattern.compile(DEPRECATED_DOC_TYPE_PATTERN),
};

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

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

Expand Down Expand Up @@ -192,7 +211,7 @@ private static Map<String, String> getInlineCommentDocsVars(@NotNull PsiElement
for (Pattern pattern : INLINE_DOC_REGEX) {
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
variables.put(matcher.group(1), matcher.group(2));
variables.put(matcher.group("var"), matcher.group("class"));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2682,13 +2682,14 @@ private static void visitTemplateVariables(@NotNull PsiElement scope, @NotNull C
}
} else if (psiElement instanceof PsiComment) {
// {# @var foobar Class #}
// {# @var Class foobar #}
String text = psiElement.getText();

if(StringUtils.isNotBlank(text)) {
for (Pattern pattern : TwigTypeResolveUtil.INLINE_DOC_REGEX) {
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
consumer.consume(Pair.create(matcher.group(1), psiElement));
consumer.consume(Pair.create(matcher.group("var"), psiElement));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public void testSimpleTestNavigationToExtension() {

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

public void testGetVarClassGotoDeprecated() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package fr.adrienbrault.idea.symfony2plugin.tests.templating.util;

import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.twig.TwigFile;
import com.jetbrains.twig.TwigLanguage;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;

import java.util.Map;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
Expand All @@ -17,6 +23,49 @@ public void testThatTwigGetAttributeSupportShortcuts() {
assertEquals("foo", TwigTypeResolveUtil.getPropertyShortcutMethodName(createMethod("isFoo")));
}

/**
* @see TwigTypeResolveUtil#findFileVariableDocBlock
*/
public void testFindFileVariableDocBlock() {
PsiFile fileFromText = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE, "" +
"{# @var foo_1 \\AppBundle\\Entity\\MeterValueDTO #}\n" +
"{# @var foo_2 \\AppBundle\\Entity\\MeterValueDTO[] #}\n" +
"{# @var \\AppBundle\\Entity\\MeterValueDTO foo_3 #}\n" +
"{# @var \\AppBundle\\Entity\\MeterValueDTO[] foo_4 #}\n" +
"" +
"{#\n" +
"@var \\AppBundle\\Entity\\MeterValueDTO foo_5\n" +
"@var foo_6 \\AppBundle\\Entity\\MeterValueDTO\n" +
"#}\n"
);

Map<String, String> fileVariableDocBlock = TwigTypeResolveUtil.findFileVariableDocBlock((TwigFile) fileFromText);

assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_1"));
assertEquals("\\AppBundle\\Entity\\MeterValueDTO[]", fileVariableDocBlock.get("foo_2"));
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_3"));
assertEquals("\\AppBundle\\Entity\\MeterValueDTO[]", fileVariableDocBlock.get("foo_4"));

assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_5"));
assertEquals("\\AppBundle\\Entity\\MeterValueDTO", fileVariableDocBlock.get("foo_6"));
}

public void testReqExForInlineDocVariables() {
assertMatches("{# @var foo_1 \\AppBundle\\Entity\\MeterValueDTO #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
assertMatches("{# @var \\AppBundle\\Entity\\MeterValueDTO foo_1 #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
assertMatches("{# foo_1 \\AppBundle\\Entity\\MeterValueDTO #}", TwigTypeResolveUtil.DOC_TYPE_PATTERN_SINGLE);
}

private void assertMatches(@NotNull String content, @NotNull String... regularExpressions) {
for (String regularExpression : regularExpressions) {
if(content.matches(regularExpression)) {
return;
}
}

fail("invalid regular expression: " + content);
}

@NotNull
private Method createMethod(@NotNull String method) {
return PhpPsiElementFactory.createFromText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ public void testVisitTemplateVariables() {

// file doc
assertContainsElements(elementMap.keySet(), "inline_foo");
assertContainsElements(elementMap.keySet(), "inline_foo_2");

// if
assertContainsElements(elementMap.keySet(), "if_foo");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{% for foo in for_bar %}{% endfor %}

{# @var inline_foo Class #}
{# @var Class inline_foo_2 #}

{% block my_block %}
{% set my_block_set_foo = '' %}
Expand Down