mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
SSR: better search plan for faster and more correct index search
This commit is contained in:
+39
-25
@@ -31,6 +31,8 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor.OccurenceKind.*;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
@@ -64,7 +66,7 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
@Override
|
||||
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
|
||||
final String word = reference.getReferenceName();
|
||||
if (!handleWord(word, myCompilingVisitor.getContext())) return;
|
||||
if (!handleWord(word, CODE, myCompilingVisitor.getContext())) return;
|
||||
if (reference.isQualified() && isClassFromJavaLangPackage(reference.resolve())) return;
|
||||
super.visitReferenceElement(reference);
|
||||
}
|
||||
@@ -83,38 +85,64 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMethod(PsiMethod method) {
|
||||
if (!handleWord(method.getName(), myCompilingVisitor.getContext())) return;
|
||||
if (!handleWord(method.getName(), CODE, myCompilingVisitor.getContext())) return;
|
||||
super.visitMethod(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVariable(PsiVariable variable) {
|
||||
if (!handleWord(variable.getName(), myCompilingVisitor.getContext())) return;
|
||||
if (!handleWord(variable.getName(), CODE, myCompilingVisitor.getContext())) return;
|
||||
super.visitVariable(variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCatchSection(PsiCatchSection section) {
|
||||
final PsiParameter parameter = section.getParameter();
|
||||
if (parameter != null && !handleWord(parameter.getName(), CODE, myCompilingVisitor.getContext())) return;
|
||||
super.visitCatchSection(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {
|
||||
if (!handleWord(aClass.getName(), myCompilingVisitor.getContext())) return;
|
||||
final CompileContext context = myCompilingVisitor.getContext();
|
||||
if (!handleWord(aClass.getName(), CODE, context)) return;
|
||||
if (aClass.isInterface()) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.INTERFACE, true, CODE, context);
|
||||
}
|
||||
else if (aClass.isEnum()) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.ENUM, true, CODE, context);
|
||||
}
|
||||
else {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.INTERFACE, false, CODE, context);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.ENUM, false, CODE, context);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.CLASS, true, CODE, context);
|
||||
}
|
||||
super.visitClass(aClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLiteralExpression(PsiLiteralExpression expression) {
|
||||
final PsiType type = expression.getType();
|
||||
if (PsiType.BOOLEAN.equals(type) || PsiType.NULL.equals(type)) {
|
||||
// don't search index for literals of other types, as they can be written in many many kinds of ways for the same value.
|
||||
if (!handleWord(expression.getText(), CODE, myCompilingVisitor.getContext())) return;
|
||||
}
|
||||
super.visitLiteralExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
super.visitElement(element);
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord("::", true, GlobalCompilingVisitor.OccurenceKind.CODE,
|
||||
myCompilingVisitor.getContext());
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord("::", true, CODE, myCompilingVisitor.getContext());
|
||||
}
|
||||
else if (element instanceof PsiLambdaExpression) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord("->", true, GlobalCompilingVisitor.OccurenceKind.CODE,
|
||||
myCompilingVisitor.getContext());
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord("->", true, CODE, myCompilingVisitor.getContext());
|
||||
}
|
||||
else if (element instanceof PsiKeyword) {
|
||||
final String keyword = element.getText();
|
||||
if (!excludedKeywords.contains(keyword)) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(keyword, true, GlobalCompilingVisitor.OccurenceKind.CODE,
|
||||
myCompilingVisitor.getContext());
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(keyword, true, CODE, myCompilingVisitor.getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +252,7 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
|
||||
if (StringUtil.isQuotedString(text)) {
|
||||
@Nullable MatchingHandler handler =
|
||||
myCompilingVisitor.processPatternStringWithFragments(text, GlobalCompilingVisitor.OccurenceKind.LITERAL);
|
||||
myCompilingVisitor.processPatternStringWithFragments(text, LITERAL);
|
||||
|
||||
if (PsiType.CHAR.equals(expression.getType()) &&
|
||||
(handler instanceof LiteralWithSubstitutionHandler || handler == null && expression.getValue() == null)) {
|
||||
@@ -464,20 +492,6 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
}
|
||||
|
||||
GlobalCompilingVisitor.setFilter(handler, ClassFilter.getInstance());
|
||||
|
||||
if (!(handler instanceof SubstitutionHandler) || ((SubstitutionHandler)handler).getMinOccurs() > 0) {
|
||||
if (psiClass.isInterface()) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.INTERFACE, true, GlobalCompilingVisitor.OccurenceKind.CODE, context);
|
||||
}
|
||||
else if (psiClass.isEnum()) {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.ENUM, true, GlobalCompilingVisitor.OccurenceKind.CODE, context);
|
||||
}
|
||||
else {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.INTERFACE, false, GlobalCompilingVisitor.OccurenceKind.CODE, context);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.ENUM, false, GlobalCompilingVisitor.OccurenceKind.CODE, context);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(PsiKeyword.CLASS, true, GlobalCompilingVisitor.OccurenceKind.CODE, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createAndSetSubstitutionHandlerFromReference(final PsiElement expr, final String referenceText, boolean classQualifier) {
|
||||
|
||||
+4
-64
@@ -2,6 +2,7 @@
|
||||
package com.intellij.structuralsearch.impl.matcher.compiler;
|
||||
|
||||
import com.intellij.dupLocator.util.NodeFilter;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.structuralsearch.MalformedPatternException;
|
||||
import com.intellij.structuralsearch.StructuralSearchProfile;
|
||||
@@ -31,6 +32,7 @@ public class GlobalCompilingVisitor {
|
||||
@NonNls private static final String SUBSTITUTION_PATTERN_STR = "\\b(__\\$_\\w+)\\b";
|
||||
private static final Pattern ourSubstitutionPattern = Pattern.compile(SUBSTITUTION_PATTERN_STR);
|
||||
private static final Set<String> ourReservedWords = new HashSet<>(Arrays.asList(MODIFIER_ANNOTATION_NAME, INSTANCE_MODIFIER_NAME));
|
||||
private static final NodeFilter ourFilter = LexicalNodesFilter.getInstance();
|
||||
|
||||
static {
|
||||
for (StructuralSearchProfile profile : StructuralSearchProfile.EP_NAME.getExtensionList()) {
|
||||
@@ -38,16 +40,10 @@ public class GlobalCompilingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern ourAlternativePattern = Pattern.compile("^\\((.+)\\)$");
|
||||
@NonNls private static final String WORD_SEARCH_PATTERN_STR = ".*?\\b(.+?)\\b.*?";
|
||||
static final Pattern ourWordSearchPattern = Pattern.compile(WORD_SEARCH_PATTERN_STR);
|
||||
private CompileContext context;
|
||||
private final List<PsiElement> myLexicalNodes = new SmartList<>();
|
||||
|
||||
private int myCodeBlockLevel;
|
||||
|
||||
private static final NodeFilter ourFilter = LexicalNodesFilter.getInstance();
|
||||
|
||||
public static NodeFilter getFilter() {
|
||||
return ourFilter;
|
||||
}
|
||||
@@ -243,68 +239,12 @@ public class GlobalCompilingVisitor {
|
||||
}
|
||||
|
||||
public void processTokenizedName(String name, boolean skipComments, GlobalCompilingVisitor.OccurenceKind kind) {
|
||||
WordTokenizer tokenizer = new WordTokenizer(name);
|
||||
for (Iterator<String> i = tokenizer.iterator(); i.hasNext();) {
|
||||
String nextToken = i.next();
|
||||
if (skipComments &&
|
||||
(nextToken.equals("/*") || nextToken.equals("/**") || nextToken.equals("*/") || nextToken.equals("*") || nextToken.equals("//"))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher matcher = ourAlternativePattern.matcher(nextToken);
|
||||
if (matcher.matches()) {
|
||||
StringTokenizer alternatives = new StringTokenizer(matcher.group(1), "|");
|
||||
while (alternatives.hasMoreTokens()) {
|
||||
addFilesToSearchForGivenWord(alternatives.nextToken(), !alternatives.hasMoreTokens(), kind, getContext());
|
||||
}
|
||||
}
|
||||
else {
|
||||
addFilesToSearchForGivenWord(nextToken, true, kind, getContext());
|
||||
}
|
||||
for (String word : StringUtil.getWordsInStringLongestFirst(name)) {
|
||||
addFilesToSearchForGivenWord(word, true, kind, getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public enum OccurenceKind {
|
||||
LITERAL, COMMENT, CODE, TEXT
|
||||
}
|
||||
|
||||
private static class WordTokenizer {
|
||||
private final List<String> myWords = new SmartList<>();
|
||||
|
||||
WordTokenizer(String text) {
|
||||
final StringTokenizer tokenizer = new StringTokenizer(text);
|
||||
Matcher matcher = null;
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String nextToken = tokenizer.nextToken();
|
||||
if (matcher == null) {
|
||||
matcher = ourWordSearchPattern.matcher(nextToken);
|
||||
}
|
||||
else {
|
||||
matcher.reset(nextToken);
|
||||
}
|
||||
|
||||
nextToken = (matcher.matches()) ? matcher.group(1) : nextToken;
|
||||
int lastWordStart = 0;
|
||||
int i;
|
||||
for (i = 0; i < nextToken.length(); ++i) {
|
||||
if (!Character.isJavaIdentifierStart(nextToken.charAt(i))) {
|
||||
if (i != lastWordStart) {
|
||||
myWords.add(nextToken.substring(lastWordStart, i));
|
||||
}
|
||||
lastWordStart = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != lastWordStart) {
|
||||
myWords.add(nextToken.substring(lastWordStart, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<String> iterator() {
|
||||
return myWords.iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -11,8 +11,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor.OccurenceKind.CODE;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
@@ -20,9 +18,10 @@ public interface WordOptimizer {
|
||||
|
||||
/**
|
||||
* @param text text to check index with
|
||||
* @param kind LITERAL, COMMENT, CODE or TEXT
|
||||
* @return true, if psi tree should be processed deeper, false otherwise.
|
||||
*/
|
||||
default boolean handleWord(@Nullable String text, CompileContext compileContext) {
|
||||
default boolean handleWord(@Nullable String text, GlobalCompilingVisitor.OccurenceKind kind, CompileContext compileContext) {
|
||||
final OptimizingSearchHelper searchHelper = compileContext.getSearchHelper();
|
||||
if (!searchHelper.doOptimizing()) {
|
||||
return false;
|
||||
@@ -35,7 +34,7 @@ public interface WordOptimizer {
|
||||
if (pattern.isTypedVar(word)) {
|
||||
final SubstitutionHandler handler = (SubstitutionHandler)pattern.getHandler(word);
|
||||
if (handler == null || handler.getMinOccurs() == 0) {
|
||||
// don't call super
|
||||
// don't call super visit so psi tree is not processed deeper
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,12 +48,12 @@ public interface WordOptimizer {
|
||||
searchHelper.endTransaction();
|
||||
}
|
||||
else {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(predicate.getRegExp(), true, CODE, compileContext);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(predicate.getRegExp(), true, kind, compileContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(word, true, CODE, compileContext);
|
||||
GlobalCompilingVisitor.addFilesToSearchForGivenWord(word, true, kind, compileContext);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
+5
-6
@@ -9,6 +9,7 @@ import com.intellij.structuralsearch.impl.matcher.CompiledPattern;
|
||||
import com.intellij.structuralsearch.impl.matcher.filters.TagValueFilter;
|
||||
import com.intellij.structuralsearch.impl.matcher.handlers.TopLevelMatchingHandler;
|
||||
|
||||
import static com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor.OccurenceKind.CODE;
|
||||
import static com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor.OccurenceKind.TEXT;
|
||||
|
||||
/**
|
||||
@@ -35,23 +36,21 @@ public class XmlCompilingVisitor extends XmlRecursiveElementVisitor {
|
||||
|
||||
@Override
|
||||
public void visitXmlTag(XmlTag tag) {
|
||||
if (!handleWord(tag.getName(), myCompilingVisitor.getContext())) return;
|
||||
if (!handleWord(tag.getName(), CODE, myCompilingVisitor.getContext())) return;
|
||||
super.visitXmlTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitXmlAttribute(XmlAttribute attribute) {
|
||||
if (!handleWord(attribute.getName(), myCompilingVisitor.getContext())) return;
|
||||
handleWord(attribute.getValue(), myCompilingVisitor.getContext());
|
||||
if (!handleWord(attribute.getName(), CODE, myCompilingVisitor.getContext())) return;
|
||||
handleWord(attribute.getValue(), CODE, myCompilingVisitor.getContext());
|
||||
super.visitXmlAttribute(attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitXmlText(XmlText text) {
|
||||
final String string = text.getText();
|
||||
if (!myCompilingVisitor.getContext().getPattern().isTypedVar(string)) {
|
||||
myCompilingVisitor.processTokenizedName(string, false, TEXT);
|
||||
}
|
||||
handleWord(string, TEXT, myCompilingVisitor.getContext());
|
||||
super.visitXmlText(text);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-11
@@ -37,7 +37,7 @@ public class OptimizedSearchScanTest extends StructuralSearchTestCase {
|
||||
"class C {" +
|
||||
" void '_m{0,1} () throws OMGWTFBBQException {}" +
|
||||
"}");
|
||||
assertEquals("exception should not be in plan", "[in code:class|in code:enum|in code:interface][in code:C]", plan);
|
||||
assertEquals("exception should not be in plan", "[in code:C][in code:class|in code:enum|in code:interface]", plan);
|
||||
|
||||
final String plan2 = findWordsToBeUsedWhenSearchingFor(
|
||||
"class C {" +
|
||||
@@ -47,17 +47,17 @@ public class OptimizedSearchScanTest extends StructuralSearchTestCase {
|
||||
" }" +
|
||||
"}");
|
||||
assertEquals("throws should not be in plan",
|
||||
"[in code:class|in code:enum|in code:interface][in code:C][in code:m][in code:String][in code:println][in code:out]" +
|
||||
"[in code:System][in code:return]",
|
||||
"[in code:C][in code:class|in code:enum|in code:interface][in code:m][in code:String][in code:println][in code:out]" +
|
||||
"[in code:System][in code:return][in code:null]",
|
||||
plan2);
|
||||
}
|
||||
|
||||
public void testExtendsImplements() {
|
||||
final String plan1 = findWordsToBeUsedWhenSearchingFor("class A extends '_B{0,0} {}");
|
||||
assertEquals("extends should not be in plan", "[in code:class|in code:enum|in code:interface][in code:A]", plan1);
|
||||
assertEquals("extends should not be in plan", "[in code:A][in code:class|in code:enum|in code:interface]", plan1);
|
||||
|
||||
final String plan2 = findWordsToBeUsedWhenSearchingFor("class B implements '_I{0,0} {}");
|
||||
assertEquals("implements should not be in plan", "[in code:class|in code:enum|in code:interface][in code:B]", plan2);
|
||||
assertEquals("implements should not be in plan", "[in code:B][in code:class|in code:enum|in code:interface]", plan2);
|
||||
}
|
||||
|
||||
public void testLambda() {
|
||||
@@ -81,23 +81,24 @@ public class OptimizedSearchScanTest extends StructuralSearchTestCase {
|
||||
|
||||
public void testClasses() {
|
||||
final String plan1 = findWordsToBeUsedWhenSearchingFor("class A {}");
|
||||
assertEquals("[in code:class|in code:enum|in code:interface][in code:A]", plan1);
|
||||
assertEquals("[in code:A][in code:class|in code:enum|in code:interface]", plan1);
|
||||
final String plan2 = findWordsToBeUsedWhenSearchingFor("interface I {}");
|
||||
assertEquals("[in code:interface][in code:I]", plan2);
|
||||
assertEquals("[in code:I][in code:interface]", plan2);
|
||||
final String plan3 = findWordsToBeUsedWhenSearchingFor("enum E {}");
|
||||
assertEquals("[in code:enum][in code:E]", plan3);
|
||||
assertEquals("[in code:E][in code:enum]", plan3);
|
||||
}
|
||||
|
||||
public void testDescendants() {
|
||||
final String plan = findWordsToBeUsedWhenSearchingFor("class '_A:*List {}");
|
||||
assertEquals("classes outside search scope should alse be added to descendants plan",
|
||||
"[in code:class|in code:enum|in code:interface][in code:AbstractList|in code:AbstractSequentialList|in code:ArrayList|" +
|
||||
"[in code:AbstractList|in code:AbstractSequentialList|in code:ArrayList|" +
|
||||
"in code:CheckedList|in code:CheckedRandomAccessList|in code:CopiesList|in code:EmptyList|in code:List|" +
|
||||
"in code:SingletonList|in code:SubList|in code:SynchronizedList|in code:SynchronizedRandomAccessList|" +
|
||||
"in code:UnmodifiableList|in code:UnmodifiableRandomAccessList]", plan);
|
||||
"in code:UnmodifiableList|in code:UnmodifiableRandomAccessList]" +
|
||||
"[in code:class|in code:enum|in code:interface]", plan);
|
||||
|
||||
final String plan2 = findWordsToBeUsedWhenSearchingFor("enum '_E:*Zyxwvuts {}");
|
||||
assertEquals("non-existing class name should be added to plan", "[in code:enum][in code:Zyxwvuts]", plan2);
|
||||
assertEquals("non-existing class name should be added to plan", "[in code:Zyxwvuts][in code:enum]", plan2);
|
||||
}
|
||||
|
||||
public void testQualifiedReference() {
|
||||
@@ -107,4 +108,28 @@ public class OptimizedSearchScanTest extends StructuralSearchTestCase {
|
||||
final String plan2 = findWordsToBeUsedWhenSearchingFor("new java.lang.reflect.InvocationTargetException('_x)");
|
||||
assertEquals("[in code:new][in code:InvocationTargetException][in code:reflect][in code:lang][in code:java]", plan2);
|
||||
}
|
||||
|
||||
public void testTryWithoutCatch() {
|
||||
final String plan = findWordsToBeUsedWhenSearchingFor("try {" +
|
||||
" '_st*;" +
|
||||
"} catch ('_Type '_exception{0,0}) {" +
|
||||
" '_st2*;" +
|
||||
"}");
|
||||
assertEquals("[in code:try]", plan);
|
||||
}
|
||||
|
||||
public void testComment() {
|
||||
final String plan = findWordsToBeUsedWhenSearchingFor("/* one/two (3|4|5) */");
|
||||
assertEquals("[in comments:one][in comments:two][in comments:3][in comments:4][in comments:5]", plan);
|
||||
}
|
||||
|
||||
public void testPackageLocal() {
|
||||
final String plan = findWordsToBeUsedWhenSearchingFor("@Modifier(\"packageLocal\") '_FieldType '_Field = '_Init?;");
|
||||
assertEquals("", plan);
|
||||
}
|
||||
|
||||
public void testLiterals() {
|
||||
final String plan = findWordsToBeUsedWhenSearchingFor("assert '_exp != null && true: \"'_exp is null\";");
|
||||
assertEquals("[in literals:null][in literals:is][in code:assert][in code:null][in code:true]", plan);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user