public static int insertStringAtCaret( Editor editor, @NotNull String s, boolean toProcessOverwriteMode, boolean toMoveCaret, int caretShift) { final SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.hasSelection()) { editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart(), true); } // There is a possible case that particular soft wraps become hard wraps if the caret is located // at soft wrap-introduced virtual // space, hence, we need to give editor a chance to react accordingly. editor.getSoftWrapModel().beforeDocumentChangeAtCaret(); int oldOffset = editor.getCaretModel().getOffset(); String filler = calcStringToFillVirtualSpace(editor); if (filler.length() > 0) { s = filler + s; } Document document = editor.getDocument(); if (editor.isInsertMode() || !toProcessOverwriteMode) { if (selectionModel.hasSelection()) { oldOffset = selectionModel.getSelectionStart(); document.replaceString( selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), s); } else { document.insertString(oldOffset, s); } } else { deleteSelectedText(editor); int lineNumber = editor.getCaretModel().getLogicalPosition().line; if (lineNumber >= document.getLineCount()) { return insertStringAtCaret(editor, s, false, toMoveCaret); } int endOffset = document.getLineEndOffset(lineNumber); document.replaceString(oldOffset, Math.min(endOffset, oldOffset + s.length()), s); } int offset = oldOffset + filler.length() + caretShift; if (toMoveCaret) { editor.getCaretModel().moveToOffset(offset, true); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); selectionModel.removeSelection(); } else if (editor.getCaretModel().getOffset() != oldOffset) { // handling the case when caret model tracks document changes editor.getCaretModel().moveToOffset(oldOffset); } return offset; }
/** * Emulates pressing <code>Enter</code> at current caret position. * * @param editor target editor * @param project target project * @param shifts two-elements array which is expected to be filled with the following info: 1. The * first element holds added lines number; 2. The second element holds added symbols number; */ private static void emulateEnter( @NotNull final Editor editor, @NotNull Project project, int[] shifts) { final DataContext dataContext = prepareContext(editor.getComponent(), project); int caretOffset = editor.getCaretModel().getOffset(); Document document = editor.getDocument(); SelectionModel selectionModel = editor.getSelectionModel(); int startSelectionOffset = 0; int endSelectionOffset = 0; boolean restoreSelection = selectionModel.hasSelection(); if (restoreSelection) { startSelectionOffset = selectionModel.getSelectionStart(); endSelectionOffset = selectionModel.getSelectionEnd(); selectionModel.removeSelection(); } int textLengthBeforeWrap = document.getTextLength(); int lineCountBeforeWrap = document.getLineCount(); DataManager.getInstance() .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, true); CommandProcessor commandProcessor = CommandProcessor.getInstance(); try { Runnable command = new Runnable() { @Override public void run() { EditorActionManager.getInstance() .getActionHandler(IdeActions.ACTION_EDITOR_ENTER) .execute(editor, dataContext); } }; if (commandProcessor.getCurrentCommand() == null) { commandProcessor.executeCommand(editor.getProject(), command, WRAP_LINE_COMMAND_NAME, null); } else { command.run(); } } finally { DataManager.getInstance() .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, null); } int symbolsDiff = document.getTextLength() - textLengthBeforeWrap; if (restoreSelection) { int newSelectionStart = startSelectionOffset; int newSelectionEnd = endSelectionOffset; if (startSelectionOffset >= caretOffset) { newSelectionStart += symbolsDiff; } if (endSelectionOffset >= caretOffset) { newSelectionEnd += symbolsDiff; } selectionModel.setSelection(newSelectionStart, newSelectionEnd); } shifts[0] = document.getLineCount() - lineCountBeforeWrap; shifts[1] = symbolsDiff; }
public static void deleteSelectedText(Editor editor) { SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.hasBlockSelection()) deleteBlockSelection(editor); if (!selectionModel.hasSelection()) return; int selectionStart = selectionModel.getSelectionStart(); int selectionEnd = selectionModel.getSelectionEnd(); editor.getCaretModel().moveToOffset(selectionStart); selectionModel.removeSelection(); editor.getDocument().deleteString(selectionStart, selectionEnd); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); }
private void dumpEditorMarkupAndSelection(PrintStream dumpStream) { dumpStream.println(mySearchResults.getFindModel()); if (myReplacementPreviewText != null) { dumpStream.println("--"); dumpStream.println("Replacement Preview: " + myReplacementPreviewText); } dumpStream.println("--"); Editor editor = mySearchResults.getEditor(); RangeHighlighter[] highlighters = editor.getMarkupModel().getAllHighlighters(); List<Pair<Integer, Character>> ranges = new ArrayList<Pair<Integer, Character>>(); for (RangeHighlighter highlighter : highlighters) { ranges.add(new Pair<Integer, Character>(highlighter.getStartOffset(), '[')); ranges.add(new Pair<Integer, Character>(highlighter.getEndOffset(), ']')); } SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.getSelectionStart() != selectionModel.getSelectionEnd()) { ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionStart(), '<')); ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionEnd(), '>')); } ranges.add(new Pair<Integer, Character>(-1, '\n')); ranges.add(new Pair<Integer, Character>(editor.getDocument().getTextLength() + 1, '\n')); ContainerUtil.sort( ranges, new Comparator<Pair<Integer, Character>>() { @Override public int compare(Pair<Integer, Character> pair, Pair<Integer, Character> pair2) { int res = pair.first - pair2.first; if (res == 0) { Character c1 = pair.second; Character c2 = pair2.second; if (c1 == '<' && c2 == '[') { return 1; } else if (c1 == '[' && c2 == '<') { return -1; } return c1.compareTo(c2); } return res; } }); Document document = editor.getDocument(); for (int i = 0; i < ranges.size() - 1; ++i) { Pair<Integer, Character> pair = ranges.get(i); Pair<Integer, Character> pair1 = ranges.get(i + 1); dumpStream.print( pair.second + document.getText( TextRange.create( Math.max(pair.first, 0), Math.min(pair1.first, document.getTextLength())))); } dumpStream.println("\n--"); if (NotFound) { dumpStream.println("Not Found"); dumpStream.println("--"); NotFound = false; } for (RangeHighlighter highlighter : highlighters) { dumpStream.println(highlighter + " : " + highlighter.getTextAttributes()); } dumpStream.println("------------"); }
public final void move(Editor editor, final PsiFile file) { myMover.beforeMove(editor, myInfo, myIsDown); final Document document = editor.getDocument(); final int start = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.startLine); final int end = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove.endLine); myInfo.range1 = document.createRangeMarker(start, end); String textToInsert = document.getCharsSequence().subSequence(start, end).toString(); if (!StringUtil.endsWithChar(textToInsert, '\n')) textToInsert += '\n'; final int start2 = document.getLineStartOffset(myInfo.toMove2.startLine); final int end2 = StatementUpDownMover.getLineStartSafeOffset(document, myInfo.toMove2.endLine); String textToInsert2 = document.getCharsSequence().subSequence(start2, end2).toString(); if (!StringUtil.endsWithChar(textToInsert2, '\n')) textToInsert2 += '\n'; myInfo.range2 = document.createRangeMarker(start2, end2); if (myInfo.range1.getStartOffset() < myInfo.range2.getStartOffset()) { myInfo.range1.setGreedyToLeft(true); myInfo.range1.setGreedyToRight(false); myInfo.range2.setGreedyToLeft(true); myInfo.range2.setGreedyToRight(true); } else { myInfo.range1.setGreedyToLeft(true); myInfo.range1.setGreedyToRight(true); myInfo.range2.setGreedyToLeft(true); myInfo.range2.setGreedyToRight(false); } final CaretModel caretModel = editor.getCaretModel(); final int caretRelativePos = caretModel.getOffset() - start; final SelectionModel selectionModel = editor.getSelectionModel(); final int selectionStart = selectionModel.getSelectionStart(); final int selectionEnd = selectionModel.getSelectionEnd(); final boolean hasSelection = selectionModel.hasSelection(); // to prevent flicker caretModel.moveToOffset(0); // There is a possible case that the user performs, say, method move. It's also possible that // one (or both) of moved methods // are folded. We want to preserve their states then. The problem is that folding processing is // based on PSI element pointers // and the pointers behave as following during move up/down: // method1() {} // method2() {} // Pointer for the fold region from method1 points to 'method2()' now and vice versa (check // range markers processing on // document change for further information). I.e. information about fold regions statuses holds // the data swapped for // 'method1' and 'method2'. Hence, we want to apply correct 'collapsed' status. FoldRegion topRegion = null; FoldRegion bottomRegion = null; for (FoldRegion foldRegion : editor.getFoldingModel().getAllFoldRegions()) { if (!foldRegion.isValid() || (!contains(myInfo.range1, foldRegion) && !contains(myInfo.range2, foldRegion))) { continue; } if (contains(myInfo.range1, foldRegion) && !contains(topRegion, foldRegion)) { topRegion = foldRegion; } else if (contains(myInfo.range2, foldRegion) && !contains(bottomRegion, foldRegion)) { bottomRegion = foldRegion; } } document.insertString(myInfo.range1.getStartOffset(), textToInsert2); document.deleteString( myInfo.range1.getStartOffset() + textToInsert2.length(), myInfo.range1.getEndOffset()); document.insertString(myInfo.range2.getStartOffset(), textToInsert); int s = myInfo.range2.getStartOffset() + textToInsert.length(); int e = myInfo.range2.getEndOffset(); if (e > s) { document.deleteString(s, e); } final Project project = file.getProject(); PsiDocumentManager.getInstance(project).commitAllDocuments(); // Swap fold regions status if necessary. if (topRegion != null && bottomRegion != null) { final FoldRegion finalTopRegion = topRegion; final FoldRegion finalBottomRegion = bottomRegion; editor .getFoldingModel() .runBatchFoldingOperation( new Runnable() { @Override public void run() { boolean topExpanded = finalTopRegion.isExpanded(); finalTopRegion.setExpanded(finalBottomRegion.isExpanded()); finalBottomRegion.setExpanded(topExpanded); } }); } CodeFoldingManager.getInstance(project).allowFoldingOnCaretLine(editor); if (hasSelection) { restoreSelection(editor, selectionStart, selectionEnd, start, myInfo.range2.getStartOffset()); } caretModel.moveToOffset(myInfo.range2.getStartOffset() + caretRelativePos); if (myInfo.indentTarget) { indentLinesIn(editor, file, document, project, myInfo.range2); } if (myInfo.indentSource) { indentLinesIn(editor, file, document, project, myInfo.range1); } myMover.afterMove(editor, file, myInfo, myIsDown); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); }
private static void doPaste( final Editor editor, final Project project, final PsiFile file, final Document document, final Producer<Transferable> producer) { Transferable content = null; if (producer != null) { content = producer.produce(); } else { CopyPasteManager manager = CopyPasteManager.getInstance(); if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) { content = manager.getContents(); if (content != null) { manager.stopKillRings(); } } } if (content != null) { String text = null; try { text = (String) content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { editor.getComponent().getToolkit().beep(); } if (text == null) return; final CodeInsightSettings settings = CodeInsightSettings.getInstance(); final Map<CopyPastePostProcessor, TextBlockTransferableData> extraData = new HashMap<CopyPastePostProcessor, TextBlockTransferableData>(); for (CopyPastePostProcessor processor : Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) { TextBlockTransferableData data = processor.extractTransferableData(content); if (data != null) { extraData.put(processor, data); } } text = TextBlockTransferable.convertLineSeparators(text, "\n", extraData.values()); 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). RawText rawText = RawText.fromTransferable(content); String newText = text; for (CopyPastePreProcessor preProcessor : Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) { newText = preProcessor.preprocessOnPaste(project, file, editor, newText, rawText); } int indentOptions = text.equals(newText) ? settings.REFORMAT_ON_PASTE : CodeInsightSettings.REFORMAT_BLOCK; text = newText; if (LanguageFormatting.INSTANCE.forContext(file) == null && indentOptions != CodeInsightSettings.NO_REFORMAT) { indentOptions = CodeInsightSettings.INDENT_BLOCK; } final String _text = text; ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { EditorModificationUtil.insertStringAtCaret(editor, _text, false, true); } }); int length = text.length(); int offset = caretModel.getOffset() - length; if (offset < 0) { length += offset; offset = 0; } final RangeMarker bounds = document.createRangeMarker(offset, offset + length); caretModel.moveToOffset(bounds.getEndOffset()); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); selectionModel.removeSelection(); final Ref<Boolean> indented = new Ref<Boolean>(Boolean.FALSE); for (Map.Entry<CopyPastePostProcessor, TextBlockTransferableData> e : extraData.entrySet()) { //noinspection unchecked e.getKey() .processTransferableData(project, editor, bounds, caretOffset, indented, e.getValue()); } 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 indentOptions1 = indentOptions; ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { switch (indentOptions1) { case CodeInsightSettings.INDENT_BLOCK: if (!indented.get()) { indentBlock( project, editor, bounds.getStartOffset(), bounds.getEndOffset(), blockIndentAnchorColumn); } break; case CodeInsightSettings.INDENT_EACH_LINE: if (!indented.get()) { indentEachLine( project, editor, bounds.getStartOffset(), bounds.getEndOffset()); } break; case CodeInsightSettings.REFORMAT_BLOCK: indentEachLine( project, editor, bounds.getStartOffset(), bounds .getEndOffset()); // this is needed for example when inserting a // comment before method reformatBlock( project, editor, bounds.getStartOffset(), bounds.getEndOffset()); break; } } }); } if (bounds.isValid()) { caretModel.moveToOffset(bounds.getEndOffset()); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); selectionModel.removeSelection(); editor.putUserData(EditorEx.LAST_PASTED_REGION, TextRange.create(bounds)); } } }
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { myProject = project; myFile = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage()); myEditor = editor; PsiElement context = myFile.getContext(); if (context != null && (context.textContains('\'') || context.textContains('\"'))) { String s = context.getText(); if (StringUtil.startsWith(s, "\"") || StringUtil.startsWith(s, "\'")) { myFile = context.getContainingFile(); myEditor = editor instanceof EditorWindow ? ((EditorWindow) editor).getDelegate() : editor; } } myDocument = myEditor.getDocument(); if (!FileDocumentManager.getInstance().requestWriting(myDocument, project)) { return; } PsiDocumentManager.getInstance(project).commitDocument(myDocument); FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line"); myCodeStyleManager = CodeStyleManager.getInstance(myProject); final SelectionModel selectionModel = myEditor.getSelectionModel(); boolean hasSelection = selectionModel.hasSelection(); myStartOffset = selectionModel.getSelectionStart(); myEndOffset = selectionModel.getSelectionEnd(); FoldRegion fold = myEditor.getFoldingModel().getCollapsedRegionAtOffset(myStartOffset); if (fold != null && fold.shouldNeverExpand() && fold.getStartOffset() == myStartOffset && fold.getEndOffset() == myEndOffset) { // Foldings that never expand are automatically selected, so the fact it is selected must not // interfer with commenter's logic hasSelection = false; } if (myDocument.getTextLength() == 0) return; while (true) { int lastLineEnd = myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset)); FoldRegion collapsedAt = myEditor.getFoldingModel().getCollapsedRegionAtOffset(lastLineEnd); if (collapsedAt != null) { final int endOffset = collapsedAt.getEndOffset(); if (endOffset <= myEndOffset) { break; } myEndOffset = endOffset; } else { break; } } boolean wholeLinesSelected = !hasSelection || myStartOffset == myDocument.getLineStartOffset(myDocument.getLineNumber(myStartOffset)) && myEndOffset == myDocument.getLineEndOffset(myDocument.getLineNumber(myEndOffset - 1)) + 1; boolean startingNewLineComment = !hasSelection && isLineEmpty(myDocument.getLineNumber(myStartOffset)) && !Comparing.equal( IdeActions.ACTION_COMMENT_LINE, ActionManagerEx.getInstanceEx().getPrevPreformedActionId()); doComment(); if (startingNewLineComment) { final Commenter commenter = myCommenters[0]; if (commenter != null) { String prefix; if (commenter instanceof SelfManagingCommenter) { prefix = ((SelfManagingCommenter) commenter) .getCommentPrefix( myStartLine, myDocument, myCommenterStateMap.get((SelfManagingCommenter) commenter)); if (prefix == null) prefix = ""; // TODO } else { prefix = commenter.getLineCommentPrefix(); if (prefix == null) prefix = commenter.getBlockCommentPrefix(); } int lineStart = myDocument.getLineStartOffset(myStartLine); lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t"); lineStart += prefix.length(); lineStart = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), lineStart, " \t"); if (lineStart > myDocument.getTextLength()) lineStart = myDocument.getTextLength(); myEditor.getCaretModel().moveToOffset(lineStart); myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } } else { if (!hasSelection) { // Don't tweak caret position if we're already located on the last document line. LogicalPosition position = myEditor.getCaretModel().getLogicalPosition(); if (position.line < myDocument.getLineCount() - 1) { int verticalShift = 1 + myEditor.getSoftWrapModel().getSoftWrapsForLine(position.line).size() - position.softWrapLinesOnCurrentLogicalLine; myEditor.getCaretModel().moveCaretRelatively(0, verticalShift, false, false, true); } } else { if (wholeLinesSelected) { selectionModel.setSelection(myStartOffset, selectionModel.getSelectionEnd()); } } } }