Skip to content

Commit 8ece464

Browse files
committedJan 1, 2020
Support Twig function navigation inside IF statement
1 parent ac80d4f commit 8ece464

File tree

3 files changed

+57
-73
lines changed

3 files changed

+57
-73
lines changed
 

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

+23-18
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,7 @@ public static ElementPattern<PsiElement> getPrintBlockOrTagFunctionPattern(Strin
158158
return PlatformPatterns
159159
.psiElement(TwigTokenTypes.STRING_TEXT)
160160
.withParent(
161-
PlatformPatterns.or(
162-
163-
// old and inconsistently implementations of FUNCTION_CALL:
164-
// eg {% if asset('') %} does not provide a FUNCTION_CALL whereas a print block does
165-
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),
166-
PlatformPatterns.psiElement(TwigElementTypes.TAG),
167-
PlatformPatterns.psiElement(TwigElementTypes.IF_TAG),
168-
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG),
169-
PlatformPatterns.psiElement(TwigElementTypes.ELSE_TAG),
170-
PlatformPatterns.psiElement(TwigElementTypes.ELSEIF_TAG),
171-
172-
// PhpStorm 2017.3.2: {{ asset('') }}
173-
PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
174-
)
161+
getFunctionCallScopePattern()
175162
)
176163
.afterLeafSkipping(
177164
PlatformPatterns.or(
@@ -567,13 +554,31 @@ public static ElementPattern<PsiElement> getEmbedPattern() {
567554
return getTagNameParameterPattern(TwigElementTypes.EMBED_TAG, "embed");
568555
}
569556

570-
public static ElementPattern<PsiElement> getPrintBlockFunctionPattern() {
571-
return PlatformPatterns.psiElement().withParent(PlatformPatterns.or(
557+
static ElementPattern<PsiElement> getPrintBlockFunctionPattern() {
558+
return PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withParent(getFunctionCallScopePattern()).withLanguage(TwigLanguage.INSTANCE);
559+
}
560+
561+
/**
562+
* Provide a workaround for getting a FUNCTION scope as it not consistent in all Twig elements
563+
*
564+
* {% if asset('') %}
565+
* {{ asset('') }}
566+
*/
567+
@NotNull
568+
private static ElementPattern<PsiElement> getFunctionCallScopePattern() {
569+
return PlatformPatterns.or(
570+
// old and inconsistently implementations of FUNCTION_CALL:
571+
// eg {% if asset('') %} does not provide a FUNCTION_CALL whereas a print block does
572572
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),
573+
PlatformPatterns.psiElement(TwigElementTypes.TAG),
574+
PlatformPatterns.psiElement(TwigElementTypes.IF_TAG),
575+
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG),
576+
PlatformPatterns.psiElement(TwigElementTypes.ELSE_TAG),
577+
PlatformPatterns.psiElement(TwigElementTypes.ELSEIF_TAG),
573578

574-
// PhpStorm 2017.3.2
579+
// PhpStorm 2017.3.2: {{ asset('') }}
575580
PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
576-
)).withLanguage(TwigLanguage.INSTANCE);
581+
);
577582
}
578583

579584
/**

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

+19-30
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class TwigTemplateGoToDeclarationHandler implements GotoDeclarationHandle
4949
@Nullable
5050
@Override
5151
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, Editor editor) {
52-
if(!Symfony2ProjectComponent.isEnabled(psiElement) || !PlatformPatterns.psiElement().withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {
52+
if (!Symfony2ProjectComponent.isEnabled(psiElement) || !PlatformPatterns.psiElement().withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {
5353
return null;
5454
}
5555

@@ -63,7 +63,7 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
6363
targets.addAll(getRouteParameterGoTo(psiElement));
6464
}
6565

66-
if(TwigPattern.getTemplateFileReferenceTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source").accepts(psiElement)) {
66+
if (TwigPattern.getTemplateFileReferenceTagPattern().accepts(psiElement) || TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source").accepts(psiElement)) {
6767
// support: {% include() %}, {{ include() }}
6868
targets.addAll(getTwigFiles(psiElement, offset));
6969
} else if (PlatformPatterns.psiElement(TwigTokenTypes.STRING_TEXT).withText(PlatformPatterns.string().endsWith(".twig")).accepts(psiElement)) {
@@ -72,15 +72,15 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
7272
targets.addAll(getTwigFiles(psiElement, offset));
7373
}
7474

75-
if(TwigPattern.getAutocompletableRoutePattern().accepts(psiElement)) {
75+
if (TwigPattern.getAutocompletableRoutePattern().accepts(psiElement)) {
7676
targets.addAll(getRouteGoTo(psiElement));
7777
}
7878

7979
// find trans('', {}, '|')
8080
// tricky way to get the function string trans(...)
8181
if (TwigPattern.getTransDomainPattern().accepts(psiElement)) {
8282
PsiElement psiElementTrans = PsiElementUtils.getPrevSiblingOfType(psiElement, PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER).withText(PlatformPatterns.string().oneOf("trans", "transchoice")));
83-
if(psiElementTrans != null && TwigUtil.getTwigMethodString(psiElementTrans) != null) {
83+
if (psiElementTrans != null && TwigUtil.getTwigMethodString(psiElementTrans) != null) {
8484
targets.addAll(getTranslationDomainGoto(psiElement));
8585
}
8686
}
@@ -95,15 +95,15 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
9595
targets.addAll(getTranslationKeyGoTo(psiElement));
9696
}
9797

98-
if(TwigPattern.getPrintBlockOrTagFunctionPattern("controller").accepts(psiElement) || TwigPattern.getStringAfterTagNamePattern("render").accepts(psiElement)) {
98+
if (TwigPattern.getPrintBlockOrTagFunctionPattern("controller").accepts(psiElement) || TwigPattern.getStringAfterTagNamePattern("render").accepts(psiElement)) {
9999
targets.addAll(getControllerGoTo(psiElement));
100100
}
101101

102-
if(TwigPattern.getTransDefaultDomainPattern().accepts(psiElement)) {
102+
if (TwigPattern.getTransDefaultDomainPattern().accepts(psiElement)) {
103103
targets.addAll(TranslationUtil.getDomainPsiFiles(psiElement.getProject(), psiElement.getText()));
104104
}
105105

106-
if(PlatformPatterns.or(TwigPattern.getFilterPattern(), TwigPattern.getApplyFilterPattern()).accepts(psiElement)) {
106+
if (PlatformPatterns.or(TwigPattern.getFilterPattern(), TwigPattern.getApplyFilterPattern()).accepts(psiElement)) {
107107
targets.addAll(getFilterGoTo(psiElement));
108108
}
109109

@@ -113,9 +113,12 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
113113
targets.addAll(getAfterIsToken(psiElement));
114114
}
115115

116-
// {{ goto_me() }}
116+
// {{ goto<caret>_me() }}
117+
// {% if goto<caret>_me() %}
118+
// {% set foo = foo<caret>_test() %}
117119
if (TwigPattern.getPrintBlockFunctionPattern().accepts(psiElement)) {
118120
targets.addAll(this.getMacros(psiElement));
121+
targets.addAll(this.getFunctions(psiElement));
119122
}
120123

121124
// {% from 'boo.html.twig' import goto_me %}
@@ -134,59 +137,45 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset,
134137
targets.addAll(getSets(psiElement));
135138
}
136139

137-
// {{ function( }}
138-
// {{ function }}
139-
if (PlatformPatterns
140-
.psiElement(TwigTokenTypes.IDENTIFIER)
141-
.withParent(PlatformPatterns.or(
142-
PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK),
143-
PlatformPatterns.psiElement(TwigElementTypes.SET_TAG),
144-
145-
PlatformPatterns.psiElement(TwigElementTypes.FUNCTION_CALL)
146-
)).withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {
147-
148-
targets.addAll(this.getFunctions(psiElement));
149-
}
150-
151140
// {{ foo.fo<caret>o }}
152-
if(TwigPattern.getTypeCompletionPattern().accepts(psiElement)
141+
if (TwigPattern.getTypeCompletionPattern().accepts(psiElement)
153142
|| TwigPattern.getPrintBlockFunctionPattern().accepts(psiElement)
154143
|| TwigPattern.getVariableTypePattern().accepts(psiElement))
155144
{
156145
targets.addAll(getTypeGoto(psiElement));
157146
}
158147

159-
if(TwigPattern.getTwigDocBlockMatchPattern(ControllerDocVariableCollector.DOC_PATTERN).accepts(psiElement)) {
148+
if (TwigPattern.getTwigDocBlockMatchPattern(ControllerDocVariableCollector.DOC_PATTERN).accepts(psiElement)) {
160149
targets.addAll(getControllerNameGoto(psiElement));
161150
}
162151

163152
// {{ parent() }}
164-
if(TwigPattern.getParentFunctionPattern().accepts(psiElement)) {
153+
if (TwigPattern.getParentFunctionPattern().accepts(psiElement)) {
165154
targets.addAll(getParentGoto(psiElement));
166155
}
167156

168157
// constant('Post::PUBLISHED')
169-
if(TwigPattern.getPrintBlockOrTagFunctionPattern("constant").accepts(psiElement)) {
158+
if (TwigPattern.getPrintBlockOrTagFunctionPattern("constant").accepts(psiElement)) {
170159
targets.addAll(getConstantGoto(psiElement));
171160
}
172161

173162
// {# @var user \Foo #}
174-
if(TwigPattern.getTwigTypeDocBlockPattern().accepts(psiElement)) {
163+
if (TwigPattern.getTwigTypeDocBlockPattern().accepts(psiElement)) {
175164
targets.addAll(getVarClassGoto(psiElement));
176165
}
177166

178167
// {# @see Foo.html.twig #}
179168
// {# @see \Class #}
180-
if(TwigPattern.getTwigDocSeePattern().accepts(psiElement)) {
169+
if (TwigPattern.getTwigDocSeePattern().accepts(psiElement)) {
181170
targets.addAll(getSeeDocTagTargets(psiElement));
182171
}
183172

184173
// {% FOO_TOKEN %}
185-
if(TwigPattern.getTagTokenBlockPattern().accepts(psiElement)) {
174+
if (TwigPattern.getTagTokenBlockPattern().accepts(psiElement)) {
186175
targets.addAll(getTokenTargets(psiElement));
187176
}
188177

189-
return targets.toArray(new PsiElement[targets.size()]);
178+
return targets.toArray(new PsiElement[0]);
190179
}
191180

192181
/**

‎src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/TwigTemplateGoToDeclarationHandlerTest.java

+15-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package fr.adrienbrault.idea.symfony2plugin.tests.templating;
22

3-
import com.intellij.openapi.application.ApplicationManager;
4-
import com.intellij.openapi.vfs.VfsUtil;
5-
import com.intellij.openapi.vfs.VirtualFile;
63
import com.intellij.patterns.PlatformPatterns;
74
import com.jetbrains.php.lang.psi.elements.Field;
85
import com.jetbrains.php.lang.psi.elements.Function;
@@ -13,9 +10,7 @@
1310
import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern;
1411
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
1512
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
16-
import org.jetbrains.annotations.NotNull;
1713

18-
import java.io.IOException;
1914
import java.util.regex.Pattern;
2015

2116
/**
@@ -197,32 +192,27 @@ public void testFunctionNavigation() {
197192
TwigFileType.INSTANCE,
198193
"{% set foo = foo<caret>_test() %}", PlatformPatterns.psiElement(Function.class).withName("foo_test")
199194
);
200-
}
201195

202-
public void testTokenTagNavigation() {
203196
assertNavigationMatch(
204197
TwigFileType.INSTANCE,
205-
"{% tag_<caret>foobar 'foo' %}", PlatformPatterns.psiElement()
198+
"{% if foo<caret>_test() %}{% endif %}", PlatformPatterns.psiElement(Function.class).withName("foo_test")
206199
);
207-
}
208200

209-
private void createWorkaroundFile(@NotNull String file, @NotNull String content) {
201+
assertNavigationMatch(
202+
TwigFileType.INSTANCE,
203+
"{% if %}{% else foo<caret>_test() %}{% endif %}", PlatformPatterns.psiElement(Function.class).withName("foo_test")
204+
);
210205

211-
try {
212-
createDummyFiles(file);
213-
} catch (Exception e) {
214-
e.printStackTrace();
215-
}
206+
assertNavigationMatch(
207+
TwigFileType.INSTANCE,
208+
"{% if %}{% elseif foo<caret>_test() %}{% endif %}", PlatformPatterns.psiElement(Function.class).withName("foo_test")
209+
);
210+
}
216211

217-
// build pseudo file with block
218-
final VirtualFile relativeFile = VfsUtil.findRelativeFile(getProject().getBaseDir(), file.split("/"));
219-
ApplicationManager.getApplication().runWriteAction(() -> {
220-
try {
221-
relativeFile.setBinaryContent(content.getBytes());
222-
} catch (IOException e2) {
223-
e2.printStackTrace();
224-
}
225-
relativeFile.refresh(false, false);
226-
});
212+
public void testTokenTagNavigation() {
213+
assertNavigationMatch(
214+
TwigFileType.INSTANCE,
215+
"{% tag_<caret>foobar 'foo' %}", PlatformPatterns.psiElement()
216+
);
227217
}
228218
}

0 commit comments

Comments
 (0)
Please sign in to comment.