Skip to content

Commit 0a00f8f

Browse files
committed
provide controller render navigation from PHP controller to its template
1 parent 58c4d36 commit 0a00f8f

File tree

4 files changed

+99
-45
lines changed

4 files changed

+99
-45
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package fr.adrienbrault.idea.symfony2plugin.navigation.controller;
2+
3+
import com.intellij.openapi.vfs.VirtualFile;
4+
import com.intellij.psi.PsiFile;
5+
import com.intellij.psi.search.GlobalSearchScope;
6+
import com.intellij.util.indexing.FileBasedIndex;
7+
import com.jetbrains.php.lang.psi.elements.Method;
8+
import com.jetbrains.php.lang.psi.elements.PhpClass;
9+
import com.jetbrains.twig.TwigFileType;
10+
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
11+
import fr.adrienbrault.idea.symfony2plugin.dic.RelatedPopupGotoLineMarker;
12+
import fr.adrienbrault.idea.symfony2plugin.extension.ControllerActionGotoRelatedCollector;
13+
import fr.adrienbrault.idea.symfony2plugin.extension.ControllerActionGotoRelatedCollectorParameter;
14+
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigControllerStubIndex;
15+
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
16+
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
17+
import icons.TwigIcons;
18+
import org.apache.commons.lang.StringUtils;
19+
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.HashSet;
23+
24+
/**
25+
* Attach "Foo\FoobarController::FooAction" to its controller
26+
*
27+
* @author Daniel Espendiller <[email protected]>
28+
*/
29+
public class TwigControllerUsageControllerRelatedGotoCollector implements ControllerActionGotoRelatedCollector {
30+
@Override
31+
public void collectGotoRelatedItems(ControllerActionGotoRelatedCollectorParameter parameter) {
32+
Method method = parameter.getMethod();
33+
PhpClass containingClass = method.getContainingClass();
34+
35+
if (containingClass == null) {
36+
return;
37+
}
38+
39+
String controllerAction = StringUtils.stripStart(containingClass.getPresentableFQN(), "\\") + "::" + method.getName();
40+
41+
Collection<VirtualFile> targets = new HashSet<>();
42+
FileBasedIndex.getInstance().getFilesWithKey(TwigControllerStubIndex.KEY, new HashSet<>(Collections.singletonList(controllerAction)), virtualFile -> {
43+
targets.add(virtualFile);
44+
return true;
45+
}, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(parameter.getProject()), TwigFileType.INSTANCE));
46+
47+
for (PsiFile psiFile: PsiElementUtils.convertVirtualFilesToPsiFiles(parameter.getProject(), targets)) {
48+
TwigUtil.visitControllerFunctions(psiFile, pair -> {
49+
if (pair.getFirst().equalsIgnoreCase(controllerAction)) {
50+
parameter.add(new RelatedPopupGotoLineMarker.PopupGotoRelatedItem(pair.getSecond()).withIcon(TwigIcons.TwigFileIcon, Symfony2Icons.TWIG_LINE_MARKER));
51+
}
52+
});
53+
}
54+
}
55+
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/TwigControllerStubIndex.java

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
package fr.adrienbrault.idea.symfony2plugin.stubs.indexes;
22

3-
import com.intellij.psi.PsiElement;
43
import com.intellij.psi.PsiFile;
5-
import com.intellij.psi.util.PsiTreeUtil;
64
import com.intellij.util.indexing.*;
75
import com.intellij.util.io.DataExternalizer;
86
import com.intellij.util.io.EnumeratorStringDescriptor;
97
import com.intellij.util.io.KeyDescriptor;
108
import com.intellij.util.io.VoidDataExternalizer;
119
import com.jetbrains.twig.TwigFile;
1210
import com.jetbrains.twig.TwigFileType;
13-
import com.jetbrains.twig.TwigTokenTypes;
14-
import com.jetbrains.twig.elements.TwigElementTypes;
1511
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
16-
import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern;
17-
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
12+
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
1813
import gnu.trove.THashMap;
19-
import org.apache.commons.lang.StringUtils;
2014
import org.jetbrains.annotations.NotNull;
2115

16+
import java.util.Collections;
2217
import java.util.Map;
2318

