diff --git a/platform/platform-api/src/com/intellij/patterns/ObjectPattern.java b/platform/platform-api/src/com/intellij/patterns/ObjectPattern.java index 328a420cd87e..a76ee94c6b73 100644 --- a/platform/platform-api/src/com/intellij/patterns/ObjectPattern.java +++ b/platform/platform-api/src/com/intellij/patterns/ObjectPattern.java @@ -24,10 +24,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * @author peter @@ -92,7 +89,15 @@ public abstract class ObjectPattern> impl @NotNull public Self oneOf(final T... values) { - final List list = Arrays.asList(values); + final Collection list; + + if (values.length >= 11) { + list = new HashSet(Arrays.asList(values)); + } + else { + list = Arrays.asList(values); + } + return with(new ValuePatternCondition("oneOf") { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java index 2c3574eff630..6fec6fe5b883 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java @@ -372,7 +372,14 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw } if (myBeforeChangeState.endCacheEntryIndex < myCache.size() - 1) { - myNotAffectedByUpdateTailCacheEntries.addAll(myCache.subList(myBeforeChangeState.endCacheEntryIndex + 1, myCache.size())); + int cacheIndexToUse = myBeforeChangeState.endCacheEntryIndex; + // There is a possible case that end cache entry index points to the entry that lays withing the changed document range, + // hence, we need not to store it then. + if (cacheIndexToUse < myCache.size() && myCache.get(cacheIndexToUse).startOffset < event.getNewEndOffset()) { + cacheIndexToUse++; + } + + myNotAffectedByUpdateTailCacheEntries.addAll(myCache.subList(cacheIndexToUse, myCache.size())); if (DEBUG_SOFT_WRAP_PROCESSING) { log("xxxxxxxxxxxxx CachingSoftWrapDataMapper.onRecalculationStart(). Marked the following " + myNotAffectedByUpdateTailCacheEntries.size() + " entries for update: "); @@ -408,7 +415,7 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw myAfterChangeState.updateByDocumentOffsets(event.getNewStartOffset(), event.getNewEndOffset(), event.getNewLogicalLinesDiff()); myCache.addAll(myNotAffectedByUpdateTailCacheEntries); - applyStateChange(event.getExactOffsetsDiff()); + applyStateChange(event.getExactOffsetsDiff(), event.getNewEndOffset()); myNotAffectedByUpdateTailCacheEntries.clear(); if (DEBUG_SOFT_WRAP_PROCESSING) { @@ -480,7 +487,20 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw * */ - private void applyStateChange(int offsetsDiff) { + 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 softWraps = myStorage.getSoftWraps(); + for (int i = softWrapIndex; i < softWraps.size(); i++) { + softWraps.get(i).advance(offsetsDiff); + } + } + if (myNotAffectedByUpdateTailCacheEntries.isEmpty()) { return; } @@ -513,21 +533,6 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw cacheEntry.startFoldedLines += foldedLinesDiff; cacheEntry.endFoldedLines += foldedLinesDiff; } - - int offset = myNotAffectedByUpdateTailCacheEntries.get(0).startOffset + 1/* in order to exclude soft wrap from previous line if any*/; - if (offsetsDiff == 0 || offset >= myEditor.getDocument().getTextLength()) { - return; - } - - int softWrapIndex = myStorage.getSoftWrapIndex(offset); - if (softWrapIndex < 0) { - softWrapIndex = -softWrapIndex - 1; - } - - List softWraps = myStorage.getSoftWraps(); - for (int i = softWrapIndex; i < softWraps.size(); i++) { - softWraps.get(i).advance(offsetsDiff); - } } @Override @@ -579,8 +584,8 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw endCacheEntryIndex = -endCacheEntryIndex - 1; } - logicalLines = logicalLinesDiff + 1; - visualLines = logicalLinesDiff + 1; + logicalLines = logicalLinesDiff; + visualLines = logicalLinesDiff; softWrapLines = myStorage.getNumberOfSoftWrapsInRange(startOffset, endOffset); visualLines += softWrapLines; diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java index 3347c2bc15e4..fe0bdf699b4e 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java @@ -15,12 +15,12 @@ */ package com.intellij.openapi.editor.impl.softwrap.mapping; -import com.intellij.openapi.editor.FoldRegion; -import com.intellij.openapi.editor.FoldingModel; -import com.intellij.openapi.editor.VisualPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.SoftWrapModelEx; import com.intellij.openapi.editor.impl.SoftWrapModelImpl; import com.intellij.testFramework.LightPlatformCodeInsightTestCase; +import gnu.trove.TIntHashSet; +import gnu.trove.TIntProcedure; import java.awt.*; import java.io.IOException; @@ -174,11 +174,41 @@ public class SoftWrapApplianceOnDocumentModificationTest extends LightPlatformCo assertEquals(new VisualPosition(7, 1), myEditor.offsetToVisualPosition(myEditor.getDocument().getTextLength())); } + public void testTrailingSoftWrapOffsetShiftOnTyping() throws IOException { + // The main idea is to type on a logical line before soft wrap in order to ensure that its offset is correctly shifted back. + String text = + "line1\n" + + "second line that is rather long to be soft wrapped"; + init(100, text); + + TIntHashSet offsetsBefore = collectSoftWrapStartOffsets(1); + assertTrue(!offsetsBefore.isEmpty()); + + type('2'); + final TIntHashSet offsetsAfter = collectSoftWrapStartOffsets(1); + assertSame(offsetsBefore.size(), offsetsAfter.size()); + offsetsBefore.forEach(new TIntProcedure() { + @Override + public boolean execute(int value) { + assertTrue(offsetsAfter.contains(value + 1)); + return true; + } + }); + } + //private void init(final int visibleWidth) throws Exception { // configureByFile(PATH + getFileName()); // initCommon(visibleWidth); //} + private static TIntHashSet collectSoftWrapStartOffsets(int documentLine) { + TIntHashSet result = new TIntHashSet(); + for (SoftWrap softWrap : myEditor.getSoftWrapModel().getSoftWrapsForLine(documentLine)) { + result.add(softWrap.getStart()); + } + return result; + } + private void init(int visibleWidth, String fileText) throws IOException { configureFromFileText(getFileName(), fileText); initCommon(visibleWidth);