mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
get rid of NegatingComparable indirection
This commit is contained in:
@@ -16,15 +16,26 @@
|
||||
package com.intellij.codeInsight.lookup;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public abstract class LookupElementWeigher {
|
||||
private final String myId;
|
||||
private final boolean myNegated;
|
||||
|
||||
protected LookupElementWeigher(String id, boolean negated) {
|
||||
myId = id;
|
||||
myNegated = negated;
|
||||
}
|
||||
|
||||
protected LookupElementWeigher(String id) {
|
||||
myId = id;
|
||||
this(id, false);
|
||||
}
|
||||
|
||||
public boolean isNegated() {
|
||||
return myNegated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,7 +43,7 @@ public abstract class LookupElementWeigher {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public abstract Comparable weigh(@NotNull LookupElement element);
|
||||
|
||||
}
|
||||
|
||||
+355
-356
@@ -1,356 +1,355 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.completion.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
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.patterns.ElementPattern;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.Weigher;
|
||||
import com.intellij.psi.WeighingService;
|
||||
import com.intellij.psi.codeStyle.MinusculeMatcher;
|
||||
import com.intellij.psi.codeStyle.NameUtil;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class CompletionServiceImpl extends CompletionService{
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.impl.CompletionServiceImpl");
|
||||
private static volatile CompletionPhase ourPhase = CompletionPhase.NoCompletion;
|
||||
private static String ourPhaseTrace;
|
||||
private static CodeInsightSettings ourSettings = CodeInsightSettings.getInstance();
|
||||
|
||||
public CompletionServiceImpl() {
|
||||
ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerAdapter() {
|
||||
@Override
|
||||
public void projectClosing(Project project) {
|
||||
CompletionProgressIndicator indicator = getCurrentCompletion();
|
||||
if (indicator != null && indicator.getProject() == project) {
|
||||
LookupManager.getInstance(indicator.getProject()).hideActiveLookup();
|
||||
setCompletionPhase(CompletionPhase.NoCompletion);
|
||||
}
|
||||
else if (indicator == null) {
|
||||
setCompletionPhase(CompletionPhase.NoCompletion);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass"})
|
||||
public static CompletionServiceImpl getCompletionService() {
|
||||
return (CompletionServiceImpl)CompletionService.getCompletionService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdvertisementText() {
|
||||
final CompletionProgressIndicator completion = getCompletionService().getCurrentCompletion();
|
||||
return completion == null ? null : completion.getLookup().getAdvertisementText();
|
||||
}
|
||||
|
||||
public void setAdvertisementText(@Nullable final String text) {
|
||||
final CompletionProgressIndicator completion = getCompletionService().getCurrentCompletion();
|
||||
if (completion != null) {
|
||||
completion.getLookup().setAdvertisementText(text);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionResultSet createResultSet(final CompletionParameters parameters, final Consumer<CompletionResult> consumer,
|
||||
@NotNull final CompletionContributor contributor) {
|
||||
final PsiElement position = parameters.getPosition();
|
||||
final String prefix = CompletionData.findPrefixStatic(position, parameters.getOffset());
|
||||
final String textBeforePosition = parameters.getPosition().getContainingFile().getText().substring(0, parameters.getOffset());
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (!(indicator instanceof CompletionProgressIndicator)) {
|
||||
throw new AssertionError("createResultSet may be invoked only from completion thread: " + indicator + "!=" + getCurrentCompletion() + "; phase set at " + ourPhaseTrace);
|
||||
}
|
||||
CompletionProgressIndicator process = (CompletionProgressIndicator)indicator;
|
||||
CamelHumpMatcher matcher = new CamelHumpMatcher(prefix);
|
||||
CompletionSorterImpl sorter = defaultSorter(parameters, matcher);
|
||||
return new CompletionResultSetImpl(consumer, textBeforePosition, matcher, contributor,parameters, sorter, process, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionProgressIndicator getCurrentCompletion() {
|
||||
if (isPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.CommittingDocuments.class,
|
||||
CompletionPhase.Synchronous.class)) {
|
||||
return ourPhase.indicator;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getPrefixMatchingDegree(LookupElement item, CompletionLocation location) {
|
||||
final MinusculeMatcher matcher = getMinusculeMatcher(location.getCompletionParameters().getLookup().itemPattern(item));
|
||||
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (String lookupString : item.getAllLookupStrings()) {
|
||||
max = Math.max(max, matcher.matchingDegree(lookupString));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private static volatile Pair<String, MinusculeMatcher> lastMatcher;
|
||||
|
||||
private static MinusculeMatcher getMinusculeMatcher(String prefix) {
|
||||
final int setting = ourSettings.COMPLETION_CASE_SENSITIVE;
|
||||
final NameUtil.MatchingCaseSensitivity sensitivity =
|
||||
setting == CodeInsightSettings.NONE ? NameUtil.MatchingCaseSensitivity.NONE :
|
||||
setting == CodeInsightSettings.FIRST_LETTER ? NameUtil.MatchingCaseSensitivity.FIRST_LETTER : NameUtil.MatchingCaseSensitivity.ALL;
|
||||
|
||||
Pair<String, MinusculeMatcher> pair = lastMatcher;
|
||||
if (pair != null && pair.first.equals(prefix)) {
|
||||
return pair.second;
|
||||
}
|
||||
|
||||
MinusculeMatcher matcher = new MinusculeMatcher(CamelHumpMatcher.applyMiddleMatching(prefix), sensitivity);
|
||||
lastMatcher = Pair.create(prefix, matcher);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private static class CompletionResultSetImpl extends CompletionResultSet {
|
||||
private final String myTextBeforePosition;
|
||||
private final CompletionParameters myParameters;
|
||||
private final CompletionSorterImpl mySorter;
|
||||
private final CompletionProgressIndicator myProcess;
|
||||
@Nullable private final CompletionResultSetImpl myOriginal;
|
||||
|
||||
public CompletionResultSetImpl(final Consumer<CompletionResult> consumer, final String textBeforePosition,
|
||||
final PrefixMatcher prefixMatcher,
|
||||
CompletionContributor contributor,
|
||||
CompletionParameters parameters,
|
||||
@NotNull CompletionSorterImpl sorter,
|
||||
@NotNull CompletionProgressIndicator process,
|
||||
@Nullable CompletionResultSetImpl original) {
|
||||
super(prefixMatcher, consumer, contributor);
|
||||
myTextBeforePosition = textBeforePosition;
|
||||
myParameters = parameters;
|
||||
mySorter = sorter;
|
||||
myProcess = process;
|
||||
myOriginal = original;
|
||||
}
|
||||
|
||||
public void addElement(@NotNull final LookupElement element) {
|
||||
CompletionResult matched = CompletionResult.wrap(element, getPrefixMatcher(), mySorter);
|
||||
if (matched != null) {
|
||||
passResult(matched);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletionResultSet withPrefixMatcher(@NotNull final PrefixMatcher matcher) {
|
||||
if (!myTextBeforePosition.endsWith(matcher.getPrefix())) {
|
||||
final int len = myTextBeforePosition.length();
|
||||
final String fragment = len > 100 ? myTextBeforePosition.substring(len - 100) : myTextBeforePosition;
|
||||
PsiFile positionFile = myParameters.getPosition().getContainingFile();
|
||||
LOG.error("prefix should be some actual file string just before caret: " + matcher.getPrefix() +
|
||||
"\n text=" + fragment +
|
||||
"\ninjected=" + (InjectedLanguageUtil.getTopLevelFile(positionFile) != positionFile) +
|
||||
"\nlang=" + positionFile.getLanguage());
|
||||
}
|
||||
return new CompletionResultSetImpl(getConsumer(), myTextBeforePosition, matcher, myContributor, myParameters, mySorter, myProcess, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopHere() {
|
||||
super.stopHere();
|
||||
if (myOriginal != null) {
|
||||
myOriginal.stopHere();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletionResultSet withPrefixMatcher(@NotNull final String prefix) {
|
||||
return withPrefixMatcher(new CamelHumpMatcher(prefix));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CompletionResultSet withRelevanceSorter(@NotNull CompletionSorter sorter) {
|
||||
return new CompletionResultSetImpl(getConsumer(), myTextBeforePosition, getPrefixMatcher(), myContributor, myParameters, (CompletionSorterImpl)sorter, myProcess, this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CompletionResultSet caseInsensitive() {
|
||||
return withPrefixMatcher(new CamelHumpMatcher(getPrefixMatcher().getPrefix(), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartCompletionOnPrefixChange(ElementPattern<String> prefixCondition) {
|
||||
final CompletionProgressIndicator indicator = getCompletionService().getCurrentCompletion();
|
||||
if (indicator != null) {
|
||||
indicator.addWatchedPrefix(myTextBeforePosition.length() - getPrefixMatcher().getPrefix().length(), prefixCondition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartCompletionWhenNothingMatches() {
|
||||
final CompletionProgressIndicator indicator = getCompletionService().getCurrentCompletion();
|
||||
if (indicator != null) {
|
||||
indicator.getLookup().setStartCompletionWhenNothingMatches(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean assertPhase(Class<? extends CompletionPhase>... possibilities) {
|
||||
if (!isPhase(possibilities)) {
|
||||
LOG.error(ourPhase + "; set at " + ourPhaseTrace);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPhase(Class<? extends CompletionPhase>... possibilities) {
|
||||
CompletionPhase phase = getCompletionPhase();
|
||||
for (Class<? extends CompletionPhase> possibility : possibilities) {
|
||||
if (possibility.isInstance(phase)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setCompletionPhase(@NotNull CompletionPhase phase) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
CompletionPhase oldPhase = getCompletionPhase();
|
||||
CompletionProgressIndicator oldIndicator = oldPhase.indicator;
|
||||
if (oldIndicator != null && !(phase instanceof CompletionPhase.BgCalculation)) {
|
||||
LOG.assertTrue(!oldIndicator.isRunning() || oldIndicator.isCanceled(), "don't change phase during running completion: oldPhase=" + oldPhase);
|
||||
}
|
||||
|
||||
Disposer.dispose(oldPhase);
|
||||
ourPhase = phase;
|
||||
ourPhaseTrace = DebugUtil.currentStackTrace();
|
||||
}
|
||||
|
||||
public static CompletionPhase getCompletionPhase() {
|
||||
// ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
CompletionPhase phase = getPhaseRaw();
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (indicator != null) {
|
||||
indicator.checkCanceled();
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
|
||||
public static CompletionPhase getPhaseRaw() {
|
||||
return ourPhase;
|
||||
}
|
||||
|
||||
public CompletionSorterImpl defaultSorter(CompletionParameters parameters, final PrefixMatcher matcher) {
|
||||
final CompletionLocation location = new CompletionLocation(parameters);
|
||||
|
||||
CompletionSorterImpl sorter = emptySorter();
|
||||
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 PrefixMatchingClassifier(id, location));
|
||||
}
|
||||
else {
|
||||
sorter = sorter.weigh(new LookupElementWeigher(id) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Comparable weigh(@NotNull LookupElement element) {
|
||||
return new NegatingComparable(weigher.weigh(element, location));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (parameters.getCompletionType() == CompletionType.SMART) {
|
||||
return sorter;
|
||||
}
|
||||
|
||||
return sorter.withClassifier("priority", true, new ClassifierFactory<LookupElement>("liftShorter") {
|
||||
@Override
|
||||
public Classifier<LookupElement> createClassifier(final Classifier<LookupElement> next) {
|
||||
return new LiftShorterItemsClassifier(next, new LiftShorterItemsClassifier.LiftingCondition());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.completion.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
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.patterns.ElementPattern;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.Weigher;
|
||||
import com.intellij.psi.WeighingService;
|
||||
import com.intellij.psi.codeStyle.MinusculeMatcher;
|
||||
import com.intellij.psi.codeStyle.NameUtil;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class CompletionServiceImpl extends CompletionService{
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.impl.CompletionServiceImpl");
|
||||
private static volatile CompletionPhase ourPhase = CompletionPhase.NoCompletion;
|
||||
private static String ourPhaseTrace;
|
||||
private static CodeInsightSettings ourSettings = CodeInsightSettings.getInstance();
|
||||
|
||||
public CompletionServiceImpl() {
|
||||
ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerAdapter() {
|
||||
@Override
|
||||
public void projectClosing(Project project) {
|
||||
CompletionProgressIndicator indicator = getCurrentCompletion();
|
||||
if (indicator != null && indicator.getProject() == project) {
|
||||
LookupManager.getInstance(indicator.getProject()).hideActiveLookup();
|
||||
setCompletionPhase(CompletionPhase.NoCompletion);
|
||||
}
|
||||
else if (indicator == null) {
|
||||
setCompletionPhase(CompletionPhase.NoCompletion);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass"})
|
||||
public static CompletionServiceImpl getCompletionService() {
|
||||
return (CompletionServiceImpl)CompletionService.getCompletionService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdvertisementText() {
|
||||
final CompletionProgressIndicator completion = getCompletionService().getCurrentCompletion();
|
||||
return completion == null ? null : completion.getLookup().getAdvertisementText();
|
||||
}
|
||||
|
||||
public void setAdvertisementText(@Nullable final String text) {
|
||||
final CompletionProgressIndicator completion = getCompletionService().getCurrentCompletion();
|
||||
if (completion != null) {
|
||||
completion.getLookup().setAdvertisementText(text);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionResultSet createResultSet(final CompletionParameters parameters, final Consumer<CompletionResult> consumer,
|
||||
@NotNull final CompletionContributor contributor) {
|
||||
final PsiElement position = parameters.getPosition();
|
||||
final String prefix = CompletionData.findPrefixStatic(position, parameters.getOffset());
|
||||
final String textBeforePosition = parameters.getPosition().getContainingFile().getText().substring(0, parameters.getOffset());
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (!(indicator instanceof CompletionProgressIndicator)) {
|
||||
throw new AssertionError("createResultSet may be invoked only from completion thread: " + indicator + "!=" + getCurrentCompletion() + "; phase set at " + ourPhaseTrace);
|
||||
}
|
||||
CompletionProgressIndicator process = (CompletionProgressIndicator)indicator;
|
||||
CamelHumpMatcher matcher = new CamelHumpMatcher(prefix);
|
||||
CompletionSorterImpl sorter = defaultSorter(parameters, matcher);
|
||||
return new CompletionResultSetImpl(consumer, textBeforePosition, matcher, contributor,parameters, sorter, process, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionProgressIndicator getCurrentCompletion() {
|
||||
if (isPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.CommittingDocuments.class,
|
||||
CompletionPhase.Synchronous.class)) {
|
||||
return ourPhase.indicator;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getPrefixMatchingDegree(LookupElement item, CompletionLocation location) {
|
||||
final MinusculeMatcher matcher = getMinusculeMatcher(location.getCompletionParameters().getLookup().itemPattern(item));
|
||||
|
||||
int max = Integer.MIN_VALUE;
|
||||
for (String lookupString : item.getAllLookupStrings()) {
|
||||
max = Math.max(max, matcher.matchingDegree(lookupString));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private static volatile Pair<String, MinusculeMatcher> lastMatcher;
|
||||
|
||||
private static MinusculeMatcher getMinusculeMatcher(String prefix) {
|
||||
final int setting = ourSettings.COMPLETION_CASE_SENSITIVE;
|
||||
final NameUtil.MatchingCaseSensitivity sensitivity =
|
||||
setting == CodeInsightSettings.NONE ? NameUtil.MatchingCaseSensitivity.NONE :
|
||||
setting == CodeInsightSettings.FIRST_LETTER ? NameUtil.MatchingCaseSensitivity.FIRST_LETTER : NameUtil.MatchingCaseSensitivity.ALL;
|
||||
|
||||
Pair<String, MinusculeMatcher> pair = lastMatcher;
|
||||
if (pair != null && pair.first.equals(prefix)) {
|
||||
return pair.second;
|
||||
}
|
||||
|
||||
MinusculeMatcher matcher = new MinusculeMatcher(CamelHumpMatcher.applyMiddleMatching(prefix), sensitivity);
|
||||
lastMatcher = Pair.create(prefix, matcher);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private static class CompletionResultSetImpl extends CompletionResultSet {
|
||||
private final String myTextBeforePosition;
|
||||
private final CompletionParameters myParameters;
|
||||
private final CompletionSorterImpl mySorter;
|
||||
private final CompletionProgressIndicator myProcess;
|
||||
@Nullable private final CompletionResultSetImpl myOriginal;
|
||||
|
||||
public CompletionResultSetImpl(final Consumer<CompletionResult> consumer, final String textBeforePosition,
|
||||
final PrefixMatcher prefixMatcher,
|
||||
CompletionContributor contributor,
|
||||
CompletionParameters parameters,
|
||||
@NotNull CompletionSorterImpl sorter,
|
||||
@NotNull CompletionProgressIndicator process,
|
||||
@Nullable CompletionResultSetImpl original) {
|
||||
super(prefixMatcher, consumer, contributor);
|
||||
myTextBeforePosition = textBeforePosition;
|
||||
myParameters = parameters;
|
||||
mySorter = sorter;
|
||||
myProcess = process;
|
||||
myOriginal = original;
|
||||
}
|
||||
|
||||
public void addElement(@NotNull final LookupElement element) {
|
||||
CompletionResult matched = CompletionResult.wrap(element, getPrefixMatcher(), mySorter);
|
||||
if (matched != null) {
|
||||
passResult(matched);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletionResultSet withPrefixMatcher(@NotNull final PrefixMatcher matcher) {
|
||||
if (!myTextBeforePosition.endsWith(matcher.getPrefix())) {
|
||||
final int len = myTextBeforePosition.length();
|
||||
final String fragment = len > 100 ? myTextBeforePosition.substring(len - 100) : myTextBeforePosition;
|
||||
PsiFile positionFile = myParameters.getPosition().getContainingFile();
|
||||
LOG.error("prefix should be some actual file string just before caret: " + matcher.getPrefix() +
|
||||
"\n text=" + fragment +
|
||||
"\ninjected=" + (InjectedLanguageUtil.getTopLevelFile(positionFile) != positionFile) +
|
||||
"\nlang=" + positionFile.getLanguage());
|
||||
}
|
||||
return new CompletionResultSetImpl(getConsumer(), myTextBeforePosition, matcher, myContributor, myParameters, mySorter, myProcess, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopHere() {
|
||||
super.stopHere();
|
||||
if (myOriginal != null) {
|
||||
myOriginal.stopHere();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CompletionResultSet withPrefixMatcher(@NotNull final String prefix) {
|
||||
return withPrefixMatcher(new CamelHumpMatcher(prefix));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CompletionResultSet withRelevanceSorter(@NotNull CompletionSorter sorter) {
|
||||
return new CompletionResultSetImpl(getConsumer(), myTextBeforePosition, getPrefixMatcher(), myContributor, myParameters, (CompletionSorterImpl)sorter, myProcess, this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CompletionResultSet caseInsensitive() {
|
||||
return withPrefixMatcher(new CamelHumpMatcher(getPrefixMatcher().getPrefix(), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartCompletionOnPrefixChange(ElementPattern<String> prefixCondition) {
|
||||
final CompletionProgressIndicator indicator = getCompletionService().getCurrentCompletion();
|
||||
if (indicator != null) {
|
||||
indicator.addWatchedPrefix(myTextBeforePosition.length() - getPrefixMatcher().getPrefix().length(), prefixCondition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartCompletionWhenNothingMatches() {
|
||||
final CompletionProgressIndicator indicator = getCompletionService().getCurrentCompletion();
|
||||
if (indicator != null) {
|
||||
indicator.getLookup().setStartCompletionWhenNothingMatches(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean assertPhase(Class<? extends CompletionPhase>... possibilities) {
|
||||
if (!isPhase(possibilities)) {
|
||||
LOG.error(ourPhase + "; set at " + ourPhaseTrace);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPhase(Class<? extends CompletionPhase>... possibilities) {
|
||||
CompletionPhase phase = getCompletionPhase();
|
||||
for (Class<? extends CompletionPhase> possibility : possibilities) {
|
||||
if (possibility.isInstance(phase)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setCompletionPhase(@NotNull CompletionPhase phase) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
CompletionPhase oldPhase = getCompletionPhase();
|
||||
CompletionProgressIndicator oldIndicator = oldPhase.indicator;
|
||||
if (oldIndicator != null && !(phase instanceof CompletionPhase.BgCalculation)) {
|
||||
LOG.assertTrue(!oldIndicator.isRunning() || oldIndicator.isCanceled(), "don't change phase during running completion: oldPhase=" + oldPhase);
|
||||
}
|
||||
|
||||
Disposer.dispose(oldPhase);
|
||||
ourPhase = phase;
|
||||
ourPhaseTrace = DebugUtil.currentStackTrace();
|
||||
}
|
||||
|
||||
public static CompletionPhase getCompletionPhase() {
|
||||
// ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
CompletionPhase phase = getPhaseRaw();
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (indicator != null) {
|
||||
indicator.checkCanceled();
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
|
||||
public static CompletionPhase getPhaseRaw() {
|
||||
return ourPhase;
|
||||
}
|
||||
|
||||
public CompletionSorterImpl defaultSorter(CompletionParameters parameters, final PrefixMatcher matcher) {
|
||||
final CompletionLocation location = new CompletionLocation(parameters);
|
||||
|
||||
CompletionSorterImpl sorter = emptySorter();
|
||||
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 PrefixMatchingClassifier(id, location));
|
||||
}
|
||||
else {
|
||||
sorter = sorter.weigh(new LookupElementWeigher(id, true) {
|
||||
@Override
|
||||
public Comparable weigh(@NotNull LookupElement element) {
|
||||
return weigher.weigh(element, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (parameters.getCompletionType() == CompletionType.SMART) {
|
||||
return sorter;
|
||||
}
|
||||
|
||||
return sorter.withClassifier("priority", true, new ClassifierFactory<LookupElement>("liftShorter") {
|
||||
@Override
|
||||
public Classifier<LookupElement> createClassifier(final Classifier<LookupElement> next) {
|
||||
return new LiftShorterItemsClassifier(next, new LiftShorterItemsClassifier.LiftingCondition());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-11
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.lookup;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.ForceableComparable;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
@@ -28,22 +29,17 @@ import java.util.Map;
|
||||
public class CachingComparingClassifier extends ComparingClassifier<LookupElement> {
|
||||
private final Map<LookupElement, Comparable> myWeights = new IdentityHashMap<LookupElement, Comparable>();
|
||||
private final LookupElementWeigher myWeigher;
|
||||
private Comparable myFirstWeight;
|
||||
private Ref<Comparable> myFirstWeight;
|
||||
private boolean myPrimitive = true;
|
||||
|
||||
public CachingComparingClassifier(Classifier<LookupElement> next, LookupElementWeigher weigher) {
|
||||
super(next, weigher.toString());
|
||||
super(next, weigher.toString(), weigher.isNegated());
|
||||
myWeigher = weigher;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final Comparable getWeight(LookupElement t) {
|
||||
final Comparable weight = myWeights.get(t);
|
||||
if (weight == null) {
|
||||
throw new AssertionError(myName + "; " + myWeights.containsKey(t) + "; element=" + t);
|
||||
}
|
||||
return weight;
|
||||
return myWeights.get(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,8 +59,8 @@ public class CachingComparingClassifier extends ComparingClassifier<LookupElemen
|
||||
}
|
||||
if (myPrimitive) {
|
||||
if (myFirstWeight == null) {
|
||||
myFirstWeight = weight;
|
||||
} else if (!myFirstWeight.equals(weight)) {
|
||||
myFirstWeight = Ref.create(weight);
|
||||
} else if (!Comparing.equal(myFirstWeight.get(), weight)) {
|
||||
myPrimitive = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ package com.intellij.codeInsight.lookup;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -29,50 +28,69 @@ import java.util.*;
|
||||
public abstract class ComparingClassifier<T> extends Classifier<T> {
|
||||
protected final Classifier<T> myNext;
|
||||
protected final String myName;
|
||||
private final boolean myNegated;
|
||||
|
||||
public ComparingClassifier(Classifier<T> next, String name) {
|
||||
myNext = next;
|
||||
myName = name;
|
||||
this(next, name, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected ComparingClassifier(Classifier<T> next, String name, boolean negated) {
|
||||
myNext = next;
|
||||
myName = name;
|
||||
myNegated = negated;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract Comparable getWeight(T t);
|
||||
|
||||
public void addElement(T t) {
|
||||
myNext.addElement(t);
|
||||
}
|
||||
|
||||
private TreeMap<Comparable, List<T>> groupByWeights(Iterable<T> source) {
|
||||
TreeMap<Comparable, List<T>> map = new TreeMap<Comparable, List<T>>();
|
||||
for (T t : source) {
|
||||
final Comparable weight = getWeight(t);
|
||||
List<T> list = map.get(weight);
|
||||
if (list == null) {
|
||||
map.put(weight, list = new SmartList<T>());
|
||||
}
|
||||
list.add(t);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> classify(Iterable<T> source, ProcessingContext context) {
|
||||
return ContainerUtil.flatten(groupByWeights(myNext.classify(source, context)).values());
|
||||
List<T> nulls = null;
|
||||
TreeMap<Comparable, List<T>> map = new TreeMap<Comparable, List<T>>();
|
||||
int count = 0;
|
||||
for (T t : myNext.classify(source, context)) {
|
||||
count++;
|
||||
final Comparable weight = getWeight(t);
|
||||
if (weight == null) {
|
||||
if (nulls == null) nulls = new SmartList<T>();
|
||||
nulls.add(t);
|
||||
} else {
|
||||
List<T> list = map.get(weight);
|
||||
if (list == null) {
|
||||
map.put(weight, list = new SmartList<T>());
|
||||
}
|
||||
list.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<T> result = new ArrayList<T>(count);
|
||||
Collection<List<T>> values = myNegated ? map.descendingMap().values() : map.values();
|
||||
for (List<T> value : values) {
|
||||
result.addAll(value);
|
||||
}
|
||||
if (nulls != null) {
|
||||
result.addAll(nulls);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeItems(LinkedHashMap<T, StringBuilder> map, ProcessingContext context) {
|
||||
final Map<Comparable, List<T>> treeMap = groupByWeights(new ArrayList<T>(map.keySet()));
|
||||
if (treeMap.size() > 1 || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
for (Map.Entry<Comparable, List<T>> entry: treeMap.entrySet()){
|
||||
for (T t : entry.getValue()) {
|
||||
final StringBuilder builder = map.get(t);
|
||||
if (builder.length() > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(myName).append("=").append(entry.getKey());
|
||||
Map<T, String> weights = new IdentityHashMap<T, String>();
|
||||
for (T t : map.keySet()) {
|
||||
weights.put(t, String.valueOf(getWeight(t)));
|
||||
}
|
||||
if (new HashSet<String>(weights.values()).size() > 1 || ApplicationManager.getApplication().isUnitTestMode() || true) {
|
||||
for (T t : map.keySet()) {
|
||||
final StringBuilder builder = map.get(t);
|
||||
if (builder.length() > 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(myName).append("=").append(weights.get(t));
|
||||
}
|
||||
}
|
||||
myNext.describeItems(map, context);
|
||||
|
||||
Reference in New Issue
Block a user