diff --git a/platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java b/platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java index 1be9135b3939..1371104f5af3 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java @@ -244,6 +244,19 @@ public class EditorModificationUtil { int anchorLineEndOffset = document.getLineEndOffset(lineNumber); List softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logicalPosition.line); for (TextChange softWrap : softWraps) { + if (!editor.getSoftWrapModel().isVisible(softWrap)) { + continue; + } + if (softWrap.getStart() == 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 (softWrap.getStart() > caretOffset) { anchorLineEndOffset = softWrap.getStart(); break; diff --git a/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java b/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java index e1755408067c..dabb8b37735b 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java @@ -106,6 +106,16 @@ public interface SoftWrapModel { @NotNull List getSoftWrapsForLine(int documentLine); + /** + * Allows to answer if given soft wrap is shown. + *

+ * The soft wrap may be not shown if it's located, for example, inside collapsed folding region. + * + * @param softWrap soft wrap to check + * @return true if given soft wrap is visible; false otherwise + */ + boolean isVisible(TextChange softWrap); + /** * Notifies current model that target document is about to be changed at current caret location. *

diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index 8980a1decc0f..10012c0ec016 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -215,7 +215,7 @@ public class EditorActionUtil { if (currentVisCaret.line > caretLogLineStartVis.line) { // Caret is located not at the first visual line of soft-wrapped logical line. - moveCaretToStartOfSoftWrappedLine(editor, currentVisCaret); + moveCaretToStartOfSoftWrappedLine(editor, currentVisCaret, currentVisCaret.line - caretLogLineStartVis.line); setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); return; @@ -249,7 +249,7 @@ public class EditorActionUtil { LogicalPosition logLineEndLog = editor.offsetToLogicalPosition(document.getLineEndOffset(logLineToUse)); VisualPosition logLineEndVis = editor.logicalToVisualPosition(logLineEndLog); if (logLineEndLog.softWrapLinesOnCurrentLogicalLine > 0) { - moveCaretToStartOfSoftWrappedLine(editor, logLineEndVis); + moveCaretToStartOfSoftWrappedLine(editor, logLineEndVis, logLineEndLog.softWrapLinesOnCurrentLogicalLine); } else { int line = logLineEndVis.line; @@ -265,7 +265,7 @@ public class EditorActionUtil { editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - private static void moveCaretToStartOfSoftWrappedLine(Editor editor, VisualPosition currentVisual) { + private static void moveCaretToStartOfSoftWrappedLine(Editor editor, VisualPosition currentVisual, int softWrappedLines) { CaretModel caretModel = editor.getCaretModel(); int line = currentVisual.line; @@ -275,8 +275,17 @@ public class EditorActionUtil { if (currentVisual.column <= 1) { line--; + softWrappedLines--; + // There is a possible case that caret is located at the start of the second visual line of soft-wrapped line. + // Hence, it should be moved to the start of the previous visual line which anchor column is not '1' + // (after soft wrap drawing) but '0'. int nonSpaceColumn = findFirstNonSpaceColumnOnTheLine(editor, line); - column = nonSpaceColumn >= 1 ? nonSpaceColumn : 1; + if (softWrappedLines <= 0) { + column = nonSpaceColumn >= 0 ? nonSpaceColumn : 0; + } + else { + column = nonSpaceColumn >= 1 ? nonSpaceColumn : 1; + } } else { int nonSpaceColumn = findFirstNonSpaceColumnOnTheLine(editor, currentVisual.line); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index 7e7378af51f4..e7097de43bdb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -317,7 +317,7 @@ public class EditorUtil { if (column == columnNumber) { return offset; } - if (column > columnNumber && text.charAt(offset) == '\t') { + if (column > columnNumber && offset > 0 && text.charAt(offset - 1) == '\t') { return offset - 1; } currentColumn.set(column); @@ -419,15 +419,18 @@ public class EditorUtil { public static int textWidthInColumns(@NotNull Editor editor, CharSequence text, int start, int end, int x) { int result = 0; int prevX; + int spaceSize = getSpaceWidth(Font.PLAIN, editor); for (int i = start; i < end; i++) { char c = text.charAt(i); prevX = x; switch (c) { - case '\t': x = nextTabStop(x, editor); break; + case '\t': + x = nextTabStop(x, editor); + result += columnsNumber(x - prevX, spaceSize); + break; case '\n': x = result = 0; break; - default: x += charWidth(c, Font.PLAIN, editor); + default: x += charWidth(c, Font.PLAIN, editor); result++; } - result += columnsNumber(c, x, prevX, getSpaceWidth(Font.PLAIN, editor)); } return result; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java index a866ba0daa4f..790256ccfcd5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java @@ -38,11 +38,6 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen return EditorUtil.textWidthInColumns(myEditor, text, start, end, x); } - @Override - public int toVisualColumnSymbolsNumber(int width) { - return EditorUtil.columnsNumber(width, EditorUtil.getSpaceWidth(Font.PLAIN, myEditor)); - } - @Override public int textWidth(@NotNull CharSequence text, int start, int end, int x) { return EditorUtil.textWidth(myEditor, text, start, end, Font.PLAIN, x); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index c6e6602858aa..cf7a03f268df 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -1033,7 +1033,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int linesToSkip = logical.softWrapLinesOnCurrentLogicalLine; List softWraps = getSoftWrapModel().getSoftWrapsForLine(logLine); for (TextChange softWrap : softWraps) { - if (myFoldingModel.isOffsetCollapsed(softWrap.getStart())) { + if (myFoldingModel.isOffsetCollapsed(softWrap.getStart()) && myFoldingModel.isOffsetCollapsed(softWrap.getStart() - 1)) { continue; } int lineFeeds = StringUtil.countNewLines(softWrap.getText()); @@ -1061,7 +1061,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } - if (logLine <= 0) { + if (logLine < 0) { lineStartOffset = 0; } else if (lineStartOffset < 0) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java index 6e391667b15e..fcf97a1f59be 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java @@ -41,14 +41,6 @@ public interface EditorTextRepresentationHelper { */ int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x); - /** - * Allows to answer how many visual columns is necessary for representing text of the given width. - * - * @param width target width - * @return number of visual columns necessary for representation of the text with the given width - */ - int toVisualColumnSymbolsNumber(int width); - /** * Allows to retrieve width (in pixels) necessary to represent given region ([start; end)) starting * at the given 'x' offset from visual line start using given font type. diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index 3dd07cde9a4b..f508694a29f7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -17,6 +17,7 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.SoftWrapChangeListener; import com.intellij.openapi.editor.ex.SoftWrapModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; @@ -168,6 +169,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { } } + @Override public List getRegisteredSoftWraps() { if (!isSoftWrappingEnabled()) { return Collections.emptyList(); @@ -175,6 +177,20 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { return myStorage.getSoftWraps(); } + @Override + public boolean isVisible(TextChange 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); + } + @Override public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { if (!isSoftWrappingEnabled()) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java index 9250d2cc4be1..87889a12274a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java @@ -264,9 +264,8 @@ public class SoftWrapDataMapper { int i = CharArrayUtil.shiftBackwardUntil(text, region.getEndOffset() - 1, "\n"); // Process multi-line folding. if (i >= region.getStartOffset()) { - int width = myTextRepresentationHelper.textWidth(text, i + 1, region.getEndOffset(), 0); - afterFolding.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); - afterFolding.x = width; + afterFolding.x = myTextRepresentationHelper.textWidth(text, i + 1, region.getEndOffset(), 0); + afterFolding.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i + 1, region.getEndOffset(), 0); afterFolding.softWrapLinesBefore += afterFolding.softWrapLinesCurrent; afterFolding.softWrapLinesCurrent = 0; afterFolding.softWrapColumnDiff = 0; @@ -276,7 +275,9 @@ public class SoftWrapDataMapper { // Process single-line folding else { int width = myTextRepresentationHelper.textWidth(text, region.getStartOffset(), region.getEndOffset(), context.x); - int logicalColumnInc = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); + int logicalColumnInc = myTextRepresentationHelper.toVisualColumnSymbolsNumber( + text, region.getStartOffset(), region.getEndOffset(), context.x + ); afterFolding.logicalColumn += logicalColumnInc; afterFolding.x += width; afterFolding.foldingColumnDiff += visualColumnInc - logicalColumnInc; @@ -342,19 +343,20 @@ public class SoftWrapDataMapper { // Update state to the offset that corresponds to the same logical line that was used last time. if (currentLogicalLine == lastUsedLogicalLine) { int width = myTextRepresentationHelper.textWidth(text, result.offset, newOffset, result.x); - int columnDiff = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); + int columnDiff = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, result.offset, newOffset, result.x); result.x += width; result.logicalColumn += columnDiff; result.visualColumn += columnDiff; } - // Update state to the start of the folding that doesn't belong to the same logical line that was used last time. + // Update state to offset that doesn't correspond to the same logical line that was used last time. else { int lineDiff = currentLogicalLine - lastUsedLogicalLine; result.logicalLine += lineDiff; result.visualLine += lineDiff; int startLineOffset = document.getLineStartOffset(currentLogicalLine); - result.x = myTextRepresentationHelper.textWidth(text, startLineOffset, newOffset, result.x); - result.visualColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(result.x); + int newX = myTextRepresentationHelper.textWidth(text, startLineOffset, newOffset, result.x); + result.visualColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, startLineOffset, newOffset, 0); + result.x = newX; result.logicalColumn = result.visualColumn; result.onNewLine(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java index 82d3d3c530d8..bd60686b00ef 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java @@ -53,6 +53,11 @@ public class TextComponentSoftWrapModel implements SoftWrapModel { return Collections.emptyList(); } + @Override + public boolean isVisible(TextChange softWrap) { + return false; + } + @Override public void beforeDocumentChangeAtCaret() { } diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java index 39b69460cdaa..a1dd059acccb 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java @@ -858,15 +858,6 @@ public class SoftWrapDataMapperTest { return result; } - @Override - public int toVisualColumnSymbolsNumber(int width) { - int result = width / SPACE_SIZE; - if (width % SPACE_SIZE > 0) { - result++; - } - return result; - } - @Override public int textWidth(@NotNull CharSequence text, int start, int end, int x) { int result = 0;