diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java
index 1793195ca3e3..285eaa828930 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * Copyright 2000-2012 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.
@@ -94,7 +94,7 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
int caret = editor.getCaretModel().getOffset();
- if (caret == 0) {
+ if (caret <= 0) {
return Result.Continue;
}
diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
index c0accddd92fd..46341c274783 100644
--- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
+++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
@@ -774,6 +774,16 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx {
myDelegate.setStickySelection(enable);
}
+ @Override
+ public boolean isPurePaintingMode() {
+ return myDelegate.isPurePaintingMode();
+ }
+
+ @Override
+ public void setPurePaintingMode(boolean enabled) {
+ myDelegate.setPurePaintingMode(enabled);
+ }
+
@Override
public void setPrefixTextAndAttributes(@Nullable String prefixText, @Nullable TextAttributes attributes) {
myDelegate.setPrefixTextAndAttributes(prefixText, attributes);
diff --git a/platform/platform-api/src/com/intellij/openapi/editor/LineWrapPositionStrategy.java b/platform/platform-api/src/com/intellij/openapi/editor/LineWrapPositionStrategy.java
index 2e13a3fb8f09..d4ec5493437c 100644
--- a/platform/platform-api/src/com/intellij/openapi/editor/LineWrapPositionStrategy.java
+++ b/platform/platform-api/src/com/intellij/openapi/editor/LineWrapPositionStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * Copyright 2000-2012 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.
@@ -36,17 +36,17 @@ public interface LineWrapPositionStrategy {
*
* @param document target document which text is being processed
* @param project target project
- * @param startOffset start offset to use with the given text holder (inclusive)
+ * @param startOffset start offset to use with the given text holder (exclusive)
* @param endOffset end offset to use with the given text holder (exclusive)
* @param maxPreferredOffset this method is expected to do its best to return offset that belongs to
* (startOffset; maxPreferredOffset] interval. However, it's allowed
- * to return value from (maxPreferredOffset; endOffset] interval
+ * to return value from (maxPreferredOffset; endOffset) interval
* unless 'allowToBeyondMaxPreferredOffset' if 'false'
* @param allowToBeyondMaxPreferredOffset indicates if it's allowed to return value from
* (maxPreferredOffset; endOffset] interval in case of inability to
* find appropriate offset from (startOffset; maxPreferredOffset] interval
* @param virtual identifies if current request is for virtual wrap (soft wrap) position
- * @return offset from (startOffset; endOffset] interval where
+ * @return offset from (startOffset; endOffset) interval where
* target line should be wrapped OR -1 if no wrapping should be performed
*/
int calculateWrapPosition(
diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
index bf92d9d976b0..afaf3b773465 100644
--- a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
+++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2012 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.
@@ -89,7 +89,13 @@ public class EditorFragmentComponent extends JPanel {
textGraphics.translate(0, -y1);
textGraphics.setClip(0, y1, textWidth, height);
final boolean wasVisible = editor.setCaretVisible(false);
- editor.getContentComponent().paint(textGraphics);
+ editor.setPurePaintingMode(true);
+ try {
+ editor.getContentComponent().paint(textGraphics);
+ }
+ finally {
+ editor.setPurePaintingMode(false);
+ }
if (wasVisible) {
editor.setCaretVisible(true);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
index 33c3e34cc175..0ecb86e779bf 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
@@ -203,6 +203,12 @@ public interface EditorEx extends Editor {
*/
void setStickySelection(boolean enable);
+ /**
+ * @return width in pixels of the {@link #setPrefixTextAndAttributes(String, TextAttributes) prefix} used with the current editor if any;
+ * zero otherwise
+ */
+ int getPrefixTextWidthInPixels();
+
/**
* Allows to define prefix to be displayed on every editor line and text attributes to use for its coloring.
*
@@ -212,8 +218,21 @@ public interface EditorEx extends Editor {
void setPrefixTextAndAttributes(@Nullable String prefixText, @Nullable TextAttributes attributes);
/**
- * @return width in pixels of the {@link #setPrefixTextAndAttributes(String, TextAttributes) prefix} used with the current editor if any;
- * zero otherwise
+ * @return current 'pure painting mode' status
+ * @see #setPurePaintingMode(boolean)
*/
- int getPrefixTextWidthInPixels();
+ boolean isPurePaintingMode();
+
+ /**
+ * We often re-use the logic encapsulated at the editor. For example, every time we show editor fragment (folding, preview etc) we
+ * create a dedicated graphics object and ask the editor to paint into it.
+ *
+ * The thing is that the editor itself may change its state if any postponed operation is triggered by the painting request
+ * (e.g. soft wraps recalculation is triggered by the paint request and newly calculated soft wraps cause caret to change its position).
+ *
+ * This method allows to inform the editor that all subsequent painting request should not change the editor state.
+ *
+ * @param enabled 'pure painting mode' status to use
+ */
+ void setPurePaintingMode(boolean enabled);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
index 0263f6332cd8..2cdabfa5e067 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
@@ -116,7 +116,9 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
@Override
public void updateFinished(@NotNull Document doc) {
if (doc != myEditor.getDocument() || myIsInUpdate) return;
- if (savedBeforeBulkCaretMarker != null && savedBeforeBulkCaretMarker.isValid()) {
+ if (savedBeforeBulkCaretMarker != null && savedBeforeBulkCaretMarker.isValid()
+ && savedBeforeBulkCaretMarker.getStartOffset() != myOffset)
+ {
moveToOffset(savedBeforeBulkCaretMarker.getStartOffset());
}
releaseBulkCaretMarker();
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 28e18587875e..35762cecc63b 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
@@ -285,6 +285,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private int myStickySelectionStart;
private boolean myScrollToCaret = true;
+ private boolean myPurePaintingMode;
private boolean myPaintSelection;
private final EditorSizeAdjustmentStrategy mySizeAdjustmentStrategy = new EditorSizeAdjustmentStrategy();
@@ -499,6 +500,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
}
+ @Override
+ public int getPrefixTextWidthInPixels() {
+ return myPrefixWidthInPixels;
+ }
+
@Override
public void setPrefixTextAndAttributes(@Nullable String prefixText, @Nullable TextAttributes attributes) {
myPrefixText = prefixText == null ? null: prefixText.toCharArray();
@@ -513,8 +519,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
@Override
- public int getPrefixTextWidthInPixels() {
- return myPrefixWidthInPixels;
+ public boolean isPurePaintingMode() {
+ return myPurePaintingMode;
+ }
+
+ @Override
+ public void setPurePaintingMode(boolean enabled) {
+ myPurePaintingMode = enabled;
}
@Override
@@ -2001,7 +2012,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
boolean locateBeforeSoftWrap = !SoftWrapHelper.isCaretAfterSoftWrap(this);
int start = clipStartOffset;
int end = clipEndOffset;
- getSoftWrapModel().registerSoftWrapsIfNecessary();
+ if (!myPurePaintingMode) {
+ getSoftWrapModel().registerSoftWrapsIfNecessary();
+ }
LineIterator lIterator = createLineIterator();
lIterator.start(start);
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 4c09db41fac7..c520e149730b 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
@@ -222,7 +222,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi
@Override
public boolean isSoftWrappingEnabled() {
- if (!myUseSoftWraps || myEditor.isOneLineMode()) {
+ if (!myUseSoftWraps || myEditor.isOneLineMode() || myEditor.isPurePaintingMode()) {
return false;
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java
index 80f20e15a270..63574c2bcba2 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CacheEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * Copyright 2000-2012 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.
@@ -25,10 +25,7 @@ import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
/**
* Encapsulates information to cache for the single visual line.
@@ -131,6 +128,23 @@ class CacheEntry implements Comparable, Cloneable {
return result;
}
+ /**
+ * Removes fold data for all fold regions that start at or after the given offset.
+ *
+ * @param offset target offset
+ */
+ public void removeAllFoldDataAtOrAfter(final int offset) {
+ if (myFoldingData == DUMMY || myFoldingData.isEmpty()) {
+ return;
+ }
+ myFoldingData.retainEntries(new TIntObjectProcedure() {
+ @Override
+ public boolean execute(int a, FoldingData b) {
+ return a < offset;
+ }
+ });
+ }
+
@Nullable
public FoldingData getFoldingData(@NotNull final FoldRegion region) {
FoldingData candidate = myFoldingData.get(region.getStartOffset());
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 a6c245998eea..de2a4ea5f19e 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
@@ -285,7 +285,11 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
@Override
public void revertToOffset(final int offset, int visualLine) {
- // Do nothing in assumption that we store only information about start and end visual line positions and
+ final CacheEntry entry = getCacheEntryForVisualLine(visualLine, false);
+ if (entry != null) {
+ entry.removeAllFoldDataAtOrAfter(offset);
+ }
+ // Do nothing more in assumption that we store only information about start and end visual line positions and
// that start information remains the same and end of line is not reached yet.
}
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 52745723e8e7..49b19dee6117 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
@@ -24,6 +24,7 @@ import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.event.VisibleAreaEvent;
import com.intellij.openapi.editor.event.VisibleAreaListener;
+import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.ScrollingModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
@@ -80,7 +81,6 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
private final SoftWrapsStorage myStorage;
private final EditorEx myEditor;
private final SoftWrapPainter myPainter;
- private final EditorTextRepresentationHelper myRepresentationHelper;
private final SoftWrapDataMapper myDataMapper;
/**
@@ -93,15 +93,16 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
*/
private int myLastTopLeftCornerOffset = -1;
- private VisibleAreaWidthProvider myWidthProvider;
- private LineWrapPositionStrategy myLineWrapPositionStrategy;
- private IncrementalCacheUpdateEvent myEventBeingProcessed;
- private boolean myVisualAreaListenerAttached;
- private boolean myCustomIndentUsedLastTime;
- private int myCustomIndentValueUsedLastTime;
- private int myVisibleAreaWidth;
- private boolean myInProgress;
- private boolean myHasLinesWithFailedWrap;
+ private VisibleAreaWidthProvider myWidthProvider;
+ private EditorTextRepresentationHelper myRepresentationHelper;
+ private LineWrapPositionStrategy myLineWrapPositionStrategy;
+ private IncrementalCacheUpdateEvent myEventBeingProcessed;
+ private boolean myVisualAreaListenerAttached;
+ private boolean myCustomIndentUsedLastTime;
+ private int myCustomIndentValueUsedLastTime;
+ private int myVisibleAreaWidth;
+ private boolean myInProgress;
+ private boolean myHasLinesWithFailedWrap;
public SoftWrapApplianceManager(@NotNull SoftWrapsStorage storage,
@NotNull EditorEx editor,
@@ -303,6 +304,7 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
* @param foldRegion target collapsed fold region to process
* @return true if processing should be continued; false otherwise
*/
+ @SuppressWarnings("MagicConstant")
private boolean processCollapsedFoldRegion(FoldRegion foldRegion) {
if (processOutOfDateFoldRegion(foldRegion)) {
return false;
@@ -312,10 +314,9 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
if (placeholder.isEmpty()) {
return true;
}
- FontInfo fontInfo = EditorUtil.fontForChar(placeholder.charAt(0), myContext.fontType, myEditor);
int placeholderWidthInPixels = 0;
for (int i = 0; i < placeholder.length(); i++) {
- placeholderWidthInPixels += fontInfo.charWidth(placeholder.charAt(i));
+ placeholderWidthInPixels += myRepresentationHelper.charWidth(placeholder.charAt(i), myContext.fontType);
}
int newX = myContext.currentPosition.x + placeholderWidthInPixels;
@@ -491,19 +492,50 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
}
}
+ @SuppressWarnings("MagicConstant")
private void createSoftWrapIfPossible() {
final int offset = myContext.currentPosition.offset;
myContext.logicalLineData.update(offset);
int softWrapStartOffset = myContext.softWrapStartOffset;
+ int preferredOffset = Math.max(softWrapStartOffset, offset - 1 /* reserve a column for the soft wrap sign */);
SoftWrap softWrap = registerSoftWrap(
- softWrapStartOffset, Math.max(softWrapStartOffset, offset),
- calculateSoftWrapEndOffset(softWrapStartOffset, myContext.logicalLineData.endLineOffset), myContext.getSpaceWidth(),
+ softWrapStartOffset,
+ preferredOffset,
+ calculateSoftWrapEndOffset(softWrapStartOffset, myContext.logicalLineData.endLineOffset),
+ myContext.getSpaceWidth(),
myContext.logicalLineData
);
+ boolean revertedToFoldRegion = false;
if (softWrap == null) {
- myContext.tryToShiftToNextLine();
- myHasLinesWithFailedWrap = true;
- return;
+ EditorPosition wrapPosition = null;
+
+ // Try to insert soft wrap after the last collapsed fold region that is located on the current visual line.
+ if (myContext.lastFoldEndPosition != null && myStorage.getSoftWrap(myContext.lastFoldEndPosition.offset) == null
+ && myContext.lastFoldEndPosition.offset + 1/* reserve one visual column for the soft wrap sign */
+ < myContext.currentPosition.offset)
+ {
+ wrapPosition = myContext.lastFoldEndPosition;
+ }
+
+ if (wrapPosition == null && myContext.lastFoldStartPosition != null
+ && myStorage.getSoftWrap(myContext.lastFoldStartPosition.offset) == null
+ && myContext.lastFoldStartPosition.offset < myContext.currentPosition.offset)
+ {
+ wrapPosition = myContext.lastFoldStartPosition;
+ }
+
+ if (wrapPosition != null){
+ revertListeners(wrapPosition.offset, wrapPosition.visualLine);
+ myContext.currentPosition = wrapPosition;
+ softWrap = registerSoftWrap(wrapPosition.offset, myContext.getSpaceWidth(), myContext.logicalLineData);
+ myContext.tokenStartOffset = wrapPosition.offset;
+ revertedToFoldRegion = true;
+ }
+ else {
+ myContext.tryToShiftToNextLine();
+ myHasLinesWithFailedWrap = true;
+ return;
+ }
}
myContext.skipToLineEnd = false;
@@ -528,14 +560,16 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
return;
}
else if (actualSoftWrapOffset < offset) {
- revertListeners(actualSoftWrapOffset, myContext.currentPosition.visualLine);
- for (int j = offset - 1; j >= actualSoftWrapOffset; j--) {
- int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor];
- int tmpFontType = myOffset2fontType.get(j);
- int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getSpaceWidth(tmpFontType));
- myContext.currentPosition.offset--;
- myContext.currentPosition.logicalColumn -= columnsDiff;
- myContext.currentPosition.visualColumn -= columnsDiff;
+ if (!revertedToFoldRegion) {
+ revertListeners(actualSoftWrapOffset, myContext.currentPosition.visualLine);
+ for (int j = offset - 1; j >= actualSoftWrapOffset; j--) {
+ int pixelsDiff = myOffset2widthInPixels.data[j - myOffset2widthInPixels.anchor];
+ int tmpFontType = myOffset2fontType.get(j);
+ int columnsDiff = calculateWidthInColumns(myContext.text.charAt(j), pixelsDiff, myContext.getSpaceWidth(tmpFontType));
+ myContext.currentPosition.offset--;
+ myContext.currentPosition.logicalColumn -= columnsDiff;
+ myContext.currentPosition.visualColumn -= columnsDiff;
+ }
}
}
else if (actualSoftWrapOffset > offset) {
@@ -549,6 +583,10 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
myContext.currentPosition.offset = actualSoftWrapOffset;
myOffset2fontType.clear();
myOffset2widthInPixels.clear();
+
+ if (revertedToFoldRegion && myContext.currentPosition.offset == myContext.lastFold.getStartOffset()) {
+ processCollapsedFoldRegion(myContext.lastFold);
+ }
}
private int calculateNewX(char c) {
@@ -570,7 +608,7 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
return i;
}
}
- return end;
+ return Math.max(start, end);
}
private static int calculateWidthInColumns(char c, int widthInPixels, int spaceWithInPixels) {
@@ -619,7 +657,9 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
}
if (softWrapOffset >= lineData.endLineOffset || softWrapOffset < 0
- || (myCustomIndentUsedLastTime && softWrapOffset == lineData.nonWhiteSpaceSymbolOffset))
+ || (myCustomIndentUsedLastTime && softWrapOffset == lineData.nonWhiteSpaceSymbolOffset)
+ || (softWrapOffset > preferredOffset && myContext.lastFoldStartPosition != null // Prefer to wrap on fold region backwards
+ && myContext.lastFoldStartPosition.offset <= preferredOffset)) // to wrapping forwards.
{
return null;
}
@@ -923,8 +963,14 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
recalculateIfNecessary();
}
- public void setWidthProvider(VisibleAreaWidthProvider widthProvider) {
+ public void setWidthProvider(@NotNull VisibleAreaWidthProvider widthProvider) {
myWidthProvider = widthProvider;
+ reset();
+ }
+
+ public void setRepresentationHelper(@NotNull EditorTextRepresentationHelper representationHelper) {
+ myRepresentationHelper = representationHelper;
+ reset();
}
@NotNull
@@ -1132,6 +1178,14 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
public CharSequence text;
public EditorPosition lineStartPosition;
public EditorPosition currentPosition;
+ /**
+ * Start position of the last collapsed fold region that is located at the current visual line and can be used as a fall back
+ * position for soft wrapping.
+ */
+ public EditorPosition lastFoldStartPosition;
+ public EditorPosition lastFoldEndPosition;
+ /** A fold region referenced by the {@link #lastFoldStartPosition}. */
+ public FoldRegion lastFold;
public SoftWrap delayedSoftWrap;
public JComponent contentComponent;
public int reservedWidthInPixels;
@@ -1156,6 +1210,9 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
text = null;
lineStartPosition = null;
currentPosition = null;
+ lastFoldStartPosition = null;
+ lastFoldEndPosition = null;
+ lastFold = null;
delayedSoftWrap = null;
contentComponent = null;
reservedWidthInPixels = 0;
@@ -1187,10 +1244,14 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
/**
* Asks current context to update its state assuming that it begins to point to the line next to its current position.
*/
+ @SuppressWarnings("MagicConstant")
public void onNewLine() {
notifyListenersOnVisualLineEnd();
currentPosition.onNewLine();
softWrapStartOffset = currentPosition.offset;
+ lastFoldStartPosition = null;
+ lastFoldEndPosition = null;
+ lastFold = null;
lineStartPosition.from(currentPosition);
logicalLineData.update(currentPosition.logicalLine, getSpaceWidth(), myEditor);
fontType = myOffset2fontType.get(currentPosition.offset);
@@ -1213,6 +1274,7 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
onNonLineFeedSymbol(c, newX);
}
+ @SuppressWarnings("MagicConstant")
public void onNonLineFeedSymbol(char c, int newX) {
int widthInPixels = newX - myContext.currentPosition.x;
@@ -1247,18 +1309,27 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
* @param foldRegion collapsed fold region to process
*/
private void advance(FoldRegion foldRegion, int placeHolderWidthInPixels) {
+ lastFoldStartPosition = currentPosition.clone();
+ lastFold = foldRegion;
int visualLineBefore = currentPosition.visualLine;
+ int logicalLineBefore = currentPosition.logicalLine;
int logicalColumnBefore = currentPosition.logicalColumn;
currentPosition.advance(foldRegion);
currentPosition.x += placeHolderWidthInPixels;
int collapsedFoldingWidthInColumns = currentPosition.logicalColumn;
- if (currentPosition.visualLine <= visualLineBefore) {
+ if (currentPosition.logicalLine <= logicalLineBefore) {
// Single-line fold region.
collapsedFoldingWidthInColumns = currentPosition.logicalColumn - logicalColumnBefore;
}
+ else {
+ final DocumentEx document = myEditor.getDocument();
+ int endFoldLine = document.getLineNumber(foldRegion.getEndOffset());
+ logicalLineData.endLineOffset = document.getLineEndOffset(endFoldLine);
+ }
notifyListenersOnFoldRegion(foldRegion, collapsedFoldingWidthInColumns, visualLineBefore);
tokenStartOffset = myContext.currentPosition.offset;
softWrapStartOffset = foldRegion.getEndOffset();
+ lastFoldEndPosition = currentPosition.clone();
}
/**
@@ -1288,7 +1359,7 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
* @return true if given 'x' coordinate exceeds visual area's right edge; false otherwise
*/
public boolean exceedsVisualEdge(int x) {
- return x + reservedWidthInPixels >= myVisibleAreaWidth;
+ return x >= myVisibleAreaWidth;
}
}
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java
index 28ffb0cbe25e..ed10dc32596d 100644
--- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/softwrap/mapping/SoftWrapApplianceOnDocumentModificationTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * Copyright 2000-2012 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.
@@ -17,9 +17,7 @@ package com.intellij.openapi.editor.impl.softwrap.mapping;
import com.intellij.codeInsight.folding.CodeFoldingManager;
import com.intellij.openapi.editor.*;
-import com.intellij.openapi.editor.impl.AbstractEditorProcessingOnDocumentModificationTest;
-import com.intellij.openapi.editor.impl.EditorImpl;
-import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
+import com.intellij.openapi.editor.impl.*;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.testFramework.TestFileType;
@@ -64,7 +62,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
public void testSoftWrapAdditionOnTyping() throws Exception {
String text =
"this is a test string that is expected to end just before right margin";
- init(800, text);
+ init(100, text);
int offset = myEditor.getDocument().getTextLength() + 1;
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
@@ -86,7 +84,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"123\n" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- init(100, text);
+ init(15, text);
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
type('1');
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
@@ -105,7 +103,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" }\n" +
"}";
- init(300, text);
+ init(40, text);
final FoldingModel foldingModel = myEditor.getFoldingModel();
assertEmpty(foldingModel.getAllFoldRegions());
@@ -133,7 +131,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" }\n" +
"}";
- init(300, text);
+ init(40, text);
type('\n');
VisualPosition position = myEditor.getCaretModel().getVisualPosition();
assertEquals(new VisualPosition(5, 0), position);
@@ -148,7 +146,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"}\n" +
"abcde";
- init(300, text);
+ init(40, text);
int offset = text.indexOf("abcde");
myEditor.getSelectionModel().setSelection(offset, text.length());
delete();
@@ -161,7 +159,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"}\n" +
"";
- init(300, text);
+ init(40, text);
type('\t');
assertEquals(new VisualPosition(2, 4), myEditor.getCaretModel().getVisualPosition());
}
@@ -171,7 +169,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"line1\n" +
"long line";
- init(48, text);
+ init(48, text, 7);
int softWrapsBefore = getSoftWrapModel().getRegisteredSoftWraps().size();
assertTrue(softWrapsBefore > 0);
@@ -191,7 +189,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" }\n" +
"}";
- init(700, text);
+ init(100, text);
int startFoldOffset = text.indexOf('@');
int endFoldOffset = text.indexOf(')');
@@ -215,7 +213,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"line5\n" +
"line6";
- init(700, text);
+ init(100, text);
type("\nq");
assertEquals(new VisualPosition(7, 1), myEditor.offsetToVisualPosition(myEditor.getDocument().getTextLength()));
}
@@ -225,7 +223,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
String text =
"line1\n" +
"second line that is long enough to be soft wrapped";
- init(100, text);
+ init(15, text);
TIntHashSet offsetsBefore = collectSoftWrapStartOffsets(1);
assertTrue(!offsetsBefore.isEmpty());
@@ -257,7 +255,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"ordinary line 7\n" +
"ordinary line 8\n";
- init(200, text);
+ init(30, text);
LogicalPosition position = myEditor.visualToLogicalPosition(new VisualPosition(8, 0));
assertSame(7, position.line); // Position from soft-wrapped part of the line
@@ -274,7 +272,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" */\n" +
"public class Test {\n" +
"}";
- init(500, text);
+ init(100, text);
addCollapsedFoldRegion(0, text.indexOf("public") - 1, "/**...*/");
@@ -293,7 +291,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
// There was a problem that removing text from the last document line that was soft-wrapped removed soft wraps as well.
String text =
"This a long string that is expected to be wrapped in more than one visual line";
- init(150, text);
+ init(20, text);
List extends SoftWrap> softWrapsBeforeModification = new ArrayList(getSoftWrapModel().getRegisteredSoftWraps());
assertTrue(softWrapsBeforeModification.size() > 0);
@@ -307,7 +305,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
String text =
"Line1\n" +
"Long line2 that is expected to be soft-wrapped";
- init(150, text);
+ init(20, text);
List extends SoftWrap> softWrapsBeforeModification = new ArrayList(getSoftWrapModel().getRegisteredSoftWraps());
assertTrue(softWrapsBeforeModification.size() > 0);
@@ -331,7 +329,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"fold line3\n" +
"fold line4\n" +
"normal line1";
- init(150, text);
+ init(20, text);
int afterFoldOffset = text.indexOf("normal line1");
addCollapsedFoldRegion(text.indexOf("fold line1") + 2, afterFoldOffset - 1, "...");
@@ -352,7 +350,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"line5\n" +
"this is long line that is expected to be soft-wrapped\n" +
"line6";
- init(100, text);
+ init(15, text);
assertTrue(!getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
@@ -392,7 +390,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" */\n" +
"class Test {\n" +
"}";
- init(700, text);
+ init(100, text);
String placeholder = "/**...*/";
addCollapsedFoldRegion(0, text.indexOf("class") - 1, placeholder);
@@ -417,7 +415,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" */\n" +
"class Test {\n" +
"}";
- init(700, text);
+ init(100, text);
String placeholder = "/**...*/";
addCollapsedFoldRegion(2, text.indexOf("class") - 1, placeholder);
@@ -436,7 +434,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"class Test {\n" +
"}";
- init(700, text);
+ init(100, text);
int startOffset = text.indexOf("@");
int endOffset = text.indexOf("class") - 1;
addCollapsedFoldRegion(startOffset, endOffset, "xxx");
@@ -456,7 +454,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"line4\n" +
"line5";
- init(30, text);
+ init(30, text, 7);
int start = text.indexOf("line3") - 1;
addCollapsedFoldRegion(start, text.length(), "...");
assertEquals(1, getSoftWrapModel().getRegisteredSoftWraps().size());
@@ -469,7 +467,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" public String s = \"this is a long string literal that is expected to be soft-wrapped into multiple visual lines\";\n" +
"}";
- init(250, text);
+ init(30, text);
myEditor.getCaretModel().moveToOffset(text.indexOf("}") - 1);
List extends SoftWrap> softWraps = new ArrayList(getSoftWrapModel().getRegisteredSoftWraps());
@@ -510,7 +508,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" public String s = \"this is a long string literal that is expected to be soft-wrapped into multiple visual lines\"; \n" +
"}";
- init(250, text);
+ init(30, text);
myEditor.getCaretModel().moveToOffset(text.indexOf("\n") + 1);
List extends SoftWrap> softWraps = new ArrayList(getSoftWrapModel().getRegisteredSoftWraps());
@@ -574,7 +572,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"this is line 4\n" +
"this is line 5";
- init(50, text);
+ init(50, text, 7);
VisualPosition changePosition = new VisualPosition(1, 0);
myEditor.getCaretModel().moveToVisualPosition(changePosition);
@@ -635,7 +633,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"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);
+ init(15, text);
Document document = myEditor.getDocument();
int start = document.getLineStartOffset(1);
int end = document.getLineEndOffset(1) + 1;
@@ -663,7 +661,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"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);
+ init(15, text);
CaretModel caretModel = myEditor.getCaretModel();
caretModel.moveToOffset(text.indexOf("2.") + 2);
@@ -685,7 +683,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
" this is a string that starts with white space and is long enough to be soft-wrapped\n" +
" this is a 'prefix' text before collapsed multi-line folding that is long enough to be soft-wrapped first fold line\n" +
"second fold line";
- init(200, text);
+ init(30, text);
addCollapsedFoldRegion(text.indexOf("first fold line"), text.length(), "...");
@@ -715,7 +713,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"\n" +
"class Test {\n" +
"}";
- init(300, text);
+ init(40, text);
final int foldStartOffset = "import".length() + 1;
int foldEndOffset = text.indexOf("class") - 2;
@@ -749,7 +747,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"\n" +
"class Test {\n" +
"}";
- init(300, text);
+ init(40, text);
final int foldStartOffset = text.indexOf("java.util.Collections");
final int foldEndOffset = text.indexOf("class") - 2;
@@ -768,7 +766,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"222\n" +
"33333333 33333333333333333333333333333333\n" +
"444";
- init(100, text);
+ init(15, text);
assertEquals(new LogicalPosition(3, 0), myEditor.visualToLogicalPosition(new VisualPosition(4, 0)));
}
@@ -781,7 +779,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"444 55\n" +
"666 77";
- init(100, text);
+ init(15, text);
int foldStart = text.indexOf("5");
int foldEnd = text.indexOf("7");
@@ -798,7 +796,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
public void testNoWrapAtFirstNonWsSymbolWithCustomIndent() throws IOException {
String text =
" 1111111111111111111111111111111";
- init(70, text);
+ init(10, text);
getEditor().getSettings().setCustomSoftWrapIndent(0);
getEditor().getSettings().setUseCustomSoftWrapIndent(true);
int textLength = getEditor().getDocument().getTextLength();
@@ -814,7 +812,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
// Inspired by IDEA-76353. The point is that we need to consider cached information about tab symbols width during logical
// position to offset mapping
String text = "\t test";
- init(100, text);
+ init(15, text);
((EditorImpl)myEditor).setPrefixTextAndAttributes(" ", new TextAttributes());
myEditor.getCaretModel().moveToOffset(text.length());
}
@@ -827,7 +825,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"\t third line";
// Make soft wraps to build a document info cache.
- init(300, text);
+ init(40, text);
// Modify document while soft wraps processing is off.
final EditorSettings settings = getEditor().getSettings();
@@ -856,7 +854,7 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
"\t my text";
// Build soft wraps cache.
- init(300, text);
+ init(40, text);
VisualPosition caretPositionBefore = getEditor().getCaretModel().getVisualPosition();
@@ -871,12 +869,86 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
getEditor().getCaretModel().getVisualPosition()
);
}
-
- private void init(final int visibleWidth, @NotNull String fileText) throws IOException {
- init(visibleWidth, fileText, TestFileType.TEXT);
+
+ public void testNoPreliminarySoftWrapAtLineEnd() throws IOException {
+ // We used to make soft wrap when the string was couple of visual columns before the right screen edge even if it could
+ // be completely shown.
+ init(37, "a b c", 7);
+ assertEmpty(getSoftWrapModel().getRegisteredSoftWraps());
+ }
+
+ public void testNoPreliminarySoftWrapBeforeFoldingAtLineEnd() throws IOException {
+ final String text = "a b c test";
+ init(50, text, 7);
+ addCollapsedFoldRegion(text.indexOf("t"), text.length(), ".");
+ assertEmpty(getSoftWrapModel().getRegisteredSoftWraps());
+ }
+
+ public void testMultiLineFoldRegionBeforeWrapPosition() throws IOException {
+ final String text =
+ "package org.denis;\n" +
+ "\n" +
+ "\n" +
+ "public class BrokenAlignment {\n" +
+ "\n" +
+ " void method1(int a) {\n" +
+ " }\n" +
+ "\n" +
+ " Object method2(Object ... data) {\n" +
+ " return new Runnable() {\n" +
+ " public void run() {\n" +
+ " System.out.println();\n" +
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ "\n" +
+ "}";
+ init(511, text, TestFileType.JAVA, 10);
+
+ addCollapsedFoldRegion(text.indexOf("new Runnable"), text.indexOf("System"), "Runnable() { ");
+
+ int start = text.indexOf("System");
+ start = text.indexOf("\n", start);
+ int end = text.indexOf(';', start);
+ addCollapsedFoldRegion(start, end, " }");
+
+ final List extends SoftWrap> wraps = getSoftWrapModel().getRegisteredSoftWraps();
+ assertEquals(1, wraps.size());
+
+ assertEquals(start, wraps.get(0).getStart());
+ assertEquals(myEditor.offsetToVisualPosition(start), myEditor.offsetToVisualPosition(start + 1));
+ assertEquals(myEditor.offsetToVisualPosition(start).line, myEditor.offsetToVisualPosition(end).line);
}
- private void init(final int visibleWidth, @NotNull String fileText, @NotNull TestFileType fileType) throws IOException {
+ public void testEnsureBeforeSoftWrapSignIsVisible() throws IOException {
+ final String text = "a.b.c.d";
+ init(43, text, 7);
+
+ checkSoftWraps(text.indexOf('c') + 1);
+ }
+
+ public void testWrapAfterCollapsedFoldRegion() throws IOException {
+ final String text =
+ "this is a long text to fold more";
+ init(12, text);
+
+ int start = text.indexOf("long");
+ int end = text.indexOf("more");
+ addCollapsedFoldRegion(start, end, "placeholder which is long enough to be wrapped");
+
+ checkSoftWraps(start, end);
+ }
+
+ private void init(final int visibleWidthInColumns, @NotNull String fileText) throws IOException {
+ int symbolWidthInPixels = 7;
+ init(visibleWidthInColumns * symbolWidthInPixels, fileText, symbolWidthInPixels);
+ }
+
+ private void init(final int visibleWidth, @NotNull String fileText, int symbolWidth) throws IOException {
+ init(visibleWidth, fileText, TestFileType.TEXT, symbolWidth);
+ }
+
+ private void init(final int visibleWidth, @NotNull String fileText, @NotNull TestFileType fileType, final int symbolWidth) throws IOException {
init(fileText, fileType);
myEditor.getSettings().setUseSoftWraps(true);
SoftWrapModelImpl model = (SoftWrapModelImpl)myEditor.getSoftWrapModel();
@@ -889,9 +961,27 @@ public class SoftWrapApplianceOnDocumentModificationTest extends AbstractEditorP
return visibleWidth;
}
});
+
+ if (symbolWidth > 0) {
+ applianceManager.setRepresentationHelper(new DefaultEditorTextRepresentationHelper(myEditor) {
+ @Override
+ public int charWidth(char c, int fontType) {
+ return symbolWidth;
+ }
+ });
+ }
+
applianceManager.registerSoftWrapIfNecessary();
}
+ private static void checkSoftWraps(int... startOffsets) {
+ final List extends SoftWrap> softWraps = getSoftWrapModel().getRegisteredSoftWraps();
+ assertEquals("soft wraps number", startOffsets.length, softWraps.size());
+ for (int i = 0; i < startOffsets.length; i++) {
+ assertEquals(startOffsets[i], softWraps.get(i).getStart());
+ }
+ }
+
private static SoftWrapModelImpl getSoftWrapModel() {
return (SoftWrapModelImpl)myEditor.getSoftWrapModel();
}