@Override public void beforeDocumentChangeAtCaret() { CaretModel caretModel = myEditor.getCaretModel(); VisualPosition visualCaretPosition = caretModel.getVisualPosition(); if (!isInsideSoftWrap(visualCaretPosition)) { return; } SoftWrap softWrap = myStorage.getSoftWrap(caretModel.getOffset()); if (softWrap == null) { return; } myEditor .getDocument() .replaceString(softWrap.getStart(), softWrap.getEnd(), softWrap.getText()); caretModel.moveToVisualPosition(visualCaretPosition); }
/** * Applies given caret/selection state to the editor. Editor text must have been set up * previously. */ public static void setCaretsAndSelection(Editor editor, CaretAndSelectionState caretsState) { CaretModel caretModel = editor.getCaretModel(); if (caretModel.supportsMultipleCarets()) { List<CaretState> states = new ArrayList<CaretState>(caretsState.carets.size()); for (CaretInfo caret : caretsState.carets) { states.add( new CaretState( caret.position == null ? null : editor.offsetToLogicalPosition(caret.getCaretOffset(editor.getDocument())), caret.selection == null ? null : editor.offsetToLogicalPosition(caret.selection.getStartOffset()), caret.selection == null ? null : editor.offsetToLogicalPosition(caret.selection.getEndOffset()))); } caretModel.setCaretsAndSelections(states); } else { assertEquals("Multiple carets are not supported by the model", 1, caretsState.carets.size()); CaretInfo caret = caretsState.carets.get(0); if (caret.position != null) { caretModel.moveToOffset(caret.getCaretOffset(editor.getDocument())); } if (caret.selection != null) { editor .getSelectionModel() .setSelection(caret.selection.getStartOffset(), caret.selection.getEndOffset()); } else { editor.getSelectionModel().removeSelection(); } } if (caretsState.blockSelection != null) { editor .getSelectionModel() .setBlockSelection( editor.offsetToLogicalPosition(caretsState.blockSelection.getStartOffset()), editor.offsetToLogicalPosition(caretsState.blockSelection.getEndOffset())); } }
@Override @SuppressWarnings({"AssignmentToForLoopParameter"}) public void paint( @NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) { int startOffset = highlighter.getStartOffset(); final Document doc = highlighter.getDocument(); if (startOffset >= doc.getTextLength()) return; final int endOffset = highlighter.getEndOffset(); final int endLine = doc.getLineNumber(endOffset); int off; int startLine = doc.getLineNumber(startOffset); IndentGuideDescriptor descriptor = editor.getIndentsModel().getDescriptor(startLine, endLine); final CharSequence chars = doc.getCharsSequence(); do { int start = doc.getLineStartOffset(startLine); int end = doc.getLineEndOffset(startLine); off = CharArrayUtil.shiftForward(chars, start, end, " \t"); startLine--; } while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n'); final VisualPosition startPosition = editor.offsetToVisualPosition(off); int indentColumn = startPosition.column; // It's considered that indent guide can cross not only white space but comments, javadocs // etc. Hence, there is a possible // case that the first indent guide line is, say, single-line comment where comment // symbols ('//') are located at the first // visual column. We need to calculate correct indent guide column then. int lineShift = 1; if (indentColumn <= 0 && descriptor != null) { indentColumn = descriptor.indentLevel; lineShift = 0; } if (indentColumn <= 0) return; final FoldingModel foldingModel = editor.getFoldingModel(); if (foldingModel.isOffsetCollapsed(off)) return; final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset( doc.getLineStartOffset(doc.getLineNumber(endOffset))); if (tailRegion != null && tailRegion == headerRegion) return; final boolean selected; final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide(); if (guide != null) { final CaretModel caretModel = editor.getCaretModel(); final int caretOffset = caretModel.getOffset(); selected = caretOffset >= off && caretOffset < endOffset && caretModel.getLogicalPosition().column == indentColumn; } else { selected = false; } Point start = editor.visualPositionToXY( new VisualPosition(startPosition.line + lineShift, indentColumn)); final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset); Point end = editor.visualPositionToXY(new VisualPosition(endPosition.line, endPosition.column)); int maxY = end.y; if (endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line) { maxY += editor.getLineHeight(); } Rectangle clip = g.getClipBounds(); if (clip != null) { if (clip.y >= maxY || clip.y + clip.height <= start.y) { return; } maxY = Math.min(maxY, clip.y + clip.height); } final EditorColorsScheme scheme = editor.getColorsScheme(); g.setColor( selected ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR) : scheme.getColor(EditorColors.INDENT_GUIDE_COLOR)); // There is a possible case that indent line intersects soft wrap-introduced text. // Example: // this is a long line <soft-wrap> // that| is soft-wrapped // | // | <- vertical indent // // Also it's possible that no additional intersections are added because of soft wrap: // this is a long line <soft-wrap> // | that is soft-wrapped // | // | <- vertical indent // We want to use the following approach then: // 1. Show only active indent if it crosses soft wrap-introduced text; // 2. Show indent as is if it doesn't intersect with soft wrap-introduced text; if (selected) { g.drawLine(start.x + 2, start.y, start.x + 2, maxY); } else { int y = start.y; int newY = start.y; SoftWrapModel softWrapModel = editor.getSoftWrapModel(); int lineHeight = editor.getLineHeight(); for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) { List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForLine(i); int logicalLineHeight = softWraps.size() * lineHeight; if (i > startLine + lineShift) { logicalLineHeight += lineHeight; // We assume that initial 'y' value points just below the target // line. } if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) { if (y < newY || i > startLine + lineShift) { // There is a possible case that soft wrap is located on // indent start line. g.drawLine(start.x + 2, y, start.x + 2, newY + lineHeight); } newY += logicalLineHeight; y = newY; } else { newY += logicalLineHeight; } FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i)); if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) { i = doc.getLineNumber(foldRegion.getEndOffset()); } } if (y < maxY) { g.drawLine(start.x + 2, y, start.x + 2, maxY); } } }
public static void verifyCaretAndSelectionState( Editor editor, CaretAndSelectionState caretState, String message) { boolean hasChecks = false; for (int i = 0; i < caretState.carets.size(); i++) { EditorTestUtil.CaretInfo expected = caretState.carets.get(i); if (expected.position != null || expected.selection != null) { hasChecks = true; break; } } if (!hasChecks) { return; // nothing to check, so we skip caret/selection assertions } String messageSuffix = message == null ? "" : (message + ": "); CaretModel caretModel = editor.getCaretModel(); List<Caret> allCarets = new ArrayList<Caret>(caretModel.getAllCarets()); assertEquals( messageSuffix + " Unexpected number of carets", caretState.carets.size(), allCarets.size()); for (int i = 0; i < caretState.carets.size(); i++) { String caretDescription = caretState.carets.size() == 1 ? "" : "caret " + (i + 1) + "/" + caretState.carets.size() + " "; Caret currentCaret = allCarets.get(i); int actualCaretLine = editor.getDocument().getLineNumber(currentCaret.getOffset()); int actualCaretColumn = currentCaret.getOffset() - editor.getDocument().getLineStartOffset(actualCaretLine); LogicalPosition actualCaretPosition = new LogicalPosition(actualCaretLine, actualCaretColumn); int selectionStart = currentCaret.getSelectionStart(); int selectionEnd = currentCaret.getSelectionEnd(); LogicalPosition actualSelectionStart = editor.offsetToLogicalPosition(selectionStart); LogicalPosition actualSelectionEnd = editor.offsetToLogicalPosition(selectionEnd); CaretInfo expected = caretState.carets.get(i); if (expected.position != null) { assertEquals( messageSuffix + caretDescription + "unexpected caret position", expected.position, actualCaretPosition); } if (expected.selection != null) { LogicalPosition expectedSelectionStart = editor.offsetToLogicalPosition(expected.selection.getStartOffset()); LogicalPosition expectedSelectionEnd = editor.offsetToLogicalPosition(expected.selection.getEndOffset()); assertEquals( messageSuffix + caretDescription + "unexpected selection start", expectedSelectionStart, actualSelectionStart); assertEquals( messageSuffix + caretDescription + "unexpected selection end", expectedSelectionEnd, actualSelectionEnd); } else { assertFalse( messageSuffix + caretDescription + "should has no selection, but was: (" + actualSelectionStart + ", " + actualSelectionEnd + ")", currentCaret.hasSelection()); } } }