IDEA-229306 (IDEA-CR-56836) Goto Class should prefer start matches

GitOrigin-RevId: 4481c7ac60053834905010e7d0ac69f495a0629d
This commit is contained in:
Mikhail Sokolov
2019-12-23 16:37:55 +00:00
committed by intellij-monorepo-bot
parent 259ad973f4
commit ee23cde7a8
6 changed files with 98 additions and 36 deletions
@@ -296,6 +296,7 @@ public final class NameUtil {
private String separators = "";
private MatchingCaseSensitivity caseSensitivity = MatchingCaseSensitivity.NONE;
private boolean typoTolerant = Registry.is("ide.completion.typo.tolerance");
private boolean preferStartMatches = false;
public MatcherBuilder(String pattern) {
this.pattern = pattern;
@@ -316,9 +317,15 @@ public final class NameUtil {
return this;
}
public MatcherBuilder preferringStartMatches() {
preferStartMatches = true;
return this;
}
public MinusculeMatcher build() {
return typoTolerant ? FixingLayoutTypoTolerantMatcher.create(pattern, caseSensitivity, separators)
: new FixingLayoutMatcher(pattern, caseSensitivity, separators);
MinusculeMatcher matcher = typoTolerant ? FixingLayoutTypoTolerantMatcher.create(pattern, caseSensitivity, separators)
: new FixingLayoutMatcher(pattern, caseSensitivity, separators);
return preferStartMatches ? new PreferStartMatchMatcherWrapper(matcher) : matcher;
}
}
@@ -0,0 +1,39 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.codeStyle;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.containers.FList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PreferStartMatchMatcherWrapper extends MinusculeMatcher {
public static final int START_MATCH_WEIGHT = 10000;
@NotNull
private final MinusculeMatcher myDelegateMatcher;
public PreferStartMatchMatcherWrapper(@NotNull MinusculeMatcher matcher) {
myDelegateMatcher = matcher;
}
@Override
@NotNull
public String getPattern() {
return myDelegateMatcher.getPattern();
}
@Override
public FList<TextRange> matchingFragments(@NotNull String name) {
return myDelegateMatcher.matchingFragments(name);
}
@Override
public int matchingDegree(@NotNull String name,
boolean valueStartCaseMatch,
@Nullable FList<? extends TextRange> fragments) {
int degree = myDelegateMatcher.matchingDegree(name, valueStartCaseMatch, fragments);
if (fragments == null || fragments.isEmpty()) return degree;
if (fragments.getHead().getStartOffset() == 0) degree += START_MATCH_WEIGHT;
return degree;
}
}
@@ -144,7 +144,12 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
@NotNull
public static MinusculeMatcher getQualifiedNameMatcher(@NotNull String pattern) {
return NameUtil.buildMatcher("*" + StringUtil.replace(StringUtil.replace(pattern, "\\", "*\\*"), "/", "*/*"), NameUtil.MatchingCaseSensitivity.NONE);
pattern = "*" + StringUtil.replace(StringUtil.replace(pattern, "\\", "*\\*"), "/", "*/*");
return NameUtil.buildMatcher(pattern)
.withCaseSensitivity(NameUtil.MatchingCaseSensitivity.NONE)
.preferringStartMatches()
.build();
}
@NotNull
@@ -318,7 +323,15 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
SuffixMatches(@NotNull String pattern, int from, @NotNull ProgressIndicator indicator) {
patternSuffix = pattern.substring(from);
matcher = NameUtil.buildMatcher((from > 0 ? " " : "*") + patternSuffix, NameUtil.MatchingCaseSensitivity.NONE);
boolean preferStartMatches = from == 0 && !patternSuffix.startsWith("*");
String matchPattern = (from > 0 ? " " : "*") + patternSuffix;
NameUtil.MatcherBuilder builder = NameUtil.buildMatcher(matchPattern).withCaseSensitivity(NameUtil.MatchingCaseSensitivity.NONE);
if (preferStartMatches) {
builder.preferringStartMatches();
}
this.matcher = builder.build();
this.indicator = indicator;
}
@@ -355,7 +368,7 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
}
}
List<List<MatchResult>> groups = groupByMatchingDegree(!parameters.getCompletePattern().startsWith("*"), matchingNames);
List<List<MatchResult>> groups = groupByMatchingDegree(matchingNames);
for (List<MatchResult> group : groups) {
JBIterable<FoundItemDescriptor<PsiFileSystemItem>> filesMatchingPath = getFilesMatchingPath(parameters, group, dirMatcher, indicator);
Iterable<FoundItemDescriptor<PsiFileSystemItem>> matchedFiles =
@@ -389,13 +402,13 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
}
@NotNull
private List<List<MatchResult>> groupByMatchingDegree(boolean preferStartMatches, @NotNull List<MatchResult> matchingNames) {
private List<List<MatchResult>> groupByMatchingDegree(@NotNull List<MatchResult> matchingNames) {
Comparator<MatchResult> comparator = (mr1, mr2) -> {
boolean exactPrefix1 = StringUtil.startsWith(mr1.elementName, patternSuffix);
boolean exactPrefix2 = StringUtil.startsWith(mr2.elementName, patternSuffix);
if (exactPrefix1 && exactPrefix2) return 0;
if (exactPrefix1 != exactPrefix2) return exactPrefix1 ? -1 : 1;
return mr1.compareDegrees(mr2, preferStartMatches);
return mr1.compareDegrees(mr2);
};
return sortAndGroup(matchingNames, comparator);
@@ -62,7 +62,12 @@ public class RecentFilesSEContributor extends FileSearchEverywhereContributor {
}
String searchString = filterControlSymbols(pattern);
MinusculeMatcher matcher = NameUtil.buildMatcher("*" + searchString).build();
boolean preferStartMatches = !searchString.startsWith("*");
NameUtil.MatcherBuilder builder = NameUtil.buildMatcher("*" + searchString);
if (preferStartMatches) {
builder = builder.preferringStartMatches();
}
MinusculeMatcher matcher = builder.build();
List<VirtualFile> opened = Arrays.asList(FileEditorManager.getInstance(myProject).getSelectedFiles());
List<VirtualFile> history = Lists.reverse(EditorHistoryManager.getInstance(myProject).getFileList());
@@ -114,7 +114,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
indicator.checkCanceled();
return processByNames(base, everywhere, indicator, context, consumer, preferStartMatches, namesList, parameters);
return processByNames(base, everywhere, indicator, context, consumer, namesList, parameters);
}
@NotNull
@@ -152,7 +152,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull ProgressIndicator indicator,
@Nullable Supplier<String[]> allNamesProducer,
String namePattern, boolean preferStartMatches) {
List<MatchResult> namesList = getAllNames(base, parameters, indicator, allNamesProducer, namePattern);
List<MatchResult> namesList = getAllNames(base, parameters, indicator, allNamesProducer, namePattern, preferStartMatches);
indicator.checkCanceled();
String pattern = parameters.getCompletePattern();
@@ -160,7 +160,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
long started = System.currentTimeMillis();
namesList.sort(Comparator.comparing((MatchResult mr) -> !pattern.equalsIgnoreCase(mr.elementName))
.thenComparing((MatchResult mr) -> !namePattern.equalsIgnoreCase(mr.elementName))
.thenComparing((mr1, mr2) -> mr1.compareWith(mr2, preferStartMatches)));
.thenComparing(Comparator.naturalOrder()));
if (LOG.isDebugEnabled()) {
LOG.debug("sorted:"+ (System.currentTimeMillis() - started) + ",results:" + namesList.size());
}
@@ -172,7 +172,8 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull FindSymbolParameters parameters,
@NotNull ProgressIndicator indicator,
@Nullable Supplier<String[]> allNamesProducer,
@NotNull String namePattern) {
@NotNull String namePattern,
boolean preferStartMatches) {
List<MatchResult> namesList = new ArrayList<>();
final CollectConsumer<MatchResult> collect = new SynchronizedCollectConsumer<>(namesList);
@@ -182,7 +183,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
indicator.checkCanceled();
long started = System.currentTimeMillis();
String fullPattern = parameters.getCompletePattern();
MinusculeMatcher matcher = buildPatternMatcher(namePattern);
MinusculeMatcher matcher = buildPatternMatcher(namePattern, preferStartMatches);
((ChooseByNameModelEx)model).processNames(sequence -> {
indicator.checkCanceled();
MatchResult result = matches(base, fullPattern, matcher, sequence);
@@ -202,7 +203,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
}
String[] names = allNamesProducer.get();
long started = System.currentTimeMillis();
processNamesByPattern(base, names, namePattern, indicator, collect);
processNamesByPattern(base, names, namePattern, indicator, collect, preferStartMatches);
if (LOG.isDebugEnabled()) {
LOG.debug("matched:"+ (System.currentTimeMillis() - started)+ "," + names.length);
}
@@ -225,7 +226,6 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull ProgressIndicator indicator,
@Nullable PsiElement context,
@NotNull Processor<? super FoundItemDescriptor<?>> consumer,
boolean preferStartMatches,
@NotNull List<? extends MatchResult> namesList,
@NotNull FindSymbolParameters parameters) {
List<Pair<Object, MatchResult>> sameNameElements = new SmartList<>();
@@ -239,7 +239,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@Override
public int compare(Pair<Object, MatchResult> o1, Pair<Object, MatchResult> o2) {
int result = modelComparator.compare(o1.first, o2.first);
return result != 0 ? result : o1.second.compareWith(o2.second, preferStartMatches);
return result != 0 ? result : o1.second.compareTo(o2.second);
}
};
@@ -333,6 +333,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull
@Override
public List<String> filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) {
boolean preferStartMatches = pattern.startsWith("*");
pattern = convertToMatchingPattern(base, pattern);
if (pattern.isEmpty() && !base.canShowListForEmptyPattern()) return Collections.emptyList();
@@ -341,7 +342,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
synchronized (filtered) {
filtered.add(result.elementName);
}
});
}, preferStartMatches);
synchronized (filtered) {
return filtered;
}
@@ -351,8 +352,9 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull final String[] names,
@NotNull final String pattern,
final ProgressIndicator indicator,
@NotNull final Consumer<? super MatchResult> consumer) {
MinusculeMatcher matcher = buildPatternMatcher(pattern);
@NotNull final Consumer<? super MatchResult> consumer,
boolean preferStartMatches) {
MinusculeMatcher matcher = buildPatternMatcher(pattern, preferStartMatches);
Processor<String> processor = name -> {
ProgressManager.checkCanceled();
MatchResult result = matches(base, pattern, matcher, name);
@@ -415,8 +417,13 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
}
@NotNull
private static MinusculeMatcher buildPatternMatcher(@NotNull String pattern) {
return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
private static MinusculeMatcher buildPatternMatcher(@NotNull String pattern, boolean preferStartMatches) {
NameUtil.MatcherBuilder builder = NameUtil.buildMatcher(pattern).withCaseSensitivity(NameUtil.MatchingCaseSensitivity.NONE);
if (preferStartMatches) {
builder = builder.preferringStartMatches();
}
return builder.build();
}
protected static class PathProximityComparator implements Comparator<Object> {
@@ -17,7 +17,7 @@ package com.intellij.ide.util.gotoByName;
import org.jetbrains.annotations.NotNull;
public class MatchResult {
public class MatchResult implements Comparable<MatchResult> {
@NotNull
public final String elementName;
public final int matchingDegree;
@@ -29,22 +29,13 @@ public class MatchResult {
this.startMatch = startMatch;
}
public int compareDegrees(@NotNull MatchResult that, boolean preferStartMatches) {
boolean start1 = startMatch;
boolean start2 = that.startMatch;
int startMatchResult = start1 == start2 ? 0 : start1 ? -1 : 1;
if (preferStartMatches && startMatchResult != 0) return startMatchResult;
int degree1 = matchingDegree;
int degree2 = that.matchingDegree;
if (degree2 < degree1) return -1;
if (degree2 > degree1) return 1;
return -startMatchResult;
public int compareDegrees(@NotNull MatchResult that) {
return Integer.compare(that.matchingDegree, matchingDegree);
}
int compareWith(@NotNull MatchResult that, boolean preferStartMatches) {
int result = compareDegrees(that, preferStartMatches);
@Override
public int compareTo(@NotNull MatchResult that) {
int result = compareDegrees(that);
return result != 0 ? result : elementName.compareToIgnoreCase(that.elementName);
}