Skip to content

Commit d634474

Browse files
committed
fully replace recursive template method scanning
1 parent 2a40cde commit d634474

File tree

2 files changed

+40
-75
lines changed

2 files changed

+40
-75
lines changed

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

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

3+
import com.intellij.openapi.util.NotNullLazyValue;
34
import com.intellij.psi.PsiFile;
45
import com.intellij.util.indexing.*;
56
import com.intellij.util.io.DataExternalizer;
@@ -16,24 +17,23 @@
1617
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1718
import fr.adrienbrault.idea.symfony2plugin.stubs.dict.TemplateUsage;
1819
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.externalizer.ObjectStreamDataExternalizer;
20+
import fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil;
1921
import kotlin.Triple;
2022
import org.apache.commons.lang.StringUtils;
2123
import org.jetbrains.annotations.NotNull;
2224

2325
import java.util.*;
2426
import java.util.function.Consumer;
2527

26-
import static fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil.TemplateRenderPsiRecursiveElementWalkingVisitor.*;
27-
2828
/**
2929
* @author Daniel Espendiller <daniel@espendiller.net>
3030
*/
3131
public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension<String, TemplateUsage> {
3232

3333
public static final ID<String, TemplateUsage> KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.twig_php_usage");
3434
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor();
35-
private static int MAX_FILE_BYTE_SIZE = 2097152;
36-
private static ObjectStreamDataExternalizer<TemplateUsage> EXTERNALIZER = new ObjectStreamDataExternalizer<>();
35+
private static final int MAX_FILE_BYTE_SIZE = 2097152;
36+
private static final ObjectStreamDataExternalizer<TemplateUsage> EXTERNALIZER = new ObjectStreamDataExternalizer<>();
3737

3838
@NotNull
3939
@Override
@@ -68,15 +68,16 @@ public DataIndexer<String, TemplateUsage, FileContent> getIndexer() {
6868

6969
items.get(templateName).add(StringUtils.stripStart(triple.getSecond().getFQN(), "\\"));
7070
};
71-
Set<String> methods = collectMethods(psiFile.getProject());
71+
72+
@NotNull NotNullLazyValue<Set<String>> methods = PhpMethodVariableResolveUtil.TemplateRenderVisitor.createLazyMethodNamesCollector(psiFile.getProject());
7273
for (PhpNamedElement topLevelElement : ((PhpFile) psiFile).getTopLevelDefs().values()) {
7374
if (topLevelElement instanceof PhpClass clazz) {
7475
for (Method method : clazz.getOwnMethods()) {
75-
processMethodAttributes(method, consumer);
76+
PhpMethodVariableResolveUtil.TemplateRenderVisitor.processMethodAttributes(method, consumer);
7677
PhpDocComment docComment = method.getDocComment();
7778
if (docComment != null) {
7879
PhpDocUtil.processTagElementsByName(docComment, null, docTag -> {
79-
processDocTag(docTag, consumer);
80+
PhpMethodVariableResolveUtil.TemplateRenderVisitor.processDocTag(docTag, consumer);
8081
return true;
8182
});
8283
}
@@ -98,12 +99,12 @@ public DataIndexer<String, TemplateUsage, FileContent> getIndexer() {
9899
};
99100
}
100101

101-
private static void processMethodReferences(Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer, Set<String> methods, Function function) {
102+
private static void processMethodReferences(@NotNull Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer, @NotNull NotNullLazyValue<Set<String>> methods, @NotNull Function function) {
102103
PhpControlFlowUtil.processFlow(function.getControlFlow(), new PhpInstructionProcessor() {
103104
@Override
104105
public boolean processPhpCallInstruction(PhpCallInstruction instruction) {
105106
if (instruction.getFunctionReference() instanceof MethodReference methodReference) {
106-
processMethodReference(methodReference, methods, consumer);
107+
PhpMethodVariableResolveUtil.TemplateRenderVisitor.processMethodReference(methodReference, methods, consumer);
107108
}
108109
return super.processPhpCallInstruction(instruction);
109110
}

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

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package fr.adrienbrault.idea.symfony2plugin.templating.util;
22

33
import com.intellij.openapi.project.Project;
4+
import com.intellij.openapi.util.NotNullLazyValue;
45
import com.intellij.openapi.util.Pair;
56
import com.intellij.psi.PsiElement;
6-
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
77
import com.intellij.psi.util.PsiTreeUtil;
8+
import com.jetbrains.php.codeInsight.controlFlow.PhpControlFlowUtil;
9+
import com.jetbrains.php.codeInsight.controlFlow.PhpInstructionProcessor;
10+
import com.jetbrains.php.codeInsight.controlFlow.instructions.PhpCallInstruction;
811
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
912
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
1013
import com.jetbrains.php.lang.parser.PhpElementTypes;
@@ -238,57 +241,32 @@ public static Map<String, PsiVariable> getTypesOnArrayHash(@NotNull ArrayCreatio
238241
*
239242
* As annotations are not in scope of the method itself
240243
*/
241-
public static void visitRenderTemplateFunctions(@NotNull Method method, @NotNull Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
242-
TemplateRenderPsiRecursiveElementWalkingVisitor psiElementVisitor = new TemplateRenderPsiRecursiveElementWalkingVisitor(method, consumer);
243-
244-
PhpDocComment docComment = method.getDocComment();
244+
public static void visitRenderTemplateFunctions(@NotNull Function function, @NotNull Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
245+
PhpDocComment docComment = function.getDocComment();
245246
for (PhpDocTag phpDocTag : PsiTreeUtil.getChildrenOfTypeAsList(docComment, PhpDocTag.class)) {
246-
psiElementVisitor.visitPhpDocTag(phpDocTag);
247-
}
248-
249-
for (PhpAttributesList phpAttributesList : PsiTreeUtil.getChildrenOfTypeAsList(method, PhpAttributesList.class)) {
250-
psiElementVisitor.visitPhpAttribute(phpAttributesList);
247+
TemplateRenderVisitor.processDocTag(phpDocTag, consumer);
251248
}
252249

253-
method.accept(psiElementVisitor);
254-
}
255-
256-
/**
257-
* Visit all possible elements for render clements, scope shop be the class or a file itself
258-
*/
259-
public static void visitRenderTemplateFunctions(@NotNull PsiElement context, @NotNull Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
260-
context.accept(new TemplateRenderPsiRecursiveElementWalkingVisitor(context, consumer));
261-
}
262-
263-
public static class TemplateRenderPsiRecursiveElementWalkingVisitor extends PsiRecursiveElementWalkingVisitor {
264-
private final PsiElement context;
265-
private final Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer;
266-
private Set<String> methods;
267-
268-
TemplateRenderPsiRecursiveElementWalkingVisitor(PsiElement context, Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
269-
this.context = context;
270-
this.consumer = consumer;
271-
methods = null;
272-
}
273-
274-
@Override
275-
public void visitElement(@NotNull PsiElement element) {
276-
if(element instanceof MethodReference) {
277-
visitMethodReference((MethodReference) element);
278-
} else if(element instanceof PhpDocTag) {
279-
visitPhpDocTag((PhpDocTag) element);
280-
} else if(element instanceof PhpAttributesList) {
281-
visitPhpAttribute((PhpAttributesList) element);
250+
for (PhpAttributesList phpAttributesList : PsiTreeUtil.getChildrenOfTypeAsList(function, PhpAttributesList.class)) {
251+
if (phpAttributesList.getParent() instanceof Method phpAttributeMethod) {
252+
TemplateRenderVisitor.processMethodAttributes(phpAttributeMethod, consumer);
282253
}
283-
super.visitElement(element);
284254
}
285255

286-
private void visitPhpAttribute(@NotNull PhpAttributesList phpAttributesList) {
287-
if (phpAttributesList.getParent() instanceof Method method) {
288-
processMethodAttributes(method, consumer);
256+
NotNullLazyValue<Set<String>> lazyMethodNamesCollector = TemplateRenderVisitor.createLazyMethodNamesCollector(function.getProject());
257+
258+
PhpControlFlowUtil.processFlow(function.getControlFlow(), new PhpInstructionProcessor() {
259+
@Override
260+
public boolean processPhpCallInstruction(PhpCallInstruction instruction) {
261+
if (instruction.getFunctionReference() instanceof MethodReference methodReference) {
262+
TemplateRenderVisitor.processMethodReference(methodReference, lazyMethodNamesCollector, consumer);
263+
}
264+
return super.processPhpCallInstruction(instruction);
289265
}
290-
}
266+
});
267+
}
291268

269+
public static class TemplateRenderVisitor {
292270
public static void processMethodAttributes(@NotNull Method method, Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
293271
Collection<@NotNull PhpAttribute> attributes = method.getAttributes(TwigUtil.TEMPLATE_ANNOTATION_CLASS);
294272
for (PhpAttribute attribute : attributes) {
@@ -306,22 +284,8 @@ public static void processMethodAttributes(@NotNull Method method, Consumer<Trip
306284
}
307285
}
308286

309-
private void visitMethodReference(@NotNull MethodReference methodReference) {
310-
String methodName = methodReference.getName();
311-
if (methodName == null) {
312-
return;
313-
}
314-
315-
// init methods once per file
316-
if(methods == null) {
317-
methods = collectMethods(context.getProject());
318-
}
319-
320-
processMethodReference(methodReference, methods, consumer);
321-
}
322-
323287
@NotNull
324-
public static Set<String> collectMethods(Project project) {
288+
private static Set<String> collectMethodInner(@NotNull Project project) {
325289
Set<String> methods = new HashSet<>();
326290

327291
PluginConfigurationExtension[] extensions = Symfony2ProjectComponent.PLUGIN_CONFIGURATION_EXTENSION.getExtensions();
@@ -336,13 +300,17 @@ public static Set<String> collectMethods(Project project) {
336300
return methods;
337301
}
338302

339-
public static void processMethodReference(@NotNull MethodReference methodReference, Set<String> methods, Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
303+
public static @NotNull NotNullLazyValue<Set<String>> createLazyMethodNamesCollector(@NotNull Project project) {
304+
return NotNullLazyValue.lazy(() -> collectMethodInner(project));
305+
}
306+
307+
public static void processMethodReference(@NotNull MethodReference methodReference, NotNullLazyValue<Set<String>> methods, Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
340308
String methodName = methodReference.getName();
341309
if (methodName == null) {
342310
return;
343311
}
344312

345-
if(!methods.contains(methodName) && !methodName.toLowerCase().contains("render")) {
313+
if(!methods.get().contains(methodName) && !methodName.toLowerCase().contains("render")) {
346314
return;
347315
}
348316

@@ -432,12 +400,8 @@ private static void addStringLiteralScope(@NotNull MethodReference methodReferen
432400
* "@Template("foobar.html.twig")"
433401
* "@Template(template="foobar.html.twig")"
434402
*/
435-
private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
436-
processDocTag(phpDocTag, consumer);
437-
}
438-
439403
public static void processDocTag(@NotNull PhpDocTag phpDocTag, Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer) {
440-
// "@var" and user non related tags dont need an action
404+
// "@var" and user non-related tags don't need an action
441405
if(AnnotationBackportUtil.NON_ANNOTATION_TAGS.contains(phpDocTag.getName())) {
442406
return;
443407
}

0 commit comments

Comments
 (0)