[peter] 'no sug' fixed
[fedora-idea.git] / lang-impl / src / com / intellij / codeInsight / lookup / impl / LookupImpl.java
blobd05a9dafb2460a2b7d58a5322adca980de45e66a
1 package com.intellij.codeInsight.lookup.impl;
3 import com.intellij.codeInsight.CodeInsightSettings;
4 import com.intellij.codeInsight.completion.CompletionPreferencePolicy;
5 import com.intellij.codeInsight.completion.PrefixMatcher;
6 import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
7 import com.intellij.codeInsight.hint.HintUtil;
8 import com.intellij.codeInsight.hint.HintManagerImpl;
9 import com.intellij.codeInsight.lookup.*;
10 import com.intellij.featureStatistics.FeatureUsageTracker;
11 import com.intellij.ide.IdeEventQueue;
12 import com.intellij.lang.LangBundle;
13 import com.intellij.openapi.Disposable;
14 import com.intellij.openapi.application.ApplicationManager;
15 import com.intellij.openapi.command.CommandProcessor;
16 import com.intellij.openapi.diagnostic.Logger;
17 import com.intellij.openapi.editor.*;
18 import com.intellij.openapi.editor.event.*;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.util.Disposer;
21 import com.intellij.openapi.util.text.StringUtil;
22 import com.intellij.psi.PsiDocumentManager;
23 import com.intellij.psi.PsiElement;
24 import com.intellij.psi.PsiFile;
25 import com.intellij.psi.util.proximity.PsiProximityComparator;
26 import com.intellij.ui.LightweightHint;
27 import com.intellij.ui.ListScrollingUtil;
28 import com.intellij.ui.plaf.beg.BegPopupMenuBorder;
29 import com.intellij.util.SmartList;
30 import com.intellij.util.ui.AsyncProcessIcon;
31 import gnu.trove.THashSet;
32 import org.jetbrains.annotations.Nullable;
33 import org.jetbrains.annotations.TestOnly;
35 import javax.swing.*;
36 import javax.swing.event.ListSelectionEvent;
37 import javax.swing.event.ListSelectionListener;
38 import java.awt.*;
39 import java.awt.event.MouseAdapter;
40 import java.awt.event.MouseEvent;
41 import java.util.*;
42 import java.util.List;
44 public class LookupImpl extends LightweightHint implements Lookup, Disposable {
45 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupImpl");
46 private static final int MAX_PREFERRED_COUNT = 5;
48 private static final LookupItem EMPTY_LOOKUP_ITEM = new LookupItem("preselect", "preselect");
50 private final Project myProject;
51 private final Editor myEditor;
52 private final SortedSet<LookupElement> myItems;
53 private final SortedMap<LookupItemWeightComparable, SortedSet<LookupElement>> myItemsMap;
54 private int myMinPrefixLength;
55 private int myPreferredItemsCount;
56 private int myInitialOffset;
57 private final LookupItemPreferencePolicy myItemPreferencePolicy;
59 private RangeMarker myLookupStartMarker;
60 private final JList myList;
61 private final LookupCellRenderer myCellRenderer;
62 private Boolean myPositionedAbove = null;
64 private CaretListener myEditorCaretListener;
65 private EditorMouseListener myEditorMouseListener;
67 private final ArrayList<LookupListener> myListeners = new ArrayList<LookupListener>();
69 private boolean myDisposed = false;
70 private LookupElement myPreselectedItem = EMPTY_LOOKUP_ITEM;
71 private boolean myDirty;
72 private String myAdditionalPrefix = "";
73 private PsiElement myElement;
74 private final AsyncProcessIcon myProcessIcon;
75 private final Comparator<? super LookupElement> myComparator;
76 private volatile boolean myCalculating;
78 public LookupImpl(Project project,
79 Editor editor,
80 LookupElement[] items, LookupItemPreferencePolicy itemPreferencePolicy, final String bottomText){
81 super(new JPanel(new BorderLayout()));
82 myProject = project;
83 myEditor = editor;
84 myItemPreferencePolicy = itemPreferencePolicy;
85 myInitialOffset = myEditor.getSelectionModel().hasSelection() ? myEditor.getSelectionModel().getSelectionStart() : myEditor.getCaretModel().getOffset();
87 final Document document = myEditor.getDocument();
88 final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
89 myElement = psiFile == null ? null : psiFile.findElementAt(myEditor.getCaretModel().getOffset());
91 final PsiProximityComparator proximityComparator = new PsiProximityComparator(myElement == null ? psiFile : myElement);
92 myComparator = new Comparator<LookupElement>() {
93 public int compare(LookupElement o1, LookupElement o2) {
94 if (o1 instanceof LookupItem && o2 instanceof LookupItem) {
95 double priority1 = ((LookupItem)o1).getPriority();
96 double priority2 = ((LookupItem)o2).getPriority();
97 if (priority1 > priority2) return -1;
98 if (priority2 > priority1) return 1;
100 int grouping1 = ((LookupItem)o1).getGrouping();
101 int grouping2 = ((LookupItem)o2).getGrouping();
102 if (grouping1 > grouping2) return -1;
103 if (grouping2 > grouping1) return 1;
106 int stringCompare = o1.getLookupString().compareToIgnoreCase(o2.getLookupString());
107 if (stringCompare != 0) return stringCompare;
109 final int proximityCompare = proximityComparator.compare(o1.getObject(), o2.getObject());
110 if (proximityCompare != 0) return proximityCompare;
112 return o1.hashCode() - o2.hashCode();
115 myItems = new TreeSet<LookupElement>(myComparator);
116 myItemsMap = new TreeMap<LookupItemWeightComparable, SortedSet<LookupElement>>();
118 myProcessIcon = new AsyncProcessIcon("Completion progress");
119 myProcessIcon.setVisible(false);
120 myList = new JList(new DefaultListModel());
121 myCellRenderer = new LookupCellRenderer(this);
122 myList.setCellRenderer(myCellRenderer);
123 myList.setFixedCellWidth(50);
125 for (final LookupElement item : items) {
126 addItem(item);
129 updateList();
131 myList.setFocusable(false);
133 myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
134 myList.setBackground(LookupCellRenderer.BACKGROUND_COLOR);
136 JScrollPane scrollPane = new JScrollPane(myList);
137 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
138 getComponent().add(scrollPane, BorderLayout.NORTH);
139 scrollPane.setBorder(null);
141 JPanel bottomPanel = new JPanel(new BorderLayout());
143 bottomPanel.add(myProcessIcon, BorderLayout.EAST);
144 final JComponent adComponent = HintUtil.createAdComponent(bottomText);
145 if (StringUtil.isNotEmpty(bottomText)) {
146 adComponent.setPreferredSize(new Dimension(adComponent.getPreferredSize().width, myProcessIcon.getPreferredSize().height));
148 bottomPanel.add(adComponent, BorderLayout.CENTER);
149 getComponent().add(bottomPanel, BorderLayout.SOUTH);
150 getComponent().setBorder(new BegPopupMenuBorder());
153 selectMostPreferableItem();
156 public AsyncProcessIcon getProcessIcon() {
157 return myProcessIcon;
160 public void setCalculating(final boolean calculating) {
161 myCalculating = calculating;
164 public int getPreferredItemsCount() {
165 return myPreferredItemsCount;
168 public void markDirty() {
169 myDirty = true;
170 myPreselectedItem = null;
173 @TestOnly
174 public void resort() {
175 final ArrayList<LookupElement> items = new ArrayList<LookupElement>(myItems);
176 myDirty = false;
177 myPreselectedItem = EMPTY_LOOKUP_ITEM;
178 myItemsMap.clear();
179 myItems.clear();
180 for (final LookupElement item : items) {
181 addItem(item);
183 updateList();
186 public synchronized void addItem(LookupElement item) {
187 myItems.add(item);
188 addItemWeight(item);
189 int maxWidth = myCellRenderer.updateMaximumWidth(item);
190 myList.setFixedCellWidth(Math.max(maxWidth, myList.getFixedCellWidth()));
193 private void addItemWeight(final LookupElement item) {
194 final Comparable[] weight = getWeight(myItemPreferencePolicy, myElement, item);
195 final LookupItemWeightComparable key = new LookupItemWeightComparable(item instanceof LookupItem ? ((LookupItem)item).getPriority() : 0, weight);
196 SortedSet<LookupElement> list = myItemsMap.get(key);
197 if (list == null) myItemsMap.put(key, list = new TreeSet<LookupElement>(myComparator));
198 list.add(item);
201 public LookupItemPreferencePolicy getItemPreferencePolicy() {
202 return myItemPreferencePolicy;
205 private static Comparable[] getWeight(final LookupItemPreferencePolicy itemPreferencePolicy, final PsiElement context,
206 final LookupElement item) {
207 if (itemPreferencePolicy instanceof CompletionPreferencePolicy) {
208 return ((CompletionPreferencePolicy)itemPreferencePolicy).getWeight(item);
210 Comparable i = null;
211 if (item.getObject() instanceof PsiElement) {
212 i = PsiProximityComparator.getProximity((PsiElement)item.getObject(), context);
214 return new Comparable[]{i};
217 public int getMinPrefixLength() {
218 return myMinPrefixLength;
221 public JList getList() {
222 return myList;
225 public synchronized LookupElement[] getItems(){
226 return myItems.toArray(new LookupElement[myItems.size()]);
229 public String getAdditionalPrefix() {
230 return myAdditionalPrefix;
233 public void setAdditionalPrefix(final String additionalPrefix) {
234 myAdditionalPrefix = additionalPrefix;
235 markDirty();
236 updateList();
239 public final synchronized void updateList() {
240 int minPrefixLength = myItems.isEmpty() ? 0 : Integer.MAX_VALUE;
241 for (final LookupElement item : myItems) {
242 minPrefixLength = Math.min(item.getPrefixMatcher().getPrefix().length(), minPrefixLength);
244 if (myMinPrefixLength != minPrefixLength) {
245 myLookupStartMarker = null;
247 myMinPrefixLength = minPrefixLength;
249 Object oldSelected = !myDirty ? null : myList.getSelectedValue();
250 DefaultListModel model = (DefaultListModel)myList.getModel();
251 model.clear();
253 ArrayList<LookupElement> allItems = new ArrayList<LookupElement>();
254 Set<LookupElement> firstItems = new THashSet<LookupElement>();
256 final boolean hasPreselectedItem = !myDirty && myPreselectedItem != EMPTY_LOOKUP_ITEM;
257 boolean addPreselected = hasPreselectedItem;
259 for (final LookupItemWeightComparable comparable : myItemsMap.keySet()) {
260 final List<LookupElement> suitable = new SmartList<LookupElement>();
261 for (final LookupElement item : myItemsMap.get(comparable)) {
262 if (prefixMatches(item)) {
263 suitable.add(item);
267 if (allItems.size() + suitable.size() + (addPreselected ? 1 : 0) > MAX_PREFERRED_COUNT) break;
268 for (final LookupElement item : suitable) {
269 allItems.add(item);
270 firstItems.add(item);
271 model.addElement(item);
273 if (hasPreselectedItem && item == myPreselectedItem) {
274 addPreselected = false;
278 if (addPreselected) {
279 allItems.add(myPreselectedItem);
280 firstItems.add(myPreselectedItem);
281 model.addElement(myPreselectedItem);
284 myPreferredItemsCount = allItems.size();
286 for (LookupElement item : myItems) {
287 if (!firstItems.contains(item) && prefixMatches(item)) {
288 model.addElement(item);
289 allItems.add(item);
292 boolean isEmpty = allItems.isEmpty();
293 if (isEmpty) {
294 LookupItem<String> item = new EmptyLookupItem(myCalculating ? " " : LangBundle.message("completion.no.suggestions"));
295 item.setPrefixMatcher(new CamelHumpMatcher(""));
296 if (!myCalculating) {
297 final int maxWidth = myCellRenderer.updateMaximumWidth(item);
298 myList.setFixedCellWidth(Math.max(maxWidth, myList.getFixedCellWidth()));
301 model.addElement(item);
302 allItems.add(item);
304 myList.setFixedCellHeight(myCellRenderer.getListCellRendererComponent(myList, myList.getModel().getElementAt(0), 0, false, false).getPreferredSize().height);
306 myList.setVisibleRowCount(Math.min(myList.getModel().getSize(), CodeInsightSettings.getInstance().LOOKUP_HEIGHT));
308 if (!isEmpty) {
309 if (oldSelected != null) {
310 if (!ListScrollingUtil.selectItem(myList, oldSelected)) {
311 selectMostPreferableItem();
313 } else {
314 if (myPreselectedItem == EMPTY_LOOKUP_ITEM) {
315 selectMostPreferableItem();
316 myPreselectedItem = getCurrentItem();
317 } else if (hasPreselectedItem) {
318 ListScrollingUtil.selectItem(myList, myPreselectedItem);
319 } else {
320 selectMostPreferableItem();
326 private boolean prefixMatches(final LookupElement item) {
327 if (myAdditionalPrefix.length() == 0) return item.isPrefixMatched();
329 return item.getPrefixMatcher().cloneWithPrefix(item.getPrefixMatcher().getPrefix() + myAdditionalPrefix).prefixMatches(item);
333 * @return point in layered pane coordinate system.
335 public Point calculatePosition(){
336 Dimension dim = getComponent().getPreferredSize();
337 int lookupStart = getLookupStart();
338 LogicalPosition pos = myEditor.offsetToLogicalPosition(lookupStart);
339 Point location = myEditor.logicalPositionToXY(pos);
340 location.y += myEditor.getLineHeight();
341 JComponent editorComponent = myEditor.getComponent();
342 JComponent internalComponent = myEditor.getContentComponent();
343 final JRootPane rootPane = editorComponent.getRootPane();
344 LOG.assertTrue(rootPane != null, myItems);
345 JLayeredPane layeredPane = rootPane.getLayeredPane();
346 Point layeredPanePoint=SwingUtilities.convertPoint(internalComponent,location, layeredPane);
347 layeredPanePoint.x -= myCellRenderer.getIconIndent();
348 if (dim.width > layeredPane.getWidth()){
349 dim.width = layeredPane.getWidth();
351 int wshift = layeredPane.getWidth() - (layeredPanePoint.x + dim.width);
352 if (wshift < 0){
353 layeredPanePoint.x += wshift;
356 int shiftLow = layeredPane.getHeight() - (layeredPanePoint.y + dim.height);
357 int shiftHigh = layeredPanePoint.y - dim.height;
358 myPositionedAbove = shiftLow < 0 && shiftLow < shiftHigh ? Boolean.TRUE : Boolean.FALSE;
360 if (myPositionedAbove.booleanValue()){
361 layeredPanePoint.y -= dim.height + myEditor.getLineHeight();
363 return layeredPanePoint;
366 public void finishLookup(final char completionChar) {
367 final LookupElement item = (LookupElement)myList.getSelectedValue();
368 doHide(false);
369 if (item == null ||
370 item instanceof EmptyLookupItem ||
371 item.getObject() instanceof DeferredUserLookupValue && item instanceof LookupItem &&
372 !((DeferredUserLookupValue)item.getObject()).handleUserSelection((LookupItem)item, myProject)) {
373 fireItemSelected(null, completionChar);
374 return;
377 ApplicationManager.getApplication().runWriteAction(new Runnable() {
378 public void run(){
379 EditorModificationUtil.deleteSelectedText(myEditor);
380 final int caretOffset = myEditor.getCaretModel().getOffset();
381 final String prefix = item.getPrefixMatcher().getPrefix();
382 int lookupStart = caretOffset - prefix.length() - myAdditionalPrefix.length();
384 final String lookupString = item.getLookupString();
385 if (!lookupString.startsWith(prefix + myAdditionalPrefix)) {
386 FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.camelHumps");
389 myEditor.getDocument().replaceString(lookupStart, caretOffset, lookupString);
391 int offset = lookupStart + lookupString.length();
392 myEditor.getCaretModel().moveToOffset(offset);
393 myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
394 myEditor.getSelectionModel().removeSelection();
398 fireItemSelected(item, completionChar);
401 public int getLookupStart() {
402 return myLookupStartMarker != null ? myLookupStartMarker.getStartOffset() : calcLookupStart();
405 public void show(){
406 int lookupStart = calcLookupStart();
407 myLookupStartMarker = myEditor.getDocument().createRangeMarker(lookupStart, lookupStart);
408 myLookupStartMarker.setGreedyToLeft(true);
410 myEditor.getDocument().addDocumentListener(new DocumentAdapter() {
411 public void documentChanged(DocumentEvent e) {
412 if (myLookupStartMarker != null && !myLookupStartMarker.isValid()){
413 hide();
416 }, this);
418 myEditorCaretListener = new CaretListener() {
419 public void caretPositionChanged(CaretEvent e){
420 int curOffset = myEditor.getCaretModel().getOffset();
421 final LookupElement item = getCurrentItem();
422 if (item == null || item == EMPTY_LOOKUP_ITEM) return;
424 if (curOffset != myInitialOffset + myAdditionalPrefix.length()) {
425 hide();
429 myEditor.getCaretModel().addCaretListener(myEditorCaretListener);
431 myEditorMouseListener = new EditorMouseAdapter() {
432 public void mouseClicked(EditorMouseEvent e){
433 e.consume();
434 hide();
437 myEditor.addEditorMouseListener(myEditorMouseListener);
439 myList.addListSelectionListener(new ListSelectionListener() {
440 public void valueChanged(ListSelectionEvent e){
441 LookupElement item = getCurrentItem();
442 fireCurrentItemChanged(item);
446 myList.addMouseListener(new MouseAdapter() {
447 public void mouseClicked(MouseEvent e){
448 if (e.getClickCount() == 2){
449 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
450 public void run() {
451 finishLookup(NORMAL_SELECT_CHAR);
453 }, "", null);
458 if (ApplicationManager.getApplication().isUnitTestMode()) return;
460 Point p = calculatePosition();
461 HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
462 hintManager.showEditorHint(this, myEditor, p, HintManagerImpl.HIDE_BY_ESCAPE | HintManagerImpl.UPDATE_BY_SCROLLING, 0, false);
465 private int calcLookupStart() {
466 int offset = myEditor.getSelectionModel().hasSelection()
467 ? myEditor.getSelectionModel().getSelectionStart()
468 : myEditor.getCaretModel().getOffset();
469 return offset - myMinPrefixLength;
472 private void selectMostPreferableItem(){
473 final int index = doSelectMostPreferableItem(myItemPreferencePolicy, ((DefaultListModel)myList.getModel()).toArray());
474 myList.setSelectedIndex(index);
476 if (index >= 0 && index < myList.getModel().getSize()){
477 ListScrollingUtil.selectItem(myList, index);
479 else if (!myItems.isEmpty()) {
480 ListScrollingUtil.selectItem(myList, 0);
484 @Nullable
485 public LookupElement getCurrentItem(){
486 LookupElement item = (LookupElement)myList.getSelectedValue();
487 return item instanceof EmptyLookupItem ? null : item;
490 public void setCurrentItem(LookupElement item){
491 ListScrollingUtil.selectItem(myList, item);
494 public void addLookupListener(LookupListener listener){
495 myListeners.add(listener);
498 public void removeLookupListener(LookupListener listener){
499 myListeners.remove(listener);
502 public Rectangle getCurrentItemBounds(){
503 int index = myList.getSelectedIndex();
504 Rectangle itmBounds = myList.getCellBounds(index, index);
505 if (itmBounds == null){
506 return null;
508 Rectangle listBounds=myList.getBounds();
509 final JRootPane pane = myList.getRootPane();
510 if (pane == null) {
511 LOG.assertTrue(false, myItems);
513 JLayeredPane layeredPane= pane.getLayeredPane();
514 Point layeredPanePoint=SwingUtilities.convertPoint(myList,listBounds.x,listBounds.y,layeredPane);
515 itmBounds.x = layeredPanePoint.x;
516 itmBounds.y = layeredPanePoint.y;
517 return itmBounds;
520 public void fireItemSelected(final LookupElement item, char completionChar){
521 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
523 if (item != null && myItemPreferencePolicy != null){
524 myItemPreferencePolicy.itemSelected(item, this);
527 if (!myListeners.isEmpty()){
528 LookupEvent event = new LookupEvent(this, item, completionChar);
529 LookupListener[] listeners = myListeners.toArray(new LookupListener[myListeners.size()]);
530 for (LookupListener listener : listeners) {
531 try {
532 listener.itemSelected(event);
534 catch (Throwable e) {
535 LOG.error(e);
541 private void fireLookupCanceled(){
542 if (!myListeners.isEmpty()){
543 LookupEvent event = new LookupEvent(this, null);
544 LookupListener[] listeners = myListeners.toArray(new LookupListener[myListeners.size()]);
545 for (LookupListener listener : listeners) {
546 try {
547 listener.lookupCanceled(event);
549 catch (Throwable e) {
550 LOG.error(e);
556 private void fireCurrentItemChanged(LookupElement item){
557 if (!myListeners.isEmpty()){
558 LookupEvent event = new LookupEvent(this, item);
559 LookupListener[] listeners = myListeners.toArray(new LookupListener[myListeners.size()]);
560 for (LookupListener listener : listeners) {
561 listener.currentItemChanged(event);
566 private static int divideString(String lookupString, PrefixMatcher matcher) {
567 for (int i = matcher.getPrefix().length(); i <= lookupString.length(); i++) {
568 if (matcher.prefixMatches(lookupString.substring(0, i))) {
569 return i;
572 return -1;
575 public boolean fillInCommonPrefix(boolean explicitlyInvoked) {
576 if (explicitlyInvoked && myCalculating) return false;
577 if (!explicitlyInvoked && myDirty) return false;
579 ListModel listModel = myList.getModel();
580 if (listModel.getSize() <= 1) return false;
582 if (listModel.getSize() == 0) return false;
584 final LookupElement firstItem = (LookupElement)listModel.getElementAt(0);
585 if (listModel.getSize() == 1 && firstItem instanceof EmptyLookupItem) return false;
587 final PrefixMatcher firstItemMatcher = firstItem.getPrefixMatcher();
588 final String oldPrefix = firstItemMatcher.getPrefix();
589 String presentPrefix = oldPrefix + myAdditionalPrefix;
590 final PrefixMatcher matcher = firstItemMatcher.cloneWithPrefix(presentPrefix);
591 String lookupString = firstItem.getLookupString();
592 int div = divideString(lookupString, matcher);
593 if (div < 0) return false;
595 String beforeCaret = lookupString.substring(0, div);
596 String afterCaret = lookupString.substring(div);
599 for (int i = 1; i < listModel.getSize(); i++) {
600 LookupElement item = (LookupElement)listModel.getElementAt(i);
601 if (!oldPrefix.equals(item.getPrefixMatcher().getPrefix())) return false;
603 lookupString = item.getLookupString();
604 div = divideString(lookupString, item.getPrefixMatcher());
605 if (div < 0) return false;
607 String _afterCaret = lookupString.substring(div);
608 if (beforeCaret != null) {
609 if (div != beforeCaret.length() || !lookupString.startsWith(beforeCaret)) {
610 beforeCaret = null;
614 while (afterCaret.length() > 0) {
615 if (_afterCaret.startsWith(afterCaret)) {
616 break;
618 afterCaret = afterCaret.substring(0, afterCaret.length() - 1);
620 if (afterCaret.length() == 0) return false;
623 EditorModificationUtil.deleteSelectedText(myEditor);
624 int offset = myEditor.getCaretModel().getOffset();
625 if (beforeCaret != null) { // correct case, expand camel-humps
626 final int start = offset - presentPrefix.length();
627 myInitialOffset = start + beforeCaret.length();
628 myEditor.getDocument().replaceString(start, offset, beforeCaret);
629 presentPrefix = beforeCaret;
632 offset = myEditor.getCaretModel().getOffset();
633 myEditor.getDocument().insertString(offset, afterCaret);
635 final String newPrefix = presentPrefix + afterCaret;
636 for (Iterator<LookupElement> it = myItems.iterator(); it.hasNext();) {
637 LookupElement item = it.next();
638 if (!item.setPrefixMatcher(item.getPrefixMatcher().cloneWithPrefix(newPrefix))) {
639 it.remove();
642 myAdditionalPrefix = "";
643 updateList();
645 offset += afterCaret.length();
646 myInitialOffset = offset;
647 myEditor.getCaretModel().moveToOffset(offset);
648 return true;
651 public PsiFile getPsiFile() {
652 return PsiDocumentManager.getInstance(myEditor.getProject()).getPsiFile(myEditor.getDocument());
655 public boolean isCompletion() {
656 return myItemPreferencePolicy instanceof CompletionPreferencePolicy;
659 public PsiElement getPsiElement() {
660 PsiFile file = getPsiFile();
661 if (file == null) return null;
663 int offset = getEditor().getCaretModel().getOffset();
664 if (offset > 0) return file.findElementAt(offset - 1);
666 return file.findElementAt(offset + 1);
669 public Editor getEditor() {
670 return myEditor;
673 public boolean isPositionedAboveCaret(){
674 return myPositionedAbove != null && myPositionedAbove.booleanValue();
677 public void hide(){
678 ApplicationManager.getApplication().assertIsDispatchThread();
680 if (IdeEventQueue.getInstance().getPopupManager().closeAllPopups()) return;
682 if (myDisposed) return;
684 doHide(true);
687 private void doHide(final boolean fireCanceled) {
688 assert !myDisposed;
690 super.hide();
692 Disposer.dispose(this);
694 if (fireCanceled) {
695 fireLookupCanceled();
699 public void dispose() {
700 assert !myDisposed;
701 myDisposed = true;
702 myProcessIcon.dispose();
703 if (myEditorCaretListener != null) {
704 myEditor.getCaretModel().removeCaretListener(myEditorCaretListener);
706 if (myEditorMouseListener != null) {
707 myEditor.removeEditorMouseListener(myEditorMouseListener);
711 public static int doSelectMostPreferableItem(final LookupItemPreferencePolicy itemPreferencePolicy, Object[] items) {
712 if (itemPreferencePolicy == null){
713 return -1;
715 else{
716 LookupElement prefItem = null;
717 int prefItemIndex = -1;
719 for(int i = 0; i < items.length; i++){
720 LookupElement item = (LookupElement)items[i];
721 final Object obj = item.getObject();
722 if (obj instanceof PsiElement && !((PsiElement)obj).isValid()) continue;
723 if (prefItem == null){
724 prefItem = item;
725 prefItemIndex = i;
727 else{
728 int d = itemPreferencePolicy.compare(item, prefItem);
729 if (d < 0){
730 prefItem = item;
731 prefItemIndex = i;
735 return prefItem != null ? prefItemIndex : -1;
739 public void adaptSize() {
740 if (isVisible()) {
741 HintManagerImpl.getInstanceImpl().adjustEditorHintPosition(this, myEditor, getComponent().getLocation());
745 public synchronized LookupElement[] getSortedItems() {
746 final LookupElement[] result = new LookupElement[myList.getModel().getSize()];
747 ((DefaultListModel)myList.getModel()).copyInto(result);
748 return result;