@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(); }
protected void completionFinished( final int offset1, final int offset2, final CompletionProgressIndicator indicator, final LookupElement[] items, boolean hasModifiers) { if (items.length == 0) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); indicator.handleEmptyLookup(true); checkNotSync(indicator, items); return; } LOG.assertTrue(!indicator.isRunning(), "running"); LOG.assertTrue(!indicator.isCanceled(), "canceled"); indicator.getLookup().refreshUi(true, false); final AutoCompletionDecision decision = shouldAutoComplete(indicator, items); if (decision == AutoCompletionDecision.SHOW_LOOKUP) { CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated(indicator)); indicator.getLookup().setCalculating(false); indicator.showLookup(); } else if (decision instanceof AutoCompletionDecision.InsertItem) { final Runnable restorePrefix = rememberDocumentState(indicator.getEditor()); final LookupElement item = ((AutoCompletionDecision.InsertItem) decision).getElement(); CommandProcessor.getInstance() .executeCommand( indicator.getProject(), new Runnable() { @Override public void run() { indicator.setMergeCommand(); indicator.getLookup().finishLookup(Lookup.AUTO_INSERT_SELECT_CHAR, item); } }, "Autocompletion", null); // the insert handler may have started a live template with completion if (CompletionService.getCompletionService().getCurrentCompletion() == null && !ApplicationManager.getApplication().isUnitTestMode()) { CompletionServiceImpl.setCompletionPhase( hasModifiers ? new CompletionPhase.InsertedSingleItem(indicator, restorePrefix) : CompletionPhase.NoCompletion); } checkNotSync(indicator, items); } else if (decision == AutoCompletionDecision.CLOSE_LOOKUP) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); checkNotSync(indicator, items); } }
public void addSparedChars( CompletionProgressIndicator indicator, LookupElement item, InsertionContext context, char completionChar) { String textInserted; if (context.getStartOffset() >= 0 && context.getTailOffset() >= context.getStartOffset()) { textInserted = context .getDocument() .getText() .substring(context.getStartOffset(), context.getTailOffset()); } else { textInserted = item.getLookupString(); } String withoutSpaces = StringUtil.replace( textInserted, new String[] {" ", "\t", "\n"}, new String[] {"", "", ""}); int spared = withoutSpaces.length() - indicator.getLookup().itemPattern(item).length(); if (!LookupEvent.isSpecialCompletionChar(completionChar) && withoutSpaces.contains(String.valueOf(completionChar))) { spared--; } if (spared > 0) { mySpared += spared; } }
protected void lookupItemSelected( final CompletionProgressIndicator indicator, @NotNull final LookupElement item, final char completionChar, final List<LookupElement> items) { if (indicator.isAutopopupCompletion()) { FeatureUsageTracker.getInstance() .triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_BASIC); } CompletionAssertions.WatchingInsertionContext context = null; try { Lookup lookup = indicator.getLookup(); CompletionLookupArranger.StatisticsUpdate update = CompletionLookupArranger.collectStatisticChanges(item, lookup); context = insertItemHonorBlockSelection(indicator, item, completionChar, items, update); CompletionLookupArranger.trackStatistics(context, update); } finally { afterItemInsertion(indicator, context == null ? null : context.getLaterRunnable()); } }
private AutoCompletionDecision shouldAutoComplete( final CompletionProgressIndicator indicator, final LookupElement[] items) { if (!invokedExplicitly) { return AutoCompletionDecision.SHOW_LOOKUP; } final CompletionParameters parameters = indicator.getParameters(); final LookupElement item = items[0]; if (items.length == 1) { final AutoCompletionPolicy policy = getAutocompletionPolicy(item); if (policy == AutoCompletionPolicy.NEVER_AUTOCOMPLETE) return AutoCompletionDecision.SHOW_LOOKUP; if (policy == AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) return AutoCompletionDecision.insertItem(item); if (!indicator.getLookup().itemMatcher(item).isStartMatch(item)) return AutoCompletionDecision.SHOW_LOOKUP; } if (!isAutocompleteOnInvocation(parameters.getCompletionType())) { return AutoCompletionDecision.SHOW_LOOKUP; } if (isInsideIdentifier(indicator.getOffsetMap())) { return AutoCompletionDecision.SHOW_LOOKUP; } if (items.length == 1 && getAutocompletionPolicy(item) == AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE) { return AutoCompletionDecision.insertItem(item); } AutoCompletionContext context = new AutoCompletionContext( parameters, items, indicator.getOffsetMap(), indicator.getLookup()); for (final CompletionContributor contributor : CompletionContributor.forParameters(parameters)) { final AutoCompletionDecision decision = contributor.handleAutoCompletionPossibility(context); if (decision != null) { return decision; } } return AutoCompletionDecision.SHOW_LOOKUP; }
private static void addCompletionChar( Project project, CompletionAssertions.WatchingInsertionContext context, LookupElement item, Editor editor, CompletionProgressIndicator indicator, char completionChar) { int tailOffset = context.getTailOffset(); if (tailOffset < 0) { LOG.info( "tailOffset<0 after inserting " + item + " of " + item.getClass() + "; invalidated at: " + context.invalidateTrace + "\n--------"); } else { editor.getCaretModel().moveToOffset(tailOffset); } if (context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR) { final Language language = PsiUtilBase.getLanguageInEditor(editor, project); if (language != null) { for (SmartEnterProcessor processor : SmartEnterProcessors.INSTANCE.forKey(language)) { if (processor.processAfterCompletion(editor, indicator.getParameters().getOriginalFile())) break; } } } else if (!editor .getCaretModel() .supportsMultipleCarets()) { // this will be done outside of runForEach caret context DataContext dataContext = DataManager.getInstance().getDataContext(editor.getContentComponent()); EditorActionManager.getInstance() .getTypedAction() .getHandler() .execute(editor, completionChar, dataContext); } }
private static void afterItemInsertion( final CompletionProgressIndicator indicator, final Runnable laterRunnable) { if (laterRunnable != null) { final Runnable runnable1 = new Runnable() { @Override public void run() { if (!indicator.getProject().isDisposed()) { laterRunnable.run(); } indicator.disposeIndicator(); } }; if (ApplicationManager.getApplication().isUnitTestMode()) { runnable1.run(); } else { ApplicationManager.getApplication().invokeLater(runnable1); } } else { indicator.disposeIndicator(); } }
private static CompletionAssertions.WatchingInsertionContext insertItem( final CompletionProgressIndicator indicator, final LookupElement item, final char completionChar, List<LookupElement> items, final CompletionLookupArranger.StatisticsUpdate update, final Editor editor, final PsiFile psiFile, final int caretOffset, final int idEndOffset) { editor.getCaretModel().moveToOffset(caretOffset); final int initialStartOffset = caretOffset - item.getLookupString().length(); assert initialStartOffset >= 0 : "negative startOffset: " + caretOffset + "; " + item.getLookupString(); indicator .getOffsetMap() .addOffset(CompletionInitializationContext.START_OFFSET, initialStartOffset); indicator .getOffsetMap() .addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, caretOffset); indicator .getOffsetMap() .addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, idEndOffset); final CompletionAssertions.WatchingInsertionContext context = new CompletionAssertions.WatchingInsertionContext( indicator.getOffsetMap(), psiFile, completionChar, items, editor); ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { if (caretOffset != idEndOffset && completionChar == Lookup.REPLACE_SELECT_CHAR) { editor.getDocument().deleteString(caretOffset, idEndOffset); } assert context.getStartOffset() >= 0 : "stale startOffset: was " + initialStartOffset + "; selEnd=" + caretOffset + "; idEnd=" + idEndOffset + "; file=" + context.getFile(); assert context.getTailOffset() >= 0 : "stale tail: was " + initialStartOffset + "; selEnd=" + caretOffset + "; idEnd=" + idEndOffset + "; file=" + context.getFile(); item.handleInsert(context); Project project = indicator.getProject(); PostprocessReformattingAspect.getInstance(project).doPostponedFormatting(); if (context.shouldAddCompletionChar()) { addCompletionChar(project, context, item, editor, indicator, completionChar); } if (!editor .getCaretModel() .supportsMultipleCarets()) { // done later, outside of this method context.stopWatching(); } editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } }); update.addSparedChars(indicator, item, context, completionChar); return context; }
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 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(); }
@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") @Override public Result beforeCharTyped( final char charTyped, Project project, final Editor editor, PsiFile file, FileType fileType) { assert !inside; inside = true; try { final LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(editor); if (lookup == null) { return Result.CONTINUE; } if (charTyped == ' ' && ChooseItemReplaceAction.hasTemplatePrefix(lookup, TemplateSettings.SPACE_CHAR)) { return Result.CONTINUE; } final CharFilter.Result result = getLookupAction(charTyped, lookup); if (lookup.isLookupDisposed()) { return Result.CONTINUE; } if (!lookup.performGuardedChange( new Runnable() { @Override public void run() { EditorModificationUtil.deleteSelectedText(editor); } })) { return Result.STOP; } if (result == CharFilter.Result.ADD_TO_PREFIX) { Document document = editor.getDocument(); long modificationStamp = document.getModificationStamp(); if (!lookup.performGuardedChange( new Runnable() { @Override public void run() { EditorModificationUtil.typeInStringAtCaretHonorBlockSelection( editor, String.valueOf(charTyped), true); } })) { return Result.STOP; } lookup.appendPrefix(charTyped); if (lookup.isStartCompletionWhenNothingMatches() && lookup.getItems().isEmpty()) { final CompletionProgressIndicator completion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); if (completion != null) { completion.scheduleRestart(); } else { AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor, null); } } AutoHardWrapHandler.getInstance() .wrapLineIfNecessary( editor, DataManager.getInstance().getDataContext(editor.getContentComponent()), modificationStamp); final CompletionProgressIndicator completion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); if (completion != null) { completion.prefixUpdated(); } return Result.STOP; } if (result == CharFilter.Result.SELECT_ITEM_AND_FINISH_LOOKUP && lookup.isFocused()) { LookupElement item = lookup.getCurrentItem(); if (item != null) { if (completeTillTypedCharOccurrence(charTyped, lookup, item)) { return Result.STOP; } inside = false; ((CommandProcessorEx) CommandProcessor.getInstance()).enterModal(); try { finishLookup(charTyped, lookup); } finally { ((CommandProcessorEx) CommandProcessor.getInstance()).leaveModal(); } return Result.STOP; } } lookup.hide(); TypedHandler.autoPopupCompletion(editor, charTyped, project); return Result.CONTINUE; } finally { inside = false; } }
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; }
@NotNull private CompletionSorterImpl obtainSorter(LookupElement element) { return myProcess.getSorter(element); }
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; }