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.progress.ProcessCanceledException;
45 import com.intellij.openapi.progress.ProgressManager;
46 import com.intellij.openapi.progress.util.ProgressIndicatorBase;
47 import com.intellij.openapi.progress.util.ProgressWrapper;
48 import com.intellij.openapi.project.DumbService;
49 import com.intellij.openapi.project.IndexNotReadyException;
50 import com.intellij.openapi.project.Project;
51 import com.intellij.openapi.ui.MessageType;
52 import com.intellij.openapi.util.Disposer;
53 import com.intellij.openapi.util.Pair;
54 import com.intellij.openapi.util.TextRange;
55 import com.intellij.openapi.util.registry.Registry;
56 import com.intellij.openapi.util.text.StringUtil;
57 import com.intellij.patterns.ElementPattern;
58 import com.intellij.psi.PsiFile;
59 import com.intellij.psi.PsiReference;
60 import com.intellij.psi.ReferenceRange;
61 import com.intellij.psi.util.PsiUtilCore;
62 import com.intellij.ui.LightweightHint;
63 import com.intellij.util.Alarm;
64 import com.intellij.util.ObjectUtils;
65 import com.intellij.util.ThreeState;
66 import com.intellij.util.concurrency.Semaphore;
67 import com.intellij.util.containers.ContainerUtil;
68 import com.intellij.util.messages.MessageBusConnection;
69 import com.intellij.util.ui.update.MergingUpdateQueue;
70 import com.intellij.util.ui.update.Update;
71 import org.jetbrains.annotations.NotNull;
72 import org.jetbrains.annotations.Nullable;
73 import org.jetbrains.annotations.TestOnly;
77 import java.awt.event.KeyAdapter;
78 import java.awt.event.KeyEvent;
79 import java.beans.PropertyChangeEvent;
80 import java.beans.PropertyChangeListener;
81 import java.util.List;
82 import java.util.Queue;
83 import java.util.concurrent.ConcurrentLinkedQueue;
84 import java.util.concurrent.ConcurrentMap;
89 public class CompletionProgressIndicator extends ProgressIndicatorBase implements CompletionProcess, Disposable {
90 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionProgressIndicator");
91 private final Editor myEditor;
93 private final Caret myCaret;
94 private final CompletionParameters myParameters;
95 private final CodeCompletionHandlerBase myHandler;
96 private final LookupImpl myLookup;
97 private final MergingUpdateQueue myQueue;
98 private final Update myUpdate = new Update("update") {
102 myQueue.setMergingTimeSpan(300);
105 private final Semaphore myFreezeSemaphore;
106 private final OffsetMap myOffsetMap;
107 private final List<Pair<Integer, ElementPattern<String>>> myRestartingPrefixConditions = ContainerUtil.createLockFreeCopyOnWriteList();
108 private final LookupAdapter myLookupListener = new LookupAdapter() {
110 public void itemSelected(LookupEvent event) {
111 finishCompletionProcess(false);
113 LookupElement item = event.getItem();
114 if (item == null) return;
118 myHandler.lookupItemSelected(CompletionProgressIndicator.this, item, event.getCompletionChar(), myLookup.getItems());
123 public void lookupCanceled(final LookupEvent event) {
124 finishCompletionProcess(true);
127 private volatile int myCount;
128 private volatile boolean myHasPsiElements;
129 private boolean myLookupUpdated;
130 private final ConcurrentMap<LookupElement, CompletionSorterImpl> myItemSorters =
131 ContainerUtil.newConcurrentMap(ContainerUtil.<LookupElement>identityStrategy());
132 private final PropertyChangeListener myLookupManagerListener;
133 private final Queue<Runnable> myAdvertiserChanges = new ConcurrentLinkedQueue<Runnable>();
134 private final int myStartCaret;
136 public CompletionProgressIndicator(final Editor editor,
137 @NotNull Caret caret,
138 CompletionParameters parameters,
139 CodeCompletionHandlerBase handler,
140 Semaphore freezeSemaphore,
141 final OffsetMap offsetMap,
142 boolean hasModifiers,
146 myParameters = parameters;
148 myFreezeSemaphore = freezeSemaphore;
149 myOffsetMap = offsetMap;
151 myStartCaret = myEditor.getCaretModel().getOffset();
153 myAdvertiserChanges.offer(new Runnable() {
156 myLookup.getAdvertiser().clearAdvertisements();
160 myLookup.setArranger(new CompletionLookupArranger(parameters, this));
162 myLookup.addLookupListener(myLookupListener);
163 myLookup.setCalculating(true);
165 myLookupManagerListener = new PropertyChangeListener() {
167 public void propertyChange(PropertyChangeEvent evt) {
168 if (evt.getNewValue() != null) {
169 LOG.error("An attempt to change the lookup during completion, phase = " + CompletionServiceImpl.getCompletionPhase());
173 LookupManager.getInstance(getProject()).addPropertyChangeListener(myLookupManagerListener);
175 myQueue = new MergingUpdateQueue("completion lookup progress", 100, true, myEditor.getContentComponent());
176 myQueue.setPassThrough(false);
178 ApplicationManager.getApplication().assertIsDispatchThread();
179 Disposer.register(this, offsetMap);
181 if (hasModifiers && !ApplicationManager.getApplication().isUnitTestMode()) {
186 public OffsetMap getOffsetMap() {
190 public int getSelectionEndOffset() {
191 return getOffsetMap().getOffset(CompletionInitializationContext.SELECTION_END_OFFSET);
194 void duringCompletion(CompletionInitializationContext initContext) {
195 if (isAutopopupCompletion()) {
196 if (shouldPreselectFirstSuggestion(myParameters)) {
197 if (!CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS) {
198 myLookup.setFocusDegree(LookupImpl.FocusDegree.SEMI_FOCUSED);
199 if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_FINISH_BY_CONTROL_DOT, getProject())) {
200 String dotShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_DOT);
201 if (StringUtil.isNotEmpty(dotShortcut)) {
202 addAdvertisement("Press " + dotShortcut + " to choose the selected (or first) suggestion and insert a dot afterwards", null);
206 myLookup.setFocusDegree(LookupImpl.FocusDegree.FOCUSED);
209 if (!myEditor.isOneLineMode() &&
210 FeatureUsageTracker.getInstance()
211 .isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ARROWS, getProject())) {
212 String downShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_DOWN);
213 String upShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_UP);
214 if (StringUtil.isNotEmpty(downShortcut) && StringUtil.isNotEmpty(upShortcut)) {
215 addAdvertisement(downShortcut + " and " + upShortcut + " will move caret down and up in the editor", null);
218 } else if (DumbService.isDumb(getProject())) {
219 addAdvertisement("The results might be incomplete while indexing is in progress", MessageType.WARNING.getPopupBackground());
222 ProgressManager.checkCanceled();
224 if (!initContext.getOffsetMap().wasModified(CompletionInitializationContext.IDENTIFIER_END_OFFSET)) {
226 final int selectionEndOffset = initContext.getSelectionEndOffset();
227 final PsiReference reference = TargetElementUtilBase.findReference(myEditor, selectionEndOffset);
228 if (reference != null) {
229 initContext.setReplacementOffset(findReplacementOffset(selectionEndOffset, reference));
232 catch (IndexNotReadyException ignored) {
236 for (CompletionContributor contributor : CompletionContributor.forLanguage(initContext.getPositionLanguage())) {
237 ProgressManager.checkCanceled();
238 if (DumbService.getInstance(initContext.getProject()).isDumb() && !DumbService.isDumbAware(contributor)) {
242 contributor.duringCompletion(initContext);
247 CompletionSorterImpl getSorter(LookupElement element) {
248 return myItemSorters.get(element);
252 public void dispose() {
255 private static int findReplacementOffset(int selectionEndOffset, PsiReference reference) {
256 final List<TextRange> ranges = ReferenceRange.getAbsoluteRanges(reference);
257 for (TextRange range : ranges) {
258 if (range.contains(selectionEndOffset)) {
259 return range.getEndOffset();
263 return selectionEndOffset;
267 void scheduleAdvertising() {
268 if (myLookup.isAvailableToUser()) {
271 for (final CompletionContributor contributor : CompletionContributor.forParameters(myParameters)) {
272 if (!myLookup.isCalculating() && !myLookup.isVisible()) return;
274 @SuppressWarnings("deprecation") String s = contributor.advertise(myParameters);
276 addAdvertisement(s, null);
281 private boolean isOutdated() {
282 return CompletionServiceImpl.getCompletionPhase().indicator != this;
285 private void trackModifiers() {
286 assert !isAutopopupCompletion();
288 final JComponent contentComponent = myEditor.getContentComponent();
289 contentComponent.addKeyListener(new ModifierTracker(contentComponent));
292 public void setMergeCommand() {
293 CommandProcessor.getInstance().setCurrentCommandGroupId(getCompletionCommandName());
296 private String getCompletionCommandName() {
297 return "Completion" + hashCode();
300 public boolean showLookup() {
301 return updateLookup();
304 public CompletionParameters getParameters() {
308 public CodeCompletionHandlerBase getHandler() {
312 public LookupImpl getLookup() {
316 private boolean updateLookup() {
317 ApplicationManager.getApplication().assertIsDispatchThread();
318 if (isOutdated() || !shouldShowLookup()) return false;
321 Runnable action = myAdvertiserChanges.poll();
322 if (action == null) break;
326 if (!myLookupUpdated) {
327 if (myLookup.getAdvertisements().isEmpty() && !isAutopopupCompletion() && !DumbService.isDumb(getProject())) {
328 DefaultCompletionContributor.addDefaultAdvertisements(myParameters, myLookup, myHasPsiElements);
330 myLookup.getAdvertiser().showRandomText();
333 boolean justShown = false;
334 if (!myLookup.isShown()) {
335 if (hideAutopopupIfMeaningless()) {
339 if (Registry.is("dump.threads.on.empty.lookup") && myLookup.isCalculating() && myLookup.getItems().isEmpty()) {
340 PerformanceWatcher.getInstance().dumpThreads("emptyLookup/", true);
343 if (!myLookup.showLookup()) {
348 myLookupUpdated = true;
349 myLookup.refreshUi(true, justShown);
350 hideAutopopupIfMeaningless();
352 myLookup.ensureSelectionVisible(true);
357 private boolean shouldShowLookup() {
358 if (isAutopopupCompletion()) {
362 if (myLookup.isCalculating() && Registry.is("ide.completion.delay.autopopup.until.completed")) {
369 final boolean isInsideIdentifier() {
370 return getIdentifierEndOffset() != getSelectionEndOffset();
373 public int getIdentifierEndOffset() {
374 return myOffsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET);
377 public synchronized void addItem(final CompletionResult item) {
378 if (!isRunning()) return;
379 ProgressManager.checkCanceled();
381 final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
383 LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
386 LOG.assertTrue(myParameters.getPosition().isValid());
388 LookupElement lookupElement = item.getLookupElement();
389 if (!myHasPsiElements && lookupElement.getPsiElement() != null) {
390 myHasPsiElements = true;
392 myItemSorters.put(lookupElement, (CompletionSorterImpl)item.getSorter());
393 if (!myLookup.addItem(lookupElement, item.getPrefixMatcher())) {
399 new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this).addRequest(new Runnable() {
402 myFreezeSemaphore.up();
406 myQueue.queue(myUpdate);
409 public void closeAndFinish(boolean hideLookup) {
410 if (!myLookup.isLookupDisposed()) {
411 Lookup lookup = LookupManager.getActiveLookup(myEditor);
412 LOG.assertTrue(lookup == myLookup, "lookup changed: " + lookup + "; " + this);
414 myLookup.removeLookupListener(myLookupListener);
415 finishCompletionProcess(true);
416 CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
419 LookupManager.getInstance(getProject()).hideActiveLookup();
423 private void finishCompletionProcess(boolean disposeOffsetMap) {
426 ApplicationManager.getApplication().assertIsDispatchThread();
427 Disposer.dispose(myQueue);
428 LookupManager.getInstance(getProject()).removePropertyChangeListener(myLookupManagerListener);
430 CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
431 LOG.assertTrue(currentCompletion == this, currentCompletion + "!=" + this);
433 CompletionServiceImpl
434 .assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class,
435 CompletionPhase.CommittingDocuments.class);
436 CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
437 if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
438 LOG.assertTrue(((CompletionPhase.CommittingDocuments)oldPhase).isRestartingCompletion(), oldPhase);
439 ((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
441 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
442 if (disposeOffsetMap) {
447 void disposeIndicator() {
448 // our offset map should be disposed under write action, so that duringCompletion (read action) won't access it after disposing
449 AccessToken token = WriteAction.start();
451 Disposer.dispose(this);
459 public static void cleanupForNextTest() {
460 CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
461 if (currentCompletion != null) {
462 currentCompletion.finishCompletionProcess(true);
463 CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
466 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
468 CompletionLookupArranger.cancelLastCompletionStatisticsUpdate();
475 myQueue.cancelAllUpdates();
476 myFreezeSemaphore.up();
478 ApplicationManager.getApplication().invokeLater(new Runnable() {
481 final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
482 if (!(phase instanceof CompletionPhase.BgCalculation) || phase.indicator != CompletionProgressIndicator.this) return;
484 LOG.assertTrue(!getProject().isDisposed(), "project disposed");
486 if (myEditor.isDisposed()) {
487 LookupManager.getInstance(getProject()).hideActiveLookup();
488 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
492 if (myEditor instanceof EditorWindow) {
493 LOG.assertTrue(((EditorWindow)myEditor).getInjectedFile().isValid(), "injected file !valid");
494 LOG.assertTrue(((DocumentWindow)myEditor.getDocument()).isValid(), "docWindow !valid");
496 PsiFile file = myLookup.getPsiFile();
497 LOG.assertTrue(file == null || file.isValid(), "file !valid");
499 myLookup.setCalculating(false);
502 LookupManager.getInstance(getProject()).hideActiveLookup();
503 if (!isAutopopupCompletion()) {
504 final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
505 LOG.assertTrue(current == null, current + "!=" + CompletionProgressIndicator.this);
507 handleEmptyLookup(!((CompletionPhase.BgCalculation)phase).modifiersChanged);
511 CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated(CompletionProgressIndicator.this));
515 }, myQueue.getModalityState());
518 private boolean hideAutopopupIfMeaningless() {
519 if (!myLookup.isLookupDisposed() && isAutopopupCompletion() && !myLookup.isSelectionTouched() && !myLookup.isCalculating()) {
520 myLookup.refreshUi(true, false);
521 final List<LookupElement> items = myLookup.getItems();
523 for (LookupElement item : items) {
524 if (!myLookup.itemPattern(item).equals(item.getLookupString())) {
528 if (item.isValid() && item.isWorthShowingInAutoPopup()) {
533 myLookup.hideLookup(false);
534 LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null);
535 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
541 public boolean fillInCommonPrefix(final boolean explicit) {
542 if (isInsideIdentifier()) {
546 final Boolean aBoolean = new WriteCommandAction<Boolean>(getProject()) {
548 protected void run(@NotNull Result<Boolean> result) throws Throwable {
553 result.setResult(myLookup.fillInCommonPrefix(explicit));
555 catch (Exception e) {
559 }.execute().getResultObject();
560 return aBoolean.booleanValue();
563 public void restorePrefix(@NotNull final Runnable customRestore) {
564 new WriteCommandAction(getProject()) {
566 protected void run(@NotNull Result result) throws Throwable {
574 public int nextInvocationCount(int invocation, boolean reused) {
575 return reused ? Math.max(getParameters().getInvocationCount() + 1, 2) : invocation;
578 public Editor getEditor() {
583 public Caret getCaret() {
587 public boolean isRepeatedInvocation(CompletionType completionType, Editor editor) {
588 if (completionType != myParameters.getCompletionType() || editor != myEditor) {
592 if (isAutopopupCompletion() && !myLookup.mayBeNoticed()) {
600 public boolean isAutopopupCompletion() {
601 return myParameters.getInvocationCount() == 0;
605 public Project getProject() {
606 return ObjectUtils.assertNotNull(myEditor.getProject());
609 public void addWatchedPrefix(int startOffset, ElementPattern<String> restartCondition) {
610 myRestartingPrefixConditions.add(Pair.create(startOffset, restartCondition));
613 public void prefixUpdated() {
614 final int caretOffset = myEditor.getCaretModel().getOffset();
615 if (caretOffset < myStartCaret) {
617 myRestartingPrefixConditions.clear();
621 final CharSequence text = myEditor.getDocument().getCharsSequence();
622 for (Pair<Integer, ElementPattern<String>> pair : myRestartingPrefixConditions) {
623 int start = pair.first;
624 if (caretOffset >= start && start >= 0) {
625 final String newPrefix = text.subSequence(start, caretOffset).toString();
626 if (pair.second.accepts(newPrefix)) {
628 myRestartingPrefixConditions.clear();
634 hideAutopopupIfMeaningless();
637 public void scheduleRestart() {
638 ApplicationManager.getApplication().assertIsDispatchThread();
641 final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
642 if (this != current) {
643 LOG.error(current + "!=" + this);
646 hideAutopopupIfMeaningless();
648 CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
649 if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
650 ((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
653 final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor);
654 CompletionServiceImpl.setCompletionPhase(phase);
655 phase.ignoreCurrentDocumentChange();
657 final Project project = getProject();
658 ApplicationManager.getApplication().invokeLater(new Runnable() {
661 CompletionAutoPopupHandler.runLaterWithCommitted(project, myEditor.getDocument(), new Runnable() {
664 if (phase.checkExpired()) return;
666 CompletionAutoPopupHandler.invokeCompletion(myParameters.getCompletionType(),
667 isAutopopupCompletion(), project, myEditor, myParameters.getInvocationCount(),
672 }, project.getDisposed());
676 public String toString() {
677 return "CompletionProgressIndicator[count=" +
680 CompletionServiceImpl.getCompletionPhase() +
682 System.identityHashCode(this);
685 protected void handleEmptyLookup(final boolean awaitSecondInvocation) {
686 if (isAutopopupCompletion() && ApplicationManager.getApplication().isUnitTestMode()) {
690 LOG.assertTrue(!isAutopopupCompletion());
692 if (ApplicationManager.getApplication().isUnitTestMode() || !myHandler.invokedExplicitly) {
693 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
697 for (final CompletionContributor contributor : CompletionContributor.forParameters(getParameters())) {
698 final String text = contributor.handleEmptyLookup(getParameters(), getEditor());
699 if (StringUtil.isNotEmpty(text)) {
700 LightweightHint hint = showErrorHint(getProject(), getEditor(), text);
701 CompletionServiceImpl.setCompletionPhase(
702 awaitSecondInvocation ? new CompletionPhase.NoSuggestionsHint(hint, this) : CompletionPhase.NoCompletion);
706 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
709 private static LightweightHint showErrorHint(Project project, Editor editor, String text) {
710 final LightweightHint[] result = {null};
711 final EditorHintListener listener = new EditorHintListener() {
713 public void hintShown(final Project project, final LightweightHint hint, final int flags) {
717 final MessageBusConnection connection = project.getMessageBus().connect();
718 connection.subscribe(EditorHintListener.TOPIC, listener);
720 HintManager.getInstance().showErrorHint(editor, text, HintManager.UNDER);
721 connection.disconnect();
725 private static boolean shouldPreselectFirstSuggestion(CompletionParameters parameters) {
726 if (!Registry.is("ide.completion.autopopup.choose.by.enter")) {
730 if (!ApplicationManager.getApplication().isUnitTestMode()) {
734 switch (CodeInsightSettings.getInstance().AUTOPOPUP_FOCUS_POLICY) {
735 case CodeInsightSettings.ALWAYS:
737 case CodeInsightSettings.NEVER:
741 final Language language = PsiUtilCore.getLanguageAtOffset(parameters.getPosition().getContainingFile(), parameters.getOffset());
742 for (CompletionConfidence confidence : CompletionConfidenceEP.forLanguage(language)) {
743 //noinspection deprecation
744 final ThreeState result = confidence.shouldFocusLookup(parameters);
745 if (result != ThreeState.UNSURE) {
746 LOG.debug(confidence + " has returned shouldFocusLookup=" + result);
747 return result == ThreeState.YES;
753 void startCompletion(final CompletionInitializationContext initContext) {
754 boolean sync = ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup;
755 final CompletionThreading strategy = sync ? new SyncCompletion() : new AsyncCompletion();
757 strategy.startThread(ProgressWrapper.wrap(this), new Runnable() {
760 scheduleAdvertising();
763 final WeighingDelegate weigher = strategy.delegateWeighing(this);
765 class CalculateItems implements Runnable {
769 calculateItems(initContext, weigher);
771 catch (ProcessCanceledException ignore) {
772 cancel(); // some contributor may just throw PCE; if indicator is not canceled everything will hang
774 catch (Throwable t) {
780 strategy.startThread(this, new CalculateItems());
783 private LookupElement[] calculateItems(CompletionInitializationContext initContext, WeighingDelegate weigher) {
784 duringCompletion(initContext);
785 ProgressManager.checkCanceled();
787 LookupElement[] result = CompletionService.getCompletionService().performCompletion(myParameters, weigher);
788 ProgressManager.checkCanceled();
791 ProgressManager.checkCanceled();
796 public void addAdvertisement(@NotNull final String text, @Nullable final Color bgColor) {
797 myAdvertiserChanges.offer(new Runnable() {
800 myLookup.addAdvertisement(text, bgColor);
804 myQueue.queue(myUpdate);
807 private static class ModifierTracker extends KeyAdapter {
808 private final JComponent myContentComponent;
810 public ModifierTracker(JComponent contentComponent) {
811 myContentComponent = contentComponent;
815 public void keyPressed(KeyEvent e) {
820 public void keyReleased(KeyEvent e) {
824 private void processModifier(KeyEvent e) {
825 final int code = e.getKeyCode();
826 if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_META || code == KeyEvent.VK_ALT || code == KeyEvent.VK_SHIFT) {
827 myContentComponent.removeKeyListener(this);
828 final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
829 if (phase instanceof CompletionPhase.BgCalculation) {
830 ((CompletionPhase.BgCalculation)phase).modifiersChanged = true;
832 else if (phase instanceof CompletionPhase.InsertedSingleItem) {
833 CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);