Skip to content

Commit 65d5fa1

Browse files
authored
Merge pull request #2082 from pestretsov/indexing-performance
Speed up indexing: replace recursive visitors with linear scans
2 parents b344bc8 + 69868ca commit 65d5fa1

File tree

4 files changed

+145
-78
lines changed

4 files changed

+145
-78
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/DoctrineUtil.java

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@
88
import com.intellij.psi.xml.XmlAttribute;
99
import com.intellij.psi.xml.XmlFile;
1010
import com.intellij.psi.xml.XmlTag;
11+
import com.intellij.util.containers.ContainerUtil;
1112
import com.jetbrains.php.config.PhpLanguageLevel;
13+
import com.jetbrains.php.lang.documentation.phpdoc.PhpDocUtil;
1214
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocElementTypes;
1315
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
1416
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
1517
import com.jetbrains.php.lang.psi.PhpFile;
18+
import com.jetbrains.php.lang.psi.PhpPsiUtil;
19+
import com.jetbrains.php.lang.psi.elements.PhpAttribute;
1620
import com.jetbrains.php.lang.psi.elements.PhpClass;
21+
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
1722
import com.jetbrains.php.lang.psi.elements.PhpPsiElement;
1823
import com.jetbrains.php.refactoring.PhpNameUtil;
1924
import de.espend.idea.php.annotation.util.AnnotationUtil;
2025
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.visitor.AnnotationElementWalkingVisitor;
2126
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.visitor.AttributeElementWalkingVisitor;
27+
import fr.adrienbrault.idea.symfony2plugin.util.AnnotationBackportUtil;
28+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2229
import fr.adrienbrault.idea.symfony2plugin.util.PhpPsiAttributesUtil;
2330
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
2431
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
@@ -28,9 +35,7 @@
2835
import org.jetbrains.annotations.Nullable;
2936
import org.jetbrains.yaml.psi.*;
3037

31-
import java.util.ArrayList;
32-
import java.util.Collection;
33-
import java.util.Set;
38+
import java.util.*;
3439

