From 67ecab9f8b9af11ba303c9aa6b6daef38f0a3e0f Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Thu, 16 Sep 2010 11:39:08 +0400 Subject: [PATCH] EA-22010 - AIOOBE: OffsetToLogicalCalculationStrategy$1.compute Incremental update of soft wraps cache is corrected --- .../softwrap/SoftWrapAdditionOnTyping.txt | 1 + .../editor/impl/SoftWrapModelImpl.java | 14 ++++- .../mapping/CachingSoftWrapDataMapper.java | 8 +-- .../mapping/SoftWrapApplianceManager.java | 51 +++++++++++++--- .../SoftWrapAwareDocumentParsingListener.java | 6 +- .../mapping/SoftWrapApplianceManagerTest.java | 61 +++++++++++++++++++ 6 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/softwrap/SoftWrapAdditionOnTyping.txt create mode 100644 platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManagerTest.java diff --git a/java/java-tests/testData/codeInsight/softwrap/SoftWrapAdditionOnTyping.txt b/java/java-tests/testData/codeInsight/softwrap/SoftWrapAdditionOnTyping.txt new file mode 100644 index 000000000000..22294ce02333 --- /dev/null +++ b/java/java-tests/testData/codeInsight/softwrap/SoftWrapAdditionOnTyping.txt @@ -0,0 +1 @@ +this is a test string that is expected to end just before right margin \ No newline at end of file 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 7a00f7d48abe..39f63fa20912 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 @@ -51,9 +51,9 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi private final List myFoldListeners = new ArrayList(); private final CachingSoftWrapDataMapper myDataMapper; - private final SoftWrapsStorage myStorage; - private final SoftWrapPainter myPainter; - private final SoftWrapApplianceManager myApplianceManager; + private final SoftWrapsStorage myStorage; + private final SoftWrapPainter myPainter; + private final SoftWrapApplianceManager myApplianceManager; private final EditorEx myEditor; /** Holds number of 'active' calls, i.e. number of methods calls of the current object within the current call stack. */ @@ -460,4 +460,12 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi myApplianceManager.release(); myStorage.removeAll(); } + + public void refreshSettings() { + myLastSettingsCheckTimeMillis = 0; + } + + public SoftWrapApplianceManager getApplianceManager() { + return myApplianceManager; + } } 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 9b5aefba32f4..1edb08479eed 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 @@ -371,9 +371,10 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw if (!myBeforeChangeState.valid) { return; } - int endIndex = Math.max(0, myCache.size() - 2); // -1 because of zero-based indexing; one more -1 in assumption that - // re-parsing always adds number of target cache entries plus one - // (because of line feed at the end). + int endIndex = Math.max(0, myCache.size() - 1/*because of zero-based indexing*/); + if (endOffset < myEditor.getDocument().getTextLength() - 1) { + endIndex--; // We assume that non-last document line ends with line feed symbol. + } myAfterChangeState.updateByCacheIndices(myBeforeChangeState.startCacheEntryIndex, endIndex); myCache.subList(myAfterChangeState.endCacheEntryIndex + 1, myCache.size()).clear(); myCache.addAll(myNotAffectedByUpdateTailCacheEntries); @@ -393,7 +394,6 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw //System.out.println("text length: " + text.length() + ", soft wraps: " + myStorage.getSoftWraps()); //for (int i = 0; i < myCache.size(); i++) { // CacheEntry entry = myCache.get(i); - // // TODO den unwrap // try { // System.out.printf("line %d. %d-%d: '%s'%n", i, entry.startOffset, entry.endOffset, // text.subSequence(entry.startOffset,Math.min(entry.endOffset, text.length()))); 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 32f885b3acf5..6b2403b10f40 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 @@ -21,7 +21,6 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.FoldingListener; -import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper; import com.intellij.openapi.editor.impl.FontInfo; @@ -73,6 +72,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen private final SoftWrapPainter myPainter; private final EditorTextRepresentationHelper myRepresentationHelper; + private VisibleAreaWidthProvider myWidthProvider; private LineWrapPositionStrategy myLineWrapPositionStrategy; private boolean myCustomIndentUsedLastTime; private int myCustomIndentValueUsedLastTime; @@ -87,6 +87,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen myEditor = editor; myPainter = painter; myRepresentationHelper = representationHelper; + myWidthProvider = new DefaultVisibleAreaWidthProvider(editor); } public void registerSoftWrapIfNecessary(@NotNull Rectangle clip, int startOffset) { @@ -183,7 +184,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen revertListeners(softWrap.getStart(), context.visualLine); for (int j = currentFold.getStartOffset() - 1; j >= softWrap.getStart(); j--) { int pixelsDiff = offset2widthInPixels.get(j); - int columnsDiff = calculateWidthInColumns(pixelsDiff, fontType2spaceWidth.get(offset2fontType.get(j))); + int columnsDiff = calculateWidthInColumns(text.charAt(j), pixelsDiff, fontType2spaceWidth.get(offset2fontType.get(j))); context.offset--; context.logicalColumn -= columnsDiff; context.visualColumn -= columnsDiff; @@ -269,7 +270,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen revertListeners(newI, context.visualLine); for (int j = i - 1; j >= newI; j--) { int pixelsDiff = offset2widthInPixels.get(j); - int columnsDiff = calculateWidthInColumns(pixelsDiff, fontType2spaceWidth.get(offset2fontType.get(j))); + int columnsDiff = calculateWidthInColumns(text.charAt(j), pixelsDiff, fontType2spaceWidth.get(offset2fontType.get(j))); context.offset--; context.logicalColumn -= columnsDiff; context.visualColumn -= columnsDiff; @@ -362,7 +363,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen } context.symbolWidthInPixels = newX - context.x; - context.symbolWidthInColumns = calculateWidthInColumns(context.symbolWidthInPixels, spaceWidth); + context.symbolWidthInColumns = calculateWidthInColumns(context.symbol, context.symbolWidthInPixels, spaceWidth); notifyListenersOnProcessedSymbol(context); context.visualColumn += context.symbolWidthInColumns; context.logicalColumn += context.symbolWidthInColumns; @@ -371,7 +372,10 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen context.offset++; } - private static int calculateWidthInColumns(int widthInPixels, int spaceWithInPixels) { + private static int calculateWidthInColumns(char c, int widthInPixels, int spaceWithInPixels) { + if (c != '\t') { + return 1; + } int result = widthInPixels / spaceWithInPixels; if (widthInPixels % spaceWithInPixels > 0) { result++; @@ -422,7 +426,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen myCustomIndentValueUsedLastTime = currentCustomIndent; // Check if we need to recalculate soft wraps due to visible area width change. - int currentVisibleAreaWidth = myEditor.getScrollingModel().getVisibleArea().width; + int currentVisibleAreaWidth = myWidthProvider.getVisibleAreaWidth(); if (!indentChanged && myVisibleAreaWidth == currentVisibleAreaWidth) { return; } @@ -573,7 +577,12 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen Document document = myEditor.getDocument(); int startLine = document.getLineNumber(endRange.getStartOffset()); int endLine = document.getLineNumber(endRange.getEndOffset()); - endRange = new TextRange(document.getLineStartOffset(startLine), document.getLineEndOffset(endLine)); + int endOffset = document.getLineEndOffset(endLine); + int textLength = document.getTextLength(); + if (textLength > 0 && endOffset >= textLength) { + endOffset = textLength - 1; + } + endRange = new TextRange(document.getLineStartOffset(startLine), endOffset); } } @@ -598,7 +607,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen case ' ': indentInColumns += 1; indentInPixels += spaceWidth; break; case '\t': int x = EditorUtil.nextTabStop(indentInPixels, editor); - indentInColumns += calculateWidthInColumns(x - indentInPixels, spaceWidth); + indentInColumns += calculateWidthInColumns(c, x - indentInPixels, spaceWidth); indentInPixels = x; break; default: myNonWhiteSpaceSymbolOffset = i; return; @@ -629,4 +638,30 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen } } } + + public void setWidthProvider(VisibleAreaWidthProvider widthProvider) { + myWidthProvider = widthProvider; + } + + /** + * This interface is introduced mostly for encapsulating GUI-specific values retrieval and make it possible to write + * tests for soft wraps processing. + */ + public interface VisibleAreaWidthProvider { + int getVisibleAreaWidth(); + } + + private static class DefaultVisibleAreaWidthProvider implements VisibleAreaWidthProvider { + + private final Editor myEditor; + + DefaultVisibleAreaWidthProvider(Editor editor) { + myEditor = editor; + } + + @Override + public int getVisibleAreaWidth() { + return myEditor.getScrollingModel().getVisibleArea().width; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapAwareDocumentParsingListener.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapAwareDocumentParsingListener.java index f5cfc1503331..096d85984da3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapAwareDocumentParsingListener.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapAwareDocumentParsingListener.java @@ -98,10 +98,10 @@ interface SoftWrapAwareDocumentParsingListener { *