2419
/**
@@ -41,44 +36,13 @@ public ID<String, Void> getName() {
4136
@Override
4237
public DataIndexer<String, Void, FileContent> getIndexer() {
4338
return inputData -> {
44-
final Map<String, Void> map = new THashMap<>();
45-
4639
PsiFile psiFile = inputData.getPsiFile();
47-
if(!Symfony2ProjectComponent.isEnabledForIndex(psiFile.getProject())) {
48-
return map;
49-
}
50-
51-
if(!(psiFile instanceof TwigFile)) {
52-
return map;
53-
}
54-
55-
// collect: controller()
56-
PsiElement[] psiElements = PsiTreeUtil.collectElements(psiFile, psiElement -> {
57-
if (psiElement.getNode().getElementType() != TwigElementTypes.FUNCTION_CALL) {
58-
return false;
59-
}
60-
61-
PsiElement firstChild = psiElement.getFirstChild();
62-
if (firstChild.getNode().getElementType() != TwigTokenTypes.IDENTIFIER) {
63-
return false;
64-
}
65-
66-
return "controller".equalsIgnoreCase(firstChild.getText());
67-
});
68-
69-
// find parameter: controller("foobar::action")
70-
for (PsiElement functionCall: psiElements) {
71-
PsiElement includeTag = PsiElementUtils.getChildrenOfType(functionCall, TwigPattern.getPrintBlockOrTagFunctionPattern("controller"));
72-
if(includeTag != null) {
73-
String controllerName = includeTag.getText();
74-
if(StringUtils.isNotBlank(controllerName)) {
75-
// escaping
76-
// "Foobar\\Test"
77-
map.put(controllerName.replace("\\\\", "\\"), null);
78-
}
79-
}
40+
if(!Symfony2ProjectComponent.isEnabledForIndex(psiFile.getProject()) || !(psiFile instanceof TwigFile)) {
41+
return Collections.emptyMap();
8042
}
8143

44+
final Map<String, Void> map = new THashMap<>();
45+
TwigUtil.visitControllerFunctions(psiFile, pair -> map.put(pair.getFirst(), null));
8246
return map;
8347
};
8448

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2745,7 +2745,7 @@ public static Collection<LookupElement> getBlockLookupElements(@NotNull Project
27452745
return lookupElements;
27462746
}
27472747

2748-
public static void visitTemplateExtends(@NotNull TwigFile twigFile, Consumer<Pair<String, PsiElement>> consumer) {
2748+
public static void visitTemplateExtends(@NotNull TwigFile twigFile,@NotNull Consumer<Pair<String, PsiElement>> consumer) {
27492749
twigFile.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
27502750
@Override
27512751
public void visitElement(PsiElement element) {
@@ -2770,6 +2770,39 @@ public void visitElement(PsiElement element) {
27702770
});
27712771
}
27722772

2773+
/**
2774+
* Visit each "controller()" with its normalized parameter
2775+
*/
2776+
public static void visitControllerFunctions(@NotNull PsiFile psiFile,@NotNull Consumer<Pair<String, PsiElement>> consumer) {
2777+
//
2778+
PsiElement[] psiElements = PsiTreeUtil.collectElements(psiFile, psiElement -> {
2779+
if (psiElement.getNode().getElementType() != TwigElementTypes.FUNCTION_CALL) {
2780+
return false;
2781+
}
2782+
2783+
PsiElement firstChild = psiElement.getFirstChild();
2784+
if (firstChild.getNode().getElementType() != TwigTokenTypes.IDENTIFIER) {
2785+
return false;
2786+
}
2787+
2788+
return "controller".equalsIgnoreCase(firstChild.getText());
2789+
});
2790+
2791+
// find parameter: controller("foobar::action")
2792+
for (PsiElement functionCall: psiElements) {
2793+
PsiElement includeTag = PsiElementUtils.getChildrenOfType(functionCall, TwigPattern.getPrintBlockOrTagFunctionPattern("controller"));
2794+
if(includeTag != null) {
2795+
String controllerName = includeTag.getText();
2796+
if(StringUtils.isNotBlank(controllerName)) {
2797+
// escaping
2798+
// "Foobar\\Test"
2799+
String replace = StringUtils.stripStart(controllerName.replace("\\\\", "\\"), "\\");
2800+
consumer.consume(Pair.create(replace, includeTag));
2801+
}
2802+
}
2803+
}
2804+
}
2805+
27732806
public static class DomainScope {
27742807
@NotNull
27752808
private final String defaultDomain;

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/TwigControllerStubIndexTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ public void setUp() throws Exception {
1515
myFixture.configureByText(TwigFileType.INSTANCE, "" +
1616
"{{ controller('test::action') }}\n" +
1717
"{{ controller ('test::action2') }}\n" +
18-
"{{ controller ('te\\\\st::action3') }}\n"
18+
"{{ controller ('te\\\\st::action3') }}\n" +
19+
"{{ controller ('\\te\\\\st::action4') }}\n" +
20+
"{{ controller ('\\\\te\\\\st::action5') }}\n"
1921
);
2022
}
2123

2224
public void testTemplateIncludeIndexer() {
23-
assertIndexContains(TwigControllerStubIndex.KEY, "test::action", "test::action2", "te\\st::action3");
25+
assertIndexContains(TwigControllerStubIndex.KEY, "test::action", "test::action2", "te\\st::action3", "te\\st::action4", "te\\st::action5");
2426
}
2527
}

0 commit comments

Comments
 (0)