an ability to sort lookup items alphabetically (IDEA-60529)

This commit is contained in:
peter
2011-02-01 15:21:52 +01:00
parent 610bf4a6cd
commit 5311b6cb0d
7 changed files with 107 additions and 29 deletions
@@ -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