Skip to content

Commit 6a692ab

Browse files
authored
Merge pull request Haehnchen#1361 from Haehnchen/feature/template-var-context
support template name references resolve when complete / navigate to Twig variables on twig render() parameter
2 parents 665ead3 + 83585da commit 6a692ab

File tree

4 files changed

+101
-8
lines changed

4 files changed

+101
-8
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/RenderParameterGotoCompletionRegistrar.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,10 @@ public static Collection<String> getTemplatesForScope(@NotNull PsiElement psiEle
172172
// foobar('foo.html.twig', ['<caret>'])
173173
PsiElement prevSibling = arrayCreationElement.getPrevPsiSibling();
174174
if(prevSibling != null) {
175-
String stringValue = PhpElementsUtil.getStringValue(prevSibling);
176-
if(stringValue != null && stringValue.toLowerCase().endsWith(".twig")) {
177-
templates.add(
178-
TwigUtil.normalizeTemplateName(stringValue)
179-
);
175+
for (String stringValue : PhpElementsUtil.StringResolver.findStringValues(prevSibling)) {
176+
if(stringValue != null && stringValue.toLowerCase().endsWith(".twig")) {
177+
templates.add(TwigUtil.normalizeTemplateName(stringValue));
178+
}
180179
}
181180
}
182181
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java

+80
Original file line numberDiff line numberDiff line change
@@ -1458,4 +1458,84 @@ public static Set<Variable> visit(@NotNull PsiElement scope, @NotNull String nam
14581458
return visitor.variables;
14591459
}
14601460
}
1461+
1462+
1463+
/**
1464+
* Find string values based on a given PsiElement and its references
1465+
*
1466+
* - render(true === true ? 'foo.twig.html' : 'foobar.twig.html')
1467+
* - foo(self::foo), foo($var), foo($this->foo), ...
1468+
* - render($foo ?? 'foo.twig.html')
1469+
*/
1470+
public static class StringResolver {
1471+
public static Collection<String> findStringValues(@NotNull PsiElement psiElement) {
1472+
Collection<String> strings = new HashSet<>();
1473+
1474+
if (psiElement instanceof StringLiteralExpression) {
1475+
strings.add(resolveString((StringLiteralExpression) psiElement));
1476+
} else if(psiElement instanceof TernaryExpression) {
1477+
// render(true === true ? 'foo.twig.html' : 'foobar.twig.html')
1478+
for (PhpPsiElement phpPsiElement : new PhpPsiElement[]{((TernaryExpression) psiElement).getTrueVariant(), ((TernaryExpression) psiElement).getFalseVariant()}) {
1479+
if (phpPsiElement == null) {
1480+
continue;
1481+
}
1482+
1483+
if (phpPsiElement instanceof StringLiteralExpression) {
1484+
strings.add(resolveString((StringLiteralExpression) phpPsiElement));
1485+
} else if(phpPsiElement instanceof PhpReference) {
1486+
strings.add(resolvePhpReference((PhpReference) phpPsiElement));
1487+
}
1488+
}
1489+
} else if(psiElement instanceof PhpReference) {
1490+
// foo(self::foo)
1491+
// foo($this->foo)
1492+
// foo($var)
1493+
strings.add(resolvePhpReference((PhpReference) psiElement));
1494+
} else if(psiElement instanceof BinaryExpression) {
1495+
// render($foo ?? 'foo.twig.html')
1496+
PsiElement phpPsiElement = ((BinaryExpression) psiElement).getRightOperand();
1497+
1498+
if (phpPsiElement instanceof StringLiteralExpression) {
1499+
strings.add(resolveString((StringLiteralExpression) phpPsiElement));
1500+
} else if(phpPsiElement instanceof PhpReference) {
1501+
strings.add(resolvePhpReference((PhpReference) phpPsiElement));
1502+
}
1503+
}
1504+
1505+
return strings;
1506+
}
1507+
1508+
@Nullable
1509+
private static String resolveString(@NotNull StringLiteralExpression parameter) {
1510+
String contents = parameter.getContents();
1511+
return StringUtils.isBlank(contents) ? null : contents;
1512+
}
1513+
1514+
@Nullable
1515+
private static String resolvePhpReference(@NotNull PhpReference parameter) {
1516+
for (PhpNamedElement phpNamedElement : ((PhpReference) parameter).resolveLocal()) {
1517+
// foo(self::foo)
1518+
// foo($this->foo)
1519+
if (phpNamedElement instanceof Field) {
1520+
PsiElement defaultValue = ((Field) phpNamedElement).getDefaultValue();
1521+
if (defaultValue instanceof StringLiteralExpression) {
1522+
return resolveString((StringLiteralExpression) defaultValue);
1523+
}
1524+
}
1525+
1526+
// foo($var) => $var = 'test.html.twig'
1527+
if (phpNamedElement instanceof Variable) {
1528+
PsiElement assignmentExpression = phpNamedElement.getParent();
1529+
if (assignmentExpression instanceof AssignmentExpression) {
1530+
PhpPsiElement value = ((AssignmentExpression) assignmentExpression).getValue();
1531+
if (value instanceof StringLiteralExpression) {
1532+
return resolveString((StringLiteralExpression) value);
1533+
}
1534+
}
1535+
}
1536+
}
1537+
1538+
return null;
1539+
}
1540+
}
14611541
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/RenderParameterGotoCompletionRegistrarTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public void testTemplateNameExtractionForFunctionForFunctions() {
3636
assertContainsElements(RenderParameterGotoCompletionRegistrar.getTemplatesForScope(psiElement), "foo.html.twig");
3737
}
3838

39+
public void testSpecialReferencesTemplateNameResolve() {
40+
myFixture.configureByText(PhpFileType.INSTANCE, "<?php $var = 'foo.html.twig'; foo($var, ['<caret>']);");
41+
PsiElement psiElement = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
42+
assertContainsElements(RenderParameterGotoCompletionRegistrar.getTemplatesForScope(psiElement), "foo.html.twig");
43+
}
44+
3945
public void testTemplateNameExtractionForFunctionForFunctionsAsReturn() {
4046
myFixture.configureByText(PhpFileType.INSTANCE, "" +
4147
"<?php\n" +

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/util/PhpElementsUtilTest.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
import com.intellij.util.containers.ContainerUtil;
55
import com.jetbrains.php.lang.PhpFileType;
66
import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
7-
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
8-
import com.jetbrains.php.lang.psi.elements.PhpClass;
9-
import com.jetbrains.php.lang.psi.elements.Variable;
7+
import com.jetbrains.php.lang.psi.elements.*;
108
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
119
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
1210
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
@@ -293,4 +291,14 @@ public void testIsMethodInstanceOf() {
293291
new MethodMatcher.CallToSignature("FooBar\\Foo", "getBar"))
294292
);
295293
}
294+
295+
/**
296+
* @see PhpElementsUtil.StringResolver#findStringValues
297+
*/
298+
public void testThatPhpThatStringValueCanBeResolvedViaChainResolve() {
299+
assertContainsElements(PhpElementsUtil.StringResolver.findStringValues(PhpPsiElementFactory.createPhpPsiFromText(getProject(), StringLiteralExpression.class, "<?php foo('test.html');")), "test.html");
300+
assertContainsElements(PhpElementsUtil.StringResolver.findStringValues(PhpPsiElementFactory.createPhpPsiFromText(getProject(), BinaryExpression.class, "<?php foo($var ?? 'test.html');")), "test.html");
301+
assertContainsElements(PhpElementsUtil.StringResolver.findStringValues(PhpPsiElementFactory.createPhpPsiFromText(getProject(), ParameterList.class, "<?php $var = 'test.html'; foo($var);").getFirstPsiChild()), "test.html");
302+
assertContainsElements(PhpElementsUtil.StringResolver.findStringValues(PhpPsiElementFactory.createPhpPsiFromText(getProject(), TernaryExpression.class, "<?php $var = 'test.html'; $x = true == true ? $var : 'test2.html';")), "test.html", "test2.html");
303+
}
296304
}

0 commit comments

Comments
 (0)