3540
/**
3641
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -57,8 +62,8 @@ public static Collection<Pair<String, String>> getClassRepositoryPair(@NotNull P
5762
pairs = getClassRepositoryPair((XmlFile) psiFile);
5863
} else if(psiFile instanceof YAMLFile) {
5964
pairs = getClassRepositoryPair((YAMLFile) psiFile);
60-
} else if(psiFile instanceof PhpFile) {
61-
pairs = getClassRepositoryPair((PsiElement) psiFile);
65+
} else if(psiFile instanceof PhpFile phpFile) {
66+
pairs = getClassRepositoryPair(phpFile);
6267
}
6368

6469
return pairs;
@@ -114,44 +119,48 @@ private static Collection<Pair<String, String>> getClassRepositoryPair(@NotNull
114119
* We support multiple use case like orm an so on
115120
*/
116121
@NotNull
117-
public static Collection<Pair<String, String>> getClassRepositoryPair(@NotNull PsiElement phpFile) {
122+
public static Collection<Pair<String, String>> getClassRepositoryPair(@NotNull PhpFile phpFile) {
118123
final Collection<Pair<String, String>> pairs = new ArrayList<>();
124+
Collection<PhpClass> classes = PhpPsiUtil.findAllClasses(phpFile);
119125

120-
// Annotations:
121-
// @ORM\Entity("repositoryClass": YYY)
122-
phpFile.acceptChildren(new AnnotationElementWalkingVisitor(phpDocTag -> {
123-
PhpDocComment phpDocComment = PsiTreeUtil.getParentOfType(phpDocTag, PhpDocComment.class);
124-
if (phpDocComment == null) {
125-
return false;
126+
for (PhpClass phpClass : classes) {
127+
PhpDocComment docComment = phpClass.getDocComment();
128+
if (docComment != null) {
129+
pairs.addAll(extractAnnotations(phpClass, docComment));
126130
}
127-
128-
PhpPsiElement phpClass = phpDocComment.getNextPsiSibling();
129-
if (!(phpClass instanceof PhpClass phpClassScope)) {
130-
return false;
131+
for (PhpAttribute attribute : phpClass.getAttributes()) {
132+
String attributeFQN = attribute.getFQN();
133+
if (attributeFQN == null) continue;
134+
if (PhpElementsUtil.isEqualClassName(attributeFQN, MODEL_CLASS_ANNOTATION)) {
135+
String repositoryClass = PhpPsiAttributesUtil.getAttributeValueByNameAsString(attribute, "repositoryClass");
136+
pairs.add(Pair.create(StringUtils.stripStart(phpClass.getFQN(), "\\"),
137+
repositoryClass != null ? StringUtils.stripStart(repositoryClass, "\\") : null));
138+
}
131139
}
140+
}
132141

133-
pairs.add(Pair.create(
134-
phpClassScope.getPresentableFQN(),
135-
getAnnotationRepositoryClass(phpDocTag, phpClassScope))
136-
);
137-
138-
return false;
139-
}, MODEL_CLASS_ANNOTATION));
140-
141-
// Attributes:
142-
// #[Entity(repositoryClass: UserRepository::class)]
143-
phpFile.acceptChildren(new AttributeElementWalkingVisitor(pair -> {
144-
String repositoryClass = PhpPsiAttributesUtil.getAttributeValueByNameAsString(pair.getFirst(), "repositoryClass");
142+
return pairs;
143+
}
145144

146-
pairs.add(Pair.create(
147-
StringUtils.stripStart(pair.getSecond().getFQN(), "\\"),
148-
repositoryClass != null ? StringUtils.stripStart(repositoryClass, "\\") : null
149-
));
145+
public static Collection<Pair<String, String>> extractAnnotations(@NotNull PhpClass phpClass, @NotNull PhpDocComment docComment) {
146+
Collection<Pair<String, String>> result = new ArrayList<>();
147+
PhpDocUtil.processTagElementsByName(docComment, null, phpDocTag -> {
148+
if (AnnotationBackportUtil.NON_ANNOTATION_TAGS.contains(phpDocTag.getName())) {
149+
return true;
150+
}
150151

151-
return false;
152-
}, MODEL_CLASS_ANNOTATION));
152+
Map<String, String> fileImports = AnnotationBackportUtil.getUseImportMap(phpDocTag);
153+
if (fileImports.isEmpty()) {
154+
return true;
155+
}
153156

154-
return pairs;
157+
String annotationFqnName = AnnotationBackportUtil.getClassNameReference(phpDocTag, fileImports);
158+
if (ContainerUtil.exists(MODEL_CLASS_ANNOTATION, c -> c.equals(annotationFqnName))) {
159+
result.add(Pair.create(phpClass.getPresentableFQN(), getAnnotationRepositoryClass(phpDocTag, phpClass)));
160+
}
161+
return true;
162+
});
163+
return result;
155164
}
156165

157166
/**

src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/EntityHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public static PhpClass getEntityRepositoryClass(@NotNull Project project, @NotNu
9292
if(docAnnotation != null) {
9393
// search for repositoryClass="Foo\Bar\RegisterRepository"; repositoryClass=Foo\Bar\RegisterRepository::class
9494
// @MongoDB\Document; @ORM\Entity
95-
Collection<Pair<String, String>> classRepositoryPair = DoctrineUtil.getClassRepositoryPair(docAnnotation);
95+
Collection<Pair<String, String>> classRepositoryPair = DoctrineUtil.extractAnnotations(phpClass, docAnnotation);
9696
if (!classRepositoryPair.isEmpty()) {
9797
Pair<String, String> next = classRepositoryPair.iterator().next();
9898
if (next.getSecond() != null) {

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

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,25 @@
55
import com.intellij.util.io.DataExternalizer;
66
import com.intellij.util.io.EnumeratorStringDescriptor;
77
import com.intellij.util.io.KeyDescriptor;
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;
11+
import com.jetbrains.php.lang.documentation.phpdoc.PhpDocUtil;
12+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
813
import com.jetbrains.php.lang.psi.PhpFile;
14+
import com.jetbrains.php.lang.psi.elements.*;
915
import com.jetbrains.php.lang.psi.stubs.indexes.PhpConstantNameIndex;
1016
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1117
import fr.adrienbrault.idea.symfony2plugin.stubs.dict.TemplateUsage;
1218
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.externalizer.ObjectStreamDataExternalizer;
13-
import fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil;
19+
import kotlin.Triple;
1420
import org.apache.commons.lang.StringUtils;
1521
import org.jetbrains.annotations.NotNull;
1622

1723
import java.util.*;
24+
import java.util.function.Consumer;
25+
26+
import static fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil.TemplateRenderPsiRecursiveElementWalkingVisitor.*;
1827

1928
/**
2029
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -46,8 +55,7 @@ public DataIndexer<String, TemplateUsage, FileContent> getIndexer() {
4655
}
4756

4857
Map<String, Set<String>> items = new HashMap<>();
49-
50-
PhpMethodVariableResolveUtil.visitRenderTemplateFunctions(psiFile, triple -> {
58+
Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer = triple -> {
5159
String templateName = triple.getFirst();
5260

5361
if (templateName.length() > 255) {
@@ -59,7 +67,26 @@ public DataIndexer<String, TemplateUsage, FileContent> getIndexer() {
5967
}
6068

6169
items.get(templateName).add(StringUtils.stripStart(triple.getSecond().getFQN(), "\\"));
62-
});
70+
};
71+
Set<String> methods = collectMethods(psiFile.getProject());
72+
for (PhpNamedElement topLevelElement : ((PhpFile) psiFile).getTopLevelDefs().values()) {
73+
if (topLevelElement instanceof PhpClass clazz) {
74+
for (Method method : clazz.getOwnMethods()) {
75+
processMethodAttributes(method, consumer);
76+
PhpDocComment docComment = method.getDocComment();
77+
if (docComment != null) {
78+
PhpDocUtil.processTagElementsByName(docComment, null, docTag -> {
79+
processDocTag(docTag, consumer);
80+
return true;
81+
});
82+
}
83+
processMethodReferences(consumer, methods, method);
84+
}
85+
}
86+
if (topLevelElement instanceof Function function) {
87+
processMethodReferences(consumer, methods, function);
88+
}
89+
}
6390

6491
Map<String, TemplateUsage> map = new HashMap<>();
6592

@@ -71,6 +98,18 @@ public DataIndexer<String, TemplateUsage, FileContent> getIndexer() {
7198
};
7299
}
73100

101+
private static void processMethodReferences(Consumer<Triple<String, PhpNamedElement, FunctionReference>> consumer, Set<String> methods, Function function) {
102+
PhpControlFlowUtil.processFlow(function.getControlFlow(), new PhpInstructionProcessor() {
103+
@Override
104+
public boolean processPhpCallInstruction(PhpCallInstruction instruction) {
105+
if (instruction.getFunctionReference() instanceof MethodReference methodReference) {
106+
processMethodReference(methodReference, methods, consumer);
107+
}
108+
return super.processPhpCallInstruction(instruction);
109+
}
110+
});
111+
}
112+
74113
@NotNull
75114
@Override
76115
public KeyDescriptor<String> getKeyDescriptor() {

0 commit comments

Comments
 (0)