mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
keep lookup element matchers in LookupArranger to distinguish different prefixes for the same items in subsequent sessions (as Rider produces)
This commit is contained in:
+14
-16
@@ -156,7 +156,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addElement(Lookup lookup, LookupElement element, LookupElementPresentation presentation) {
|
||||
public void addElement(LookupElement element, LookupElementPresentation presentation) {
|
||||
StatisticsWeigher.clearBaseStatisticsInfo(element);
|
||||
|
||||
final String invariant = presentation.getItemText() + "\0###" + getTailTextOrSpace(presentation) + "###" + presentation.getTypeText();
|
||||
@@ -166,14 +166,14 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
CompletionSorterImpl sorter = obtainSorter(element);
|
||||
Classifier<LookupElement> classifier = myClassifiers.get(sorter);
|
||||
if (classifier == null) {
|
||||
myClassifiers.put(sorter, classifier = sorter.buildClassifier(new AlphaClassifier((LookupImpl)lookup)));
|
||||
myClassifiers.put(sorter, classifier = sorter.buildClassifier(new AlphaClassifier()));
|
||||
}
|
||||
ProcessingContext context = createContext(true);
|
||||
classifier.addElement(element, context);
|
||||
|
||||
super.addElement(lookup, element, presentation);
|
||||
super.addElement(element, presentation);
|
||||
|
||||
trimToLimit(lookup, context);
|
||||
trimToLimit(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,7 +181,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
myProcess.itemSelected(lookupItem, completionChar);
|
||||
}
|
||||
|
||||
private void trimToLimit(Lookup lookup, ProcessingContext context) {
|
||||
private void trimToLimit(ProcessingContext context) {
|
||||
if (myItems.size() < myLimit) return;
|
||||
|
||||
List<LookupElement> items = getMatchingItems();
|
||||
@@ -197,7 +197,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
|
||||
if (!iterator.hasNext()) return;
|
||||
|
||||
List<LookupElement> removed = retainItems(retainedSet, lookup);
|
||||
List<LookupElement> removed = retainItems(retainedSet);
|
||||
for (LookupElement element : removed) {
|
||||
removeItem(element, context);
|
||||
}
|
||||
@@ -223,11 +223,11 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
return tailText == null || tailText.isEmpty() ? " " : tailText;
|
||||
}
|
||||
|
||||
private List<LookupElement> sortByPresentation(Iterable<LookupElement> source, LookupImpl lookup) {
|
||||
private List<LookupElement> sortByPresentation(Iterable<LookupElement> source) {
|
||||
ArrayList<LookupElement> startMatches = ContainerUtil.newArrayList();
|
||||
ArrayList<LookupElement> middleMatches = ContainerUtil.newArrayList();
|
||||
for (LookupElement element : source) {
|
||||
(CompletionServiceImpl.isStartMatch(element, lookup) ? startMatches : middleMatches).add(element);
|
||||
(itemMatcher(element).isStartMatch(element) ? startMatches : middleMatches).add(element);
|
||||
}
|
||||
ContainerUtil.sort(startMatches, BY_PRESENTATION_COMPARATOR);
|
||||
ContainerUtil.sort(middleMatches, BY_PRESENTATION_COMPARATOR);
|
||||
@@ -247,7 +247,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
LookupElement relevantSelection = findMostRelevantItem(itemsBySorter);
|
||||
LookupImpl lookupImpl = (LookupImpl)lookup;
|
||||
List<LookupElement> listModel = isAlphaSorted() ?
|
||||
sortByPresentation(items, lookupImpl) :
|
||||
sortByPresentation(items) :
|
||||
fillModelByRelevance(lookupImpl, ContainerUtil.newIdentityTroveSet(items), itemsBySorter, relevantSelection);
|
||||
|
||||
int toSelect = getItemToSelect(lookupImpl, listModel, onExplicitAction, relevantSelection);
|
||||
@@ -371,7 +371,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
private ProcessingContext createContext(boolean pureRelevance) {
|
||||
ProcessingContext context = new ProcessingContext();
|
||||
context.put(PREFIX_CHANGES, myPrefixChanges);
|
||||
context.put(WEIGHING_CONTEXT, myProcess.getLookup());
|
||||
context.put(WEIGHING_CONTEXT, this);
|
||||
if (pureRelevance) {
|
||||
context.put(PURE_RELEVANCE, Boolean.TRUE);
|
||||
}
|
||||
@@ -417,7 +417,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
LookupElement item = items.get(i);
|
||||
boolean isSuddenLiveTemplate = isSuddenLiveTemplate(item);
|
||||
if (isPrefixItem(lookup, item, true) && !isSuddenLiveTemplate || item.getLookupString().equals(selectedText)) {
|
||||
if (isPrefixItem(item, true) && !isSuddenLiveTemplate || item.getLookupString().equals(selectedText)) {
|
||||
if (item instanceof LiveTemplateLookupElement) {
|
||||
// prefer most recent live template lookup item
|
||||
exactMatchIndex = i;
|
||||
@@ -428,7 +428,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
exactMatchIndex = i;
|
||||
}
|
||||
}
|
||||
else if (i == 0 && isSuddenLiveTemplate && items.size() > 1 && !CompletionServiceImpl.isStartMatch(items.get(1), lookup)) {
|
||||
else if (i == 0 && isSuddenLiveTemplate && items.size() > 1 && !CompletionServiceImpl.isStartMatch(items.get(1), this)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -593,11 +593,9 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
}
|
||||
|
||||
private class AlphaClassifier extends Classifier<LookupElement> {
|
||||
private final LookupImpl myLookup;
|
||||
|
||||
private AlphaClassifier(LookupImpl lookup) {
|
||||
private AlphaClassifier() {
|
||||
super(null, "alpha");
|
||||
myLookup = lookup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -609,7 +607,7 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterable<LookupElement> classify(@NotNull Iterable<LookupElement> source, @NotNull ProcessingContext context) {
|
||||
return sortByPresentation(source, myLookup);
|
||||
return sortByPresentation(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.EqualityPolicy;
|
||||
import com.intellij.util.containers.hash.LinkedHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -30,37 +32,54 @@ import java.util.*;
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public abstract class LookupArranger {
|
||||
protected final List<LookupElement> myItems = new ArrayList<LookupElement>();
|
||||
private final List<LookupElement> myMatchingItems = new ArrayList<LookupElement>();
|
||||
private final List<LookupElement> myExactPrefixItems = new ArrayList<LookupElement>();
|
||||
private final List<LookupElement> myInexactPrefixItems = new ArrayList<LookupElement>();
|
||||
public abstract class LookupArranger implements WeighingContext {
|
||||
protected final List<LookupElement> myItems = new ArrayList<>();
|
||||
private final List<LookupElement> myMatchingItems = new ArrayList<>();
|
||||
private final List<LookupElement> myExactPrefixItems = new ArrayList<>();
|
||||
private final List<LookupElement> myInexactPrefixItems = new ArrayList<>();
|
||||
private final Map<LookupElement, PrefixMatcher> myMatchers = ContainerUtil.createConcurrentWeakMap(ContainerUtil.identityStrategy());
|
||||
private String myAdditionalPrefix = "";
|
||||
|
||||
public void addElement(Lookup lookup, LookupElement item, LookupElementPresentation presentation) {
|
||||
public void addElement(LookupElement item, LookupElementPresentation presentation) {
|
||||
myItems.add(item);
|
||||
updateCache(lookup, item);
|
||||
updateCache(item);
|
||||
}
|
||||
|
||||
private void updateCache(Lookup lookup, LookupElement item) {
|
||||
if (!prefixMatches((LookupImpl)lookup, item)) {
|
||||
private void updateCache(LookupElement item) {
|
||||
if (!prefixMatches(item)) {
|
||||
return;
|
||||
}
|
||||
myMatchingItems.add(item);
|
||||
|
||||
if (isPrefixItem(lookup, item, true)) {
|
||||
if (isPrefixItem(item, true)) {
|
||||
myExactPrefixItems.add(item);
|
||||
} else if (isPrefixItem(lookup, item, false)) {
|
||||
} else if (isPrefixItem(item, false)) {
|
||||
myInexactPrefixItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean prefixMatches(LookupImpl lookup, LookupElement item) {
|
||||
PrefixMatcher matcher = lookup.itemMatcherNullable(item);
|
||||
if (matcher == null) {
|
||||
return false;
|
||||
}
|
||||
public void registerMatcher(@NotNull LookupElement item, @NotNull PrefixMatcher matcher) {
|
||||
myMatchers.put(item, matcher);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String itemPattern(@NotNull LookupElement element) {
|
||||
String prefix = itemMatcher(element).getPrefix();
|
||||
String additionalPrefix = myAdditionalPrefix;
|
||||
return additionalPrefix.isEmpty() ? prefix : prefix + additionalPrefix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PrefixMatcher itemMatcher(@NotNull LookupElement item) {
|
||||
PrefixMatcher matcher = myMatchers.get(item);
|
||||
if (matcher == null) {
|
||||
throw new AssertionError("Item not in lookup: item=" + item + "; lookup items=" + myItems);
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private boolean prefixMatches(LookupElement item) {
|
||||
PrefixMatcher matcher = itemMatcher(item);
|
||||
if (!myAdditionalPrefix.isEmpty()) {
|
||||
matcher = matcher.cloneWithPrefix(matcher.getPrefix() + myAdditionalPrefix);
|
||||
}
|
||||
@@ -70,22 +89,41 @@ public abstract class LookupArranger {
|
||||
public void itemSelected(@Nullable LookupElement lookupItem, char completionChar) {
|
||||
}
|
||||
|
||||
public void prefixChanged(Lookup lookup) {
|
||||
myAdditionalPrefix = ((LookupImpl)lookup).getAdditionalPrefix();
|
||||
rebuildItemCache(lookup);
|
||||
public final void prefixReplaced(Lookup lookup, String newPrefix) {
|
||||
//noinspection unchecked
|
||||
Map<LookupElement, PrefixMatcher> newMatchers = new LinkedHashMap(EqualityPolicy.IDENTITY);
|
||||
for (LookupElement item : myItems) {
|
||||
if (item.isValid()) {
|
||||
PrefixMatcher matcher = itemMatcher(item).cloneWithPrefix(newPrefix);
|
||||
if (matcher.prefixMatches(item)) {
|
||||
newMatchers.put(item, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
myMatchers.clear();
|
||||
myMatchers.putAll(newMatchers);
|
||||
myItems.clear();
|
||||
myItems.addAll(newMatchers.keySet());
|
||||
|
||||
prefixChanged(lookup);
|
||||
}
|
||||
|
||||
private void rebuildItemCache(Lookup lookup) {
|
||||
public void prefixChanged(Lookup lookup) {
|
||||
myAdditionalPrefix = ((LookupImpl)lookup).getAdditionalPrefix();
|
||||
rebuildItemCache();
|
||||
}
|
||||
|
||||
private void rebuildItemCache() {
|
||||
myMatchingItems.clear();
|
||||
myExactPrefixItems.clear();
|
||||
myInexactPrefixItems.clear();
|
||||
|
||||
for (LookupElement item : myItems) {
|
||||
updateCache(lookup, item);
|
||||
updateCache(item);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<LookupElement> retainItems(final Set<LookupElement> retained, Lookup lookup) {
|
||||
protected List<LookupElement> retainItems(final Set<LookupElement> retained) {
|
||||
List<LookupElement> filtered = ContainerUtil.newArrayList();
|
||||
List<LookupElement> removed = ContainerUtil.newArrayList();
|
||||
for (LookupElement item : myItems) {
|
||||
@@ -94,7 +132,7 @@ public abstract class LookupArranger {
|
||||
myItems.clear();
|
||||
myItems.addAll(filtered);
|
||||
|
||||
rebuildItemCache(lookup);
|
||||
rebuildItemCache();
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -106,8 +144,8 @@ public abstract class LookupArranger {
|
||||
return Collections.unmodifiableList(exactly ? myExactPrefixItems : myInexactPrefixItems);
|
||||
}
|
||||
|
||||
protected static boolean isPrefixItem(Lookup lookup, LookupElement item, final boolean exactly) {
|
||||
final String pattern = lookup.itemPattern(item);
|
||||
protected boolean isPrefixItem(LookupElement item, final boolean exactly) {
|
||||
final String pattern = itemPattern(item);
|
||||
if (Comparing.strEqual(pattern, item.getLookupString(), item.isCaseSensitive())) {
|
||||
return true;
|
||||
}
|
||||
@@ -147,7 +185,7 @@ public abstract class LookupArranger {
|
||||
|
||||
List<LookupElement> items = getMatchingItems();
|
||||
for (LookupElement item : items) {
|
||||
if (CompletionServiceImpl.isStartMatch(item, (LookupImpl)lookup)) {
|
||||
if (CompletionServiceImpl.isStartMatch(item, this)) {
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,11 +70,10 @@ import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LookupImpl extends LightweightHint implements LookupEx, Disposable, WeighingContext {
|
||||
public class LookupImpl extends LightweightHint implements LookupEx, Disposable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupImpl");
|
||||
|
||||
private final LookupOffsets myOffsets;
|
||||
@@ -121,8 +120,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
private boolean myChangeGuard;
|
||||
private volatile LookupArranger myArranger;
|
||||
private LookupArranger myPresentableArranger;
|
||||
private final Map<LookupElement, PrefixMatcher> myMatchers =
|
||||
ContainerUtil.createConcurrentWeakMap(ContainerUtil.identityStrategy());
|
||||
private final Map<LookupElement, Font> myCustomFonts = ContainerUtil.createConcurrentWeakMap(10, 0.75f, Runtime.getRuntime().availableProcessors(),
|
||||
ContainerUtil.identityStrategy());
|
||||
private boolean myStartCompletionWhenNothingMatches;
|
||||
@@ -238,10 +235,10 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
return false;
|
||||
}
|
||||
|
||||
myMatchers.put(item, matcher);
|
||||
updateLookupWidth(item, presentation);
|
||||
withLock(() -> {
|
||||
myArranger.addElement(this, item, presentation);
|
||||
myArranger.registerMatcher(item, matcher);
|
||||
myArranger.addElement(item, presentation);
|
||||
return null;
|
||||
});
|
||||
return true;
|
||||
@@ -423,7 +420,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
|
||||
private void addEmptyItem(CollectionListModel<LookupElement> model) {
|
||||
LookupElement item = new EmptyLookupItem(myCalculating ? " " : LangBundle.message("completion.no.suggestions"), false);
|
||||
myMatchers.put(item, new CamelHumpMatcher(""));
|
||||
model.add(item);
|
||||
|
||||
updateLookupWidth(item);
|
||||
@@ -439,23 +435,17 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
@NotNull
|
||||
@Override
|
||||
public String itemPattern(@NotNull LookupElement element) {
|
||||
String prefix = itemMatcher(element).getPrefix();
|
||||
String additionalPrefix = getAdditionalPrefix();
|
||||
return additionalPrefix.isEmpty() ? prefix : prefix + additionalPrefix;
|
||||
if (element instanceof EmptyLookupItem) return "";
|
||||
return myPresentableArranger.itemPattern(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PrefixMatcher itemMatcher(@NotNull LookupElement item) {
|
||||
PrefixMatcher matcher = itemMatcherNullable(item);
|
||||
if (matcher == null) {
|
||||
throw new AssertionError("Item not in lookup: item=" + item + "; lookup items=" + getItems());
|
||||
if (item instanceof EmptyLookupItem) {
|
||||
return new CamelHumpMatcher("");
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
public PrefixMatcher itemMatcherNullable(LookupElement item) {
|
||||
return myMatchers.get(item);
|
||||
return myPresentableArranger.itemMatcher(item);
|
||||
}
|
||||
|
||||
public void finishLookup(final char completionChar) {
|
||||
@@ -912,35 +902,18 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
}
|
||||
|
||||
public void replacePrefix(final String presentPrefix, final String newPrefix) {
|
||||
if (!performGuardedChange(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
EditorModificationUtil.deleteSelectedText(myEditor);
|
||||
int offset = myEditor.getCaretModel().getOffset();
|
||||
final int start = offset - presentPrefix.length();
|
||||
myEditor.getDocument().replaceString(start, offset, newPrefix);
|
||||
|
||||
Map<LookupElement, PrefixMatcher> newMatchers = new HashMap<LookupElement, PrefixMatcher>();
|
||||
for (LookupElement item : getItems()) {
|
||||
if (item.isValid()) {
|
||||
PrefixMatcher matcher = itemMatcher(item).cloneWithPrefix(newPrefix);
|
||||
if (matcher.prefixMatches(item)) {
|
||||
newMatchers.put(item, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
myMatchers.clear();
|
||||
myMatchers.putAll(newMatchers);
|
||||
|
||||
myOffsets.clearAdditionalPrefix();
|
||||
|
||||
myEditor.getCaretModel().moveToOffset(start + newPrefix.length());
|
||||
}
|
||||
if (!performGuardedChange(() -> {
|
||||
EditorModificationUtil.deleteSelectedText(myEditor);
|
||||
int offset = myEditor.getCaretModel().getOffset();
|
||||
final int start = offset - presentPrefix.length();
|
||||
myEditor.getDocument().replaceString(start, offset, newPrefix);
|
||||
myOffsets.clearAdditionalPrefix();
|
||||
myEditor.getCaretModel().moveToOffset(start + newPrefix.length());
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
withLock(() -> {
|
||||
myPresentableArranger.prefixChanged(this);
|
||||
myPresentableArranger.prefixReplaced(this, newPrefix);
|
||||
return null;
|
||||
});
|
||||
refreshUi(true, true);
|
||||
@@ -1162,6 +1135,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setPrefixChangeListener(PrefixChangeListener listener) {
|
||||
myPrefixChangeListener = listener;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user