diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/IncrementalCacheUpdateEvent.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/IncrementalCacheUpdateEvent.java index b59f48627323..1e638c951a54 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/IncrementalCacheUpdateEvent.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/IncrementalCacheUpdateEvent.java @@ -16,11 +16,11 @@ package com.intellij.openapi.editor.impl.softwrap.mapping; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.editor.VisualPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.editor.impl.SoftWrapModelImpl; import org.jetbrains.annotations.NotNull; /** @@ -55,15 +55,15 @@ public class IncrementalCacheUpdateEvent { * @param event object that describes document change that caused cache update */ IncrementalCacheUpdateEvent(@NotNull DocumentEvent event, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) { - this(event.getDocument(), event.getOffset(), event.getOffset() + event.getOldLength(), event.getOffset() + event.getNewLength(), mapper, editor); + this(event.getOffset(), event.getOffset() + event.getOldLength(), event.getOffset() + event.getNewLength(), mapper, editor); } /** * Creates new IncrementalCacheUpdateEvent object for the event not changing document length * (like expansion of folded region). */ - IncrementalCacheUpdateEvent(@NotNull Document document, int startOffset, int endOffset, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) { - this(document, startOffset, endOffset, endOffset, mapper, editor); + IncrementalCacheUpdateEvent(int startOffset, int endOffset, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) { + this(startOffset, endOffset, endOffset, mapper, editor); myNewEndLogicalLine = myOldEndLogicalLine; } @@ -82,22 +82,59 @@ public class IncrementalCacheUpdateEvent { myOldEndLogicalLine = myNewEndLogicalLine = Math.max(0, document.getLineCount() - 1); } - private IncrementalCacheUpdateEvent(@NotNull Document document, int startOffset, int oldEndOffset, int newEndOffset, + private IncrementalCacheUpdateEvent(int startOffset, int oldEndOffset, int newEndOffset, @NotNull CachingSoftWrapDataMapper mapper, @NotNull EditorImpl editor) { - myStartOffset = editor.myUseNewRendering ? editor.visualLineStartOffset(editor.offsetToVisualLine(startOffset) - 1) : - mapper.getPreviousVisualLineStartOffset(startOffset); - myMandatoryEndOffset = newEndOffset; - myLengthDiff = newEndOffset - oldEndOffset; - myStartLogicalPosition = editor.myUseNewRendering ? editor.offsetToLogicalPosition(myStartOffset) : - mapper.offsetToLogicalPosition(myStartOffset); if (editor.myUseNewRendering) { - myStartVisualPosition = editor.logicalToVisualPosition(myStartLogicalPosition); + VisualLineInfo info = getVisualLineInfo(editor, startOffset, false); + if (info.startsWithSoftWrap) { + info = getVisualLineInfo(editor, info.startOffset, true); + } + myStartOffset = info.startOffset; + myStartLogicalPosition = editor.offsetToLogicalPosition(myStartOffset); + myStartVisualPosition = new VisualPosition(info.visualLine, 0); } else { + myStartOffset = mapper.getPreviousVisualLineStartOffset(startOffset); + myStartLogicalPosition = mapper.offsetToLogicalPosition(myStartOffset); LOG.assertTrue(myStartLogicalPosition.visualPositionAware); myStartVisualPosition = myStartLogicalPosition.toVisualPosition(); } - myOldEndLogicalLine = document.getLineNumber(oldEndOffset); + myMandatoryEndOffset = newEndOffset; + myLengthDiff = newEndOffset - oldEndOffset; + myOldEndLogicalLine = editor.getDocument().getLineNumber(oldEndOffset); + } + + + private static VisualLineInfo getVisualLineInfo(@NotNull EditorImpl editor, int offset, boolean beforeSoftWrap) { + Document document = editor.getDocument(); + int textLength = document.getTextLength(); + if (offset <= 0 || textLength == 0) return new VisualLineInfo(0, 0, false); + offset = Math.min(offset, textLength); + + int startOffset = EditorUtil.getNotFoldedLineStartOffset(editor, offset); + + SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel(); + int wrapIndex = softWrapModel.getSoftWrapIndex(offset); + int prevSoftWrapIndex = wrapIndex < 0 ? (- wrapIndex - 2) : wrapIndex - (beforeSoftWrap ? 1 : 0); + SoftWrap prevSoftWrap = prevSoftWrapIndex < 0 ? null : softWrapModel.getRegisteredSoftWraps().get(prevSoftWrapIndex); + + int visualLine = document.getLineNumber(offset) - editor.getFoldingModel().getFoldedLinesCountBefore(offset) + (prevSoftWrapIndex + 1); + int visualLineStartOffset = prevSoftWrap == null ? startOffset : Math.max(startOffset, prevSoftWrap.getStart()); + return new VisualLineInfo(visualLine, + visualLineStartOffset, + prevSoftWrap != null && prevSoftWrap.getStart() == visualLineStartOffset); + } + + private static class VisualLineInfo { + private final int visualLine; + private final int startOffset; + private final boolean startsWithSoftWrap; + + private VisualLineInfo(int visualLine, int startOffset, boolean wrap) { + this.visualLine = visualLine; + this.startOffset = startOffset; + startsWithSoftWrap = wrap; + } } public void updateAfterDocumentChange(@NotNull Document document) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java index 3cfb4c336b19..cc48ab689852 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManager.java @@ -193,8 +193,7 @@ public class SoftWrapApplianceManager implements Dumpable { for (Segment range : ranges) { int lastOffset = lastRecalculatedOffset[0]; if (range.getEndOffset() > lastOffset) { - recalculateSoftWraps(new IncrementalCacheUpdateEvent(myEditor.getDocument(), - Math.max(range.getStartOffset(), lastOffset), range.getEndOffset(), + recalculateSoftWraps(new IncrementalCacheUpdateEvent(Math.max(range.getStartOffset(), lastOffset), range.getEndOffset(), myDataMapper, myEditor)); } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java index ebc3a285bc2a..9b1fc12e031f 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java @@ -199,4 +199,11 @@ public class EditorImplTest extends AbstractEditorTest { myEditor.getSettings().setAdditionalLinesCount(0); assertEquals(new Dimension(50, 30), myEditor.getContentComponent().getPreferredSize()); } + + public void testCollapsingRegionContainingSoftWrap() throws Exception { + initText("abcdef abcdef"); + configureSoftWraps(10); + addCollapsedFoldRegion(0, 13, "..."); + assertEquals(new VisualPosition(0, 3), myEditor.offsetToVisualPosition(13)); + } }