From 530edc975b3ebcdd561fe47715d68fc59d77af6f Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 5 Jul 2012 18:56:26 +0200 Subject: [PATCH] suggest start match classes first --- .../completion/AllClassesGetter.java | 31 +++--- .../completion/PlainPrefixMatcher.java | 10 +- .../codeInsight/completion/PrefixMatcher.java | 4 + .../completion/impl/CamelHumpMatcher.java | 11 ++- .../impl/CompletionServiceImpl.java | 99 ++++++++++--------- .../psi/codeStyle/MinusculeMatcher.java | 14 ++- 6 files changed, 103 insertions(+), 66 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java index d1e14e6d7c36..0acdaadf5c34 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java @@ -139,34 +139,39 @@ public class AllClassesGetter { } }; - public static void processJavaClasses(CompletionParameters parameters, + public static void processJavaClasses(final CompletionParameters parameters, final PrefixMatcher prefixMatcher, final boolean filterByScope, final Consumer consumer) { final PsiElement context = parameters.getPosition(); - final String packagePrefix = getPackagePrefix(context, parameters.getOffset()); - - final Set qnames = new THashSet(); - final Project project = context.getProject(); final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(project); - final boolean pkgContext = JavaCompletionUtil.inSomePackage(context); - AllClassesSearch.search(scope, project, new Condition() { - public boolean value(String s) { - return prefixMatcher.prefixMatches(s); - } - }).forEach(new Processor() { + Processor processor = new Processor() { + final Set qNames = new THashSet(); + final boolean pkgContext = JavaCompletionUtil.inSomePackage(context); + final String packagePrefix = getPackagePrefix(context, parameters.getOffset()); + public boolean process(PsiClass psiClass) { assert psiClass != null; if (isAcceptableInContext(context, psiClass, filterByScope, pkgContext)) { String qName = psiClass.getQualifiedName(); - if (qName != null && qName.startsWith(packagePrefix) && qnames.add(qName)) { + if (qName != null && qName.startsWith(packagePrefix) && qNames.add(qName)) { consumer.consume(psiClass); } } return true; } - }); + }; + AllClassesSearch.search(scope, project, new Condition() { + public boolean value(String s) { + return prefixMatcher.isStartMatch(s); + } + }).forEach(processor); + AllClassesSearch.search(scope, project, new Condition() { + public boolean value(String s) { + return prefixMatcher.prefixMatches(s); + } + }).forEach(processor); } diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/PlainPrefixMatcher.java b/platform/lang-api/src/com/intellij/codeInsight/completion/PlainPrefixMatcher.java index 1b859460b8d4..ca872d295023 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/PlainPrefixMatcher.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/PlainPrefixMatcher.java @@ -1,5 +1,6 @@ package com.intellij.codeInsight.completion; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; /** @@ -11,11 +12,14 @@ public class PlainPrefixMatcher extends PrefixMatcher { super(prefix); } + @Override + public boolean isStartMatch(String name) { + return StringUtil.startsWithIgnoreCase(name, getPrefix()); + } + @Override public boolean prefixMatches(@NotNull String name) { - final String lowerPrefix = getPrefix().toLowerCase(); - final String lowerName = name.toLowerCase(); - return lowerName.contains(lowerPrefix); + return StringUtil.containsIgnoreCase(name, getPrefix()); } @NotNull diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/PrefixMatcher.java b/platform/lang-api/src/com/intellij/codeInsight/completion/PrefixMatcher.java index 218c8927f1fb..6900adf32f43 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/PrefixMatcher.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/PrefixMatcher.java @@ -23,6 +23,10 @@ public abstract class PrefixMatcher { return false; } + public boolean isStartMatch(String name) { + return prefixMatches(name); + } + public abstract boolean prefixMatches(@NotNull String name); @NotNull diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java index 39b389b4f84f..23826b5d835e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CamelHumpMatcher.java @@ -7,15 +7,15 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; -import com.intellij.util.text.Matcher; import org.jetbrains.annotations.NotNull; /** * @author peter */ public class CamelHumpMatcher extends PrefixMatcher { - private final Matcher myMatcher; + private final MinusculeMatcher myMatcher; private final boolean myCaseSensitive; public CamelHumpMatcher(@NotNull final String prefix) { @@ -28,6 +28,11 @@ public class CamelHumpMatcher extends PrefixMatcher { myMatcher = createMatcher(myCaseSensitive); } + @Override + public boolean isStartMatch(String name) { + return myMatcher.isStartMatch(name); + } + public boolean prefixMatches(@NotNull final String name) { return myMatcher.matches(name); } @@ -55,7 +60,7 @@ public class CamelHumpMatcher extends PrefixMatcher { return new CamelHumpMatcher(prefix, myCaseSensitive); } - private Matcher createMatcher(final boolean caseSensitive) { + private MinusculeMatcher createMatcher(final boolean caseSensitive) { String prefix = applyMiddleMatching(myPrefix); if (!caseSensitive) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java index 753c56f3af91..b05af5f6371e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/impl/CompletionServiceImpl.java @@ -27,8 +27,6 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -43,7 +41,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Iterator; /** * @author peter @@ -62,7 +59,8 @@ public class CompletionServiceImpl extends CompletionService{ if (indicator != null && indicator.getProject() == project) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); setCompletionPhase(CompletionPhase.NoCompletion); - } else if (indicator == null) { + } + else if (indicator == null) { setCompletionPhase(CompletionPhase.NoCompletion); } } @@ -139,23 +137,6 @@ public class CompletionServiceImpl extends CompletionService{ return matcher; } - private static boolean isMiddleMatch(LookupElement element, CompletionLocation location) { - String prefix = location.getCompletionParameters().getLookup().itemPattern(element); - if (StringUtil.isNotEmpty(prefix)) { - MinusculeMatcher matcher = getMinusculeMatcher(prefix); - for (String ls : element.getAllLookupStrings()) { - Iterable fragments = matcher.matchingFragments(ls); - if (fragments != null) { - Iterator iterator = fragments.iterator(); - if (!iterator.hasNext() || MinusculeMatcher.isStartMatch(ls, iterator.next().getStartOffset())) { - return false; - } - } - } - } - return true; - } - private static class CompletionResultSetImpl extends CompletionResultSet { private final String myTextBeforePosition; private final CompletionParameters myParameters; @@ -169,7 +150,7 @@ public class CompletionServiceImpl extends CompletionService{ CompletionParameters parameters, @NotNull CompletionSorterImpl sorter, @NotNull CompletionProgressIndicator process, - CompletionResultSetImpl original) { + @Nullable CompletionResultSetImpl original) { super(prefixMatcher, consumer, contributor); myTextBeforePosition = textBeforePosition; myParameters = parameters; @@ -290,34 +271,12 @@ public class CompletionServiceImpl extends CompletionService{ final CompletionLocation location = new CompletionLocation(parameters); CompletionSorterImpl sorter = emptySorter(); - sorter = sorter.withClassifier(new ClassifierFactory("startMatching") { - @Override - public Classifier createClassifier(Classifier next) { - return new ComparingClassifier(next, "startMatching") { - @NotNull - @Override - public Comparable getWeight(LookupElement element) { - return isMiddleMatch(element, location); - } - }; - } - }); + sorter = sorter.withClassifier(new PreferStartMatching(location)); for (final Weigher weigher : WeighingService.getWeighers(CompletionService.RELEVANCE_KEY)) { final String id = weigher.toString(); if ("prefix".equals(id)) { - sorter = sorter.withClassifier(new ClassifierFactory(id) { - @Override - public Classifier createClassifier(Classifier next) { - return new ComparingClassifier(next, id) { - @NotNull - @Override - public Comparable getWeight(LookupElement element) { - return -getPrefixMatchingDegree(element, location); - } - }; - } - }); + sorter = sorter.withClassifier(new PrefixMatchingClassifier(id, location)); } else { sorter = sorter.weigh(new LookupElementWeigher(id) { @@ -346,4 +305,52 @@ public class CompletionServiceImpl extends CompletionService{ public CompletionSorterImpl emptySorter() { return new CompletionSorterImpl(new ArrayList>()); } + + private static class PreferStartMatching extends ClassifierFactory { + private final CompletionLocation myLocation; + + public PreferStartMatching(CompletionLocation location) { + super("startMatching"); + myLocation = location; + } + + @Override + public Classifier createClassifier(Classifier next) { + return new ComparingClassifier(next, "startMatching") { + @NotNull + @Override + public Comparable getWeight(LookupElement element) { + PrefixMatcher itemMatcher = myLocation.getCompletionParameters().getLookup().itemMatcher(element); + for (String ls : element.getAllLookupStrings()) { + if (itemMatcher.isStartMatch(ls)) { + return false; + } + } + return true; + } + }; + } + } + + private static class PrefixMatchingClassifier extends ClassifierFactory { + private final String myId; + private final CompletionLocation myLocation; + + public PrefixMatchingClassifier(String id, CompletionLocation location) { + super(id); + myId = id; + myLocation = location; + } + + @Override + public Classifier createClassifier(Classifier next) { + return new ComparingClassifier(next, myId) { + @NotNull + @Override + public Comparable getWeight(LookupElement element) { + return -getPrefixMatchingDegree(element, myLocation); + } + }; + } + } } diff --git a/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java index aff25a49390d..00b6db039de0 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java +++ b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java @@ -23,6 +23,7 @@ import com.intellij.util.text.Matcher; import org.jetbrains.annotations.Nullable; import java.util.Collections; +import java.util.Iterator; /** * @author peter @@ -287,7 +288,18 @@ public class MinusculeMatcher implements Matcher { return -fragmentCount + matchingCase * 10 + commonStart - startIndex + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; } - public static boolean isStartMatch(String name, int startIndex) { + public boolean isStartMatch(String name) { + Iterable fragments = matchingFragments(name); + if (fragments != null) { + Iterator iterator = fragments.iterator(); + if (!iterator.hasNext() || isStartMatch(name, iterator.next().getStartOffset())) { + return true; + } + } + return false; + } + + private static boolean isStartMatch(String name, int startIndex) { for (int i = 0; i < startIndex; i++) { if (!NameUtil.isWordSeparator(name.charAt(i))) { return false;