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; }
private static OffsetMap translateOffsetMapToHost( CompletionInitializationContext initContext, PsiFile context, PsiFile hostFile, Editor hostEditor) { final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(hostFile.getProject()); final OffsetMap hostMap = new OffsetMap(hostEditor.getDocument()); final OffsetMap original = initContext.getOffsetMap(); for (final OffsetKey key : original.getAllOffsets()) { hostMap.addOffset( key, injectedLanguageManager.injectedToHost(context, original.getOffset(key))); } return hostMap; }
private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection( final CompletionProgressIndicator indicator, final LookupElement item, final char completionChar, final List<LookupElement> items, final CompletionLookupArranger.StatisticsUpdate update) { final Editor editor = indicator.getEditor(); final int caretOffset = editor.getCaretModel().getOffset(); int idEndOffset = indicator.getIdentifierEndOffset(); if (idEndOffset < 0) { idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset); } final int idEndOffsetDelta = idEndOffset - caretOffset; CompletionAssertions.WatchingInsertionContext context = null; if (editor.getSelectionModel().hasBlockSelection() && editor.getSelectionModel().getBlockSelectionEnds().length > 0) { List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>(); int idDelta = 0; Document document = editor.getDocument(); int caretLine = document.getLineNumber(editor.getCaretModel().getOffset()); for (int point : editor.getSelectionModel().getBlockSelectionEnds()) { insertionPoints.add(document.createRangeMarker(point, point)); if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) { idDelta = idEndOffset - point; } } List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>(); for (RangeMarker marker : insertionPoints) { if (marker.isValid()) { int insertionPoint = marker.getStartOffset(); context = insertItem( indicator, item, completionChar, items, update, editor, indicator.getParameters().getOriginalFile(), insertionPoint, idDelta + insertionPoint); int offset = editor.getCaretModel().getOffset(); caretsAfter.add(document.createRangeMarker(offset, offset)); } } assert context != null; restoreBlockSelection(editor, caretsAfter, caretLine); for (RangeMarker insertionPoint : insertionPoints) { insertionPoint.dispose(); } for (RangeMarker marker : caretsAfter) { marker.dispose(); } } else if (editor.getCaretModel().supportsMultipleCarets()) { final List<CompletionAssertions.WatchingInsertionContext> contexts = new ArrayList<CompletionAssertions.WatchingInsertionContext>(); final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(editor); hostEditor .getCaretModel() .runForEachCaret( new CaretAction() { @Override public void perform(Caret caret) { PsiFile hostFile = InjectedLanguageUtil.getTopLevelFile( indicator.getParameters().getOriginalFile()); PsiFile targetFile = InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, caret.getOffset()); Editor targetEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, targetFile); int targetCaretOffset = targetEditor.getCaretModel().getOffset(); CompletionAssertions.WatchingInsertionContext currentContext = insertItem( indicator, item, completionChar, items, update, targetEditor, targetFile == null ? hostFile : targetFile, targetCaretOffset, targetCaretOffset + idEndOffsetDelta); contexts.add(currentContext); } }, true); context = contexts.get(contexts.size() - 1); if (context.shouldAddCompletionChar() && context.getCompletionChar() != Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { DataContext dataContext = DataManager.getInstance().getDataContext(editor.getContentComponent()); EditorActionManager.getInstance() .getTypedAction() .getHandler() .execute(editor, completionChar, dataContext); } }); } for (CompletionAssertions.WatchingInsertionContext insertionContext : contexts) { insertionContext.stopWatching(); } } else { context = insertItem( indicator, item, completionChar, items, update, editor, indicator.getParameters().getOriginalFile(), caretOffset, idEndOffset); } return context; }
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(); }
private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection( final CompletionProgressIndicator indicator, final LookupElement item, final char completionChar, final List<LookupElement> items, final CompletionLookupArranger.StatisticsUpdate update) { final Editor editor = indicator.getEditor(); final int caretOffset = editor.getCaretModel().getOffset(); int idEndOffset = indicator.getIdentifierEndOffset(); if (idEndOffset < 0) { idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset); } final int idEndOffsetDelta = idEndOffset - caretOffset; CompletionAssertions.WatchingInsertionContext context = null; if (editor.getSelectionModel().hasBlockSelection() && editor.getSelectionModel().getBlockSelectionEnds().length > 0) { List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>(); int idDelta = 0; Document document = editor.getDocument(); int caretLine = document.getLineNumber(editor.getCaretModel().getOffset()); for (int point : editor.getSelectionModel().getBlockSelectionEnds()) { insertionPoints.add(document.createRangeMarker(point, point)); if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) { idDelta = idEndOffset - point; } } List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>(); for (RangeMarker marker : insertionPoints) { if (marker.isValid()) { int insertionPoint = marker.getStartOffset(); context = insertItem( indicator, item, completionChar, items, update, editor, insertionPoint, idDelta + insertionPoint); int offset = editor.getCaretModel().getOffset(); caretsAfter.add(document.createRangeMarker(offset, offset)); } } assert context != null; restoreBlockSelection(editor, caretsAfter, caretLine); for (RangeMarker insertionPoint : insertionPoints) { insertionPoint.dispose(); } for (RangeMarker marker : caretsAfter) { marker.dispose(); } } else { final Ref<CompletionAssertions.WatchingInsertionContext> contextRef = new Ref<CompletionAssertions.WatchingInsertionContext>(); editor .getCaretModel() .runForEachCaret( new CaretAction() { @Override public void perform(Caret caret) { CompletionAssertions.WatchingInsertionContext currentContext = insertItem( indicator, item, completionChar, items, update, editor, caret.getOffset(), caret.getOffset() + idEndOffsetDelta); if (caret .getVisualPosition() .equals(editor.getCaretModel().getPrimaryCaret().getVisualPosition())) { contextRef.set(currentContext); } } }); context = contextRef.get(); } return context; }
private static CompletionAssertions.WatchingInsertionContext insertItemHonorBlockSelection( CompletionProgressIndicator indicator, LookupElement item, char completionChar, List<LookupElement> items, CompletionLookupArranger.StatisticsUpdate update) { final Editor editor = indicator.getEditor(); final int caretOffset = editor.getCaretModel().getOffset(); int idEndOffset = indicator.getIdentifierEndOffset(); if (idEndOffset < 0) { idEndOffset = CompletionInitializationContext.calcDefaultIdentifierEnd(editor, caretOffset); } CompletionAssertions.WatchingInsertionContext context = null; if (editor.getSelectionModel().hasBlockSelection() && editor.getSelectionModel().getBlockSelectionEnds().length > 0) { List<RangeMarker> insertionPoints = new ArrayList<RangeMarker>(); int idDelta = 0; Document document = editor.getDocument(); int caretLine = document.getLineNumber(editor.getCaretModel().getOffset()); for (int point : editor.getSelectionModel().getBlockSelectionEnds()) { insertionPoints.add(document.createRangeMarker(point, point)); if (document.getLineNumber(point) == document.getLineNumber(idEndOffset)) { idDelta = idEndOffset - point; } } List<RangeMarker> caretsAfter = new ArrayList<RangeMarker>(); for (RangeMarker marker : insertionPoints) { if (marker.isValid()) { int insertionPoint = marker.getStartOffset(); context = insertItem( indicator, item, completionChar, items, update, editor, insertionPoint, idDelta + insertionPoint); int offset = editor.getCaretModel().getOffset(); caretsAfter.add(document.createRangeMarker(offset, offset)); } } assert context != null; restoreBlockSelection(editor, caretsAfter, caretLine); for (RangeMarker insertionPoint : insertionPoints) { insertionPoint.dispose(); } for (RangeMarker marker : caretsAfter) { marker.dispose(); } } else { context = insertItem( indicator, item, completionChar, items, update, editor, caretOffset, idEndOffset); } return context; }