11package fr .adrienbrault .idea .symfony2plugin .templating .util ;
22
33import com .intellij .openapi .project .Project ;
4+ import com .intellij .openapi .util .NotNullLazyValue ;
45import com .intellij .openapi .util .Pair ;
56import com .intellij .psi .PsiElement ;
6- import com .intellij .psi .PsiRecursiveElementWalkingVisitor ;
77import 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 ;
811import com .jetbrains .php .lang .documentation .phpdoc .psi .PhpDocComment ;
912import com .jetbrains .php .lang .documentation .phpdoc .psi .tags .PhpDocTag ;
1013import 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