suggest start match classes first

This commit is contained in:
peter
2012-07-05 20:49:17 +02:00
parent 188b856d03
commit 530edc975b
6 changed files with 103 additions and 66 deletions
@@ -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<PsiClass> consumer) {
final PsiElement context = parameters.getPosition();
final String packagePrefix = getPackagePrefix(context, parameters.getOffset());
final Set<String> qnames = new THashSet<String>();
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<String>() {
public boolean value(String s) {
return prefixMatcher.prefixMatches(s);
}
}).forEach(new Processor<PsiClass>() {
Processor<PsiClass> processor = new Processor<PsiClass>() {
final Set<String> qNames = new THashSet<String>();
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<String>() {
public boolean value(String s) {
return prefixMatcher.isStartMatch(s);
}
}).forEach(processor);
AllClassesSearch.search(scope, project, new Condition<String>() {
public boolean value(String s) {
return prefixMatcher.prefixMatches(s);
}
}).forEach(processor);
}
@@ -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
@@ -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
@@ -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) {
@@ -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<TextRange> fragments = matcher.matchingFragments(ls);
if (fragments != null) {
Iterator<TextRange> 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<LookupElement>("startMatching") {
@Override
public Classifier<LookupElement> createClassifier(Classifier<LookupElement> next) {
return new ComparingClassifier<LookupElement>(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<LookupElement>(id) {
@Override
public Classifier<LookupElement> createClassifier(Classifier<LookupElement> next) {
return new ComparingClassifier<LookupElement>(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<ClassifierFactory<LookupElement>>());
}
private static class PreferStartMatching extends ClassifierFactory<LookupElement> {
private final CompletionLocation myLocation;
public PreferStartMatching(CompletionLocation location) {
super("startMatching");
myLocation = location;
}
@Override
public Classifier<LookupElement> createClassifier(Classifier<LookupElement> next) {
return new ComparingClassifier<LookupElement>(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<LookupElement> {
private final String myId;
private final CompletionLocation myLocation;
public PrefixMatchingClassifier(String id, CompletionLocation location) {
super(id);
myId = id;
myLocation = location;
}
@Override
public Classifier<LookupElement> createClassifier(Classifier<LookupElement> next) {
return new ComparingClassifier<LookupElement>(next, myId) {
@NotNull
@Override
public Comparable getWeight(LookupElement element) {
return -getPrefixMatchingDegree(element, myLocation);
}
};
}
}
}
@@ -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<TextRange> fragments = matchingFragments(name);
if (fragments != null) {
Iterator<TextRange> 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;