1515import fr .adrienbrault .idea .symfony2plugin .templating .variable .dict .PsiVariable ;
1616import fr .adrienbrault .idea .symfony2plugin .util .AnnotationBackportUtil ;
1717import fr .adrienbrault .idea .symfony2plugin .util .PhpElementsUtil ;
18+ import fr .adrienbrault .idea .symfony2plugin .util .PhpPsiAttributesUtil ;
1819import fr .adrienbrault .idea .symfony2plugin .util .PsiElementUtils ;
1920import kotlin .Triple ;
2021import org .apache .commons .lang .StringUtils ;
@@ -244,6 +245,10 @@ public static void visitRenderTemplateFunctions(@NotNull Method method, @NotNull
244245 psiElementVisitor .visitPhpDocTag (phpDocTag );
245246 }
246247
248+ for (PhpAttributesList phpAttributesList : PsiTreeUtil .getChildrenOfTypeAsList (method , PhpAttributesList .class )) {
249+ psiElementVisitor .visitPhpAttribute (phpAttributesList );
250+ }
251+
247252 method .accept (psiElementVisitor );
248253 }
249254
@@ -271,10 +276,36 @@ public void visitElement(@NotNull PsiElement element) {
271276 visitMethodReference ((MethodReference ) element );
272277 } else if (element instanceof PhpDocTag ) {
273278 visitPhpDocTag ((PhpDocTag ) element );
279+ } else if (element instanceof PhpAttributesList ) {
280+ visitPhpAttribute ((PhpAttributesList ) element );
274281 }
275282 super .visitElement (element );
276283 }
277284
285+ private void visitPhpAttribute (@ NotNull PhpAttributesList phpAttributesList ) {
286+ Collection <@ NotNull PhpAttribute > attributes = phpAttributesList .getAttributes (TwigUtil .TEMPLATE_ANNOTATION_CLASS );
287+ for (PhpAttribute attribute : attributes ) {
288+ if (attribute .getArguments ().isEmpty ()) {
289+ // #[@Template()]
290+ PsiElement parent = phpAttributesList .getParent ();
291+ if (parent instanceof Method ) {
292+ visitMethodForGuessing ((Method ) parent );
293+ }
294+ } else {
295+ // [@Template("foobar.html.twig")]
296+ // #[@Template(template: "foobar.html.twig")]
297+ String template = PhpPsiAttributesUtil .getAttributeValueByNameAsStringWithDefaultParameterFallback (attribute , "template" );
298+ if (StringUtils .isNotBlank (template )) {
299+ PsiElement parent = phpAttributesList .getParent ();
300+ if (parent instanceof Method ) {
301+ addTemplateWithScope (template , (Method ) parent , null );
302+
303+ }
304+ }
305+ }
306+ }
307+ }
308+
278309 private void visitMethodReference (@ NotNull MethodReference methodReference ) {
279310 String methodName = methodReference .getName ();
280311 if (methodName == null ) {
@@ -409,23 +440,7 @@ private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
409440 // App\Controller\MyNiceController::myAction => my_nice/my.html.twig
410441 Method methodScope = AnnotationBackportUtil .getMethodScope (phpDocTag );
411442 if (methodScope != null ) {
412- PhpClass phpClass = methodScope .getContainingClass ();
413- if (phpClass != null ) {
414- // App\Controller\ "MyNice" Controller
415- Matcher matcher = Pattern .compile ("Controller\\ \\ (.+)Controller$" , Pattern .MULTILINE ).matcher (StringUtils .stripStart (phpClass .getFQN (), "\\ " ));
416- if (matcher .find ()){
417- String group = underscore (matcher .group (1 ).replace ("\\ " , "/" ));
418- String name = methodScope .getName ();
419-
420- // __invoke is using controller as template name
421- if (name .equals ("__invoke" )) {
422- addTemplateWithScope (group + ".html.twig" , methodScope , null );
423- } else {
424- String action = name .endsWith ("Action" ) ? name .substring (0 , name .length () - "Action" .length ()) : name ;
425- addTemplateWithScope (group + "/" + underscore (action ) + ".html.twig" , methodScope , null );
426- }
427- }
428- }
443+ visitMethodForGuessing (methodScope );
429444 }
430445 } else if (template .endsWith (".twig" )) {
431446 Method methodScope = AnnotationBackportUtil .getMethodScope (phpDocTag );
@@ -435,6 +450,26 @@ private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
435450 }
436451 }
437452
453+ private void visitMethodForGuessing (@ NotNull Method methodScope ) {
454+ PhpClass phpClass = methodScope .getContainingClass ();
455+ if (phpClass != null ) {
456+ // App\Controller\ "MyNice" Controller
457+ Matcher matcher = Pattern .compile ("Controller\\ \\ (.+)Controller$" , Pattern .MULTILINE ).matcher (StringUtils .stripStart (phpClass .getFQN (), "\\ " ));
458+ if (matcher .find ()){
459+ String group = underscore (matcher .group (1 ).replace ("\\ " , "/" ));
460+ String name = methodScope .getName ();
461+
462+ // __invoke is using controller as template name
463+ if (name .equals ("__invoke" )) {
464+ addTemplateWithScope (group + ".html.twig" , methodScope , null );
465+ } else {
466+ String action = name .endsWith ("Action" ) ? name .substring (0 , name .length () - "Action" .length ()) : name ;
467+ addTemplateWithScope (group + "/" + underscore (action ) + ".html.twig" , methodScope , null );
468+ }
469+ }
470+ }
471+ }
472+
438473 private void addTemplateWithScope (@ NotNull String contents , @ NotNull PhpNamedElement scope , @ Nullable FunctionReference functionReference ) {
439474 String s = TwigUtil .normalizeTemplateName (contents );
440475 consumer .accept (new Triple <>(s , scope , functionReference ));
0 commit comments