* Note: given offsets may differ from the one given to {@link #onRecalculationStart(int, int)}. E.g. there is a possible * case that user removes particular block of text. {@link #onRecalculationStart(int, int)} is called with offsets of logical lines - * that hold that block and this method is called with //TODO den add doc + * that hold that block and this method is called with offsets of the logical lines that hold changed text region. * - * @param startOffset start offset of document range that is recalculated - * @param endOffset end offset of document range that is recalculated + * @param startOffset start offset of document range that is recalculated (inclusive) + * @param endOffset end offset of document range that is recalculated (inclusive) */ void onRecalculationEnd(int startOffset, int endOffset); } diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManagerTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManagerTest.java new file mode 100644 index 000000000000..1f26f8f70dc7 --- /dev/null +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceManagerTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.impl.softwrap.mapping; + +import com.intellij.openapi.editor.impl.SoftWrapModelImpl; +import com.intellij.testFramework.LightPlatformCodeInsightTestCase; + +import java.awt.*; + +/** + * @author Denis Zhdanov + * @since 09/16/2010 + */ +public class SoftWrapApplianceManagerTest extends LightPlatformCodeInsightTestCase { + + private static final String PATH = "/codeInsight/softwrap/"; + + @Override + protected void tearDown() throws Exception { + myEditor.getSettings().setUseSoftWraps(false); + super.tearDown(); + } + + public void testSoftWrapAdditionOnTyping() throws Exception { + init(800); + + int offset = myEditor.getDocument().getTextLength() + 1; + assertNull(myEditor.getSoftWrapModel().getSoftWrap(offset)); + type(" thisisalongtokenthatisnotexpectedtobebrokenintopartsduringsoftwrapping"); + assertNotNull(myEditor.getSoftWrapModel().getSoftWrap(offset)); + } + + private void init(final int visibleWidth) throws Exception { + configureByFile(PATH + getTestName(false) + ".txt"); + myEditor.getSettings().setUseSoftWraps(true); + SoftWrapModelImpl model = (SoftWrapModelImpl)myEditor.getSoftWrapModel(); + model.refreshSettings(); + + SoftWrapApplianceManager applianceManager = model.getApplianceManager(); + applianceManager.setWidthProvider(new SoftWrapApplianceManager.VisibleAreaWidthProvider() { + @Override + public int getVisibleAreaWidth() { + return visibleWidth; + } + }); + applianceManager.registerSoftWrapIfNecessary(new Rectangle(visibleWidth, visibleWidth * 2), 0); + } +}