public void testNonSmartHome() throws IOException { String text = " this is a string that starts with white space and is long enough to be soft-wrapped\n" + " this is a 'prefix' text before collapsed multi-line folding that is long enough to be soft-wrapped first fold line\n" + "second fold line"; init(30, text); addCollapsedFoldRegion(text.indexOf("first fold line"), text.length(), "..."); List<? extends SoftWrap> softWraps = getSoftWrapModel().getRegisteredSoftWraps(); assertTrue(!softWraps.isEmpty()); CaretModel caretModel = myEditor.getCaretModel(); SoftWrap softWrap = softWraps.get(0); // Test non-smart home myEditor.getSettings().setSmartHome(false); caretModel.moveToOffset(softWrap.getStart() + 1); int visLine = caretModel.getVisualPosition().line; home(); assertEquals(new VisualPosition(visLine, 0), caretModel.getVisualPosition()); caretModel.moveToOffset(text.length()); home(); visLine = caretModel.getVisualPosition().line; assertEquals(new VisualPosition(visLine, 0), caretModel.getVisualPosition()); }
public void testSoftWrapToHardWrapConversion() throws IOException { String text = "this is line 1\n" + "this is line 2\n" + "this is line 3\n" + "this is line 4\n" + "this is line 5"; init(50, text, 7); VisualPosition changePosition = new VisualPosition(1, 0); myEditor.getCaretModel().moveToVisualPosition(changePosition); int logicalLinesBefore = myEditor.offsetToLogicalPosition(text.length()).line; int offsetBefore = myEditor.getCaretModel().getOffset(); LogicalPosition logicalPositionBefore = myEditor.visualToLogicalPosition(changePosition); assertEquals(1, logicalPositionBefore.softWrapLinesOnCurrentLogicalLine); assertTrue(logicalPositionBefore.column > 0); SoftWrap softWrap = getSoftWrapModel().getSoftWrap(offsetBefore); assertNotNull(softWrap); type('a'); LogicalPosition logicalPositionAfter = myEditor.visualToLogicalPosition(changePosition); assertEquals(new LogicalPosition(1, 0, 0, 0, 0, 0, 0), logicalPositionAfter); assertEquals( offsetBefore + softWrap.getText().length() + 1, myEditor.getCaretModel().getOffset()); assertEquals(logicalLinesBefore + 1, myEditor.offsetToLogicalPosition(text.length()).line); }
private static TIntHashSet collectSoftWrapStartOffsets(int documentLine) { TIntHashSet result = new TIntHashSet(); for (SoftWrap softWrap : myEditor.getSoftWrapModel().getSoftWrapsForLine(documentLine)) { result.add(softWrap.getStart()); } return result; }
private VerticalInfo createVerticalInfo(LogicalPosition position) { Document document = myEditor.getDocument(); int logicalLine = position.line; if (logicalLine >= document.getLineCount()) { logicalLine = Math.max(0, document.getLineCount() - 1); } int startOffset = document.getLineStartOffset(logicalLine); int endOffset = document.getLineEndOffset(logicalLine); // There is a possible case that active logical line is represented on multiple lines due to // soft wraps processing. // We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for // the logical line start // and height in accordance with the number of occupied visual lines. VisualPosition visualPosition = myEditor.offsetToVisualPosition(document.getLineStartOffset(logicalLine)); int y = myEditor.visualPositionToXY(visualPosition).y; int lineHeight = myEditor.getLineHeight(); int height = lineHeight; List<? extends SoftWrap> softWraps = myEditor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset); for (SoftWrap softWrap : softWraps) { height += StringUtil.countNewLines(softWrap.getText()) * lineHeight; } return new VerticalInfo(y, height); }
private boolean isInsideSoftWrap(@NotNull VisualPosition visual, boolean countBeforeSoftWrap) { if (!isSoftWrappingEnabled()) { return false; } SoftWrapModel model = myEditor.getSoftWrapModel(); if (!model.isSoftWrappingEnabled()) { return false; } LogicalPosition logical = myEditor.visualToLogicalPosition(visual); int offset = myEditor.logicalPositionToOffset(logical); if (offset <= 0) { // Never expect to be here, just a defensive programming. return false; } SoftWrap softWrap = model.getSoftWrap(offset); if (softWrap == null) { return false; } // We consider visual positions that point after the last symbol before soft wrap and the first // symbol after soft wrap to not // belong to soft wrap-introduced virtual space. VisualPosition visualAfterSoftWrap = myEditor.offsetToVisualPosition(offset); if (visualAfterSoftWrap.line == visual.line && visualAfterSoftWrap.column <= visual.column) { return false; } if (myEditor.myUseNewRendering) { VisualPosition beforeSoftWrap = myEditor.offsetToVisualPosition(offset, true, true); return visual.line > beforeSoftWrap.line || visual.column > beforeSoftWrap.column || visual.column == beforeSoftWrap.column && countBeforeSoftWrap; } else { VisualPosition visualBeforeSoftWrap = myEditor.offsetToVisualPosition(offset - 1); int x = 0; LogicalPosition logLineStart = myEditor.visualToLogicalPosition(new VisualPosition(visualBeforeSoftWrap.line, 0)); if (logLineStart.softWrapLinesOnCurrentLogicalLine > 0) { int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart); softWrap = model.getSoftWrap(offsetLineStart); if (softWrap != null) { x = softWrap.getIndentInPixels(); } } int width = EditorUtil.textWidthInColumns( myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x); int softWrapStartColumn = visualBeforeSoftWrap.column + width; if (visual.line > visualBeforeSoftWrap.line) { return true; } return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn; } }
@Override public VisualPosition processSoftWrap(@NotNull EditorPosition position, SoftWrap softWrap) { position.visualColumn = softWrap.getIndentInColumns(); position.softWrapColumnDiff += softWrap.getIndentInColumns(); if (position.logicalLine < myTargetLogical.line || position.logicalColumn != myTargetLogical.column) { return null; } return position.buildVisualPosition(); }
/** @return total number of soft wrap-introduced new visual lines */ public int getSoftWrapsIntroducedLinesNumber() { if (!isSoftWrappingEnabled()) { return 0; } int result = 0; FoldingModel foldingModel = myEditor.getFoldingModel(); for (SoftWrap softWrap : myStorage.getSoftWraps()) { if (!foldingModel.isOffsetCollapsed(softWrap.getStart())) { result++; // Assuming that soft wrap has single line feed all the time } } return result; }
/** * Calculates difference in columns between current editor caret position and end of the logical * line fragment displayed on a current visual line. * * @param editor target editor * @return difference in columns between current editor caret position and end of the logical line * fragment displayed on a current visual line */ public static int calcAfterLineEnd(Editor editor) { Document document = editor.getDocument(); CaretModel caretModel = editor.getCaretModel(); LogicalPosition logicalPosition = caretModel.getLogicalPosition(); int lineNumber = logicalPosition.line; int columnNumber = logicalPosition.column; if (lineNumber >= document.getLineCount()) { return columnNumber; } int caretOffset = caretModel.getOffset(); int anchorLineEndOffset = document.getLineEndOffset(lineNumber); List<? extends SoftWrap> softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logicalPosition.line); for (SoftWrap softWrap : softWraps) { if (!editor.getSoftWrapModel().isVisible(softWrap)) { continue; } int softWrapOffset = softWrap.getStart(); if (softWrapOffset == caretOffset) { // There are two possible situations: // *) caret is located on a visual line before soft wrap-introduced line feed; // *) caret is located on a visual line after soft wrap-introduced line feed; VisualPosition position = editor.offsetToVisualPosition(caretOffset - 1); VisualPosition visualCaret = caretModel.getVisualPosition(); if (position.line == visualCaret.line) { return visualCaret.column - position.column - 1; } } if (softWrapOffset > caretOffset) { anchorLineEndOffset = softWrapOffset; break; } // Same offset corresponds to all soft wrap-introduced symbols, however, current method should // behave differently in // situations when the caret is located just before the soft wrap and at the next visual line. if (softWrapOffset == caretOffset) { boolean visuallyBeforeSoftWrap = caretModel.getVisualPosition().line < editor.offsetToVisualPosition(caretOffset).line; if (visuallyBeforeSoftWrap) { anchorLineEndOffset = softWrapOffset; break; } } } int lineEndColumnNumber = editor.offsetToLogicalPosition(anchorLineEndOffset).column; return columnNumber - lineEndColumnNumber; }
public void testHomeProcessing() throws IOException { String text = "class Test {\n" + " public String s = \"this is a long string literal that is expected to be soft-wrapped into multiple visual lines\";\n" + "}"; init(30, text); myEditor.getCaretModel().moveToOffset(text.indexOf("}") - 1); List<? extends SoftWrap> softWraps = new ArrayList<SoftWrap>(getSoftWrapModel().getRegisteredSoftWraps()); assertTrue(!softWraps.isEmpty()); CaretModel caretModel = myEditor.getCaretModel(); int expectedVisualLine = caretModel.getVisualPosition().line; while (!softWraps.isEmpty()) { SoftWrap softWrap = softWraps.get(softWraps.size() - 1); int caretOffsetBefore = caretModel.getOffset(); home(); // Expecting the caret to be moved at the nearest soft wrap start offset. int caretOffset = caretModel.getOffset(); assertTrue(caretOffset < caretOffsetBefore); assertEquals(softWrap.getStart(), caretOffset); assertEquals( new VisualPosition(expectedVisualLine, softWrap.getIndentInColumns()), caretModel.getVisualPosition()); // Expected that caret is moved to visual line start when it's located on soft wrap start // offset at the moment. home(); assertEquals(softWrap.getStart(), caretModel.getOffset()); assertEquals(new VisualPosition(expectedVisualLine, 0), caretModel.getVisualPosition()); softWraps.remove(softWraps.size() - 1); expectedVisualLine--; } // Expecting caret to be located on the first non-white space symbol of non-soft wrapped line. home(); assertEquals(text.indexOf("public"), caretModel.getOffset()); assertEquals( new VisualPosition(expectedVisualLine, text.indexOf("public") - text.indexOf("{\n") - 2), caretModel.getVisualPosition()); }
@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); }
@Override public boolean isVisible(SoftWrap softWrap) { FoldingModel foldingModel = myEditor.getFoldingModel(); int start = softWrap.getStart(); if (foldingModel.isOffsetCollapsed(start)) { return false; } // There is a possible case that soft wrap and collapsed folding region share the same offset, // i.e. soft wrap is represented // before the folding. We need to return 'true' in such situation. Hence, we check if offset // just before the soft wrap // is collapsed as well. return start <= 0 || !foldingModel.isOffsetCollapsed(start - 1); }
public void testEndProcessing() throws IOException { String text = "class Test {\n" + " public String s = \"this is a long string literal that is expected to be soft-wrapped into multiple visual lines\"; \n" + "}"; init(30, text); myEditor.getCaretModel().moveToOffset(text.indexOf("\n") + 1); List<? extends SoftWrap> softWraps = new ArrayList<SoftWrap>(getSoftWrapModel().getRegisteredSoftWraps()); assertTrue(!softWraps.isEmpty()); CaretModel caretModel = myEditor.getCaretModel(); int expectedVisualLine = caretModel.getVisualPosition().line; while (!softWraps.isEmpty()) { SoftWrap softWrap = softWraps.get(0); int caretOffsetBefore = caretModel.getOffset(); end(); // Expecting the caret to be moved at the last non-white space symbol on the current visual // line. int caretOffset = caretModel.getOffset(); assertTrue(caretOffset > caretOffsetBefore); assertFalse(caretOffset > softWrap.getStart()); if (caretOffset < softWrap.getStart()) { // There is a possible case that there are white space symbols between caret position // applied on 'end' processing and // soft wrap. Let's check that and emulate one more 'end' typing in order to move caret // right before soft wrap. for (int i = caretOffset; i < softWrap.getStart(); i++) { char c = text.charAt(i); assertTrue(c == ' ' || c == '\t'); } caretOffsetBefore = caretOffset; end(); caretOffset = caretModel.getOffset(); assertTrue(caretOffset > caretOffsetBefore); } assertEquals(softWrap.getStart(), caretOffset); assertEquals( new VisualPosition( expectedVisualLine, myEditor.offsetToVisualPosition(softWrap.getStart() - 1).column + 1), caretModel.getVisualPosition()); softWraps.remove(0); expectedVisualLine++; } // Check that caret is placed on a last non-white space symbol on current logical line. end(); int lastNonWhiteSpaceSymbolOffset = text.indexOf("\";") + 2; assertEquals(lastNonWhiteSpaceSymbolOffset, caretModel.getOffset()); assertEquals( myEditor.offsetToVisualPosition(lastNonWhiteSpaceSymbolOffset), caretModel.getVisualPosition()); assertEquals(expectedVisualLine, caretModel.getVisualPosition().line); // Check that caret is place to the very end of the logical line. end(); int lastSymbolOffset = myEditor.getDocument().getLineEndOffset(caretModel.getLogicalPosition().line); assertEquals(lastSymbolOffset, caretModel.getOffset()); assertEquals(myEditor.offsetToVisualPosition(lastSymbolOffset), caretModel.getVisualPosition()); assertEquals(expectedVisualLine, caretModel.getVisualPosition().line); }