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
;
36 import javax
.swing
.event
.ListSelectionEvent
;
37 import javax
.swing
.event
.ListSelectionListener
;
39 import java
.awt
.event
.MouseAdapter
;
40 import java
.awt
.event
.MouseEvent
;
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
,
80 LookupElement
[] items
, LookupItemPreferencePolicy itemPreferencePolicy
, final String bottomText
){
81 super(new JPanel(new BorderLayout()));
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
) {
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() {
170 myPreselectedItem
= null;
174 public void resort() {
175 final ArrayList
<LookupElement
> items
= new ArrayList
<LookupElement
>(myItems
);
177 myPreselectedItem
= EMPTY_LOOKUP_ITEM
;
180 for (final LookupElement item
: items
) {
186 public synchronized void addItem(LookupElement 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
));
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
);
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() {
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
;
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();
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
)) {
267 if (allItems
.size() + suitable
.size() + (addPreselected ?
1 : 0) > MAX_PREFERRED_COUNT
) break;
268 for (final LookupElement item
: suitable
) {
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
);
292 boolean isEmpty
= allItems
.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
);
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
));
309 if (oldSelected
!= null) {
310 if (!ListScrollingUtil
.selectItem(myList
, oldSelected
)) {
311 selectMostPreferableItem();
314 if (myPreselectedItem
== EMPTY_LOOKUP_ITEM
) {
315 selectMostPreferableItem();
316 myPreselectedItem
= getCurrentItem();
317 } else if (hasPreselectedItem
) {
318 ListScrollingUtil
.selectItem(myList
, myPreselectedItem
);
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
);
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();
370 item
instanceof EmptyLookupItem
||
371 item
.getObject() instanceof DeferredUserLookupValue
&& item
instanceof LookupItem
&&
372 !((DeferredUserLookupValue
)item
.getObject()).handleUserSelection((LookupItem
)item
, myProject
)) {
373 fireItemSelected(null, completionChar
);
377 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
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();
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()){
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()) {
429 myEditor
.getCaretModel().addCaretListener(myEditorCaretListener
);
431 myEditorMouseListener
= new EditorMouseAdapter() {
432 public void mouseClicked(EditorMouseEvent e
){
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() {
451 finishLookup(NORMAL_SELECT_CHAR
);
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);
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){
508 Rectangle listBounds
=myList
.getBounds();
509 final JRootPane pane
= myList
.getRootPane();
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
;
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
) {
532 listener
.itemSelected(event
);
534 catch (Throwable 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
) {
547 listener
.lookupCanceled(event
);
549 catch (Throwable 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
))) {
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
)) {
614 while (afterCaret
.length() > 0) {
615 if (_afterCaret
.startsWith(afterCaret
)) {
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
))) {
642 myAdditionalPrefix
= "";
645 offset
+= afterCaret
.length();
646 myInitialOffset
= offset
;
647 myEditor
.getCaretModel().moveToOffset(offset
);
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() {
673 public boolean isPositionedAboveCaret(){
674 return myPositionedAbove
!= null && myPositionedAbove
.booleanValue();
678 ApplicationManager
.getApplication().assertIsDispatchThread();
680 if (IdeEventQueue
.getInstance().getPopupManager().closeAllPopups()) return;
682 if (myDisposed
) return;
687 private void doHide(final boolean fireCanceled
) {
692 Disposer
.dispose(this);
695 fireLookupCanceled();
699 public void dispose() {
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){
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){
728 int d
= itemPreferencePolicy
.compare(item
, prefItem
);
735 return prefItem
!= null ? prefItemIndex
: -1;
739 public void adaptSize() {
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
);