3
3
import com .intellij .psi .PsiElement ;
4
4
import com .intellij .psi .PsiFile ;
5
5
import com .intellij .psi .PsiRecursiveElementWalkingVisitor ;
6
+ import com .intellij .psi .util .PsiTreeUtil ;
6
7
import com .intellij .util .indexing .*;
7
8
import com .intellij .util .io .DataExternalizer ;
8
9
import com .intellij .util .io .EnumeratorStringDescriptor ;
9
10
import com .intellij .util .io .KeyDescriptor ;
11
+ import com .jetbrains .php .lang .documentation .phpdoc .psi .tags .PhpDocTag ;
10
12
import com .jetbrains .php .lang .psi .PhpFile ;
13
+ import com .jetbrains .php .lang .psi .elements .Function ;
14
+ import com .jetbrains .php .lang .psi .elements .Method ;
11
15
import com .jetbrains .php .lang .psi .elements .MethodReference ;
12
16
import com .jetbrains .php .lang .psi .elements .StringLiteralExpression ;
13
17
import com .jetbrains .php .lang .psi .stubs .indexes .PhpConstantNameIndex ;
14
18
import fr .adrienbrault .idea .symfony2plugin .Symfony2ProjectComponent ;
15
19
import fr .adrienbrault .idea .symfony2plugin .TwigHelper ;
16
- import gnu .trove .THashMap ;
20
+ import fr .adrienbrault .idea .symfony2plugin .stubs .dict .TemplateUsage ;
21
+ import fr .adrienbrault .idea .symfony2plugin .stubs .indexes .externalizer .ObjectStreamDataExternalizer ;
22
+ import fr .adrienbrault .idea .symfony2plugin .util .AnnotationBackportUtil ;
17
23
import org .apache .commons .lang .StringUtils ;
18
24
import org .jetbrains .annotations .NotNull ;
19
25
20
- import java .util .HashSet ;
21
- import java .util .Map ;
22
- import java .util .Set ;
26
+ import java .util .*;
23
27
24
- public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension <String , Void > {
28
+ public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension <String , TemplateUsage > {
25
29
26
- public static final ID <String , Void > KEY = ID .create ("fr.adrienbrault.idea.symfony2plugin.twig_php_usage" );
30
+ public static final ID <String , TemplateUsage > KEY = ID .create ("fr.adrienbrault.idea.symfony2plugin.twig_php_usage" );
27
31
private final KeyDescriptor <String > myKeyDescriptor = new EnumeratorStringDescriptor ();
28
32
private static int MAX_FILE_BYTE_SIZE = 2097152 ;
33
+ private static ObjectStreamDataExternalizer <TemplateUsage > EXTERNALIZER = new ObjectStreamDataExternalizer <>();
29
34
30
35
public static Set <String > RENDER_METHODS = new HashSet <String >() {{
31
36
add ("render" );
@@ -35,38 +40,41 @@ public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension<Strin
35
40
36
41
@ NotNull
37
42
@ Override
38
- public ID <String , Void > getName () {
43
+ public ID <String , TemplateUsage > getName () {
39
44
return KEY ;
40
45
}
41
46
42
47
@ NotNull
43
48
@ Override
44
- public DataIndexer <String , Void , FileContent > getIndexer () {
45
- return new DataIndexer <String , Void , FileContent >() {
49
+ public DataIndexer <String , TemplateUsage , FileContent > getIndexer () {
50
+ return new DataIndexer <String , TemplateUsage , FileContent >() {
46
51
@ NotNull
47
52
@ Override
48
- public Map <String , Void > map (@ NotNull FileContent inputData ) {
49
- final Map <String , Void > map = new THashMap <>();
50
-
53
+ public Map <String , TemplateUsage > map (@ NotNull FileContent inputData ) {
51
54
PsiFile psiFile = inputData .getPsiFile ();
52
55
if (!Symfony2ProjectComponent .isEnabledForIndex (psiFile .getProject ())) {
53
- return map ;
56
+ return Collections . emptyMap () ;
54
57
}
55
58
56
59
if (!(inputData .getPsiFile () instanceof PhpFile ) && isValidForIndex (inputData )) {
57
- return map ;
60
+ return Collections . emptyMap () ;
58
61
}
59
62
63
+ Map <String , Set <String >> items = new HashMap <>();
64
+
60
65
psiFile .accept (new PsiRecursiveElementWalkingVisitor () {
66
+
61
67
@ Override
62
68
public void visitElement (PsiElement element ) {
63
69
if (element instanceof MethodReference ) {
64
70
visitMethodReference ((MethodReference ) element );
71
+ } else if (element instanceof PhpDocTag ) {
72
+ visitPhpDocTag ((PhpDocTag ) element );
65
73
}
66
74
super .visitElement (element );
67
75
}
68
76
69
- public void visitMethodReference (MethodReference methodReference ) {
77
+ private void visitMethodReference (@ NotNull MethodReference methodReference ) {
70
78
String methodName = methodReference .getName ();
71
79
if (!RENDER_METHODS .contains (methodName )) {
72
80
return ;
@@ -82,12 +90,60 @@ public void visitMethodReference(MethodReference methodReference) {
82
90
return ;
83
91
}
84
92
85
- map .put (TwigHelper .normalizeTemplateName (contents ), null );
93
+ Function parentOfType = PsiTreeUtil .getParentOfType (methodReference , Function .class );
94
+ if (parentOfType == null ) {
95
+ return ;
96
+ }
97
+
98
+ addTemplateWithScope (contents , StringUtils .stripStart (parentOfType .getFQN (), "\\ " ));
99
+ }
100
+
101
+ /**
102
+ * "@Template("foobar.html.twig")"
103
+ * "@Template(template="foobar.html.twig")"
104
+ */
105
+ private void visitPhpDocTag (@ NotNull PhpDocTag phpDocTag ) {
106
+ // "@var" and user non related tags dont need an action
107
+ if (AnnotationBackportUtil .NON_ANNOTATION_TAGS .contains (phpDocTag .getName ())) {
108
+ return ;
109
+ }
110
+
111
+ // init scope imports
112
+ Map <String , String > fileImports = AnnotationBackportUtil .getUseImportMap (phpDocTag );
113
+ if (fileImports .size () == 0 ) {
114
+ return ;
115
+ }
116
+
117
+ String annotationFqnName = AnnotationRoutesStubIndex .getClassNameReference (phpDocTag , fileImports );
118
+ if (!"Sensio\\ Bundle\\ FrameworkExtraBundle\\ Configuration\\ Template" .equals (StringUtils .stripStart (annotationFqnName , "\\ " ))) {
119
+ return ;
120
+ }
86
121
122
+ String template = AnnotationBackportUtil .getDefaultOrPropertyContents (phpDocTag , "template" );
123
+ if (template != null && template .endsWith (".html.twig" )) {
124
+ Method methodScope = AnnotationBackportUtil .getMethodScope (phpDocTag );
125
+ if (methodScope != null ) {
126
+ addTemplateWithScope (template , StringUtils .stripStart (methodScope .getFQN (), "\\ " ));
127
+ }
128
+ }
87
129
}
88
130
131
+ private void addTemplateWithScope (@ NotNull String contents , @ NotNull String fqn ) {
132
+ String s = TwigHelper .normalizeTemplateName (contents );
133
+ if (!items .containsKey (s )) {
134
+ items .put (s , new HashSet <>());
135
+ }
136
+
137
+ items .get (s ).add (fqn );
138
+ }
89
139
});
90
140
141
+ Map <String , TemplateUsage > map = new HashMap <>();
142
+
143
+ items .entrySet ().forEach (entry ->
144
+ map .put (entry .getKey (), new TemplateUsage (entry .getKey (), entry .getValue ()))
145
+ );
146
+
91
147
return map ;
92
148
}
93
149
};
@@ -101,8 +157,8 @@ public KeyDescriptor<String> getKeyDescriptor() {
101
157
102
158
@ NotNull
103
159
@ Override
104
- public DataExternalizer <Void > getValueExternalizer () {
105
- return ScalarIndexExtension . VOID_DATA_EXTERNALIZER ;
160
+ public DataExternalizer <TemplateUsage > getValueExternalizer () {
161
+ return EXTERNALIZER ;
106
162
}
107
163
108
164
@ NotNull
@@ -119,7 +175,7 @@ public boolean dependsOnFileContent() {
119
175
120
176
@ Override
121
177
public int getVersion () {
122
- return 2 ;
178
+ return 3 ;
123
179
}
124
180
125
181
public static boolean isValidForIndex (FileContent inputData ) {
0 commit comments