private CompletionInitializationContext runContributorsBeforeCompletion( Editor editor, PsiFile psiFile, int invocationCount, Caret caret) { final Ref<CompletionContributor> current = Ref.create(null); CompletionInitializationContext context = new CompletionInitializationContext( editor, caret, psiFile, myCompletionType, invocationCount) { CompletionContributor dummyIdentifierChanger; @Override public void setDummyIdentifier(@NotNull String dummyIdentifier) { super.setDummyIdentifier(dummyIdentifier); if (dummyIdentifierChanger != null) { LOG.error( "Changing the dummy identifier twice, already changed by " + dummyIdentifierChanger); } dummyIdentifierChanger = current.get(); } }; List<CompletionContributor> contributors = CompletionContributor.forLanguage(context.getPositionLanguage()); Project project = psiFile.getProject(); List<CompletionContributor> filteredContributors = DumbService.getInstance(project).filterByDumbAwareness(contributors); for (final CompletionContributor contributor : filteredContributors) { current.set(contributor); contributor.beforeCompletion(context); CompletionAssertions.checkEditorValid(editor); assert !PsiDocumentManager.getInstance(project).isUncommited(editor.getDocument()) : "Contributor " + contributor + " left the document uncommitted"; } return context; }
@NotNull private LookupImpl obtainLookup(Editor editor) { CompletionAssertions.checkEditorValid(editor); LookupImpl existing = (LookupImpl) LookupManager.getActiveLookup(editor); if (existing != null && existing.isCompletion()) { existing.markReused(); if (!autopopup) { existing.setFocusDegree(LookupImpl.FocusDegree.FOCUSED); } return existing; } LookupImpl lookup = (LookupImpl) LookupManager.getInstance(editor.getProject()) .createLookup( editor, LookupElement.EMPTY_ARRAY, "", new LookupArranger.DefaultArranger()); if (editor.isOneLineMode()) { lookup.setCancelOnClickOutside(true); lookup.setCancelOnOtherWindowOpen(true); } lookup.setFocusDegree( autopopup ? LookupImpl.FocusDegree.UNFOCUSED : LookupImpl.FocusDegree.FOCUSED); return lookup; }
private void insertDummyIdentifier( final CompletionInitializationContext initContext, final boolean hasModifiers, final int invocationCount) { final PsiFile originalFile = initContext.getFile(); InjectedLanguageManager manager = InjectedLanguageManager.getInstance(originalFile.getProject()); final PsiFile hostFile = manager.getTopLevelFile(originalFile); final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(initContext.getEditor()); final OffsetMap hostMap = translateOffsetMapToHost(initContext, originalFile, hostFile, hostEditor); final PsiFile[] hostCopy = {null}; DocumentUtil.writeInRunUndoTransparentAction( new Runnable() { @Override public void run() { hostCopy[0] = createFileCopy( hostFile, initContext.getStartOffset(), initContext.getSelectionEndOffset()); } }); final Document copyDocument = hostCopy[0].getViewProvider().getDocument(); assert copyDocument != null : "no document"; final OffsetTranslator translator = new OffsetTranslator(hostEditor.getDocument(), initContext.getFile(), copyDocument); CompletionAssertions.checkEditorValid(initContext.getEditor()); CommandProcessor.getInstance() .runUndoTransparentAction( new Runnable() { @Override public void run() { ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { String dummyIdentifier = initContext.getDummyIdentifier(); if (StringUtil.isEmpty(dummyIdentifier)) return; int startOffset = hostMap.getOffset(CompletionInitializationContext.START_OFFSET); int endOffset = hostMap.getOffset( CompletionInitializationContext.SELECTION_END_OFFSET); copyDocument.replaceString(startOffset, endOffset, dummyIdentifier); } }); } }); CompletionAssertions.checkEditorValid(initContext.getEditor()); final Project project = originalFile.getProject(); if (!synchronous) { if (CompletionServiceImpl.isPhase(CompletionPhase.NoCompletion.getClass()) || !CompletionServiceImpl.assertPhase(CompletionPhase.CommittingDocuments.class)) { Disposer.dispose(translator); return; } final CompletionPhase.CommittingDocuments phase = (CompletionPhase.CommittingDocuments) CompletionServiceImpl.getCompletionPhase(); CompletionAutoPopupHandler.runLaterWithCommitted( project, copyDocument, new Runnable() { @Override public void run() { if (phase.checkExpired()) { Disposer.dispose(translator); return; } doComplete( initContext, hasModifiers, invocationCount, hostCopy[0], hostMap, translator); } }); } else { PsiDocumentManager.getInstance(project).commitDocument(copyDocument); doComplete(initContext, hasModifiers, invocationCount, hostCopy[0], hostMap, translator); } }
private void doComplete( CompletionInitializationContext initContext, boolean hasModifiers, int invocationCount, PsiFile hostCopy, OffsetMap hostMap, OffsetTranslator translator) { final Editor editor = initContext.getEditor(); CompletionAssertions.checkEditorValid(editor); CompletionContext context = createCompletionContext( hostCopy, hostMap.getOffset(CompletionInitializationContext.START_OFFSET), hostMap, initContext.getFile()); LookupImpl lookup = obtainLookup(editor); CompletionParameters parameters = createCompletionParameters(invocationCount, context, editor); CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); if (phase instanceof CompletionPhase.CommittingDocuments) { if (phase.indicator != null) { phase.indicator.closeAndFinish(false); } ((CompletionPhase.CommittingDocuments) phase).replaced = true; } else { CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); } final Semaphore freezeSemaphore = new Semaphore(); freezeSemaphore.down(); final CompletionProgressIndicator indicator = new CompletionProgressIndicator( editor, parameters, this, freezeSemaphore, initContext.getOffsetMap(), hasModifiers, lookup); Disposer.register(indicator, hostMap); Disposer.register(indicator, context.getOffsetMap()); Disposer.register(indicator, translator); CompletionServiceImpl.setCompletionPhase( synchronous ? new CompletionPhase.Synchronous(indicator) : new CompletionPhase.BgCalculation(indicator)); final AtomicReference<LookupElement[]> data = indicator.startCompletion(initContext); if (!synchronous) { return; } if (freezeSemaphore.waitFor(2000)) { final LookupElement[] allItems = data.get(); if (allItems != null && !indicator.isRunning() && !indicator .isCanceled()) { // the completion is really finished, now we may auto-insert or show // lookup completionFinished( initContext.getStartOffset(), initContext.getSelectionEndOffset(), indicator, allItems, hasModifiers); checkNotSync(indicator, allItems); return; } } CompletionServiceImpl.setCompletionPhase(new CompletionPhase.BgCalculation(indicator)); indicator.showLookup(); }
public final void invokeCompletion( @NotNull final Project project, @NotNull final Editor editor, int time, boolean hasModifiers, boolean restarted, @NotNull final Caret caret) { markCaretAsProcessed(caret); if (invokedExplicitly) { CompletionLookupArranger.applyLastCompletionStatisticsUpdate(); } checkNoWriteAccess(); CompletionAssertions.checkEditorValid(editor); int offset = editor.getCaretModel().getOffset(); if (editor.isViewer() || editor.getDocument().getRangeGuard(offset, offset) != null) { editor.getDocument().fireReadOnlyModificationAttempt(); CodeInsightUtilBase.showReadOnlyViewWarning(editor); return; } if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) { return; } CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); boolean repeated = phase.indicator != null && phase.indicator.isRepeatedInvocation(myCompletionType, editor); /* if (repeated && isAutocompleteCommonPrefixOnInvocation() && phase.fillInCommonPrefix()) { return; } */ final int newTime = phase.newCompletionStarted(time, repeated); if (invokedExplicitly) { time = newTime; } final int invocationCount = time; if (CompletionServiceImpl.isPhase(CompletionPhase.InsertedSingleItem.class)) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } CompletionServiceImpl.assertPhase( CompletionPhase.NoCompletion.getClass(), CompletionPhase.CommittingDocuments.class); if (invocationCount > 1 && myCompletionType == CompletionType.BASIC) { FeatureUsageTracker.getInstance() .triggerFeatureUsed(CodeCompletionFeatures.SECOND_BASIC_COMPLETION); } final CompletionInitializationContext[] initializationContext = {null}; Runnable initCmd = new Runnable() { @Override public void run() { Runnable runnable = new Runnable() { @Override public void run() { EditorUtil.fillVirtualSpaceUntilCaret(editor); PsiDocumentManager.getInstance(project).commitAllDocuments(); CompletionAssertions.checkEditorValid(editor); final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(caret, project); assert psiFile != null : "no PSI file: " + FileDocumentManager.getInstance().getFile(editor.getDocument()); psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE); CompletionAssertions.assertCommitSuccessful(editor, psiFile); initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile, invocationCount, caret); } }; ApplicationManager.getApplication().runWriteAction(runnable); } }; if (autopopup) { CommandProcessor.getInstance().runUndoTransparentAction(initCmd); if (!restarted && shouldSkipAutoPopup(editor, initializationContext[0].getFile())) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); return; } } else { CommandProcessor.getInstance().executeCommand(project, initCmd, null, null); } insertDummyIdentifier(initializationContext[0], hasModifiers, invocationCount); }