2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.codeInsight.completion;
19 import com.intellij.codeInsight.CodeInsightSettings;
20 import com.intellij.codeInsight.TargetElementUtilBase;
21 import com.intellij.codeInsight.completion.impl.CompletionServiceImpl;
22 import com.intellij.codeInsight.completion.impl.CompletionSorterImpl;
23 import com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler;
24 import com.intellij.codeInsight.hint.EditorHintListener;
25 import com.intellij.codeInsight.hint.HintManager;
26 import com.intellij.codeInsight.lookup.*;
27 import com.intellij.codeInsight.lookup.impl.LookupImpl;
28 import com.intellij.diagnostic.PerformanceWatcher;
29 import com.intellij.featureStatistics.FeatureUsageTracker;
30 import com.intellij.injected.editor.DocumentWindow;
31 import com.intellij.injected.editor.EditorWindow;
32 import com.intellij.lang.Language;
33 import com.intellij.openapi.Disposable;
34 import com.intellij.openapi.actionSystem.IdeActions;
35 import com.intellij.openapi.application.AccessToken;
36 import com.intellij.openapi.application.ApplicationManager;
37 import com.intellij.openapi.application.Result;
38 import com.intellij.openapi.application.WriteAction;
39 import com.intellij.openapi.command.CommandProcessor;
40 import com.intellij.openapi.command.WriteCommandAction;
41 import com.intellij.openapi.diagnostic.Logger;
42 import com.intellij.openapi.editor.Caret;
43 import com.intellij.openapi.editor.Editor;
44 import com.intellij.openapi.extensions.Extensions;
45 import com.intellij.openapi.progress.ProcessCanceledException;
46 import com.intellij.openapi.progress.ProgressManager;
47 import com.intellij.openapi.progress.util.ProgressIndicatorBase;
48 import com.intellij.openapi.progress.util.ProgressWrapper;
49 import com.intellij.openapi.project.DumbService;
50 import com.intellij.openapi.project.IndexNotReadyException;
51 import com.intellij.openapi.project.Project;
52 import com.intellij.openapi.ui.MessageType;
53 import com.intellij.openapi.util.Disposer;
54 import com.intellij.openapi.util.Pair;
55 import com.intellij.openapi.util.TextRange;
56 import com.intellij.openapi.util.registry.Registry;
57 import com.intellij.openapi.util.text.StringUtil;
58 import com.intellij.patterns.ElementPattern;
59 import com.intellij.psi.PsiFile;
60 import com.intellij.psi.PsiReference;
61 import com.intellij.psi.ReferenceRange;
62 import com.intellij.psi.util.PsiUtilCore;
63 import com.intellij.ui.LightweightHint;
64 import com.intellij.util.Alarm;
65 import com.intellij.util.ObjectUtils;
66 import com.intellij.util.ThreeState;
67 import com.intellij.util.concurrency.Semaphore;
68 import com.intellij.util.containers.ContainerUtil;
69 import com.intellij.util.messages.MessageBusConnection;
70 import com.intellij.util.ui.update.MergingUpdateQueue;
71 import com.intellij.util.ui.update.Update;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
74 import org.jetbrains.annotations.TestOnly;
78 import java.awt.event.KeyAdapter;
79 import java.awt.event.KeyEvent;
80 import java.beans.PropertyChangeEvent;
81 import java.beans.PropertyChangeListener;
82 import java.util.List;
83 import java.util.Queue;
84 import java.util.concurrent.ConcurrentLinkedQueue;
85 import java.util.concurrent.ConcurrentMap;
90 public class CompletionProgressIndicator extends ProgressIndicatorBase implements CompletionProcess, Disposable {
91 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionProgressIndicator");
92 private final Editor myEditor;
94 private final Caret myCaret;
95 private final CompletionParameters myParameters;
96 private final CodeCompletionHandlerBase myHandler;
97 private final LookupImpl myLookup;
98 private final MergingUpdateQueue myQueue;
99 private final Update myUpdate = new Update("update") {
103 myQueue.setMergingTimeSpan(300);
106 private final Semaphore myFreezeSemaphore;
107 private final OffsetMap myOffsetMap;
108 private final List<Pair<Integer, ElementPattern<String>>> myRestartingPrefixConditions = ContainerUtil.createLockFreeCopyOnWriteList();
109 private final LookupAdapter myLookupListener = new LookupAdapter() {
111 public void itemSelected(LookupEvent event) {
112 finishCompletionProcess(false);
114 LookupElement item = event.getItem();
115 if (item == null) return;
119 myHandler.lookupItemSelected(CompletionProgressIndicator.this, item, event.getCompletionChar(), myLookup.getItems());
124 public void lookupCanceled(final LookupEvent event) {
125 finishCompletionProcess(true);
128 private volatile int myCount;
129 private volatile boolean myHasPsiElements;
130 private boolean myLookupUpdated;
131 private final ConcurrentMap<LookupElement, CompletionSorterImpl> myItemSorters =
132 ContainerUtil.newConcurrentMap(ContainerUtil.<LookupElement>identityStrategy());
133 private final PropertyChangeListener myLookupManagerListener;
134 private final Queue<Runnable> myAdvertiserChanges = new ConcurrentLinkedQueue<Runnable>();
135 private final int myStartCaret;
137 public CompletionProgressIndicator(final Editor editor,
138 @NotNull Caret caret,
139 CompletionParameters parameters,
140 CodeCompletionHandlerBase handler,
141 Semaphore freezeSemaphore,
142 final OffsetMap offsetMap,
143 boolean hasModifiers,
147 myParameters = parameters;
149 myFreezeSemaphore = freezeSemaphore;
150 myOffsetMap = offsetMap;
152 myStartCaret = myEditor.getCaretModel().getOffset();
154 myAdvertiserChanges.offer(new Runnable() {
157 myLookup.getAdvertiser().clearAdvertisements();
161 myLookup.setArranger(new CompletionLookupArranger(parameters, this));
163 myLookup.addLookupListener(myLookupListener);
164 myLookup.setCalculating(true);
166 myLookupManagerListener = new PropertyChangeListener() {
168 public void propertyChange(PropertyChangeEvent evt) {
169 if (evt.getNewValue() != null) {
170 LOG.error("An attempt to change the lookup during completion, phase = " + CompletionServiceImpl.getCompletionPhase());
174 LookupManager.getInstance(getProject()).addPropertyChangeListener(myLookupManagerListener);
176 myQueue = new MergingUpdateQueue("completion lookup progress", 100, true, myEditor.getContentComponent());
177 myQueue.setPassThrough(false);
179 ApplicationManager.getApplication().assertIsDispatchThread();
180 Disposer.register(this, offsetMap);
182 if (hasModifiers && !ApplicationManager.getApplication().isUnitTestMode()) {
187 public OffsetMap getOffsetMap() {
191 public int getSelectionEndOffset() {
192 return getOffsetMap().getOffset(CompletionInitializationContext.SELECTION_END_OFFSET);
195 void duringCompletion(CompletionInitializationContext initContext) {
196 if (isAutopopupCompletion()) {
197 if (shouldPreselectFirstSuggestion(myParameters)) {
198 if (!CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS) {
199 myLookup.setFocusDegree(LookupImpl.FocusDegree.SEMI_FOCUSED);
200 if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_FINISH_BY_CONTROL_DOT, getProject())) {
201 String dotShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_DOT);
202 if (StringUtil.isNotEmpty(dotShortcut)) {
203 addAdvertisement("Press " + dotShortcut + " to choose the selected (or first) suggestion and insert a dot afterwards", null);
207 myLookup.setFocusDegree(LookupImpl.FocusDegree.FOCUSED);
210 if (!myEditor.isOneLineMode() &&
211 FeatureUsageTracker.getInstance()
212 .isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ARROWS, getProject())) {
213 String downShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_DOWN);
214 String upShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_UP);
215 if (StringUtil.isNotEmpty(downShortcut) && StringUtil.isNotEmpty(upShortcut)) {
216 addAdvertisement(downShortcut + " and " + upShortcut + " will move caret down and up in the editor", null);
219 } else if (DumbService.isDumb(getProject())) {
220 addAdvertisement("The results might be incomplete while indexing is in progress", MessageType.WARNING.getPopupBackground());
223 ProgressManager.checkCanceled();
225 if (!initContext.getOffsetMap().wasModified(CompletionInitializationContext.IDENTIFIER_END_OFFSET)) {
227 final int selectionEndOffset = initContext.getSelectionEndOffset();
228 final PsiReference reference = TargetElementUtilBase.findReference(myEditor, selectionEndOffset);
229 if (reference != null) {
230 initContext.setReplacementOffset(findReplacementOffset(selectionEndOffset, reference));
233 catch (IndexNotReadyException ignored) {
237 for (CompletionContributor contributor : CompletionContributor.forLanguage(initContext.getPositionLanguage())) {
238 ProgressManager.checkCanceled();
239 if (DumbService.getInstance(initContext.getProject()).isDumb() && !DumbService.isDumbAware(contributor)) {
243 contributor.duringCompletion(initContext);
248 CompletionSorterImpl getSorter(LookupElement element) {
249 return myItemSorters.get(element);
253 public void dispose() {
256 private static int findReplacementOffset(int selectionEndOffset, PsiReference reference) {
257 final List<TextRange> ranges = ReferenceRange.getAbsoluteRanges(reference);
258 for (TextRange range : ranges) {
259 if (range.contains(selectionEndOffset)) {
260 return range.getEndOffset();
264 return selectionEndOffset;
268 void scheduleAdvertising() {
269 if (myLookup.isAvailableToUser()) {
272 for (final CompletionContributor contributor : CompletionContributor.forParameters(myParameters)) {
273 if (!myLookup.isCalculating() && !myLookup.isVisible()) return;
275 @SuppressWarnings("deprecation") String s = contributor.advertise(myParameters);
277 addAdvertisement(s, null);
282 private boolean isOutdated() {
283 return CompletionServiceImpl.getCompletionPhase().indicator != this;
286 private void trackModifiers() {
287 assert !isAutopopupCompletion();
289 final JComponent contentComponent = myEditor.getContentComponent();
290 contentComponent.addKeyListener(new ModifierTracker(contentComponent));
293 public void setMergeCommand() {
294 CommandProcessor.getInstance().setCurrentCommandGroupId(getCompletionCommandName());
297 private String getCompletionCommandName() {
298 return "Completion" + hashCode();
301 public boolean showLookup() {
302 return updateLookup();
305 public CompletionParameters getParameters() {
309 public CodeCompletionHandlerBase getHandler() {
313 public LookupImpl getLookup() {
317 private boolean updateLookup() {
318 ApplicationManager.getApplication().assertIsDispatchThread();
319 if (isOutdated() || !shouldShowLookup()) return false;
322 Runnable action = myAdvertiserChanges.poll();
323 if (action == null) break;
327 if (!myLookupUpdated) {
328 if (myLookup.getAdvertisements().isEmpty() && !isAutopopupCompletion() && !DumbService.isDumb(getProject())) {
329 DefaultCompletionContributor.addDefaultAdvertisements(myParameters, myLookup, myHasPsiElements);
331 myLookup.getAdvertiser().showRandomText();
334 boolean justShown = false;
335 if (!myLookup.isShown()) {
336 if (hideAutopopupIfMeaningless()) {
340 if (Registry.is("dump.threads.on.empty.lookup") && myLookup.isCalculating() && myLookup.getItems().isEmpty()) {
341 PerformanceWatcher.getInstance().dumpThreads("emptyLookup/", true);
344 if (!myLookup.showLookup()) {
349 myLookupUpdated = true;
350 myLookup.refreshUi(true, justShown);
351 hideAutopopupIfMeaningless();
353 myLookup.ensureSelectionVisible(true);
358 private boolean shouldShowLookup() {
359 if (isAutopopupCompletion()) {
363 if (myLookup.isCalculating() && Registry.is("ide.completion.delay.autopopup.until.completed")) {
370 final boolean isInsideIdentifier() {
371 return getIdentifierEndOffset() != getSelectionEndOffset();
374 public int getIdentifierEndOffset() {
375 return myOffsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET);
378 public synchronized void addItem(final CompletionResult item) {
379 if (!isRunning()) return;
380 ProgressManager.checkCanceled();
382 final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
384 LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
387 LOG.assertTrue(myParameters.getPosition().isValid());
389 LookupElement lookupElement = item.getLookupElement();
390 if (!myHasPsiElements && lookupElement.getPsiElement() != null) {
391 myHasPsiElements = true;
393 myItemSorters.put(lookupElement, (CompletionSorterImpl)item.getSorter());
394 if (!myLookup.addItem(lookupElement, item.getPrefixMatcher())) {
400 new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this).addRequest(new Runnable() {
403 myFreezeSemaphore.up();
407 myQueue.queue(myUpdate);
410 public void closeAndFinish(boolean hideLookup) {
411 if (!myLookup.isLookupDisposed()) {
412 Lookup lookup = LookupManager.getActiveLookup(myEditor);
413 LOG.assertTrue(lookup == myLookup, "lookup changed: " + lookup + "; " + this);
415 myLookup.removeLookupListener(myLookupListener);
416 finishCompletionProcess(true);
417 CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
420 LookupManager.getInstance(getProject()).hideActiveLookup();
424 private void finishCompletionProcess(boolean disposeOffsetMap) {
427 ApplicationManager.getApplication().assertIsDispatchThread();
428 Disposer.dispose(myQueue);
429 LookupManager.getInstance(getProject()).removePropertyChangeListener(myLookupManagerListener);
431 CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
432 LOG.assertTrue(currentCompletion == this, currentCompletion + "!=" + this);
434 CompletionServiceImpl
435 .assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class,
436 CompletionPhase.CommittingDocuments.class);
437 CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
438 if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
439 LOG.assertTrue(((CompletionPhase.CommittingDocuments)oldPhase).isRestartingCompletion(), oldPhase);
440 ((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
442 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
443 if (disposeOffsetMap) {
448 void disposeIndicator() {
449 // our offset map should be disposed under write action, so that duringCompletion (read action) won't access it after disposing
450 AccessToken token = WriteAction.start();
452 Disposer.dispose(this);
460 public static void cleanupForNextTest() {
461 CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
462 if (currentCompletion != null) {
463 currentCompletion.finishCompletionProcess(true);
464 CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
467 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
469 CompletionLookupArranger.cancelLastCompletionStatisticsUpdate();
476 myQueue.cancelAllUpdates();
477 myFreezeSemaphore.up();
479 ApplicationManager.getApplication().invokeLater(new Runnable() {
482 final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
483 if (!(phase instanceof CompletionPhase.BgCalculation) || phase.indicator != CompletionProgressIndicator.this) return;
485 LOG.assertTrue(!getProject().isDisposed(), "project disposed");
487 if (myEditor.isDisposed()) {
488 LookupManager.getInstance(getProject()).hideActiveLookup();
489 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
493 if (myEditor instanceof EditorWindow) {
494 LOG.assertTrue(((EditorWindow)myEditor).getInjectedFile().isValid(), "injected file !valid");
495 LOG.assertTrue(((DocumentWindow)myEditor.getDocument()).isValid(), "docWindow !valid");
497 PsiFile file = myLookup.getPsiFile();
498 LOG.assertTrue(file == null || file.isValid(), "file !valid");
500 myLookup.setCalculating(false);
503 LookupManager.getInstance(getProject()).hideActiveLookup();
504 if (!isAutopopupCompletion()) {
505 final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
506 LOG.assertTrue(current == null, current + "!=" + CompletionProgressIndicator.this);
508 handleEmptyLookup(!((CompletionPhase.BgCalculation)phase).modifiersChanged);
512 CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated(CompletionProgressIndicator.this));
516 }, myQueue.getModalityState());
519 private boolean hideAutopopupIfMeaningless() {
520 if (!myLookup.isLookupDisposed() && isAutopopupCompletion() && !myLookup.isSelectionTouched() && !myLookup.isCalculating()) {
521 myLookup.refreshUi(true, false);
522 final List<LookupElement> items = myLookup.getItems();
524 for (LookupElement item : items) {
525 if (!myLookup.itemPattern(item).equals(item.getLookupString())) {
529 if (item.isValid() && item.isWorthShowingInAutoPopup()) {
534 myLookup.hideLookup(false);
535 LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null);
536 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
542 public boolean fillInCommonPrefix(final boolean explicit) {
543 if (isInsideIdentifier()) {
547 final Boolean aBoolean = new WriteCommandAction<Boolean>(getProject()) {
549 protected void run(@NotNull Result<Boolean> result) throws Throwable {
554 result.setResult(myLookup.fillInCommonPrefix(explicit));
556 catch (Exception e) {
560 }.execute().getResultObject();
561 return aBoolean.booleanValue();
564 public void restorePrefix(@NotNull final Runnable customRestore) {
565 new WriteCommandAction(getProject()) {
567 protected void run(@NotNull Result result) throws Throwable {
575 public int nextInvocationCount(int invocation, boolean reused) {
576 return reused ? Math.max(getParameters().getInvocationCount() + 1, 2) : invocation;
579 public Editor getEditor() {
584 public Caret getCaret() {
588 public boolean isRepeatedInvocation(CompletionType completionType, Editor editor) {
589 if (completionType != myParameters.getCompletionType() || editor != myEditor) {
593 if (isAutopopupCompletion() && !myLookup.mayBeNoticed()) {
601 public boolean isAutopopupCompletion() {
602 return myParameters.getInvocationCount() == 0;
606 public Project getProject() {
607 return ObjectUtils.assertNotNull(myEditor.getProject());
610 public void addWatchedPrefix(int startOffset, ElementPattern<String> restartCondition) {
611 myRestartingPrefixConditions.add(Pair.create(startOffset, restartCondition));
614 public void prefixUpdated() {
615 final int caretOffset = myEditor.getCaretModel().getOffset();
616 if (caretOffset < myStartCaret) {
618 myRestartingPrefixConditions.clear();
622 final CharSequence text = myEditor.getDocument().getCharsSequence();
623 for (Pair<Integer, ElementPattern<String>> pair : myRestartingPrefixConditions) {
624 int start = pair.first;
625 if (caretOffset >= start && start >= 0) {
626 final String newPrefix = text.subSequence(start, caretOffset).toString();
627 if (pair.second.accepts(newPrefix)) {
629 myRestartingPrefixConditions.clear();
635 hideAutopopupIfMeaningless();
638 public void scheduleRestart() {
639 ApplicationManager.getApplication().assertIsDispatchThread();
642 final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
643 if (this != current) {
644 LOG.error(current + "!=" + this);
647 hideAutopopupIfMeaningless();
649 CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
650 if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
651 ((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
654 final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor);
655 CompletionServiceImpl.setCompletionPhase(phase);
656 phase.ignoreCurrentDocumentChange();
658 final Project project = getProject();
659 ApplicationManager.getApplication().invokeLater(new Runnable() {
662 CompletionAutoPopupHandler.runLaterWithCommitted(project, myEditor.getDocument(), new Runnable() {
665 if (phase.checkExpired()) return;
667 CompletionAutoPopupHandler.invokeCompletion(myParameters.getCompletionType(),
668 isAutopopupCompletion(), project, myEditor, myParameters.getInvocationCount(),
673 }, project.getDisposed());
677 public String toString() {
678 return "CompletionProgressIndicator[count=" +
681 CompletionServiceImpl.getCompletionPhase() +
683 System.identityHashCode(this);
686 protected void handleEmptyLookup(final boolean awaitSecondInvocation) {
687 if (isAutopopupCompletion() && ApplicationManager.getApplication().isUnitTestMode()) {
691 LOG.assertTrue(!isAutopopupCompletion());
693 if (ApplicationManager.getApplication().isUnitTestMode() || !myHandler.invokedExplicitly) {
694 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
698 for (final CompletionContributor contributor : CompletionContributor.forParameters(getParameters())) {
699 final String text = contributor.handleEmptyLookup(getParameters(), getEditor());
700 if (StringUtil.isNotEmpty(text)) {
701 LightweightHint hint = showErrorHint(getProject(), getEditor(), text);
702 CompletionServiceImpl.setCompletionPhase(
703 awaitSecondInvocation ? new CompletionPhase.NoSuggestionsHint(hint, this) : CompletionPhase.NoCompletion);
707 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
710 private static LightweightHint showErrorHint(Project project, Editor editor, String text) {
711 final LightweightHint[] result = {null};
712 final EditorHintListener listener = new EditorHintListener() {
714 public void hintShown(final Project project, final LightweightHint hint, final int flags) {
718 final MessageBusConnection connection = project.getMessageBus().connect();
719 connection.subscribe(EditorHintListener.TOPIC, listener);
721 HintManager.getInstance().showErrorHint(editor, text, HintManager.UNDER);
722 connection.disconnect();
726 private static boolean shouldPreselectFirstSuggestion(CompletionParameters parameters) {
727 if (!Registry.is("ide.completion.autopopup.choose.by.enter")) {
731 if (Registry.is("ide.completion.lookup.element.preselect.depends.on.context")) {
732 for (CompletionPreselectionBehaviourProvider provider : Extensions.getExtensions(CompletionPreselectionBehaviourProvider.EP_NAME)) {
733 if (!provider.shouldPreselectFirstSuggestion(parameters)) {
739 if (!ApplicationManager.getApplication().isUnitTestMode()) {
743 switch (CodeInsightSettings.getInstance().AUTOPOPUP_FOCUS_POLICY) {
744 case CodeInsightSettings.ALWAYS:
746 case CodeInsightSettings.NEVER:
750 final Language language = PsiUtilCore.getLanguageAtOffset(parameters.getPosition().getContainingFile(), parameters.getOffset());
751 for (CompletionConfidence confidence : CompletionConfidenceEP.forLanguage(language)) {
752 //noinspection deprecation
753 final ThreeState result = confidence.shouldFocusLookup(parameters);
754 if (result != ThreeState.UNSURE) {
755 LOG.debug(confidence + " has returned shouldFocusLookup=" + result);
756 return result == ThreeState.YES;
762 void startCompletion(final CompletionInitializationContext initContext) {
763 boolean sync = ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup;
764 final CompletionThreading strategy = sync ? new SyncCompletion() : new AsyncCompletion();
766 strategy.startThread(ProgressWrapper.wrap(this), new Runnable() {
769 scheduleAdvertising();
772 final WeighingDelegate weigher = strategy.delegateWeighing(this);
774 class CalculateItems implements Runnable {
778 calculateItems(initContext, weigher);
780 catch (ProcessCanceledException ignore) {
781 cancel(); // some contributor may just throw PCE; if indicator is not canceled everything will hang
783 catch (Throwable t) {
789 strategy.startThread(this, new CalculateItems());
792 private LookupElement[] calculateItems(CompletionInitializationContext initContext, WeighingDelegate weigher) {
793 duringCompletion(initContext);
794 ProgressManager.checkCanceled();
796 LookupElement[] result = CompletionService.getCompletionService().performCompletion(myParameters, weigher);
797 ProgressManager.checkCanceled();
800 ProgressManager.checkCanceled();
805 public void addAdvertisement(@NotNull final String text, @Nullable final Color bgColor) {
806 myAdvertiserChanges.offer(new Runnable() {
809 myLookup.addAdvertisement(text, bgColor);
813 myQueue.queue(myUpdate);
816 private static class ModifierTracker extends KeyAdapter {
817 private final JComponent myContentComponent;
819 public ModifierTracker(JComponent contentComponent) {
820 myContentComponent = contentComponent;
824 public void keyPressed(KeyEvent e) {
829 public void keyReleased(KeyEvent e) {
833 private void processModifier(KeyEvent e) {
834 final int code = e.getKeyCode();
835 if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_META || code == KeyEvent.VK_ALT || code == KeyEvent.VK_SHIFT) {
836 myContentComponent.removeKeyListener(this);
837 final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
838 if (phase instanceof CompletionPhase.BgCalculation) {
839 ((CompletionPhase.BgCalculation)phase).modifiersChanged = true;
841 else if (phase instanceof CompletionPhase.InsertedSingleItem) {
842 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);