mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
an ability to sort lookup items alphabetically (IDEA-60529)
This commit is contained in:
+1
@@ -90,6 +90,7 @@ public abstract class CompletionSortingTestCase extends LightCompletionTestCase
|
||||
item.putUserData(CompletionLookupArranger.WEIGHT, null);
|
||||
item.putUserData(CompletionLookupArranger.RELEVANCE_KEY, null);
|
||||
}
|
||||
lookup.setSelectionTouched(false);
|
||||
lookup.resort();
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 316 B |
Binary file not shown.
|
After Width: | Height: | Size: 462 B |
@@ -32,6 +32,22 @@ public abstract class LookupArranger {
|
||||
}
|
||||
|
||||
};
|
||||
public static final LookupArranger LEXICOGRAPHIC = new LookupArranger() {
|
||||
@Override
|
||||
public Comparable getRelevance(LookupElement element) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<LookupElement> getItemComparator() {
|
||||
return new Comparator<LookupElement>() {
|
||||
@Override
|
||||
public int compare(LookupElement o1, LookupElement o2) {
|
||||
return o1.getLookupString().compareTo(o2.getLookupString());
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public abstract Comparable getRelevance(LookupElement element);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.codeInsight.hint.HintManagerImpl;
|
||||
import com.intellij.codeInsight.hint.HintUtil;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.lang.LangBundle;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
@@ -66,6 +67,7 @@ import org.jetbrains.annotations.TestOnly;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
@@ -79,6 +81,9 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
private static final int MAX_PREFERRED_COUNT = 5;
|
||||
|
||||
private static final LookupItem EMPTY_LOOKUP_ITEM = LookupItem.fromString("preselect");
|
||||
private static final int LOOKUP_HEIGHT = Integer.getInteger("idea.lookup.height", 11).intValue();
|
||||
private static final Icon relevanceSortIcon = IconLoader.getIcon("/ide/lookupRelevance.png");
|
||||
private static final Icon lexiSortIcon = IconLoader.getIcon("/ide/lookupAlphanumeric.png");
|
||||
|
||||
private final Project myProject;
|
||||
private final Editor myEditor;
|
||||
@@ -86,7 +91,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
private int myMinPrefixLength;
|
||||
private int myPreferredItemsCount;
|
||||
private String myInitialPrefix;
|
||||
private LookupArranger myArranger;
|
||||
private LookupArranger myCustomArranger;
|
||||
|
||||
private RangeMarker myLookupStartMarker;
|
||||
private final JList myList = new JBList(new DefaultListModel());
|
||||
@@ -111,12 +116,13 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
private final JLabel myAdComponent;
|
||||
private volatile String myAdText;
|
||||
private volatile int myLookupTextWidth = 50;
|
||||
private static final int LOOKUP_HEIGHT = Integer.getInteger("idea.lookup.height", 11).intValue();
|
||||
private boolean myReused;
|
||||
private boolean myChangeGuard;
|
||||
private LookupModel myModel = new LookupModel();
|
||||
private LookupHint myElementHint = null;
|
||||
private Alarm myHintAlarm = new Alarm();
|
||||
private JLabel mySortingLabel;
|
||||
private final JScrollPane myScrollPane;
|
||||
|
||||
public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger){
|
||||
super(new JPanel(new BorderLayout()));
|
||||
@@ -133,11 +139,12 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
myList.setBackground(LookupCellRenderer.BACKGROUND_COLOR);
|
||||
|
||||
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myList);
|
||||
scrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
getComponent().add(scrollPane, BorderLayout.NORTH);
|
||||
scrollPane.setBorder(null);
|
||||
myScrollPane = ScrollPaneFactory.createScrollPane(myList);
|
||||
myScrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
myScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, -1));
|
||||
getComponent().add(myScrollPane, BorderLayout.NORTH);
|
||||
myScrollPane.setBorder(null);
|
||||
|
||||
myAdComponent = HintUtil.createAdComponent(null);
|
||||
getComponent().add(myAdComponent, BorderLayout.SOUTH);
|
||||
@@ -153,11 +160,29 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
setArranger(arranger);
|
||||
|
||||
addListeners();
|
||||
|
||||
mySortingLabel = new JLabel();
|
||||
mySortingLabel.setBorder(new LineBorder(Color.DARK_GRAY));
|
||||
mySortingLabel.setOpaque(true);
|
||||
mySortingLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = !UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY;
|
||||
updateSorting();
|
||||
}
|
||||
});
|
||||
updateSorting();
|
||||
}
|
||||
|
||||
private void updateSorting() {
|
||||
mySortingLabel.setIcon(UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY ? lexiSortIcon : relevanceSortIcon);
|
||||
myModel.setArranger(getActualArranger());
|
||||
resort();
|
||||
}
|
||||
|
||||
public void setArranger(LookupArranger arranger) {
|
||||
myArranger = arranger;
|
||||
myModel.setArranger(arranger);
|
||||
myCustomArranger = arranger;
|
||||
myModel.setArranger(getActualArranger());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,8 +221,11 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void setSelectionTouched(boolean selectionTouched) {
|
||||
mySelectionTouched = selectionTouched;
|
||||
}
|
||||
|
||||
public void resort() {
|
||||
mySelectionTouched = false;
|
||||
myFrozenItems.clear();
|
||||
myPreselectedItem = EMPTY_LOOKUP_ITEM;
|
||||
final List<LookupElement> items = myModel.getItems();
|
||||
@@ -206,6 +234,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
addItem(item);
|
||||
}
|
||||
updateList();
|
||||
ListScrollingUtil.ensureIndexIsVisible(myList, myList.getSelectedIndex(), 0);
|
||||
}
|
||||
|
||||
public void addItem(LookupElement item) {
|
||||
@@ -472,10 +501,11 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
}
|
||||
|
||||
private boolean addExactPrefixItems(DefaultListModel model, Set<LookupElement> firstItems, final Collection<LookupElement> elements) {
|
||||
final LookupArranger arranger = getActualArranger();
|
||||
List<LookupElement> sorted = new SortedList<LookupElement>(new Comparator<LookupElement>() {
|
||||
public int compare(LookupElement o1, LookupElement o2) {
|
||||
//noinspection unchecked
|
||||
return myArranger.getRelevance(o1).compareTo(myArranger.getRelevance(o2));
|
||||
return arranger.getRelevance(o1).compareTo(arranger.getRelevance(o2));
|
||||
}
|
||||
});
|
||||
for (final LookupElement item : elements) {
|
||||
@@ -492,6 +522,13 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
return !firstItems.isEmpty();
|
||||
}
|
||||
|
||||
private LookupArranger getActualArranger() {
|
||||
if (UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY) {
|
||||
return LookupArranger.LEXICOGRAPHIC;
|
||||
}
|
||||
return myCustomArranger;
|
||||
}
|
||||
|
||||
private boolean isExactPrefixItem(LookupElement item) {
|
||||
return item.getAllLookupStrings().contains(item.getPrefixMatcher().getPrefix() + myAdditionalPrefix);
|
||||
}
|
||||
@@ -520,7 +557,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
JComponent internalComponent = myEditor.getContentComponent();
|
||||
final JRootPane rootPane = editorComponent.getRootPane();
|
||||
if (rootPane == null) {
|
||||
LOG.error(myArranger + "; " + myEditor.isDisposed());
|
||||
LOG.error(myEditor.isDisposed());
|
||||
}
|
||||
JLayeredPane layeredPane = rootPane.getLayeredPane();
|
||||
Point layeredPanePoint=SwingUtilities.convertPoint(internalComponent,location, layeredPane);
|
||||
@@ -620,12 +657,16 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
|
||||
getComponent().setBorder(null);
|
||||
updateScrollbarVisibility();
|
||||
|
||||
Point p = calculatePosition();
|
||||
HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
||||
hintManager.showEditorHint(this, myEditor, p, HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false);
|
||||
HintManagerImpl.getInstanceImpl().showEditorHint(this, myEditor, p, HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false);
|
||||
|
||||
getComponent().getRootPane().getLayeredPane().add(myIconPanel, 42, 0);
|
||||
final JLayeredPane layeredPane = getComponent().getRootPane().getLayeredPane();
|
||||
layeredPane.add(myIconPanel, 42, 0);
|
||||
layeredPane.add(mySortingLabel, 10, 0);
|
||||
|
||||
layoutStatusIcons();
|
||||
}
|
||||
|
||||
public boolean mayBeNoticed() {
|
||||
@@ -800,7 +841,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
|
||||
|
||||
if (item != null) {
|
||||
myArranger.itemSelected(item, this);
|
||||
getActualArranger().itemSelected(item, this);
|
||||
}
|
||||
|
||||
if (!myListeners.isEmpty()){
|
||||
@@ -949,7 +990,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
}
|
||||
|
||||
public boolean isCompletion() {
|
||||
return myArranger instanceof CompletionLookupArranger;
|
||||
return myCustomArranger instanceof CompletionLookupArranger;
|
||||
}
|
||||
|
||||
public PsiElement getPsiElement() {
|
||||
@@ -1047,7 +1088,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
final int index = myArranger.suggestPreselectedItem(items);
|
||||
final int index = getActualArranger().suggestPreselectedItem(items);
|
||||
assert index >= 0 && index < items.size();
|
||||
return index;
|
||||
}
|
||||
@@ -1055,22 +1096,41 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
|
||||
public void refreshUi() {
|
||||
updateList();
|
||||
|
||||
if (isVisible() && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
if (isVisible()) {
|
||||
LOG.assertTrue(!ApplicationManager.getApplication().isUnitTestMode());
|
||||
|
||||
if (myEditor.getComponent().getRootPane() == null) {
|
||||
LOG.error("Null root pane");
|
||||
}
|
||||
|
||||
Point point = calculatePosition();
|
||||
|
||||
final Dimension size = myProcessIcon.getPreferredSize();
|
||||
myIconPanel.setBounds(getComponent().getRootPane().getLayeredPane().getWidth() - size.width, 0, size.width, size.height);
|
||||
|
||||
HintManagerImpl.adjustEditorHintPosition(this, myEditor, point);
|
||||
updateScrollbarVisibility();
|
||||
HintManagerImpl.adjustEditorHintPosition(this, myEditor, calculatePosition());
|
||||
layoutStatusIcons();
|
||||
}
|
||||
}
|
||||
|
||||
private void layoutStatusIcons() {
|
||||
final JLayeredPane layeredPane = getComponent().getRootPane().getLayeredPane();
|
||||
final int width = layeredPane.getWidth();
|
||||
final int height = layeredPane.getHeight();
|
||||
|
||||
final Dimension iconSize = myProcessIcon.getPreferredSize();
|
||||
myIconPanel.setBounds(width - iconSize.width, 0, iconSize.width, iconSize.height);
|
||||
|
||||
final Dimension sortSize = mySortingLabel.getPreferredSize();
|
||||
mySortingLabel.setBounds(width - sortSize.width, height - sortSize.height, sortSize.width, sortSize.height);
|
||||
|
||||
}
|
||||
|
||||
private void updateScrollbarVisibility() {
|
||||
boolean showSorting = getList().getModel().getSize() >= 3;
|
||||
mySortingLabel.setVisible(showSorting);
|
||||
myScrollPane.setVerticalScrollBarPolicy(showSorting ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public LookupArranger getArranger() {
|
||||
return myArranger;
|
||||
return getActualArranger();
|
||||
}
|
||||
|
||||
public void markReused() {
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.intellij.util.containers.SortedList;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -50,9 +49,10 @@ public class LookupModel {
|
||||
private TreeMap<Comparable, SortedList<LookupElement>> myRelevanceGroups;
|
||||
private LookupArranger myArranger;
|
||||
|
||||
@TestOnly
|
||||
public List<LookupElement> getItems() {
|
||||
return new ArrayList<LookupElement>(myItems);
|
||||
synchronized (lock) {
|
||||
return new ArrayList<LookupElement>(myItems);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearItems() {
|
||||
|
||||
@@ -84,6 +84,7 @@ public class UISettings implements PersistentStateComponent<UISettings>, Exporta
|
||||
public boolean OVERRIDE_NONIDEA_LAF_FONTS = false;
|
||||
public boolean SHOW_ICONS_IN_MENUS = true; // Only makes sense on MacOS
|
||||
public boolean DISABLE_MNEMONICS = SystemInfo.isMac; // IDEADEV-33409, should be disabled by default on MacOS
|
||||
public boolean SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = false;
|
||||
|
||||
/**
|
||||
* Defines whether asterisk is shown on modified editor tab or not
|
||||
|
||||
Reference in New Issue
Block a user