blob: 4f02d6bc5e5476e8a9954945321a9820a73f76b2 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl;
import com.intellij.codeInsight.completion.impl.CompletionSorterImpl;
import com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler;
import com.intellij.codeInsight.hint.EditorHintListener;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.lookup.*;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.diagnostic.PerformanceWatcher;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.Language;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressWrapper;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ReferenceRange;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.LightweightHint;
import com.intellij.util.Alarm;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ThreeState;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* @author peter
*/
public class CompletionProgressIndicator extends ProgressIndicatorBase implements CompletionProcess, Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionProgressIndicator");
private final Editor myEditor;
private final CompletionParameters myParameters;
private final CodeCompletionHandlerBase myHandler;
private final LookupImpl myLookup;
private final MergingUpdateQueue myQueue;
private final Update myUpdate = new Update("update") {
@Override
public void run() {
updateLookup();
}
};
private final Semaphore myFreezeSemaphore;
private final OffsetMap myOffsetMap;
private final List<Pair<Integer, ElementPattern<String>>> myRestartingPrefixConditions = ContainerUtil.createLockFreeCopyOnWriteList();
private final LookupAdapter myLookupListener = new LookupAdapter() {
@Override
public void itemSelected(LookupEvent event) {
finishCompletionProcess(false);
LookupElement item = event.getItem();
if (item == null) return;
setMergeCommand();
myHandler.lookupItemSelected(CompletionProgressIndicator.this, item, event.getCompletionChar(), myLookup.getItems());
}
@Override
public void lookupCanceled(final LookupEvent event) {
finishCompletionProcess(true);
}
};
private volatile int myCount;
private volatile boolean myHasPsiElements;
private boolean myLookupUpdated;
private final ConcurrentHashMap<LookupElement, CompletionSorterImpl> myItemSorters =
new ConcurrentHashMap<LookupElement, CompletionSorterImpl>(
ContainerUtil.<LookupElement>identityStrategy());
private final PropertyChangeListener myLookupManagerListener;
private final Queue<Runnable> myAdvertiserChanges = new ConcurrentLinkedQueue<Runnable>();
private final int myStartCaret;
public CompletionProgressIndicator(final Editor editor,
CompletionParameters parameters,
CodeCompletionHandlerBase handler,
Semaphore freezeSemaphore,
final OffsetMap offsetMap,
boolean hasModifiers,
LookupImpl lookup) {
myEditor = editor;
myParameters = parameters;
myHandler = handler;
myFreezeSemaphore = freezeSemaphore;
myOffsetMap = offsetMap;
myLookup = lookup;
myStartCaret = myEditor.getCaretModel().getOffset();
myAdvertiserChanges.offer(new Runnable() {
@Override
public void run() {
myLookup.getAdvertiser().clearAdvertisements();
}
});
myLookup.setArranger(new CompletionLookupArranger(parameters, this));
myLookup.addLookupListener(myLookupListener);
myLookup.setCalculating(true);
myLookupManagerListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() != null) {
LOG.error("An attempt to change the lookup during completion, phase = " + CompletionServiceImpl.getCompletionPhase());
}
}
};
LookupManager.getInstance(getProject()).addPropertyChangeListener(myLookupManagerListener);
myQueue = new MergingUpdateQueue("completion lookup progress", 300, true, myEditor.getContentComponent());
myQueue.setPassThrough(false);
ApplicationManager.getApplication().assertIsDispatchThread();
Disposer.register(this, offsetMap);
if (hasModifiers && !ApplicationManager.getApplication().isUnitTestMode()) {
trackModifiers();
}
}
public OffsetMap getOffsetMap() {
return myOffsetMap;
}
public int getSelectionEndOffset() {
return getOffsetMap().getOffset(CompletionInitializationContext.SELECTION_END_OFFSET);
}
void duringCompletion(CompletionInitializationContext initContext) {
if (isAutopopupCompletion()) {
if (shouldPreselectFirstSuggestion(myParameters)) {
if (!CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS) {
myLookup.setFocusDegree(LookupImpl.FocusDegree.SEMI_FOCUSED);
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_FINISH_BY_CONTROL_DOT, getProject())) {
String dotShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_DOT);
if (StringUtil.isNotEmpty(dotShortcut)) {
addAdvertisement("Press " + dotShortcut + " to choose the selected (or first) suggestion and insert a dot afterwards", null);
}
}
} else {
myLookup.setFocusDegree(LookupImpl.FocusDegree.FOCUSED);
}
}
if (!myEditor.isOneLineMode() &&
FeatureUsageTracker.getInstance()
.isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ARROWS, getProject())) {
String downShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_DOWN);
String upShortcut = CompletionContributor.getActionShortcut(IdeActions.ACTION_LOOKUP_UP);
if (StringUtil.isNotEmpty(downShortcut) && StringUtil.isNotEmpty(upShortcut)) {
addAdvertisement(downShortcut + " and " + upShortcut + " will move caret down and up in the editor", null);
}
}
} else if (DumbService.isDumb(getProject())) {
addAdvertisement("The results might be incomplete while indexing is in progress", MessageType.WARNING.getPopupBackground());
}
ProgressManager.checkCanceled();
if (!initContext.getOffsetMap().wasModified(CompletionInitializationContext.IDENTIFIER_END_OFFSET)) {
try {
final int selectionEndOffset = initContext.getSelectionEndOffset();
final PsiReference reference = TargetElementUtilBase.findReference(myEditor, selectionEndOffset);
if (reference != null) {
initContext.setReplacementOffset(findReplacementOffset(selectionEndOffset, reference));
}
}
catch (IndexNotReadyException ignored) {
}
}
for (CompletionContributor contributor : CompletionContributor.forLanguage(initContext.getPositionLanguage())) {
ProgressManager.checkCanceled();
if (DumbService.getInstance(initContext.getProject()).isDumb() && !DumbService.isDumbAware(contributor)) {
continue;
}
contributor.duringCompletion(initContext);
}
}
@NotNull
CompletionSorterImpl getSorter(LookupElement element) {
return myItemSorters.get(element);
}
@Override
public void dispose() {
}
private static int findReplacementOffset(int selectionEndOffset, PsiReference reference) {
final List<TextRange> ranges = ReferenceRange.getAbsoluteRanges(reference);
for (TextRange range : ranges) {
if (range.contains(selectionEndOffset)) {
return range.getEndOffset();
}
}
return selectionEndOffset;
}
void scheduleAdvertising() {
if (myLookup.isAvailableToUser()) {
return;
}
for (final CompletionContributor contributor : CompletionContributor.forParameters(myParameters)) {
if (!myLookup.isCalculating() && !myLookup.isVisible()) return;
@SuppressWarnings("deprecation") String s = contributor.advertise(myParameters);
if (s != null) {
addAdvertisement(s, null);
}
}
}
@Override
public void cancel() {
super.cancel();
}
private boolean isOutdated() {
return CompletionServiceImpl.getCompletionPhase().indicator != this;
}
private void trackModifiers() {
assert !isAutopopupCompletion();
final JComponent contentComponent = myEditor.getContentComponent();
contentComponent.addKeyListener(new ModifierTracker(contentComponent));
}
public void setMergeCommand() {
CommandProcessor.getInstance().setCurrentCommandGroupId(getCompletionCommandName());
}
private String getCompletionCommandName() {
return "Completion" + hashCode();
}
public boolean showLookup() {
return updateLookup();
}
public CompletionParameters getParameters() {
return myParameters;
}
public CodeCompletionHandlerBase getHandler() {
return myHandler;
}
public LookupImpl getLookup() {
return myLookup;
}
private boolean updateLookup() {
ApplicationManager.getApplication().assertIsDispatchThread();
if (isOutdated() || !shouldShowLookup()) return false;
while (true) {
Runnable action = myAdvertiserChanges.poll();
if (action == null) break;
action.run();
}
if (!myLookupUpdated) {
if (myLookup.getAdvertisements().isEmpty() && !isAutopopupCompletion() && !DumbService.isDumb(getProject())) {
DefaultCompletionContributor.addDefaultAdvertisements(myParameters, myLookup, myHasPsiElements);
}
myLookup.getAdvertiser().showRandomText();
}
boolean justShown = false;
if (!myLookup.isShown()) {
if (hideAutopopupIfMeaningless()) {
return false;
}
if (Registry.is("dump.threads.on.empty.lookup") && myLookup.isCalculating() && myLookup.getItems().isEmpty()) {
PerformanceWatcher.getInstance().dumpThreads(true);
}
if (!myLookup.showLookup()) {
return false;
}
justShown = true;
}
myLookupUpdated = true;
myLookup.refreshUi(true, justShown);
hideAutopopupIfMeaningless();
if (justShown) {
myLookup.ensureSelectionVisible(true);
}
return true;
}
private boolean shouldShowLookup() {
if (isAutopopupCompletion()) {
if (myCount == 0) {
return false;
}
if (myLookup.isCalculating() && Registry.is("ide.completion.delay.autopopup.until.completed")) {
return false;
}
}
return true;
}
final boolean isInsideIdentifier() {
return getIdentifierEndOffset() != getSelectionEndOffset();
}
public int getIdentifierEndOffset() {
return myOffsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET);
}
public synchronized void addItem(final CompletionResult item) {
if (!isRunning()) return;
ProgressManager.checkCanceled();
final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
if (!unitTestMode) {
LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
}
LOG.assertTrue(myParameters.getPosition().isValid());
LookupElement lookupElement = item.getLookupElement();
if (!myHasPsiElements && lookupElement.getPsiElement() != null) {
myHasPsiElements = true;
}
myItemSorters.put(lookupElement, (CompletionSorterImpl)item.getSorter());
if (!myLookup.addItem(lookupElement, item.getPrefixMatcher())) {
return;
}
myCount++;
if (myCount == 1) {
new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this).addRequest(new Runnable() {
@Override
public void run() {
myFreezeSemaphore.up();
}
}, 300);
}
myQueue.queue(myUpdate);
}
public void closeAndFinish(boolean hideLookup) {
if (!myLookup.isLookupDisposed()) {
Lookup lookup = LookupManager.getActiveLookup(myEditor);
LOG.assertTrue(lookup == myLookup, "lookup changed: " + lookup + "; " + this);
}
myLookup.removeLookupListener(myLookupListener);
finishCompletionProcess(true);
CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
if (hideLookup) {
LookupManager.getInstance(getProject()).hideActiveLookup();
}
}
private void finishCompletionProcess(boolean disposeOffsetMap) {
cancel();
ApplicationManager.getApplication().assertIsDispatchThread();
Disposer.dispose(myQueue);
LookupManager.getInstance(getProject()).removePropertyChangeListener(myLookupManagerListener);
CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
LOG.assertTrue(currentCompletion == this, currentCompletion + "!=" + this);
CompletionServiceImpl
.assertPhase(CompletionPhase.BgCalculation.class, CompletionPhase.ItemsCalculated.class, CompletionPhase.Synchronous.class,
CompletionPhase.CommittingDocuments.class);
CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
LOG.assertTrue(((CompletionPhase.CommittingDocuments)oldPhase).isRestartingCompletion(), oldPhase);
((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
}
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
if (disposeOffsetMap) {
disposeIndicator();
}
}
void disposeIndicator() {
// our offset map should be disposed under write action, so that duringCompletion (read action) won't access it after disposing
AccessToken token = WriteAction.start();
try {
Disposer.dispose(this);
}
finally {
token.finish();
}
}
@TestOnly
public static void cleanupForNextTest() {
CompletionProgressIndicator currentCompletion = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
if (currentCompletion != null) {
currentCompletion.finishCompletionProcess(true);
CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass());
}
else {
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
}
CompletionLookupArranger.cancelLastCompletionStatisticsUpdate();
}
@Override
public void stop() {
super.stop();
myQueue.cancelAllUpdates();
myFreezeSemaphore.up();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
if (!(phase instanceof CompletionPhase.BgCalculation) || phase.indicator != CompletionProgressIndicator.this) return;
LOG.assertTrue(!getProject().isDisposed(), "project disposed");
if (myEditor.isDisposed()) {
LookupManager.getInstance(getProject()).hideActiveLookup();
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
return;
}
if (myEditor instanceof EditorWindow) {
LOG.assertTrue(((EditorWindow)myEditor).getInjectedFile().isValid(), "injected file !valid");
LOG.assertTrue(((DocumentWindow)myEditor.getDocument()).isValid(), "docWindow !valid");
}
PsiFile file = myLookup.getPsiFile();
LOG.assertTrue(file == null || file.isValid(), "file !valid");
myLookup.setCalculating(false);
if (myCount == 0) {
LookupManager.getInstance(getProject()).hideActiveLookup();
if (!isAutopopupCompletion()) {
final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
LOG.assertTrue(current == null, current + "!=" + CompletionProgressIndicator.this);
handleEmptyLookup(!((CompletionPhase.BgCalculation)phase).modifiersChanged);
}
}
else {
CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated(CompletionProgressIndicator.this));
updateLookup();
}
}
}, myQueue.getModalityState());
}
private boolean hideAutopopupIfMeaningless() {
if (!myLookup.isLookupDisposed() && isAutopopupCompletion() && !myLookup.isSelectionTouched() && !myLookup.isCalculating()) {
myLookup.refreshUi(true, false);
final List<LookupElement> items = myLookup.getItems();
for (LookupElement item : items) {
if (!myLookup.itemPattern(item).equals(item.getLookupString())) {
return false;
}
if (item.isValid() && item.isWorthShowingInAutoPopup()) {
return false;
}
}
myLookup.hideLookup(false);
LOG.assertTrue(CompletionServiceImpl.getCompletionService().getCurrentCompletion() == null);
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
return true;
}
return false;
}
public boolean fillInCommonPrefix(final boolean explicit) {
if (isInsideIdentifier()) {
return false;
}
final Boolean aBoolean = new WriteCommandAction<Boolean>(getProject()) {
@Override
protected void run(@NotNull Result<Boolean> result) throws Throwable {
if (!explicit) {
setMergeCommand();
}
try {
result.setResult(myLookup.fillInCommonPrefix(explicit));
}
catch (Exception e) {
LOG.error(e);
}
}
}.execute().getResultObject();
return aBoolean.booleanValue();
}
public void restorePrefix(@NotNull final Runnable customRestore) {
new WriteCommandAction(getProject()) {
@Override
protected void run(@NotNull Result result) throws Throwable {
setMergeCommand();
customRestore.run();
}
}.execute();
}
public int nextInvocationCount(int invocation, boolean reused) {
return reused ? Math.max(getParameters().getInvocationCount() + 1, 2) : invocation;
}
public Editor getEditor() {
return myEditor;
}
public boolean isRepeatedInvocation(CompletionType completionType, Editor editor) {
if (completionType != myParameters.getCompletionType() || editor != myEditor) {
return false;
}
if (isAutopopupCompletion() && !myLookup.mayBeNoticed()) {
return false;
}
return true;
}
@Override
public boolean isAutopopupCompletion() {
return myHandler.autopopup;
}
@NotNull
public Project getProject() {
return ObjectUtils.assertNotNull(myEditor.getProject());
}
public void addWatchedPrefix(int startOffset, ElementPattern<String> restartCondition) {
myRestartingPrefixConditions.add(Pair.create(startOffset, restartCondition));
}
public void prefixUpdated() {
final int caretOffset = myEditor.getCaretModel().getOffset();
if (caretOffset < myStartCaret) {
scheduleRestart();
myRestartingPrefixConditions.clear();
return;
}
final CharSequence text = myEditor.getDocument().getCharsSequence();
for (Pair<Integer, ElementPattern<String>> pair : myRestartingPrefixConditions) {
int start = pair.first;
if (caretOffset >= start && start >= 0) {
final String newPrefix = text.subSequence(start, caretOffset).toString();
if (pair.second.accepts(newPrefix)) {
scheduleRestart();
myRestartingPrefixConditions.clear();
return;
}
}
}
hideAutopopupIfMeaningless();
}
public void scheduleRestart() {
ApplicationManager.getApplication().assertIsDispatchThread();
cancel();
final CompletionProgressIndicator current = CompletionServiceImpl.getCompletionService().getCurrentCompletion();
if (this != current) {
LOG.error(current + "!=" + this);
}
hideAutopopupIfMeaningless();
CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
}
final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor);
CompletionServiceImpl.setCompletionPhase(phase);
phase.ignoreCurrentDocumentChange();
final Project project = getProject();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
CompletionAutoPopupHandler.runLaterWithCommitted(project, myEditor.getDocument(), new Runnable() {
@Override
public void run() {
if (phase.checkExpired()) return;
CompletionAutoPopupHandler.invokeCompletion(myParameters.getCompletionType(),
isAutopopupCompletion(), project, myEditor, myParameters.getInvocationCount(),
true);
}
});
}
}, project.getDisposed());
}
@Override
public String toString() {
return "CompletionProgressIndicator[count=" +
myCount +
",phase=" +
CompletionServiceImpl.getCompletionPhase() +
"]@" +
System.identityHashCode(this);
}
protected void handleEmptyLookup(final boolean awaitSecondInvocation) {
LOG.assertTrue(!isAutopopupCompletion());
if (ApplicationManager.getApplication().isUnitTestMode() || !myHandler.invokedExplicitly) {
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
return;
}
for (final CompletionContributor contributor : CompletionContributor.forParameters(getParameters())) {
final String text = contributor.handleEmptyLookup(getParameters(), getEditor());
if (StringUtil.isNotEmpty(text)) {
LightweightHint hint = showErrorHint(getProject(), getEditor(), text);
CompletionServiceImpl.setCompletionPhase(
awaitSecondInvocation ? new CompletionPhase.NoSuggestionsHint(hint, this) : CompletionPhase.NoCompletion);
return;
}
}
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
}
private static LightweightHint showErrorHint(Project project, Editor editor, String text) {
final LightweightHint[] result = {null};
final EditorHintListener listener = new EditorHintListener() {
@Override
public void hintShown(final Project project, final LightweightHint hint, final int flags) {
result[0] = hint;
}
};
final MessageBusConnection connection = project.getMessageBus().connect();
connection.subscribe(EditorHintListener.TOPIC, listener);
assert text != null;
HintManager.getInstance().showErrorHint(editor, text, HintManager.UNDER);
connection.disconnect();
return result[0];
}
private static boolean shouldPreselectFirstSuggestion(CompletionParameters parameters) {
if (!Registry.is("ide.completion.autopopup.choose.by.enter")) {
return false;
}
if (!ApplicationManager.getApplication().isUnitTestMode()) {
return true;
}
switch (CodeInsightSettings.getInstance().AUTOPOPUP_FOCUS_POLICY) {
case CodeInsightSettings.ALWAYS:
return true;
case CodeInsightSettings.NEVER:
return false;
}
final Language language = PsiUtilCore.getLanguageAtOffset(parameters.getPosition().getContainingFile(), parameters.getOffset());
for (CompletionConfidence confidence : CompletionConfidenceEP.forLanguage(language)) {
//noinspection deprecation
final ThreeState result = confidence.shouldFocusLookup(parameters);
if (result != ThreeState.UNSURE) {
LOG.debug(confidence + " has returned shouldFocusLookup=" + result);
return result == ThreeState.YES;
}
}
return false;
}
void startCompletion(final CompletionInitializationContext initContext) {
boolean sync = ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup;
final CompletionThreading strategy = sync ? new SyncCompletion() : new AsyncCompletion();
strategy.startThread(ProgressWrapper.wrap(this), new Runnable() {
@Override
public void run() {
scheduleAdvertising();
}
});
final WeighingDelegate weigher = strategy.delegateWeighing(this);
class CalculateItems implements Runnable {
@Override
public void run() {
try {
calculateItems(initContext, weigher);
}
catch (ProcessCanceledException ignore) {
cancel(); // some contributor may just throw PCE; if indicator is not canceled everything will hang
}
catch (Throwable t) {
cancel();
LOG.error(t);
}
}
}
strategy.startThread(this, new CalculateItems());
}
private LookupElement[] calculateItems(CompletionInitializationContext initContext, WeighingDelegate weigher) {
duringCompletion(initContext);
ProgressManager.checkCanceled();
LookupElement[] result = CompletionService.getCompletionService().performCompletion(myParameters, weigher);
ProgressManager.checkCanceled();
weigher.waitFor();
ProgressManager.checkCanceled();
return result;
}
public void addAdvertisement(@NotNull final String text, @Nullable final Color bgColor) {
myAdvertiserChanges.offer(new Runnable() {
@Override
public void run() {
myLookup.addAdvertisement(text, bgColor);
}
});
myQueue.queue(myUpdate);
}
private static class ModifierTracker extends KeyAdapter {
private final JComponent myContentComponent;
public ModifierTracker(JComponent contentComponent) {
myContentComponent = contentComponent;
}
@Override
public void keyPressed(KeyEvent e) {
processModifier(e);
}
@Override
public void keyReleased(KeyEvent e) {
processModifier(e);
}
private void processModifier(KeyEvent e) {
final int code = e.getKeyCode();
if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_META || code == KeyEvent.VK_ALT || code == KeyEvent.VK_SHIFT) {
myContentComponent.removeKeyListener(this);
final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase();
if (phase instanceof CompletionPhase.BgCalculation) {
((CompletionPhase.BgCalculation)phase).modifiersChanged = true;
}
else if (phase instanceof CompletionPhase.InsertedSingleItem) {
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
}
}
}
}
}