void (final Editor editor, final Project project, final PsiFile file, final Document document, @NotNull final Transferable content, @NotNull final TypingActionsExtension typingActionsExtension) { CopyPasteManager.getInstance().stopKillRings(); String text = null; try { text = (String)content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { editor.getComponent().getToolkit().beep(); } if (text == null) return; int textLength = text.length(); if (BasePasteHandler.isContentTooLarge(textLength)) { BasePasteHandler.contentLengthLimitExceededMessage(textLength); return; } final CodeInsightSettings settings = CodeInsightSettings.getInstance(); final List<ProcessorAndData<?>> extraData = new ArrayList<>(); final Collection<TextBlockTransferableData> allValues = new ArrayList<>(); for (CopyPastePostProcessor<? extends TextBlockTransferableData> processor : CopyPastePostProcessor.EP_NAME.getExtensionList()) { ProcessorAndData<? extends TextBlockTransferableData> data = ProcessorAndData.create(processor, content); if (data != null) { extraData.add(data); allValues.addAll(data.data); } } text = TextBlockTransferable.convertLineSeparators(editor, text, allValues); final CaretModel caretModel = editor.getCaretModel(); final SelectionModel selectionModel = editor.getSelectionModel(); final int col = caretModel.getLogicalPosition().column; // There is a possible case that we want to perform paste while there is an active selection at the editor and caret is located // inside it (e.g. Ctrl+A is pressed while caret is not at the zero column). We want to insert the text at selection start column // then, hence, inserted block of text should be indented according to the selection start as well. final int blockIndentAnchorColumn; final int caretOffset = caretModel.getOffset(); if (selectionModel.hasSelection() && caretOffset >= selectionModel.getSelectionStart()) { blockIndentAnchorColumn = editor.offsetToLogicalPosition(selectionModel.getSelectionStart()).column; } else { blockIndentAnchorColumn = col; } // We assume that EditorModificationUtil.insertStringAtCaret() is smart enough to remove currently selected text (if any). CopyPasteOptions copyPasteOptions = CopyPasteOptionsTransferableData.valueFromTransferable(content); CopiedFromEmptySelectionPasteMode pasteMode = copyPasteOptions.isCopiedFromEmptySelection() ? getCopiedFromEmptySelectionPasteMode() : AT_CARET; boolean isInsertingEntireLineAboveCaret = pasteMode == ENTIRE_LINE_ABOVE_CARET && !selectionModel.hasSelection(); List<CaretState> caretStateToRestore = null; if (isInsertingEntireLineAboveCaret) { // Make CopyPastePreProcessors see the caret at the real insertion offset. caretStateToRestore = caretModel.getCaretsAndSelections(); int lineStartOffset = EditorUtil.getNotFoldedLineStartOffset(editor, caretOffset); caretModel.moveToOffset(lineStartOffset); } RawText rawText = RawText.fromTransferable(content); String newText = text; for (CopyPastePreProcessor preProcessor : CopyPastePreProcessor.EP_NAME.getExtensionList()) { newText = preProcessor.preprocessOnPaste(project, file, editor, newText, rawText); } if (caretStateToRestore != null) { caretModel.setCaretsAndSelections(caretStateToRestore); } final boolean pastedTextWasChanged = !text.equals(newText); int indentOptions = pastedTextWasChanged ? CodeInsightSettings.REFORMAT_BLOCK : settings.REFORMAT_ON_PASTE; text = newText; if (LanguageFormatting.INSTANCE.forContext(file) == null && indentOptions != CodeInsightSettings.NO_REFORMAT) { indentOptions = CodeInsightSettings.INDENT_BLOCK; } final String _text = text; final TextRange pastedRange = ApplicationManager.getApplication().runWriteAction((Computable<TextRange>)() -> { if (!project.isDisposed()) { ((UndoManagerImpl)UndoManager.getInstance(project)).addDocumentAsAffected(editor.getDocument()); } return isInsertingEntireLineAboveCaret ? EditorCopyPasteHelperImpl.insertEntireLineAboveCaret(editor, _text) : EditorCopyPasteHelperImpl.insertStringAtCaret(editor, _text, pasteMode == TRIM_IF_MIDDLE_LINE); }); final RangeMarker bounds = document.createRangeMarker(pastedRange); // `skipIndentation` is additionally used as marker for changed pasted test // Any value, except `null` is a signal that the text was transformed. // For the `CopyPasteFoldingProcessor` it means that folding data is not valid and cannot be applied. final Ref<Boolean> skipIndentation = new Ref<>(pastedTextWasChanged ? Boolean.FALSE : null); for (ProcessorAndData<?> data : extraData) { data.process(project, editor, bounds, caretOffset, skipIndentation); } boolean pastedTextContainsWhiteSpacesOnly = CharArrayUtil.shiftForward(document.getCharsSequence(), bounds.getStartOffset(), " \n\t") >= bounds.getEndOffset(); VirtualFile virtualFile = file.getVirtualFile(); if (!pastedTextContainsWhiteSpacesOnly && (virtualFile == null || !SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile))) { final int howtoReformat = skipIndentation.get() == Boolean.TRUE && (indentOptions == CodeInsightSettings.INDENT_BLOCK || indentOptions == CodeInsightSettings.INDENT_EACH_LINE) ? CodeInsightSettings.NO_REFORMAT : indentOptions; ApplicationManager.getApplication().runWriteAction( () -> typingActionsExtension .format(project, editor, howtoReformat, bounds.getStartOffset(), bounds.getEndOffset(), blockIndentAnchorColumn, true) ); } if (bounds.isValid()) { if (!isInsertingEntireLineAboveCaret) { caretModel.moveToOffset(bounds.getEndOffset()); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } editor.putUserData(EditorEx.LAST_PASTED_REGION, bounds.getTextRange()); } // Don't dispose the 'bounds' RangeMarker because the processors // from 'extraData' may use it later, for instance, in an invokeLater() block. }