mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-62425 Soft wraps cause editor weirdness (displayed text does not correspond to that displayed under caret)
1. Corrected trailing soft wraps offsets update on document modification; 2. Corrected document dimensions mapping algorithm; 3. Corresponding tests are added;
This commit is contained in:
+9
-1
@@ -48,6 +48,7 @@ abstract class AbstractMappingStrategy<T> implements MappingStrategy<T> {
|
||||
private EditorPosition myInitialPosition;
|
||||
private CacheEntry myTargetEntry;
|
||||
private T myEagerMatch;
|
||||
private int myLastEntryOffset;
|
||||
|
||||
AbstractMappingStrategy(@NotNull Editor editor,
|
||||
@NotNull SoftWrapsStorage storage,
|
||||
@@ -93,6 +94,9 @@ abstract class AbstractMappingStrategy<T> implements MappingStrategy<T> {
|
||||
myEagerMatch = null;
|
||||
myTargetEntry = null;
|
||||
myInitialPosition = null;
|
||||
if (!myCache.isEmpty()) {
|
||||
myLastEntryOffset = myCache.get(myCache.size() - 1).endOffset;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setInitialPosition(@NotNull EditorPosition position) {
|
||||
@@ -118,13 +122,17 @@ abstract class AbstractMappingStrategy<T> implements MappingStrategy<T> {
|
||||
|
||||
@Override
|
||||
public T advance(EditorPosition position, int offset) {
|
||||
Document document = myEditor.getDocument();
|
||||
if (offset >= myLastEntryOffset || offset >= document.getTextLength()) {
|
||||
return build(position);
|
||||
}
|
||||
|
||||
T result = buildIfExceeds(position, offset);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update context state and continue processing.
|
||||
Document document = myEditor.getDocument();
|
||||
int linesDiff = document.getLineNumber(offset) - position.logicalLine;
|
||||
position.logicalLine += linesDiff;
|
||||
position.visualLine += linesDiff;
|
||||
|
||||
+33
-15
@@ -362,6 +362,11 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
|
||||
myAffectedByUpdateCacheEntries.clear();
|
||||
myNotAffectedByUpdateTailCacheEntries.clear();
|
||||
myBeforeChangeState.updateByDocumentOffsets(event.getOldStartOffset(), event.getOldEndOffset(), event.getOldLogicalLinesDiff());
|
||||
myStorage.removeInRange(event.getOldStartOffset(), event.getOldEndOffset());
|
||||
|
||||
// Advance offsets of all soft wraps that lay beyond the changed document region.
|
||||
advanceSoftWrapOffsets(event.getExactOffsetsDiff(), event.getOldEndOffset());
|
||||
|
||||
if (!myBeforeChangeState.cacheShouldBeUpdated) {
|
||||
if (DEBUG_SOFT_WRAP_PROCESSING) {
|
||||
log(String.format("xxxxxxxxxxxx CachingSoftWrapDataMapper.onRecalculationStart(): performing eager return"));
|
||||
@@ -437,7 +442,7 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
|
||||
myAfterChangeState.foldedLines = 0;
|
||||
myCache.addAll(myNotAffectedByUpdateTailCacheEntries);
|
||||
}
|
||||
applyStateChange(exactOffsetsDiff, event.getNewEndOffset());
|
||||
applyStateChange(exactOffsetsDiff);
|
||||
|
||||
myAffectedByUpdateCacheEntries.clear();
|
||||
myNotAffectedByUpdateTailCacheEntries.clear();
|
||||
@@ -496,6 +501,32 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
|
||||
log("xxxxxxxxxx dump complete. Cache size: " + myCache.size() + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies given offsets diff to all soft wraps that lay after the given offset
|
||||
*
|
||||
* @param offsetsDiff offset diff to apply to the target soft wraps
|
||||
* @param offset offset to use for filtering soft wraps to advance. All soft wraps which offsets are strictly greater
|
||||
* than the given one should be advanced
|
||||
*/
|
||||
private void advanceSoftWrapOffsets(int offsetsDiff, int offset) {
|
||||
if (offsetsDiff == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int softWrapIndex = myStorage.getSoftWrapIndex(offset);
|
||||
if (softWrapIndex >= 0) {
|
||||
softWrapIndex++; // We want to process only soft wraps which offsets strictly more than the given one.
|
||||
}
|
||||
else {
|
||||
softWrapIndex = -softWrapIndex - 1;
|
||||
}
|
||||
|
||||
List<SoftWrapImpl> softWraps = myStorage.getSoftWraps();
|
||||
for (int i = softWrapIndex; i < softWraps.size(); i++) {
|
||||
softWraps.get(i).advance(offsetsDiff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is assumed to be called for updating {@link #myCache document dimensions cache} entries that lay after document position identified
|
||||
* by {@link #myAfterChangeState} in order to apply to them diff between {@link #myBeforeChangeState} and {@link #myAfterChangeState}.
|
||||
@@ -511,20 +542,7 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
|
||||
* </ol>
|
||||
</pre>
|
||||
*/
|
||||
private void applyStateChange(int offsetsDiff, int endOffset) {
|
||||
// Update offsets for soft wraps that lay beyond the changed region.
|
||||
if (offsetsDiff != 0 && endOffset < myEditor.getDocument().getTextLength()) {
|
||||
int softWrapIndex = myStorage.getSoftWrapIndex(endOffset);
|
||||
if (softWrapIndex < 0) {
|
||||
softWrapIndex = -softWrapIndex - 1;
|
||||
}
|
||||
|
||||
List<SoftWrapImpl> softWraps = myStorage.getSoftWraps();
|
||||
for (int i = softWrapIndex; i < softWraps.size(); i++) {
|
||||
softWraps.get(i).advance(offsetsDiff);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyStateChange(int offsetsDiff) {
|
||||
if (myNotAffectedByUpdateTailCacheEntries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
-1
@@ -137,7 +137,6 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
notifyListenersOnCacheUpdateStart(event);
|
||||
//CachingSoftWrapDataMapper.log("xxxxxxxxxxxxxxx Listeners notification on start is complete in " + (System.currentTimeMillis() - start) + " ms");
|
||||
|
||||
myStorage.removeInRange(event.getOldStartOffset(), event.getOldEndOffset());
|
||||
boolean normalCompletion = true;
|
||||
try {
|
||||
//start = System.currentTimeMillis();
|
||||
|
||||
+62
@@ -24,6 +24,7 @@ import gnu.trove.TIntProcedure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -522,6 +523,67 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
|
||||
assertEquals(expectedVisualLine, caretModel.getVisualPosition().line);
|
||||
}
|
||||
|
||||
public void testPastingInsideSelection() throws IOException {
|
||||
String text =
|
||||
"this is line number 0\n" +
|
||||
"this is line number 1\n" +
|
||||
"this is line number 2\n" +
|
||||
"this is line number 3\n" +
|
||||
"this is line number 4\n" +
|
||||
"this is line number 5\n" +
|
||||
"this is line number 6\n" +
|
||||
"this is the last line";
|
||||
|
||||
init(100, text);
|
||||
int lineToSelect = 4;
|
||||
myEditor.getCaretModel().moveToOffset(text.indexOf("number " + lineToSelect));
|
||||
Document document = myEditor.getDocument();
|
||||
|
||||
int startOffset = document.getLineStartOffset(lineToSelect);
|
||||
int endOffset = document.getLineEndOffset(lineToSelect);
|
||||
myEditor.getSelectionModel().setSelection(startOffset, endOffset);
|
||||
|
||||
VisualPosition positionBefore = myEditor.offsetToVisualPosition(document.getLineStartOffset(lineToSelect + 1));
|
||||
List<SoftWrap> softWrapsBefore = new ArrayList<SoftWrap>(getSoftWrapModel().getRegisteredSoftWraps());
|
||||
|
||||
copy();
|
||||
paste();
|
||||
|
||||
assertEquals(positionBefore, myEditor.offsetToVisualPosition(document.getLineStartOffset(lineToSelect + 1)));
|
||||
assertEquals(softWrapsBefore, getSoftWrapModel().getRegisteredSoftWraps());
|
||||
}
|
||||
|
||||
public void testRemoveHugeLogicalLineThatLaysBeforeSoftWrappedLines() throws IOException {
|
||||
String text =
|
||||
"short line\n" +
|
||||
"this is a long line that is expected to be soft wrapped into more than one or even two visual lines\n" +
|
||||
"1. just a line that is long enough to be soft wrapped\n" +
|
||||
"2. just a line that is long enough to be soft wrapped\n" +
|
||||
"3. just a line that is long enough to be soft wrapped\n" +
|
||||
"4. just a line that is long enough to be soft wrapped";
|
||||
|
||||
init(100, text);
|
||||
Document document = myEditor.getDocument();
|
||||
int start = document.getLineStartOffset(1);
|
||||
int end = document.getLineEndOffset(1) + 1;
|
||||
int visualLinesToRemove = getSoftWrapModel().getSoftWrapsForLine(1).size() + 1;
|
||||
|
||||
List<VisualPosition> positionsBefore = new ArrayList<VisualPosition>();
|
||||
for (int i = end; i < text.length(); i++) {
|
||||
positionsBefore.add(myEditor.offsetToVisualPosition(i));
|
||||
}
|
||||
Collections.reverse(positionsBefore);
|
||||
|
||||
myEditor.getSelectionModel().setSelection(start, end);
|
||||
delete();
|
||||
|
||||
// Check that all remembered positions are just shifted to expected number of visual lines.
|
||||
for (int i = start; i < document.getTextLength(); i++) {
|
||||
VisualPosition position = positionsBefore.remove(positionsBefore.size() - 1);
|
||||
assertEquals(new VisualPosition(position.line - visualLinesToRemove, position.column), myEditor.offsetToVisualPosition(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static TIntHashSet collectSoftWrapStartOffsets(int documentLine) {
|
||||
TIntHashSet result = new TIntHashSet();
|
||||
for (SoftWrap softWrap : myEditor.getSoftWrapModel().getSoftWrapsForLine(documentLine)) {
|
||||
|
||||
+8
@@ -482,6 +482,14 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest
|
||||
protected static void end() {
|
||||
doAction(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
|
||||
}
|
||||
|
||||
protected static void copy() {
|
||||
doAction(IdeActions.ACTION_EDITOR_COPY);
|
||||
}
|
||||
|
||||
protected static void paste() {
|
||||
doAction(IdeActions.ACTION_EDITOR_PASTE);
|
||||
}
|
||||
|
||||
private static void doAction(@NotNull String actionId) {
|
||||
EditorActionManager actionManager = EditorActionManager.getInstance();
|
||||
|
||||
Reference in New Issue
Block a user