diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java index 71e729473d87..9b5f8288af04 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java @@ -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) { diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java index ddbde9bb7138..efa15f14c9e3 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java @@ -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 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 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 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 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 iterator() { - return myWords.iterator(); - } - } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/WordOptimizer.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/WordOptimizer.java index bc32b2c7cdcd..f0b528d9a482 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/WordOptimizer.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/WordOptimizer.java @@ -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; diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/XmlCompilingVisitor.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/XmlCompilingVisitor.java index 93aa04994831..8280a93a0db4 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/XmlCompilingVisitor.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/XmlCompilingVisitor.java @@ -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); } } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/OptimizedSearchScanTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/OptimizedSearchScanTest.java index 117c3b04a791..6c5dc8d67480 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/OptimizedSearchScanTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/OptimizedSearchScanTest.java @@ -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); + } }