mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-56405 Soft wrap, editing: typing on line split border replaces wrap sign with line feed; AssertionError at SoftWrapModelImpl.beforeDocumentChangeAtCaret()
1. Corrected soft wraps processing on document change; 2. Implemented soft wraps resources releasing on editor disposing; 3. Corrected 'active row' background painting algorithm for soft-wrapped lines; 4. Changed default 'soft wrap arrows' to another unicode symbols (they are supposed to correctly represented by Windows 7 fonts);
This commit is contained in:
@@ -56,9 +56,14 @@ public class CaretModelWindow implements CaretModel {
|
||||
myDelegate.moveToLogicalPosition(hostPos);
|
||||
}
|
||||
|
||||
public void moveToOffset(final int offset) {
|
||||
@Override
|
||||
public void moveToOffset(int offset) {
|
||||
moveToOffset(offset, false);
|
||||
}
|
||||
|
||||
public void moveToOffset(final int offset, boolean locateBeforeSoftWrap) {
|
||||
int hostOffset = myEditorWindow.getDocument().injectedToHost(offset);
|
||||
myDelegate.moveToOffset(hostOffset);
|
||||
myDelegate.moveToOffset(hostOffset, locateBeforeSoftWrap);
|
||||
}
|
||||
|
||||
public LogicalPosition getLogicalPosition() {
|
||||
|
||||
@@ -55,12 +55,23 @@ public interface CaretModel {
|
||||
void moveToVisualPosition(VisualPosition pos);
|
||||
|
||||
/**
|
||||
* Moves the caret to the specified offset in the document.
|
||||
* Short hand for calling {@link #moveToOffset(int, boolean)} with <code>'false'</code> as a second argument.
|
||||
*
|
||||
* @param offset the offset to move to.
|
||||
* @param offset the offset to move to
|
||||
*/
|
||||
void moveToOffset(int offset);
|
||||
|
||||
/**
|
||||
* Moves the caret to the specified offset in the document.
|
||||
*
|
||||
* @param offset the offset to move to.
|
||||
* @param locateBeforeSoftWrap there is a possible case that there is a soft wrap at the given offset, hence, the same offset
|
||||
* corresponds to two different visual positions - just before soft wrap and just after soft wrap.
|
||||
* We may want to clearly indicate where to put the caret then. Given parameter allows to do that.
|
||||
* <b>Note:</b> it's ignored if there is no soft wrap at the given offset
|
||||
*/
|
||||
void moveToOffset(int offset, boolean locateBeforeSoftWrap);
|
||||
|
||||
/**
|
||||
* Returns the logical position of the caret.
|
||||
*
|
||||
|
||||
@@ -81,7 +81,7 @@ public class EditorModificationUtil {
|
||||
public static int insertStringAtCaret(Editor editor, String s, boolean toProcessOverwriteMode, boolean toMoveCaret) {
|
||||
final SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if (selectionModel.hasSelection()) {
|
||||
editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart());
|
||||
editor.getCaretModel().moveToOffset(selectionModel.getSelectionStart(), true);
|
||||
}
|
||||
|
||||
// There is a possible case that particular soft wraps become hard wraps if the caret is located at soft wrap-introduced virtual
|
||||
@@ -114,7 +114,7 @@ public class EditorModificationUtil {
|
||||
|
||||
int offset = oldOffset + s.length();
|
||||
if (toMoveCaret){
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
editor.getCaretModel().moveToOffset(offset, true);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
selectionModel.removeSelection();
|
||||
}
|
||||
|
||||
@@ -81,10 +81,10 @@ public class BackspaceAction extends EditorAction {
|
||||
}
|
||||
else {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
editor.getCaretModel().moveToOffset(offset-1);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
editor.getSelectionModel().removeSelection();
|
||||
document.deleteString(offset-1, offset);
|
||||
editor.getCaretModel().moveToOffset(offset - 1, true);
|
||||
}
|
||||
}
|
||||
else if(lineNumber > 0) {
|
||||
|
||||
@@ -97,6 +97,7 @@ public class DeleteAction extends EditorAction {
|
||||
if(afterLineEnd < 0) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
document.deleteString(offset, offset + 1);
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
return;
|
||||
}
|
||||
if(lineNumber + 1 >= document.getLineCount())
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapHelper;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -133,9 +134,13 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
}
|
||||
|
||||
public void moveToOffset(int offset) {
|
||||
moveToOffset(offset, false);
|
||||
}
|
||||
|
||||
public void moveToOffset(int offset, boolean locateBeforeSoftWrap) {
|
||||
assertIsDispatchThread();
|
||||
validateCallContext();
|
||||
moveToLogicalPosition(myEditor.offsetToLogicalPosition(offset));
|
||||
moveToLogicalPosition(myEditor.offsetToLogicalPosition(offset), locateBeforeSoftWrap);
|
||||
if (!myEditor.offsetToLogicalPosition(myOffset).equals(myEditor.offsetToLogicalPosition(offset))) {
|
||||
LOG.error("caret moved to wrong offset. Requested:" + offset + " but actual:" + myOffset);
|
||||
}
|
||||
@@ -244,6 +249,10 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
}
|
||||
|
||||
public void moveToLogicalPosition(LogicalPosition pos) {
|
||||
moveToLogicalPosition(pos, false);
|
||||
}
|
||||
|
||||
private void moveToLogicalPosition(LogicalPosition pos, boolean locateBeforeSoftWrap) {
|
||||
assertIsDispatchThread();
|
||||
validateCallContext();
|
||||
int column = pos.column;
|
||||
@@ -325,6 +334,14 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
myEditor.updateCaretCursor();
|
||||
requestRepaint(oldInfo);
|
||||
|
||||
if (locateBeforeSoftWrap && SoftWrapHelper.isCaretAfterSoftWrap(myEditor)) {
|
||||
int lineToUse = myVisibleCaret.line - 1;
|
||||
if (lineToUse >= 0) {
|
||||
moveToVisualPosition(new VisualPosition(lineToUse, EditorUtil.getLastVisualLineColumnNumber(myEditor, lineToUse)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldCaretPosition.toVisualPosition().equals(myLogicalCaret.toVisualPosition())) {
|
||||
CaretEvent event = new CaretEvent(myEditor, oldCaretPosition, myLogicalCaret);
|
||||
for (CaretListener listener : myCaretListeners) {
|
||||
@@ -408,15 +425,19 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
|
||||
DocumentEventImpl event = (DocumentEventImpl)e;
|
||||
final Document document = myEditor.getDocument();
|
||||
boolean performSoftWrapAdjustment = e.getNewLength() > 0 // We want to put caret just after the last added symbol
|
||||
// There is a possible case that the user removes text just before the soft wrap. We want to keep caret
|
||||
// on a visual line with soft wrap start then.
|
||||
|| myEditor.getSoftWrapModel().getSoftWrap(e.getOffset()) != null;
|
||||
|
||||
if (event.isWholeTextReplaced()) {
|
||||
int newLength = document.getTextLength();
|
||||
if (myOffset == newLength - e.getNewLength() + e.getOldLength() || newLength == 0) {
|
||||
moveToOffset(newLength);
|
||||
moveToOffset(newLength, performSoftWrapAdjustment);
|
||||
}
|
||||
else {
|
||||
final int line = event.translateLineViaDiff(myLogicalCaret.line);
|
||||
moveToLogicalPosition(new LogicalPosition(line, myLogicalCaret.column));
|
||||
moveToLogicalPosition(new LogicalPosition(line, myLogicalCaret.column), performSoftWrapAdjustment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -435,9 +456,8 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
|
||||
newOffset = Math.min(newOffset, document.getTextLength());
|
||||
|
||||
//TODO:ask max about this code
|
||||
// if (newOffset != myOffset) {
|
||||
moveToOffset(newOffset);
|
||||
moveToOffset(newOffset, performSoftWrapAdjustment);
|
||||
//}
|
||||
//else {
|
||||
// moveToVisualPosition(oldPosition);
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.intellij.openapi.editor.highlighter.HighlighterClient;
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelEvent;
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapHelper;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -294,6 +295,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myDocument.addDocumentListener(myCaretModel);
|
||||
myDocument.addDocumentListener(mySelectionModel);
|
||||
myDocument.addDocumentListener(myEditorDocumentAdapter);
|
||||
myDocument.addDocumentListener(mySoftWrapModel);
|
||||
|
||||
myIndentsModel = new IndentsModelImpl(this);
|
||||
myCaretModel.addCaretListener(new CaretListener() {
|
||||
@@ -522,6 +524,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
myDocument.removeDocumentListener(myFoldingModel);
|
||||
myDocument.removeDocumentListener(myCaretModel);
|
||||
myDocument.removeDocumentListener(mySelectionModel);
|
||||
myDocument.removeDocumentListener(mySoftWrapModel);
|
||||
|
||||
MarkupModelEx markupModel = (MarkupModelEx)myDocument.getMarkupModel(myProject, false);
|
||||
if (markupModel instanceof MarkupModelImpl) {
|
||||
@@ -1126,7 +1129,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
// We need to consider 'before soft wrap drawing'.
|
||||
TextChange softWrap = getSoftWrapModel().getSoftWrap(offset);
|
||||
if (softWrap != null) {
|
||||
if (softWrap != null && offset > startOffset) {
|
||||
column++;
|
||||
x += getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
|
||||
if (column >= length) {
|
||||
@@ -1585,6 +1588,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@SuppressWarnings({"StatementWithEmptyBody"})
|
||||
private void paintBackgrounds(Graphics g, Rectangle clip) {
|
||||
boolean locateBeforeSoftWrap = !SoftWrapHelper.isCaretAfterSoftWrap(this);
|
||||
Color defaultBackground = getBackgroundColor();
|
||||
g.setColor(defaultBackground);
|
||||
g.fillRect(clip.x, clip.y, clip.width, clip.height);
|
||||
@@ -1702,6 +1706,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
if (lIterator.getLineNumber() >= lastLineIndex && position.y <= clip.y + clip.height) {
|
||||
paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground);
|
||||
}
|
||||
|
||||
// Perform additional activity if soft wrap is added or removed during repainting.
|
||||
if (mySoftWrapsChanged) {
|
||||
mySoftWrapsChanged = false;
|
||||
validateSize();
|
||||
|
||||
// Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction.
|
||||
repaintToScreenBottom(xyToLogicalPosition(position).line);
|
||||
|
||||
// Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn
|
||||
// in accordance with the newly introduced soft wrap(s).
|
||||
myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y);
|
||||
|
||||
// Ask caret model to update visual caret position.
|
||||
getCaretModel().moveToOffset(getCaretModel().getOffset(), locateBeforeSoftWrap);
|
||||
}
|
||||
}
|
||||
|
||||
private void paintRectangularSelection(Graphics g) {
|
||||
@@ -1756,7 +1776,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
if (startToUse < softWrapStart) {
|
||||
position.x = drawBackground(g, backColor, text.subSequence(startToUse, softWrapStart), position, fontType, defaultBackground, clip);
|
||||
}
|
||||
drawSoftWrap(g, backColor, softWrap, position, fontType, defaultBackground, clip);
|
||||
drawSoftWrap(g, softWrap, position, fontType, defaultBackground, clip);
|
||||
startToUse = softWrapStart;
|
||||
}
|
||||
|
||||
@@ -1766,37 +1786,61 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return position.x;
|
||||
}
|
||||
|
||||
private void drawSoftWrap(Graphics g, Color backColor, TextChange softWrap, Point position,
|
||||
int fontType, Color defaultBackground, Rectangle clip)
|
||||
{
|
||||
position.x = drawBackground(
|
||||
g, backColor, getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED), position,
|
||||
defaultBackground, clip
|
||||
);
|
||||
private void drawSoftWrap(Graphics g, TextChange softWrap, Point position, int fontType, Color defaultBackground, Rectangle clip) {
|
||||
// The main idea is to to do the following:
|
||||
// *) update given drawing position coordinates in accordance with the current soft wrap;
|
||||
// *) draw 'active line' background at soft wrap-introduced virtual space if necessary;
|
||||
|
||||
CharSequence softWrapText = softWrap.getText();
|
||||
int start = 0;
|
||||
for (
|
||||
int end = CharArrayUtil.shiftForwardUntil(softWrapText, start, "\n");
|
||||
start < softWrapText.length() && end < softWrapText.length();
|
||||
end = CharArrayUtil.shiftForwardUntil(softWrapText, start, "\n"))
|
||||
{
|
||||
drawBackground(g, backColor, softWrapText.subSequence(start, end), position, fontType, defaultBackground, clip);
|
||||
start = end + 1;
|
||||
position.x = 0;
|
||||
position.y += getLineHeight();
|
||||
int activeRowY = getCaretModel().getVisualPosition().line * getLineHeight();
|
||||
if (position.y == activeRowY) {
|
||||
// Draw 'active line' background after soft wrap.
|
||||
Color caretRowColor = getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR);
|
||||
drawBackground(g, caretRowColor, clip.x + clip.width - position.x, position, defaultBackground, clip);
|
||||
}
|
||||
|
||||
if (start < softWrapText.length()) {
|
||||
position.x = drawBackground(
|
||||
g, backColor, softWrapText.subSequence(start, softWrapText.length()), position, fontType, defaultBackground, clip
|
||||
);
|
||||
int i = CharArrayUtil.lastIndexOf(softWrapText, "\n", softWrapText.length()) + 1;
|
||||
position.x = getTextSegmentWidth(softWrapText.subSequence(i, softWrapText.length()), 0, fontType, clip);
|
||||
position.x += getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
|
||||
position.y += getLineHeight();
|
||||
|
||||
if (position.y == activeRowY) {
|
||||
// Draw 'active line' background for the soft wrap-introduced virtual space.
|
||||
Color caretRowColor = getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR);
|
||||
drawBackground(g, caretRowColor, position.x, new Point(0, activeRowY), defaultBackground, clip);
|
||||
}
|
||||
|
||||
position.x = drawBackground(
|
||||
g, backColor, getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP), position,
|
||||
defaultBackground, clip
|
||||
);
|
||||
// The code below draws background for soft wrap-introduced virtual space. It's is considered that we don't want
|
||||
// to do that for now. Uncomment if that decision is changed.
|
||||
|
||||
//position.x = drawBackground(
|
||||
// g, backColor, getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED), position,
|
||||
// defaultBackground, clip
|
||||
//);
|
||||
//
|
||||
//CharSequence softWrapText = softWrap.getText();
|
||||
//int start = 0;
|
||||
//for (
|
||||
// int end = CharArrayUtil.shiftForwardUntil(softWrapText, start, "\n");
|
||||
// start < softWrapText.length() && end < softWrapText.length();
|
||||
// end = CharArrayUtil.shiftForwardUntil(softWrapText, start, "\n"))
|
||||
//{
|
||||
// drawBackground(g, backColor, softWrapText.subSequence(start, end), position, fontType, defaultBackground, clip);
|
||||
// start = end + 1;
|
||||
// position.x = 0;
|
||||
// position.y += getLineHeight();
|
||||
//}
|
||||
//
|
||||
//if (start < softWrapText.length()) {
|
||||
// position.x = drawBackground(
|
||||
// g, backColor, softWrapText.subSequence(start, softWrapText.length()), position, fontType, defaultBackground, clip
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//position.x = drawBackground(
|
||||
// g, backColor, getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP), position,
|
||||
// defaultBackground, clip
|
||||
//);
|
||||
}
|
||||
|
||||
private int drawBackground(Graphics g, Color backColor, CharSequence text, Point position, int fontType, Color defaultBackground,
|
||||
@@ -1861,7 +1905,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
// We use AtomicReference here just as a holder for LogicalPosition
|
||||
// The main idea is that there is a possible case that we need to perform painting starting from soft-wrapped logical line.
|
||||
// We may want to skip necessary of visual lines then. Hence, we remember logical position that corresponds to the starting
|
||||
// We may want to skip necessary number of visual lines then. Hence, we remember logical position that corresponds to the starting
|
||||
// visual line in order to use it for further processing. As soon as necessary number of visual lines is skipped, logical
|
||||
// position is expected to be set to null as an indication that no soft wrap-introduced visual lines should be skipped on
|
||||
// current painting iteration.
|
||||
@@ -1975,22 +2019,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
flushCachedChars(g);
|
||||
|
||||
// Perform additional activity if soft wrap is added or removed during repainting.
|
||||
if (mySoftWrapsChanged) {
|
||||
mySoftWrapsChanged = false;
|
||||
validateSize();
|
||||
|
||||
// Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction.
|
||||
repaintToScreenBottom(xyToLogicalPosition(position).line);
|
||||
|
||||
// Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn
|
||||
// in accordance with the newly introduced soft wrap(s).
|
||||
myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y);
|
||||
|
||||
// Ask caret model to update visual caret position.
|
||||
getCaretModel().moveToOffset(getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean paintSelection() {
|
||||
@@ -3536,7 +3564,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
super(orientation);
|
||||
}
|
||||
|
||||
void setPersistendUI(ScrollBarUI ui) {
|
||||
void setPersistentUI(ScrollBarUI ui) {
|
||||
myPersistentUI = ui;
|
||||
setUI(ui);
|
||||
}
|
||||
|
||||
+2
-2
@@ -463,10 +463,10 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
|
||||
|
||||
public void setErrorStripeVisible(boolean val) {
|
||||
if (val) {
|
||||
myEditor.getVerticalScrollBar().setPersistendUI(new MyErrorPanel());
|
||||
myEditor.getVerticalScrollBar().setPersistentUI(new MyErrorPanel());
|
||||
}
|
||||
else {
|
||||
myEditor.getVerticalScrollBar().setPersistendUI(ButtonlessScrollBarUI.createNormal());
|
||||
myEditor.getVerticalScrollBar().setPersistentUI(ButtonlessScrollBarUI.createNormal());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-10
@@ -16,6 +16,8 @@
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.editor.*;
|
||||
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.SoftWrapChangeListener;
|
||||
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
|
||||
@@ -27,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,7 +44,9 @@ import java.util.List;
|
||||
* @author Denis Zhdanov
|
||||
* @since Jun 8, 2010 12:47:32 PM
|
||||
*/
|
||||
public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
public class SoftWrapModelImpl implements SoftWrapModelEx, DocumentListener {
|
||||
|
||||
private final List<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
|
||||
|
||||
private final SoftWrapDataMapper myDataMapper;
|
||||
private final SoftWrapsStorage myStorage;
|
||||
@@ -69,6 +74,14 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
);
|
||||
}
|
||||
|
||||
public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
|
||||
@NotNull DefaultSoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
|
||||
@NotNull SoftWrapDocumentChangeManager documentChangeManager)
|
||||
{
|
||||
this(editor, storage, painter, (SoftWrapApplianceManager)applianceManager, dataMapper, documentChangeManager);
|
||||
myDocumentListeners.add(applianceManager.getDocumentListener());
|
||||
}
|
||||
|
||||
public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
|
||||
@NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
|
||||
@NotNull SoftWrapDocumentChangeManager documentChangeManager)
|
||||
@@ -79,6 +92,8 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
myApplianceManager = applianceManager;
|
||||
myDataMapper = dataMapper;
|
||||
myDocumentChangeManager = documentChangeManager;
|
||||
|
||||
myDocumentListeners.add(myDocumentChangeManager);
|
||||
}
|
||||
|
||||
public boolean isSoftWrappingEnabled() {
|
||||
@@ -349,26 +364,35 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChangeAtCaret() {
|
||||
CaretModel caretModel = myEditor.getCaretModel();
|
||||
VisualPosition visualCaretPosition = caretModel.getVisualPosition();
|
||||
if (!isInsideSoftWrap(visualCaretPosition)) {
|
||||
return;
|
||||
}
|
||||
int offset = caretModel.getOffset();
|
||||
TextChangeImpl softWrap = myStorage.getSoftWrap(offset);
|
||||
if (softWrap == null) {
|
||||
return;
|
||||
if (myDocumentChangeManager.makeHardWrap(caretModel.getOffset())) {
|
||||
// Restore caret position.
|
||||
caretModel.moveToVisualPosition(visualCaretPosition);
|
||||
}
|
||||
|
||||
myDocumentChangeManager.makeHardWrap(softWrap);
|
||||
|
||||
// Restore caret position.
|
||||
caretModel.moveToVisualPosition(visualCaretPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) {
|
||||
return myStorage.addSoftWrapChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
for (DocumentListener listener : myDocumentListeners) {
|
||||
listener.beforeDocumentChange(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
for (DocumentListener listener : myDocumentListeners) {
|
||||
listener.documentChanged(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -45,10 +45,10 @@ import static java.util.Arrays.asList;
|
||||
public class CompositeSoftWrapPainter implements SoftWrapPainter {
|
||||
|
||||
private static final List<Map<SoftWrapDrawingType, Character>> SYMBOLS = asList(
|
||||
asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP),
|
||||
asList('\uE48B', '\uE48C')),
|
||||
asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP),
|
||||
asList('\u2926', '\u2925')),
|
||||
asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP),
|
||||
asList('\uE48B', '\uE48C')),
|
||||
asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP),
|
||||
asList('\u21B2', '\u21B3')),
|
||||
asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP),
|
||||
|
||||
+15
-15
@@ -18,6 +18,7 @@ package com.intellij.openapi.editor.impl.softwrap;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.actions.EditorActionUtil;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
|
||||
@@ -58,7 +59,6 @@ import java.nio.CharBuffer;
|
||||
*/
|
||||
public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager {
|
||||
|
||||
|
||||
/** Enumerates possible type of soft wrap indents to use. */
|
||||
enum IndentType {
|
||||
/** Don't apply special indent to soft-wrapped line at all. */
|
||||
@@ -103,6 +103,18 @@ public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager
|
||||
}
|
||||
|
||||
private final TIntHashSet myProcessedLogicalLines = new TIntHashSet();
|
||||
private final DocumentListener myDocumentListener = new LineOrientedDocumentChangeAdapter() {
|
||||
@Override
|
||||
public void beforeDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
for (int i = startLine; i <= endLine; i++) {
|
||||
myProcessedLogicalLines.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
}
|
||||
};
|
||||
|
||||
private final EditorTextRepresentationHelper myTextRepresentationHelper;
|
||||
private final SoftWrapsStorage myStorage;
|
||||
@@ -120,7 +132,6 @@ public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager
|
||||
myEditor = editor;
|
||||
myPainter = painter;
|
||||
myTextRepresentationHelper = textRepresentationHelper;
|
||||
init(editor.getDocument());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AssignmentToForLoopParameter"})
|
||||
@@ -147,19 +158,8 @@ public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager
|
||||
}
|
||||
}
|
||||
|
||||
private void init(final Document document) {
|
||||
document.addDocumentListener(new LineOrientedDocumentChangeAdapter() {
|
||||
@Override
|
||||
public void beforeDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
for (int i = startLine; i <= endLine; i++) {
|
||||
myProcessedLogicalLines.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
}
|
||||
});
|
||||
public DocumentListener getDocumentListener() {
|
||||
return myDocumentListener;
|
||||
}
|
||||
|
||||
private void dropDataIfNecessary() {
|
||||
|
||||
+58
-61
@@ -17,11 +17,12 @@ package com.intellij.openapi.editor.impl.softwrap;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import gnu.trove.TIntProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -30,9 +31,9 @@ import java.util.List;
|
||||
* @author Denis Zhdanov
|
||||
* @since Jul 7, 2010 2:28:10 PM
|
||||
*/
|
||||
public class SoftWrapDocumentChangeManager {
|
||||
public class SoftWrapDocumentChangeManager implements DocumentListener {
|
||||
|
||||
private final List<DeferredChange> myDeferredChanges = new ArrayList<DeferredChange>();
|
||||
private final TIntHashSet mySoftWrapsToRemoveIndices = new TIntHashSet();
|
||||
|
||||
private final SoftWrapsStorage myStorage;
|
||||
private final Editor myEditor;
|
||||
@@ -40,16 +41,23 @@ public class SoftWrapDocumentChangeManager {
|
||||
public SoftWrapDocumentChangeManager(@NotNull Editor editor, @NotNull SoftWrapsStorage storage) {
|
||||
myStorage = storage;
|
||||
myEditor = editor;
|
||||
init(editor.getDocument());
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@code 'soft wrap' -> 'hard wrap'} conversion for the given soft wrap.
|
||||
* Performs {@code 'soft wrap' -> 'hard wrap'} conversion for soft wrap at the given offset if any.
|
||||
*
|
||||
* @param softWrap soft wrap to make hard wrap
|
||||
* @param offset offset that may point to soft wrap to make hard wrap
|
||||
* @return <code>true</code> if given offset points to soft wrap that was made hard wrap; <code>false</code> otherwise
|
||||
*/
|
||||
public void makeHardWrap(@NotNull TextChangeImpl softWrap) {
|
||||
public boolean makeHardWrap(int offset) {
|
||||
TextChangeImpl softWrap = myStorage.getSoftWrap(offset);
|
||||
if (softWrap == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
myStorage.removeByIndex(myStorage.getSoftWrapIndex(offset));
|
||||
myEditor.getDocument().replaceString(softWrap.getStart(), softWrap.getEnd(), softWrap.getText());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,74 +67,63 @@ public class SoftWrapDocumentChangeManager {
|
||||
* until this method is called.
|
||||
*/
|
||||
public void syncSoftWraps() {
|
||||
Document document = myEditor.getDocument();
|
||||
|
||||
TIntHashSet softWrapsToRemoveIndices = new TIntHashSet();
|
||||
|
||||
// Update offsets for soft wraps that remain after the changed line(s).
|
||||
List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
|
||||
for (DeferredChange change : myDeferredChanges) {
|
||||
if (change.startOffset >= document.getTextLength()) {
|
||||
continue;
|
||||
}
|
||||
int index = myStorage.getSoftWrapIndex(change.startOffset);
|
||||
if (index < 0) {
|
||||
index = -index -1;
|
||||
}
|
||||
for (int i = index; i < softWraps.size(); i++) {
|
||||
TextChangeImpl softWrap = softWraps.get(i);
|
||||
if (softWrapsToRemoveIndices.contains(i)) {
|
||||
continue;
|
||||
}
|
||||
if (softWrap.getStart() < change.endOffset || softWrap.getStart() >= document.getTextLength()) {
|
||||
softWrapsToRemoveIndices.add(i);
|
||||
continue;
|
||||
}
|
||||
softWrap.advance(change.symbolsDifference);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes soft wraps from changed lines.
|
||||
softWrapsToRemoveIndices.forEach(new TIntProcedure() {
|
||||
mySoftWrapsToRemoveIndices.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
public boolean execute(int value) {
|
||||
myStorage.removeByIndex(value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
myDeferredChanges.clear();
|
||||
mySoftWrapsToRemoveIndices.clear();
|
||||
}
|
||||
|
||||
private void init(final Document document) {
|
||||
document.addDocumentListener(new LineOrientedDocumentChangeAdapter() {
|
||||
@Override
|
||||
public void beforeDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
myDeferredChanges.add(
|
||||
new DeferredChange(document.getLineStartOffset(startLine), document.getLineEndOffset(endLine), symbolsDifference)
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
// Mark soft wraps to be removed.
|
||||
Document document = myEditor.getDocument();
|
||||
int startLine = document.getLineNumber(event.getOffset());
|
||||
int startOffset = document.getLineStartOffset(startLine);
|
||||
int endLine = document.getLineNumber(event.getOffset() + event.getOldLength());
|
||||
int endOffset = document.getLineEndOffset(endLine);
|
||||
markSoftWrapsForDeletion(startOffset, endOffset);
|
||||
|
||||
@Override
|
||||
public void afterDocumentChange(int startLine, int endLine, int symbolsDifference) {
|
||||
}
|
||||
});
|
||||
// Update offsets for soft wraps that remain after the changed line(s).
|
||||
applyDocumentChangeDiff(event);
|
||||
}
|
||||
|
||||
private static class DeferredChange {
|
||||
final int startOffset;
|
||||
final int endOffset;
|
||||
final int symbolsDifference;
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
}
|
||||
|
||||
DeferredChange(int startOffset, int endOffset, int symbolsDifference) {
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.symbolsDifference = symbolsDifference;
|
||||
private void markSoftWrapsForDeletion(int startOffset, int endOffset) {
|
||||
List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
|
||||
int index = myStorage.getSoftWrapIndex(startOffset);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
for (int i = index; i < softWraps.size(); i++) {
|
||||
TextChangeImpl softWrap = softWraps.get(i);
|
||||
if (softWrap.getStart() >= endOffset) {
|
||||
break;
|
||||
}
|
||||
mySoftWrapsToRemoveIndices.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDocumentChangeDiff(DocumentEvent event) {
|
||||
List<TextChangeImpl> softWraps = myStorage.getSoftWraps();
|
||||
|
||||
// We use 'offset + 1' here because soft wrap is represented before the document symbol at the same offset, hence, document
|
||||
// modification at particular offset doesn't affect soft wrap registered for the same offset.
|
||||
int index = myStorage.getSoftWrapIndex(event.getOffset() + 1);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return startOffset + "-" + endOffset + ": " + symbolsDifference;
|
||||
int diff = event.getNewLength() - event.getOldLength();
|
||||
for (int i = index; i < softWraps.size(); i++) {
|
||||
TextChangeImpl softWrap = softWraps.get(i);
|
||||
softWrap.advance(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.intellij.openapi.editor.CaretModel;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.SoftWrapModel;
|
||||
import com.intellij.openapi.editor.TextChange;
|
||||
|
||||
/**
|
||||
* Holds utility methods for soft wraps-related processing.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Aug 11, 2010 11:03:43 AM
|
||||
*/
|
||||
public class SoftWrapHelper {
|
||||
|
||||
private SoftWrapHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Every soft wrap implies that multiple visual positions correspond to the same document offset. We can classify
|
||||
* such positions by the following criteria:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>positions from visual line with soft wrap start;</li>
|
||||
* <li>positions from visual line with soft wrap end;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
* <p/>
|
||||
* This method allows to answer if caret offset of the given editor points to soft wrap and visual caret position
|
||||
* belongs to the visual line where soft wrap start is located.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @return <code>true</code> if caret offset of the given editor points to visual position that belongs to
|
||||
* visual line before the soft wrap
|
||||
*/
|
||||
public static boolean isCaretBeforeSoftWrap(Editor editor) {
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
|
||||
int offset = caretModel.getOffset();
|
||||
TextChange softWrap = softWrapModel.getSoftWrap(offset);
|
||||
if (softWrap == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.offsetToVisualPosition(offset).line != caretModel.getVisualPosition().line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every soft wrap implies that multiple visual positions correspond to the same document offset. We can classify
|
||||
* such positions by the following criteria:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>positions from visual line with soft wrap start;</li>
|
||||
* <li>positions from visual line with soft wrap end;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
* <p/>
|
||||
* This method allows to answer if caret offset of the given editor points to soft wrap and visual caret position
|
||||
* belongs to the visual line where soft wrap end is located.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @return <code>true</code> if caret offset of the given editor points to visual position that belongs to
|
||||
* visual line where soft wrap end is located
|
||||
*/
|
||||
public static boolean isCaretAfterSoftWrap(Editor editor) {
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
|
||||
int offset = caretModel.getOffset();
|
||||
TextChange softWrap = softWrapModel.getSoftWrap(offset);
|
||||
if (softWrap == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return editor.offsetToVisualPosition(offset).line == caretModel.getVisualPosition().line;
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -134,8 +134,7 @@ public class TextChangeImpl implements TextChange {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link TextChange} on the basis of the current object with given offset applied to its {@link #getStart() start}
|
||||
* and {@link #getEnd() end} properties.
|
||||
* Applies given offset applied to the {@link #getStart() start} and {@link #getEnd() end} properties of current text change object.
|
||||
*
|
||||
* @param offset offset to apply to the current change object
|
||||
* @return text change that is built on the basis of the current object that with {@link #getStart() start}
|
||||
|
||||
+7
-2
@@ -44,14 +44,19 @@ public class TextComponentCaretModel implements CaretModel {
|
||||
}
|
||||
|
||||
public void moveToLogicalPosition(final LogicalPosition pos) {
|
||||
moveToOffset(myEditor.logicalPositionToOffset(pos));
|
||||
moveToOffset(myEditor.logicalPositionToOffset(pos), false);
|
||||
}
|
||||
|
||||
public void moveToVisualPosition(final VisualPosition pos) {
|
||||
moveToLogicalPosition(myEditor.visualToLogicalPosition(pos));
|
||||
}
|
||||
|
||||
public void moveToOffset(final int offset) {
|
||||
@Override
|
||||
public void moveToOffset(int offset) {
|
||||
moveToOffset(offset, false);
|
||||
}
|
||||
|
||||
public void moveToOffset(final int offset, boolean locateBeforeSoftWrap) {
|
||||
myTextComponent.setCaretPosition(Math.min(offset, myTextComponent.getText().length()));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user