Skip to content

Commit fddd5f5

Browse files
committed
add template annotation usage indexer and add scope to reduce element visiting, also supporting function for scopes #773, #800
1 parent 03a03a3 commit fddd5f5

12 files changed

+421
-141
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package fr.adrienbrault.idea.symfony2plugin.stubs.dict;
2+
3+
import org.apache.commons.lang.builder.HashCodeBuilder;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
import java.io.Serializable;
7+
import java.util.Collection;
8+
import java.util.HashSet;
9+
import java.util.Objects;
10+
11+
/**
12+
* @author Daniel Espendiller <daniel@espendiller.net>
13+
*/
14+
public class TemplateUsage implements Serializable {
15+
@NotNull
16+
private String template;
17+
18+
@NotNull
19+
private Collection<String> scopes = new HashSet<>();
20+
21+
public TemplateUsage(@NotNull String template, @NotNull Collection<String> scopes) {
22+
this.template = template;
23+
this.scopes = scopes;
24+
}
25+
26+
@NotNull
27+
public String getTemplate() {
28+
return template;
29+
}
30+
31+
@NotNull
32+
public Collection<String> getScopes() {
33+
return scopes;
34+
}
35+
36+
@Override
37+
public int hashCode() {
38+
return new HashCodeBuilder()
39+
.append(this.template)
40+
.append(new HashSet<>(this.scopes))
41+
.toHashCode()
42+
;
43+
}
44+
45+
@Override
46+
public boolean equals(Object obj) {
47+
return obj instanceof TemplateUsage &&
48+
Objects.equals(((TemplateUsage) obj).getTemplate(), this.template) &&
49+
Objects.equals(new HashSet<>(((TemplateUsage) obj).getScopes()), new HashSet<>(this.scopes))
50+
;
51+
}
52+
}

src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/AnnotationRoutesStubIndex.java

+2-30
Original file line numberDiff line numberDiff line change
@@ -98,37 +98,9 @@ public int getVersion() {
9898
return 10;
9999
}
100100

101-
public static Map<String, String> getFileUseImports(PsiFile psiFile) {
102-
103-
// search for use alias in local file
104-
final Map<String, String> useImports = new HashMap<>();
105-
psiFile.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
106-
@Override
107-
public void visitElement(PsiElement element) {
108-
if(element instanceof PhpUse) {
109-
visitUse((PhpUse) element);
110-
}
111-
super.visitElement(element);
112-
}
113-
114-
private void visitUse(PhpUse phpUse) {
115-
String alias = phpUse.getAliasName();
116-
if(alias != null) {
117-
useImports.put(alias, phpUse.getFQN());
118-
} else {
119-
useImports.put(phpUse.getName(), phpUse.getFQN());
120-
}
121-
122-
}
123-
124-
});
125-
126-
return useImports;
127-
}
128-
129101
@Nullable
130102
public static String getClassNameReference(PhpDocTag phpDocTag) {
131-
return getClassNameReference(phpDocTag, getFileUseImports(phpDocTag.getContainingFile()));
103+
return getClassNameReference(phpDocTag, AnnotationBackportUtil.getUseImportMap(phpDocTag));
132104
}
133105

134106
@Nullable
@@ -194,7 +166,7 @@ public void visitPhpDocTag(PhpDocTag phpDocTag) {
194166

195167
// init file imports
196168
if(this.fileImports == null) {
197-
this.fileImports = getFileUseImports(phpDocTag.getContainingFile());
169+
this.fileImports = AnnotationBackportUtil.getUseImportMap(phpDocTag);
198170
}
199171

200172
if(this.fileImports.size() == 0) {

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

+75-19
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,34 @@
33
import com.intellij.psi.PsiElement;
44
import com.intellij.psi.PsiFile;
55
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
6+
import com.intellij.psi.util.PsiTreeUtil;
67
import com.intellij.util.indexing.*;
78
import com.intellij.util.io.DataExternalizer;
89
import com.intellij.util.io.EnumeratorStringDescriptor;
910
import com.intellij.util.io.KeyDescriptor;
11+
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
1012
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;
1115
import com.jetbrains.php.lang.psi.elements.MethodReference;
1216
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
1317
import com.jetbrains.php.lang.psi.stubs.indexes.PhpConstantNameIndex;
1418
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1519
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;
1723
import org.apache.commons.lang.StringUtils;
1824
import org.jetbrains.annotations.NotNull;
1925

20-
import java.util.HashSet;
21-
import java.util.Map;
22-
import java.util.Set;
26+
import java.util.*;
2327

24-
public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension<String, Void> {
28+
public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension<String, TemplateUsage> {
2529

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");
2731
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor();
2832
private static int MAX_FILE_BYTE_SIZE = 2097152;
33+
private static ObjectStreamDataExternalizer<TemplateUsage> EXTERNALIZER = new ObjectStreamDataExternalizer<>();
2934

3035
public static Set<String> RENDER_METHODS = new HashSet<String>() {{
3136
add("render");
@@ -35,38 +40,41 @@ public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension<Strin
3540

3641
@NotNull
3742
@Override
38-
public ID<String, Void> getName() {
43+
public ID<String, TemplateUsage> getName() {
3944
return KEY;
4045
}
4146

4247
@NotNull
4348
@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>() {
4651
@NotNull
4752
@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) {
5154
PsiFile psiFile = inputData.getPsiFile();
5255
if(!Symfony2ProjectComponent.isEnabledForIndex(psiFile.getProject())) {
53-
return map;
56+
return Collections.emptyMap();
5457
}
5558

5659
if(!(inputData.getPsiFile() instanceof PhpFile) && isValidForIndex(inputData)) {
57-
return map;
60+
return Collections.emptyMap();
5861
}
5962

63+
Map<String, Set<String>> items = new HashMap<>();
64+
6065
psiFile.accept(new PsiRecursiveElementWalkingVisitor() {
66+
6167
@Override
6268
public void visitElement(PsiElement element) {
6369
if(element instanceof MethodReference) {
6470
visitMethodReference((MethodReference) element);
71+
} else if(element instanceof PhpDocTag) {
72+
visitPhpDocTag((PhpDocTag) element);
6573
}
6674
super.visitElement(element);
6775
}
6876

69-
public void visitMethodReference(MethodReference methodReference) {
77+
private void visitMethodReference(@NotNull MethodReference methodReference) {
7078
String methodName = methodReference.getName();
7179
if(!RENDER_METHODS.contains(methodName)) {
7280
return;
@@ -82,12 +90,60 @@ public void visitMethodReference(MethodReference methodReference) {
8290
return;
8391
}
8492

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+
}
86121

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+
}
87129
}
88130

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+
}
89139
});
90140

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+
91147
return map;
92148
}
93149
};
@@ -101,8 +157,8 @@ public KeyDescriptor<String> getKeyDescriptor() {
101157

102158
@NotNull
103159
@Override
104-
public DataExternalizer<Void> getValueExternalizer() {
105-
return ScalarIndexExtension.VOID_DATA_EXTERNALIZER;
160+
public DataExternalizer<TemplateUsage> getValueExternalizer() {
161+
return EXTERNALIZER;
106162
}
107163

108164
@NotNull
@@ -119,7 +175,7 @@ public boolean dependsOnFileContent() {
119175

120176
@Override
121177
public int getVersion() {
122-
return 2;
178+
return 3;
123179
}
124180

125181
public static boolean isValidForIndex(FileContent inputData) {

src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/visitor/AnnotationElementWalkingVisitor.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212

1313
public class AnnotationElementWalkingVisitor extends PsiRecursiveElementWalkingVisitor {
1414

15+
@NotNull
1516
private final Processor<PhpDocTag> phpDocTagProcessor;
17+
1618
@NotNull
1719
private final String[] annotations;
18-
private Map<String, String> fileImports;
1920

2021
public AnnotationElementWalkingVisitor(@NotNull Processor<PhpDocTag> phpDocTagProcessor, @NotNull String... annotations) {
2122
this.phpDocTagProcessor = phpDocTagProcessor;
@@ -30,23 +31,19 @@ public void visitElement(PsiElement element) {
3031
super.visitElement(element);
3132
}
3233

33-
private void visitPhpDocTag(PhpDocTag phpDocTag) {
34+
private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) {
3435

3536
// "@var" and user non related tags dont need an action
3637
if(AnnotationBackportUtil.NON_ANNOTATION_TAGS.contains(phpDocTag.getName())) {
3738
return;
3839
}
3940

40-
// init file imports
41-
if(this.fileImports == null) {
42-
this.fileImports = AnnotationRoutesStubIndex.getFileUseImports(phpDocTag.getContainingFile());
43-
}
44-
45-
if(this.fileImports.size() == 0) {
41+
Map<String, String> fileImports = AnnotationBackportUtil.getUseImportMap(phpDocTag);
42+
if(fileImports.size() == 0) {
4643
return;
4744
}
4845

49-
String annotationFqnName = AnnotationRoutesStubIndex.getClassNameReference(phpDocTag, this.fileImports);
46+
String annotationFqnName = AnnotationRoutesStubIndex.getClassNameReference(phpDocTag, fileImports);
5047
for (String annotation : annotations) {
5148
if(annotation.equals(annotationFqnName)) {
5249
this.phpDocTagProcessor.process(phpDocTag);

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigControllerLineMarkerProvider.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.intellij.util.ConstantFunction;
1515
import com.intellij.util.indexing.FileBasedIndexImpl;
1616
import com.jetbrains.php.PhpIcons;
17+
import com.jetbrains.php.lang.psi.elements.Function;
1718
import com.jetbrains.php.lang.psi.elements.Method;
1819
import com.jetbrains.twig.TwigFile;
1920
import com.jetbrains.twig.TwigFileType;
@@ -93,7 +94,7 @@ public void collectSlowLineMarkers(@NotNull List<PsiElement> psiElements, @NotNu
9394

9495
private void attachController(@NotNull TwigFile twigFile, @NotNull Collection<? super RelatedItemLineMarkerInfo> result) {
9596

96-
Set<Method> methods = new HashSet<>();
97+
Set<Function> methods = new HashSet<>();
9798
Method method = TwigUtil.findTwigFileController(twigFile);
9899
if(method != null) {
99100
methods.add(method);

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class PhpMethodVariableResolveUtil {
1818
/**
1919
* search for twig template variable on common use cases
2020
*/
21-
public static Map<String, PsiVariable> collectMethodVariables(Method method) {
21+
public static Map<String, PsiVariable> collectMethodVariables(Function method) {
2222

2323
Map<String, PsiVariable> collectedTypes = new HashMap<>();
2424

@@ -45,7 +45,7 @@ public static Map<String, PsiVariable> collectMethodVariables(Method method) {
4545
/**
4646
* search for possible variables which are possible accessible inside rendered twig template
4747
*/
48-
private static List<PsiElement> collectPossibleTemplateArrays(Method method) {
48+
private static List<PsiElement> collectPossibleTemplateArrays(Function method) {
4949

5050
List<PsiElement> collectedTemplateVariables = new ArrayList<>();
5151

0 commit comments

Comments
 (0)