IDEABKL-5810 support for soft wraps in new editor code
authorDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Mon, 3 Aug 2015 07:52:21 +0000 (10:52 +0300)
committerDmitry Batrak <Dmitry.Batrak@jetbrains.com>
Mon, 3 Aug 2015 07:52:21 +0000 (10:52 +0300)
35 files changed:
platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java
platform/editor-ui-api/src/com/intellij/openapi/editor/Editor.java
platform/editor-ui-api/src/com/intellij/openapi/editor/LogicalPosition.java
platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java
platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/LazyEditor.java
platform/lang-impl/src/com/intellij/injected/editor/EditorWindowImpl.java
platform/lang-impl/src/com/intellij/injected/editor/InjectedCaret.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/MoveCaretLeftOrRightHandler.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/MoveCaretLeftOrRightWithSelectionHandler.java
platform/platform-impl/src/com/intellij/openapi/editor/actions/NextPrevWordHandler.java
platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapHelper.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorCoordinateMapper.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorSizeManager.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorView.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/IterationState.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineFragment.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/LineLayout.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TabFragment.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/TextFragment.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/view/VisualLineFragmentsIterator.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentCaret.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentEditor.java
platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/AbstractEditorTest.java
platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java
platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorRtlTest.java
platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java
platform/util/resources/misc/registry.properties
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java

index f467e246395b839ef097ad3b51835d49fbcfb4c5..87d7ed1c38996664c69c85fd07cb42aedfc18e18 100644 (file)
@@ -304,9 +304,11 @@ public interface Caret extends UserDataHolderEx, Disposable {
   boolean isAtRtlLocation();
 
   /**
-   * Returns <code>true</code> if caret is located at a boundary between LTR and RTL text fragments. Caret can located at any side of the
-   * boundary, exact location can be determined from directionality flags of caret's logical and visual position 
+   * Returns <code>true</code> if caret is located at a boundary between different runs of bidirectional text. 
+   * This means that text fragments at different sides of the boundary are non-adjacent in logical order.
+   * Caret can located at any side of the boundary, 
+   * exact location can be determined from directionality flags of caret's logical and visual position 
    * ({@link LogicalPosition#leansForward} and {@link VisualPosition#leansRight}).
    */
-  boolean isAtDirectionBoundary();
+  boolean isAtBidiRunBoundary();
 }
index b952c217521c1fbbe453a535174192f1a07e0f8c..ba116bc8e4d6da9b6054e90a65cdf44c369c9960 100644 (file)
@@ -229,6 +229,8 @@ public interface Editor extends UserDataHolder {
    * <p>
    * It's assumed that original position is associated with character immediately preceding given offset, 
    * {@link VisualPosition#leansRight leansRight} value for visual position will be determined correspondingly.
+   * <p>
+   * If there's a soft wrap at given offset, visual position on a line following the wrap will be returned.
    *
    * @param offset the offset in the document.
    * @return the corresponding visual position.
@@ -238,18 +240,17 @@ public interface Editor extends UserDataHolder {
 
   /**
    * Maps an offset in the document to a visual position.
-   * <p>
-   * It's assumed that original position is associated with character immediately preceding given offset, 
-   * {@link VisualPosition#leansRight leansRight} value for visual position will be determined correspondingly.
    *
    * @param offset the offset in the document.
    * @param leanForward if <code>true</code>, original position is associated with character after given offset, if <code>false</code> - 
    *                    with character before given offset. This can make a difference in bidirectional text (see {@link LogicalPosition},
    *                    {@link VisualPosition})
+   * @param beforeSoftWrap if <code>true</code>, visual position at line preceeding the wrap will be returned, otherwise - visual position
+   *                       at line following the wrap.
    * @return the corresponding visual position.
    */
   @NotNull
-  VisualPosition offsetToVisualPosition(int offset, boolean leanForward);
+  VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap);
 
   /**
    * Maps the pixel coordinates in the editor to a logical position.
index dd69cc3edf78302ac6773cc3be95b1cc3852da9f..a154351b0175304bdbedc175f559c78eac3e00bb 100644 (file)
@@ -66,6 +66,9 @@ public class LogicalPosition implements Comparable<LogicalPosition> {
   /**
    * Number of virtual soft wrap introduced lines on a current logical line before the visual position that corresponds
    * to the current logical position.
+   * <p>
+   * Instead of directly using this value, EditorUtil.getSoftWrapCountAfterLogicalLineStart(LogicalPosition) method can be used,
+   * it will work regardless of whether current <code>LogicalPosition</code> instance is {@link #visualPositionAware}.
    *
    * @see #visualPositionAware
    */
index 6ef0d786102dc3cbb23287043dad86290b2b8de0..27d5bc8c8d3fbf083a5e0142bee0f59232678f3a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 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.
@@ -30,6 +30,7 @@ import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.actionSystem.IdeActions;
 import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
 import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.ex.util.EditorUtil;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.impl.AbstractFileType;
@@ -264,7 +265,7 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand
             LogicalPosition position = caret.getLogicalPosition();
             if (position.line < document.getLineCount() - 1) {
               int verticalShift = 1 + block.editor.getSoftWrapModel().getSoftWrapsForLine(position.line).size()
-                                  - position.softWrapLinesOnCurrentLogicalLine;
+                                  - EditorUtil.getSoftWrapCountAfterLineStart(block.editor, position);
               caret.moveCaretRelatively(0, verticalShift, false, true);
             }
             break;
index 8b249eb78eae0a5154a224dfb904ebaaf9d13a8d..d22b20751de18be1d8489cf8ccae32f959b63e8f 100644 (file)
@@ -184,8 +184,8 @@ class LazyEditor extends UserDataHolderBase implements Editor {
 
   @Override
   @NotNull
-  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward) {
-    return getEditor().offsetToVisualPosition(offset, leanForward);
+  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap) {
+    return getEditor().offsetToVisualPosition(offset, leanForward, beforeSoftWrap);
   }
 
   @Override
index 97e89ea9c5bc30992d729780cde53e37cee77374..8a18781194ab708ee09d75d1e6994fa87f76face 100644 (file)
@@ -374,7 +374,7 @@ public class EditorWindowImpl extends UserDataHolderBase implements EditorWindow
 
   @Override
   @NotNull
-  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward) {
+  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap) {
     return logicalToVisualPosition(offsetToLogicalPosition(offset).leanForward(leanForward));
   }
 
index 468745cabb59aa534707a973e0e9f3c66a6a6ea2..50c32fc467bd42f4a12d482c41b0af48d5bb0117 100644 (file)
@@ -244,7 +244,7 @@ public class InjectedCaret implements Caret {
   }
 
   @Override
-  public boolean isAtDirectionBoundary() {
-    return myDelegate.isAtDirectionBoundary();
+  public boolean isAtBidiRunBoundary() {
+    return myDelegate.isAtBidiRunBoundary();
   }
 }
index a180c2753d1e72b2cb2023e88dd107ba56b6a18a..1cce0b895469704a121335b8ea982b56d9c0f639 100644 (file)
@@ -363,8 +363,9 @@ public class EditorActionUtil {
     else {
       LogicalPosition logLineEndLog = editor.offsetToLogicalPosition(document.getLineEndOffset(logLineToUse));
       VisualPosition logLineEndVis = editor.logicalToVisualPosition(logLineEndLog);
-      if (logLineEndLog.softWrapLinesOnCurrentLogicalLine > 0) {
-        moveCaretToStartOfSoftWrappedLine(editor, logLineEndVis, logLineEndLog.softWrapLinesOnCurrentLogicalLine);
+      int softWrapCount = EditorUtil.getSoftWrapCountAfterLineStart(editor, logLineEndLog);
+      if (softWrapCount > 0) {
+        moveCaretToStartOfSoftWrappedLine(editor, logLineEndVis, softWrapCount);
       }
       else {
         int line = logLineEndVis.line;
index 4f30cb05514a2ba26950307c225e2697621abb37..09b35cd748bd488bf24e209a8b72e43a9e32f72e 100644 (file)
@@ -77,7 +77,7 @@ class MoveCaretLeftOrRightHandler extends EditorActionHandler {
       }
     }
     VisualPosition currentPosition = caret.getVisualPosition();
-    if (caret.isAtDirectionBoundary() && (myDirection == Direction.RIGHT ^ currentPosition.leansRight)) {
+    if (caret.isAtBidiRunBoundary() && (myDirection == Direction.RIGHT ^ currentPosition.leansRight)) {
       caret.moveToVisualPosition(currentPosition.leanRight(!currentPosition.leansRight));
     }
     else {
index 0b710d8a084f8d14c62fe833c6f1fca3f786b9f8..09fc16c66b45c23426a28726a00bba703a2e2d5b 100644 (file)
@@ -34,7 +34,7 @@ class MoveCaretLeftOrRightWithSelectionHandler extends EditorActionHandler {
   protected void doExecute(Editor editor, @Nullable Caret caret, DataContext dataContext) {
     assert caret != null;
     VisualPosition currentPosition = caret.getVisualPosition();
-    if (caret.isAtDirectionBoundary() && (myMoveRight ^ currentPosition.leansRight)) {
+    if (caret.isAtBidiRunBoundary() && (myMoveRight ^ currentPosition.leansRight)) {
       int selectionStartToUse = caret.getLeadSelectionOffset();
       VisualPosition selectionStartPositionToUse = caret.getLeadSelectionPosition();
       caret.moveToVisualPosition(currentPosition.leanRight(!currentPosition.leansRight));
index 34484338a55322dca4cf1491fb833bd010e79e0a..c4fb7417d770b1e015a19cb4ddf30031a173e95a 100644 (file)
@@ -38,7 +38,7 @@ class NextPrevWordHandler extends EditorActionHandler {
   protected void doExecute(Editor editor, @Nullable Caret caret, DataContext dataContext) {
     assert caret != null;
     VisualPosition currentPosition = caret.getVisualPosition();
-    if (caret.isAtDirectionBoundary() && (myNext ^ currentPosition.leansRight)) {
+    if (caret.isAtBidiRunBoundary() && (myNext ^ currentPosition.leansRight)) {
       int selectionStartOffset = caret.getLeadSelectionOffset();
       VisualPosition selectionStartPosition = caret.getLeadSelectionPosition();
       caret.moveToVisualPosition(currentPosition.leanRight(!currentPosition.leansRight));
index 948cb144493943a183355aa525956be70cbd88ae..9da024909cab95af304eb47cf07c214ad943a28e 100644 (file)
@@ -62,6 +62,11 @@ public final class EditorUtil {
   }
 
   public static int getLastVisualLineColumnNumber(@NotNull Editor editor, final int line) {
+    if (editor instanceof EditorImpl && ((EditorImpl)editor).myUseNewRendering) {
+      LogicalPosition lineEndPosition = editor.visualToLogicalPosition(new VisualPosition(line, Integer.MAX_VALUE));
+      int lineEndOffset = editor.logicalPositionToOffset(lineEndPosition);
+      return editor.offsetToVisualPosition(lineEndOffset, true, true).column;
+    }
     Document document = editor.getDocument();
     int lastLine = document.getLineCount() - 1;
     if (lastLine < 0) {
@@ -81,7 +86,7 @@ public final class EditorUtil {
 
     int resultLogLine = Math.min(lastLogLine, lastLine);
     VisualPosition resVisStart = editor.offsetToVisualPosition(document.getLineStartOffset(resultLogLine));
-    VisualPosition resVisEnd = editor.offsetToVisualPosition(document.getLineEndOffset(resultLogLine), true);
+    VisualPosition resVisEnd = editor.offsetToVisualPosition(document.getLineEndOffset(resultLogLine));
 
     // Target logical line is not soft wrap affected.
     if (resVisStart.line == resVisEnd.line) {
@@ -909,6 +914,21 @@ public final class EditorUtil {
                ? UISettings.getInstance().PRESENTATION_MODE_FONT_SIZE - 4 : scheme.getEditorFontSize();
     return new Font(scheme.getEditorFontName(), Font.PLAIN, size);
   }
+
+  /**
+   * Number of virtual soft wrap introduced lines on a current logical line before the visual position that corresponds
+   * to the current logical position.
+   *
+   * @see LogicalPosition#softWrapLinesOnCurrentLogicalLine
+   */
+  public static int getSoftWrapCountAfterLineStart(@NotNull Editor editor, @NotNull LogicalPosition position) {
+    if (position.visualPositionAware) {
+      return position.softWrapLinesOnCurrentLogicalLine;
+    }
+    int startOffset = editor.getDocument().getLineStartOffset(position.line);
+    int endOffset = editor.logicalPositionToOffset(position);
+    return editor.getSoftWrapModel().getSoftWrapsForRange(startOffset, endOffset).size();
+  }
 }
 
 
index be50b060ed4727fdf60577a3138136bbf9e2a38d..5b3c19b1c081535197afe6ed83075d060aedfce4 100644 (file)
@@ -310,7 +310,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
           LogicalPosition log = myEditor.visualToLogicalPosition(new VisualPosition(newLineNumber, newColumnNumber, newLeansRight));
           int offset = myEditor.logicalPositionToOffset(log);
           if (offset >= document.getTextLength() && (!myEditor.myUseNewRendering || columnShift == 0)) {
-            int lastOffsetColumn = myEditor.offsetToVisualPosition(document.getTextLength(), true).column;
+            int lastOffsetColumn = myEditor.offsetToVisualPosition(document.getTextLength(), true, false).column;
             // We want to move caret to the last column if if it's located at the last line and 'Down' is pressed.
             if (lastOffsetColumn > newColumnNumber) {
               newColumnNumber = lastOffsetColumn;
@@ -324,7 +324,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
             if (offset >= 0 && offset < document.getTextLength()) {
               if (text.charAt(offset) == '\t' && (columnShift <= 0 || offset == myOffset)) {
                 if (columnShift <= 0) {
-                  newColumnNumber = myEditor.offsetToVisualPosition(offset, true).column;
+                  newColumnNumber = myEditor.offsetToVisualPosition(offset, true, false).column;
                 }
                 else {
                   SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset + 1);
@@ -648,14 +648,14 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
 
     EditorSettings editorSettings = myEditor.getSettings();
 
-    if (!editorSettings.isVirtualSpace() && line <= lastLine) {
+    if (!editorSettings.isVirtualSpace()) {
       int lineEndColumn = EditorUtil.getLastVisualLineColumnNumber(myEditor, line);
-      if (column > lineEndColumn) {
+      if (column > lineEndColumn && (!myEditor.myUseNewRendering || !myEditor.getSoftWrapModel().isInsideSoftWrap(pos))) {
         column = lineEndColumn;
         leanRight = true;
       }
 
-      if (column < 0 && line > 0) {
+      if (!myEditor.myUseNewRendering && column < 0 && line > 0) {
         line--;
         column = EditorUtil.getLastVisualLineColumnNumber(myEditor, line);
       }
@@ -1065,14 +1065,14 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
     if (hasSelection() && mySelectionMarker != null) {
       position = getRangeMarkerStartPosition();
       if (position == null) {
-        VisualPosition startPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getStartOffset(), true);
-        VisualPosition endPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getEndOffset());
+        VisualPosition startPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getStartOffset(), true, false);
+        VisualPosition endPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getEndOffset(), false, true);
         position = startPosition.after(endPosition) ? endPosition : startPosition;
       }
     }
     else {
       position = isVirtualSelectionEnabled() ? getVisualPosition() : 
-                 myEditor.offsetToVisualPosition(getOffset(), myLogicalCaret.leansForward);
+                 myEditor.offsetToVisualPosition(getOffset(), myLogicalCaret.leansForward, false);
     }
     if (hasVirtualSelection()) {
       position = new VisualPosition(position.line, position.column + myStartVirtualOffset);
@@ -1100,14 +1100,14 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
     if (hasSelection() && mySelectionMarker != null) {
       position = getRangeMarkerEndPosition();
       if (position == null) {
-        VisualPosition startPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getStartOffset(), true);
-        VisualPosition endPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getEndOffset());
+        VisualPosition startPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getStartOffset(), true, false);
+        VisualPosition endPosition = myEditor.offsetToVisualPosition(mySelectionMarker.getEndOffset(), false, true);
         position = startPosition.after(endPosition) ? startPosition : endPosition;
       }
     }
     else {
       position = isVirtualSelectionEnabled() ? getVisualPosition() : 
-                 myEditor.offsetToVisualPosition(getOffset(), myLogicalCaret.leansForward);
+                 myEditor.offsetToVisualPosition(getOffset(), myLogicalCaret.leansForward, false);
     }
     if (hasVirtualSelection()) {
       position = new VisualPosition(position.line, position.column + myEndVirtualOffset);
@@ -1130,7 +1130,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
 
   @Override
   public void setSelection(int startOffset, int endOffset, boolean updateSystemSelection) {
-    doSetSelection(myEditor.offsetToVisualPosition(startOffset, true), startOffset, myEditor.offsetToVisualPosition(endOffset), endOffset, 
+    doSetSelection(myEditor.offsetToVisualPosition(startOffset, true, false), startOffset, 
+                   myEditor.offsetToVisualPosition(endOffset, false, true), endOffset, 
                    false, updateSystemSelection);
   }
 
@@ -1141,7 +1142,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
       startPosition = getLeadSelectionPosition();
     }
     else {
-      startPosition = myEditor.offsetToVisualPosition(startOffset, true);
+      startPosition = myEditor.offsetToVisualPosition(startOffset, true, false);
     }
     setSelection(startPosition, startOffset, endPosition, endOffset);
   }
@@ -1154,8 +1155,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
   @Override
   public void setSelection(@Nullable VisualPosition startPosition, int startOffset, @Nullable VisualPosition endPosition, int endOffset, 
                            boolean updateSystemSelection) {
-    VisualPosition startPositionToUse = startPosition == null ? myEditor.offsetToVisualPosition(startOffset, true) : startPosition;
-    VisualPosition endPositionToUse = endPosition == null ? myEditor.offsetToVisualPosition(endOffset) : endPosition;
+    VisualPosition startPositionToUse = startPosition == null ? myEditor.offsetToVisualPosition(startOffset, true, false) : startPosition;
+    VisualPosition endPositionToUse = endPosition == null ? myEditor.offsetToVisualPosition(endOffset, false, true) : endPosition;
     doSetSelection(startPositionToUse, startOffset, endPositionToUse, endOffset, true, updateSystemSelection);
   }
 
@@ -1473,12 +1474,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
 
   @Override
   public boolean isAtRtlLocation() {
-    return myEditor.myUseNewRendering && myEditor.myView.isRtlLocation(myOffset, myLogicalCaret.leansForward);
+    return myEditor.myUseNewRendering && myEditor.myView.isRtlLocation(myVisibleCaret);
   }
 
   @Override
-  public boolean isAtDirectionBoundary() {
-    return myEditor.myUseNewRendering && myEditor.myView.isDirectionBoundary(myVisibleCaret);
+  public boolean isAtBidiRunBoundary() {
+    return myEditor.myUseNewRendering && myEditor.myView.isAtBidiRunBoundary(myVisibleCaret);
   }
 
   /**
@@ -1524,9 +1525,9 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
 
   private void invalidateRangeMarkerVisualPositions(RangeMarker marker) {
     SoftWrapModelImpl model = myEditor.getSoftWrapModel();
-    if (!myEditor.offsetToVisualPosition(marker.getStartOffset(), true).equals(myRangeMarkerStartPosition) &&
+    if (!myEditor.offsetToVisualPosition(marker.getStartOffset(), true, false).equals(myRangeMarkerStartPosition) &&
         model.getSoftWrap(marker.getStartOffset()) == null ||
-        !myEditor.offsetToVisualPosition(marker.getEndOffset()).equals(myRangeMarkerEndPosition)
+        !myEditor.offsetToVisualPosition(marker.getEndOffset(), false, true).equals(myRangeMarkerEndPosition)
         && model.getSoftWrap(marker.getEndOffset()) == null) {
       myRangeMarkerStartPosition = null;
       myRangeMarkerEndPosition = null;
index 40e8b6452dd747d44144f1b82d2b2b29c645959c..a20dd6cbf7d589def02892e4e3eae2514c4eb598 100644 (file)
@@ -281,8 +281,8 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
 
   private void drawEditorBackgroundForRange(Graphics g, int startOffset, int endOffset, TextAttributes attributes,
                                             Color defaultBackgroundColor, Color defaultForegroundColor, int startX) {
-    VisualPosition visualStart = myEditor.offsetToVisualPosition(startOffset);
-    VisualPosition visualEnd   = myEditor.offsetToVisualPosition(endOffset);
+    VisualPosition visualStart = myEditor.offsetToVisualPosition(startOffset, true, false);
+    VisualPosition visualEnd   = myEditor.offsetToVisualPosition(endOffset, false, true);
     for (int line = visualStart.getLine(); line <= visualEnd.getLine(); line++) {
       if (line == visualStart.getLine()) {
         if (visualStart.getColumn() == 0) {
@@ -491,7 +491,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
 
     for (int i = startLineNumber; i < endLineNumber; i++) {
       LogicalPosition logicalPosition = myEditor.visualToLogicalPosition(new VisualPosition(i, 0));
-      if (logicalPosition.softWrapLinesOnCurrentLogicalLine > 0) {
+      if (EditorUtil.getSoftWrapCountAfterLineStart(myEditor, logicalPosition) > 0) {
         continue;
       }
       int logLine = convertor.execute(logicalPosition.line);
index 86dcb5ee4b9aede38bbc79658f8877ce082a6fb8..60f8f8ff752bdde148ff0469c9e1fc5e86f2771c 100644 (file)
@@ -527,8 +527,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     myVerticalScrollBar.setOpaque(false);
     myPanel = new JPanel();
 
-    myView = myUseNewRendering ? new EditorView(this) : null;
-
     UIUtil.putClientProperty(
       myPanel, JBSwingUtilities.NOT_IN_HIERARCHY_COMPONENTS, new Iterable<JComponent>() {
         @NotNull
@@ -546,6 +544,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     myGutterComponent = new EditorGutterComponentImpl(this);
     initComponent();
     myScrollingModel = new ScrollingModelImpl(this);
+
+    if (myUseNewRendering) {
+      myView = new EditorView(this);
+      myView.reinitSettings();
+    }
+    else {
+      myView = null;
+    }
+
     if (UISettings.getInstance().PRESENTATION_MODE) {
       setFontSize(UISettings.getInstance().PRESENTATION_MODE_FONT_SIZE);
     }
@@ -578,7 +585,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
   }
 
   private boolean shouldSoftWrapsBeForced() {
-    if (myUseNewRendering || mySettings.isUseSoftWraps() ||
+    if (mySettings.isUseSoftWraps() ||
         // Disable checking for files in intermediate states - e.g. for files during refactoring.
         myProject != null && PsiDocumentManager.getInstance(myProject).isDocumentBlockedByPsi(myDocument)) {
       return false;
@@ -1023,7 +1030,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
           public void dragOver(@NotNull DropTargetDragEvent e) {
             Point location = e.getLocation();
 
-            getCaretModel().moveToLogicalPosition(getLogicalPositionForScreenPos(location.x, location.y, true));
+            if (myUseNewRendering) {
+              getCaretModel().moveToVisualPosition(getTargetPosition(location.x, location.y, true));
+            }
+            else {
+              getCaretModel().moveToLogicalPosition(getLogicalPositionForScreenPos(location.x, location.y, true));
+            }
             getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
           }
         });
@@ -1423,20 +1435,20 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
   @NotNull
   public Point offsetToXY(int offset, boolean leanTowardsLargerOffsets) {
-    return myUseNewRendering ? myView.offsetToXY(offset, leanTowardsLargerOffsets) : 
-           visualPositionToXY(offsetToVisualPosition(offset, leanTowardsLargerOffsets));
+    return myUseNewRendering ? myView.offsetToXY(offset, leanTowardsLargerOffsets, false) : 
+           visualPositionToXY(offsetToVisualPosition(offset, leanTowardsLargerOffsets, false));
   }
   
   @Override
   @NotNull
   public VisualPosition offsetToVisualPosition(int offset) {
-    return offsetToVisualPosition(offset, false);
+    return offsetToVisualPosition(offset, false, false);
   }
 
   @Override
   @NotNull
-  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward) {
-    if (myUseNewRendering) return myView.offsetToVisualPosition(offset, leanForward);
+  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap) {
+    if (myUseNewRendering) return myView.offsetToVisualPosition(offset, leanForward, beforeSoftWrap);
     return logicalToVisualPosition(offsetToLogicalPosition(offset));
   }
 
@@ -1467,7 +1479,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
 
   // optimization: do not do column calculations here since we are interested in line number only
   public int offsetToVisualLine(int offset) {
-    if (myUseNewRendering) return myView.offsetToVisualLine(offset);
+    if (myUseNewRendering) return myView.offsetToVisualLine(offset, false);
     int textLength = getDocument().getTextLength();
     if (offset >= textLength) {
       return Math.max(0, getVisibleLineCount() - 1); // lines are 0 based
@@ -3892,7 +3904,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
   @Override
   @NotNull
   public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) {
-    if (myUseNewRendering) return myView.logicalToVisualPosition(logicalPos);
+    if (myUseNewRendering) return myView.logicalToVisualPosition(logicalPos, false);
     return doLogicalToVisualPosition(logicalPos, softWrapAware,0);
   }
 
@@ -4135,7 +4147,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         newY = visibleLineToY(getVisibleLogicalLinesCount());
       }
       if (newY >= y) {
-        LogMessageEx.error(LOG, "cycled moveCaretToScreenPos() detected", 
+        LogMessageEx.error(LOG, "cycled moveCaretToScreenPos() detected",
                            String.format("x=%d, y=%d\nvisibleLineCount=%d, newY=%d\nstate=%s", x, y, visibleLineCount, newY, dumpState()));
         throw new IllegalStateException("cycled moveCaretToScreenPos() detected");
       }
@@ -4164,14 +4176,39 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         }
       }
     }
-    return pos.visualPositionAware ? 
+    return pos.visualPositionAware ?
            new LogicalPosition(
              line, column, softWrapLinesBeforeTargetLogicalLine, softWrapLinesOnTargetLogicalLine, softWrapColumns,
              pos.foldedLines, pos.foldingColumnDiff, leansForward, leansRight
-           ) : 
+           ) :
            new LogicalPosition(line, column, leansForward);
   }
 
+  private VisualPosition getTargetPosition(int x, int y, boolean trimToLineWidth) {
+    if (myDocument.getLineCount() == 0) {
+      return new VisualPosition(0, 0);
+    }
+    if (x < 0) {
+      x = 0;
+    }
+    if (y < 0) {
+      y = 0;
+    }
+    int visualLineCount = getVisibleLineCount();
+    if (yPositionToVisibleLine(y) >= visualLineCount) {
+      y = visibleLineToY(Math.max(0, visualLineCount - 1));
+    }
+    VisualPosition visualPosition = xyToVisualPosition(new Point(x, y));
+    if (trimToLineWidth && !mySettings.isVirtualSpace()) {
+      LogicalPosition logicalPosition = visualToLogicalPosition(visualPosition);
+      LogicalPosition lineEndPosition = offsetToLogicalPosition(myDocument.getLineEndOffset(logicalPosition.line));
+      if (logicalPosition.column > lineEndPosition.column) {
+        visualPosition = logicalToVisualPosition(lineEndPosition.leanForward(true));
+      }
+    }
+    return visualPosition;
+  }
+
   private boolean checkIgnore(@NotNull MouseEvent e, boolean isFinalCheck) {
     if (!myIgnoreMouseEventsConsecutiveToInitial) {
       myInitialMouseEvent = null;
@@ -4363,18 +4400,25 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       VisualPosition oldVisLeadSelectionStart = leadCaret.getLeadSelectionPosition();
       int oldCaretOffset = getCaretModel().getOffset();
       boolean multiCaretSelection = columnSelectionDrag || toggleCaretEvent;
-      LogicalPosition newLogicalCaret = getLogicalPositionForScreenPos(x, y, !multiCaretSelection);
+      VisualPosition newVisualCaret = myUseNewRendering ? getTargetPosition(x, y, !multiCaretSelection) : null;
+      LogicalPosition newLogicalCaret = myUseNewRendering ? visualToLogicalPosition(newVisualCaret) : 
+                                        getLogicalPositionForScreenPos(x, y, !multiCaretSelection);
       if (multiCaretSelection) {
         myMultiSelectionInProgress = true;
         myRectangularSelectionInProgress = columnSelectionDrag || addRectangularSelectionEvent;
         myTargetMultiSelectionPosition = xyToVisualPosition(new Point(Math.max(x, 0), Math.max(y, 0)));
       }
       else {
-        getCaretModel().moveToLogicalPosition(newLogicalCaret);
+        if (myUseNewRendering) {
+          getCaretModel().moveToVisualPosition(newVisualCaret);
+        }
+        else {
+          getCaretModel().moveToLogicalPosition(newLogicalCaret);
+        }
       }
 
       int newCaretOffset = getCaretModel().getOffset();
-      VisualPosition newVisualCaret = getCaretModel().getVisualPosition();
+      newVisualCaret = getCaretModel().getVisualPosition();
       int caretShift = newCaretOffset - mySavedSelectionStart;
 
       if (myMousePressedEvent != null && getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.EDITING_AREA &&
@@ -5896,9 +5940,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
       if (e.getSource() != myGutterComponent ||
           eventArea != EditorMouseEventArea.LINE_MARKERS_AREA && eventArea != EditorMouseEventArea.ANNOTATIONS_AREA)
       {
-        LogicalPosition pos = getLogicalPositionForScreenPos(x, y, true);
+        VisualPosition visualPosition = myUseNewRendering ? getTargetPosition(x, y, true) : null;
+        LogicalPosition pos = myUseNewRendering ? visualToLogicalPosition(visualPosition) : getLogicalPositionForScreenPos(x, y, true);
         if (toggleCaret) {
-          VisualPosition visualPosition = logicalToVisualPosition(pos);
+          if (!myUseNewRendering) {
+            visualPosition = logicalToVisualPosition(pos);
+          }
           Caret caret = getCaretModel().getCaretAt(visualPosition);
           if (e.getClickCount() == 1) {
             if (caret == null) {
@@ -5909,7 +5956,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
             }
           }
           else if (e.getClickCount() == 3 && lastPressCreatedCaret) {
-            getCaretModel().moveToLogicalPosition(pos);
+            if (myUseNewRendering) {
+              getCaretModel().moveToVisualPosition(visualPosition);
+            }
+            else {
+              getCaretModel().moveToLogicalPosition(pos);
+            }
           }
         }
         else if (myCaretModel.supportsMultipleCarets() && e.getSource() != myGutterComponent && isCreateRectangularSelectionEvent(e)) {
@@ -5917,7 +5969,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
         }
         else {
           getCaretModel().removeSecondaryCarets();
-          getCaretModel().moveToLogicalPosition(pos);
+          if (myUseNewRendering) {
+            getCaretModel().moveToVisualPosition(visualPosition);
+          }
+          else {
+            getCaretModel().moveToLogicalPosition(pos);
+          }
         }
       }
 
@@ -7041,7 +7098,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
                           Color color,
                           @NotNull FontInfo fontInfo)
     {
-      drawCharsCached(g, new CharArrayCharSequence(data), start, end, x, y, fontInfo, color, false);
+      if (myUseNewRendering) {
+        myView.drawChars(g, data, start, end, x, y, color, fontInfo);
+      }
+      else {
+        drawCharsCached(g, new CharArrayCharSequence(data), start, end, x, y, fontInfo, color, false);
+      }
     }
   }
 
index 2a51591b61750b52b75d9de5062608e9634be8ed..82f9dd86e8105dd0fe701bb1c6ce0f86ec826d76 100644 (file)
@@ -158,7 +158,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedInternalDo
   }
 
   private boolean areSoftWrapsEnabledInEditor() {
-    return myEditor.getSettings().isUseSoftWraps() && !myEditor.myUseNewRendering 
+    return myEditor.getSettings().isUseSoftWraps()
            && (!(myEditor.getDocument() instanceof DocumentImpl) || !((DocumentImpl)myEditor.getDocument()).acceptsSlashR());
   }
 
@@ -235,6 +235,9 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedInternalDo
 
   @Override
   public int getSoftWrapIndex(int offset) {
+    if (myEditor.myUseNewRendering && !isSoftWrappingEnabled()) {
+      return -1;
+    }
     return myStorage.getSoftWrapIndex(offset);
   }
 
@@ -432,7 +435,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedInternalDo
    *
    * @return      <code>true</code> if soft wraps-aware processing should be used; <code>false</code> otherwise
    */
-  private boolean prepareToMapping() {
+  public boolean prepareToMapping() {
     if (myUpdateInProgress || myBulkUpdateInProgress ||
         myActive > 0 || !isSoftWrappingEnabled() || myEditor.getDocument().getTextLength() <= 0) {
       return false;
@@ -498,22 +501,29 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedInternalDo
       return false;
     }
 
-    VisualPosition visualBeforeSoftWrap = myEditor.offsetToVisualPosition(offset - 1);
-    int x = 0;
-    LogicalPosition logLineStart = myEditor.visualToLogicalPosition(new VisualPosition(visualBeforeSoftWrap.line, 0));
-    if (logLineStart.softWrapLinesOnCurrentLogicalLine > 0) {
-      int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart);
-      softWrap = model.getSoftWrap(offsetLineStart);
-      if (softWrap != null) {
-        x = softWrap.getIndentInPixels();
-      }
+    if (myEditor.myUseNewRendering) {
+      VisualPosition beforeSoftWrap = myEditor.offsetToVisualPosition(offset, true, true);
+      return visual.line > beforeSoftWrap.line || 
+             visual.column > beforeSoftWrap.column || visual.column == beforeSoftWrap.column && countBeforeSoftWrap;
     }
-    int width = EditorUtil.textWidthInColumns(myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x);
-    int softWrapStartColumn = visualBeforeSoftWrap.column  + width;
-    if (visual.line > visualBeforeSoftWrap.line) {
-      return true;
+    else {
+      VisualPosition visualBeforeSoftWrap = myEditor.offsetToVisualPosition(offset - 1);
+      int x = 0;
+      LogicalPosition logLineStart = myEditor.visualToLogicalPosition(new VisualPosition(visualBeforeSoftWrap.line, 0));
+      if (logLineStart.softWrapLinesOnCurrentLogicalLine > 0) {
+        int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart);
+        softWrap = model.getSoftWrap(offsetLineStart);
+        if (softWrap != null) {
+          x = softWrap.getIndentInPixels();
+        }
+      }
+      int width = EditorUtil.textWidthInColumns(myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x);
+      int softWrapStartColumn = visualBeforeSoftWrap.column + width;
+      if (visual.line > visualBeforeSoftWrap.line) {
+        return true;
+      }
+      return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn;
     }
-    return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn;
   }
 
   @Override
index baf9477d1e9f02ba5f2931f98c4d2a9125db5c3c..7f73e825a643e3bdff309529b8d9aadc8a6c624d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 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,6 +17,7 @@ package com.intellij.openapi.editor.impl.softwrap;
 
 import com.intellij.openapi.editor.SoftWrap;
 import com.intellij.openapi.editor.SoftWrapModel;
+import com.intellij.openapi.editor.VisualPosition;
 import com.intellij.openapi.editor.impl.CaretImpl;
 import com.intellij.openapi.editor.impl.EditorImpl;
 
@@ -60,6 +61,13 @@ public class SoftWrapHelper {
       return false;
     }
 
-    return editor.offsetToVisualLine(offset) == caret.getVisualPosition().line;
+    if (editor.myUseNewRendering) {
+      VisualPosition afterWrapPosition = editor.offsetToVisualPosition(offset, false, false);
+      VisualPosition caretPosition = caret.getVisualPosition();
+      return caretPosition.line == afterWrapPosition.line && caretPosition.column <= afterWrapPosition.column;
+    }
+    else {
+      return editor.offsetToVisualLine(offset) == caret.getVisualPosition().line;
+    }
   }
 }
index fc340c515ac6469d7d1e47ed0ecfd629804108a6..c9f82e50f972bddf83c15d3fc7bae12b10cd903c 100644 (file)
  */
 package com.intellij.openapi.editor.impl.view;
 
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.FoldRegion;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.editor.VisualPosition;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.ex.util.EditorUtil;
+import com.intellij.openapi.editor.impl.EditorImpl;
 import com.intellij.openapi.editor.impl.FoldingModelImpl;
+import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
 import org.jetbrains.annotations.NotNull;
 
 import java.awt.*;
+import java.util.List;
 
 /**
  * Performs transformations between various location representations in editor 
@@ -95,7 +97,7 @@ class EditorCoordinateMapper {
   }
 
   @NotNull
-  VisualPosition logicalToVisualPosition(@NotNull LogicalPosition pos) {
+  VisualPosition logicalToVisualPosition(@NotNull LogicalPosition pos, boolean beforeSoftWrap) {
     int line = pos.line;
     int column = pos.column;
     int logicalLineCount = myDocument.getLineCount();
@@ -103,21 +105,20 @@ class EditorCoordinateMapper {
       return new VisualPosition(line - logicalLineCount + myView.getEditor().getVisibleLineCount(), column, pos.leansForward);
     }
     int offset = logicalPositionToOffset(pos);
-    int visualLine = offsetToVisualLine(offset);
+    int visualLine = offsetToVisualLine(offset, beforeSoftWrap);
     int maxVisualColumn = 0;
     int maxLogicalColumn = 0;
-    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset)) {
-      if (column == 0 && !pos.leansForward && 
-          fragment.getStartVisualColumn() == 0 && fragment.getStartLogicalLine() == line) {
-        return new VisualPosition(visualLine, 0);
+    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) {
+      if (!pos.leansForward && offset == fragment.getVisualLineStartOffset()) {
+        return new VisualPosition(visualLine, fragment.getStartVisualColumn());
       }
       if (fragment.isCollapsedFoldRegion()) {
         int startLogicalLine = fragment.getStartLogicalLine();
         int endLogicalLine = fragment.getEndLogicalLine();
         int startLogicalColumn = fragment.getStartLogicalColumn();
         int endLogicalColumn = fragment.getEndLogicalColumn();
-        if ((line > startLogicalLine || line == startLogicalLine && (column > startLogicalColumn || 
-                                                                     column == startLogicalColumn && pos.leansForward)) && 
+        if ((line > startLogicalLine || line == startLogicalLine && (column > startLogicalColumn ||
+                                                                     column == startLogicalColumn && pos.leansForward)) &&
             (line < endLogicalLine || line == endLogicalLine && column < endLogicalColumn)) {
           return new VisualPosition(visualLine, fragment.getStartVisualColumn(), true);
         }
@@ -139,28 +140,34 @@ class EditorCoordinateMapper {
       }
       maxVisualColumn = fragment.getEndVisualColumn();
     }
-    return new VisualPosition(visualLine, column - maxLogicalColumn + maxVisualColumn, pos.leansForward);
+    int resultColumn = column - maxLogicalColumn + maxVisualColumn;
+    if (resultColumn < 0 && maxVisualColumn > maxLogicalColumn) {
+      resultColumn = Integer.MAX_VALUE; // guarding against overflow
+    }
+    return new VisualPosition(visualLine, resultColumn, pos.leansForward);
   }
 
   @NotNull
   LogicalPosition visualToLogicalPosition(@NotNull VisualPosition pos) {
     int line = pos.line;
     int column = pos.column;
-    int logicalLine = visualToLogicalLine(line);
-    if (logicalLine >= myDocument.getLineCount()) {
-      return new LogicalPosition(logicalLine, column, pos.leansRight);
-    }
-    if (column == 0 && !pos.leansRight) {
-      return new LogicalPosition(logicalLine, 0);
+    int visualLineCount = myView.getEditor().getVisibleLineCount();
+    if (line >= visualLineCount) {
+      return new LogicalPosition(line - visualLineCount + myDocument.getLineCount(), column, pos.leansRight);
     }
-    int offset = myDocument.getLineStartOffset(logicalLine);
+    int offset = visualLineToOffset(line);
+    int logicalLine = myDocument.getLineNumber(offset);
     int maxVisualColumn = 0;
     int maxLogicalColumn = 0;
-    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset)) {
+    int maxOffset = offset;
+    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) {
       int minColumn = fragment.getStartVisualColumn();
       int maxColumn = fragment.getEndVisualColumn();
+      if (column < minColumn || column == minColumn && !pos.leansRight) {
+        return offsetToLogicalPosition(offset);
+      }
       if (column > minColumn && column < maxColumn ||
-          column == minColumn && pos.leansRight ||
+          column == minColumn ||
           column == maxColumn && !pos.leansRight) {
         return new LogicalPosition(column == maxColumn ? fragment.getEndLogicalLine() : fragment.getStartLogicalLine(), 
                                    fragment.visualToLogicalColumn(column), fragment.isCollapsedFoldRegion() ? 
@@ -171,16 +178,30 @@ class EditorCoordinateMapper {
                          fragment.getMaxLogicalColumn();
       maxVisualColumn = maxColumn;
       logicalLine = fragment.getEndLogicalLine();
+      maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
+    }
+    if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) == null) {
+      int resultColumn = column - maxVisualColumn + maxLogicalColumn;
+      if (resultColumn < 0 && maxLogicalColumn > maxVisualColumn) {
+        resultColumn = Integer.MAX_VALUE; // guarding against overflow
+      }
+      return new LogicalPosition(logicalLine, resultColumn, true);
+    }
+    else {
+      return offsetToLogicalPosition(maxOffset).leanForward(true);
     }
-    return new LogicalPosition(logicalLine, column - maxVisualColumn + maxLogicalColumn, true);
   }
 
   @NotNull
-  VisualPosition offsetToVisualPosition(int offset, boolean leanTowardsLargerOffsets) {
-    return logicalToVisualPosition(offsetToLogicalPosition(offset).leanForward(leanTowardsLargerOffsets));
+  VisualPosition offsetToVisualPosition(int offset, boolean leanTowardsLargerOffsets, boolean beforeSoftWrap) {
+    return logicalToVisualPosition(offsetToLogicalPosition(offset).leanForward(leanTowardsLargerOffsets), beforeSoftWrap);
+  }
+
+  int visualPositionToOffset(VisualPosition visualPosition) {
+    return logicalPositionToOffset(visualToLogicalPosition(visualPosition));
   }
 
-  int offsetToVisualLine(int offset) {
+  int offsetToVisualLine(int offset, boolean beforeSoftWrap) {
     int textLength = myDocument.getTextLength();
     if (offset < 0 || textLength == 0) {
       return 0;
@@ -191,49 +212,55 @@ class EditorCoordinateMapper {
     if (outermostCollapsed != null && offset > outermostCollapsed.getStartOffset()) {
       assert outermostCollapsed.isValid();
       offset = outermostCollapsed.getStartOffset();
+      beforeSoftWrap = false;
     }
 
-    return myDocument.getLineNumber(offset) - myFoldingModel.getFoldedLinesCountBefore(offset);
+    int wrapIndex = myView.getEditor().getSoftWrapModel().getSoftWrapIndex(offset);
+    int softWrapsBeforeOrAtOffset = wrapIndex < 0 ? (- wrapIndex - 1) : wrapIndex + (beforeSoftWrap ? 0 : 1);
+
+    return myDocument.getLineNumber(offset) - myFoldingModel.getFoldedLinesCountBefore(offset) + softWrapsBeforeOrAtOffset;
   }
 
-  int visualToLogicalLine(int visualLine) {
-    FoldRegion[] regions = myFoldingModel.fetchTopLevel();
-    if (regions == null || regions.length == 0) return visualLine;
+  private int visualLineToOffset(int visualLine) {
     int start = 0;
-    int end = regions.length - 1;
-    int i = 0;
+    int end = myDocument.getTextLength();
+    int current = 0;
     while (start <= end) {
-      i = (start + end) / 2;
-      FoldRegion region = regions[i];
-      assert region.isValid();
-      int regionVisualLine = offsetToVisualLine(region.getStartOffset());
-      if (regionVisualLine < visualLine) {
-        start = i + 1;
+      current = (start + end) / 2;
+      int line = offsetToVisualLine(current, false);
+      if (line < visualLine) {
+        start = current + 1;
       }
-      else if (regionVisualLine > visualLine) {
-        end = i - 1;
+      else if (line > visualLine) {
+        end = current - 1;
       }
       else {
         break;
       }
     }
-    while (i >= 0) {
-      FoldRegion region = regions[i];
-      assert region.isValid();
-      if (offsetToVisualLine(region.getStartOffset()) < visualLine) break;
-      i--;
-    }
-    i++;
-    int offset;
-    if (i < regions.length) {
-      FoldRegion region = regions[i];
-      assert region.isValid();
-      offset = region.getStartOffset();
+    return visualLineStartOffset(current, true);
+  }
+
+  private int visualLineStartOffset(int offset, boolean leanForward) {
+    EditorImpl editor = myView.getEditor();
+    int result = EditorUtil.getNotFoldedLineStartOffset(editor, offset);
+
+    SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel();
+    List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps();
+    int currentOrPrevWrapIndex = softWrapModel.getSoftWrapIndex(offset);
+    SoftWrap currentOrPrevWrap;
+    if (currentOrPrevWrapIndex < 0) {
+      currentOrPrevWrapIndex = - currentOrPrevWrapIndex - 2;
+      currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null :
+                          softWraps.get(currentOrPrevWrapIndex);
     }
     else {
-      offset = myDocument.getTextLength();
+      currentOrPrevWrap = leanForward ? softWraps.get(currentOrPrevWrapIndex) : null;
+    }
+    if (currentOrPrevWrap != null && currentOrPrevWrap.getStart() > result) {
+      result = currentOrPrevWrap.getStart();
     }
-    return visualLine + myFoldingModel.getFoldedLinesCountBefore(offset);
+    return result;
   }
 
   private float getStartX(int line) {
@@ -243,21 +270,38 @@ class EditorCoordinateMapper {
   @NotNull
   VisualPosition xyToVisualPosition(@NotNull Point p) {
     int visualLine = yToVisualLine(Math.max(p.y, 0));
-    int logicalLine = visualToLogicalLine(visualLine);
     int lastColumn = 0;
-    float x = getStartX(logicalLine);
-    if (logicalLine < myDocument.getLineCount()) {
-      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, 
-                                                                                              myDocument.getLineStartOffset(logicalLine))) {
+    float x = getStartX(visualLine);
+    if (visualLine < myView.getEditor().getVisibleLineCount()) {
+      int visualLineStartOffset = visualLineToOffset(visualLine);
+      int maxOffset = 0;
+      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) {
+        if (p.x <= fragment.getStartX()) {
+          int markerWidth = myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
+          float indent = fragment.getStartX() - markerWidth;
+          if (p.x <= indent) {
+            break;
+          }
+          boolean after = p.x >= indent + markerWidth / 2;
+          return new VisualPosition(visualLine, fragment.getStartVisualColumn() - (after ? 0 : 1), !after);
+        }
         float nextX = fragment.getEndX();
         if (p.x <= nextX) {
           int[] column = fragment.xToVisualColumn(p.x);
           return new VisualPosition(visualLine, column[0], column[1] > 0);
         }
-        else {
-          x = nextX;
-          lastColumn = fragment.getEndVisualColumn();
+        x = nextX;
+        lastColumn = fragment.getEndVisualColumn();
+        maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
+      }
+      if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) {
+        int markerWidth = myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
+        if (p.x <= x + markerWidth) {
+          boolean after = p.x >= x + markerWidth / 2;
+          return new VisualPosition(visualLine, lastColumn + (after ? 1 : 0), !after);
         }
+        p.x -= markerWidth;
+        lastColumn++;
       }
     }
     int plainSpaceWidth = myView.getPlainSpaceWidth();
@@ -272,20 +316,26 @@ class EditorCoordinateMapper {
     int visualLine = pos.line;
     int column = pos.column;
     int y = visualLineToY(visualLine);
-    int logicalLine = visualToLogicalLine(visualLine);
-    float x = getStartX(logicalLine);
+    float x = getStartX(visualLine);
     int lastColumn = 0;
-    if (logicalLine < myDocument.getLineCount()) {
-      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, 
-                                                                                              myDocument.getLineStartOffset(logicalLine))) {
+    if (visualLine < myView.getEditor().getVisibleLineCount()) {
+      int visualLineStartOffset = visualLineToOffset(visualLine);
+      int maxOffset = 0;
+      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) {
+        if (column < fragment.getStartVisualColumn()) {
+          break;
+        }
         int endColumn = fragment.getEndVisualColumn();
         if (column <= endColumn) {
           return new Point((int)fragment.visualColumnToX(column), y);
         }
-        else {
-          x = fragment.getEndX();
-          lastColumn = endColumn;
-        }
+        x = fragment.getEndX();
+        lastColumn = endColumn;
+        maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
+      }
+      if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) {
+        column--;
+        x += myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
       }
     }
     int additionalShift = column <= lastColumn ? 0 : (column - lastColumn) * myView.getPlainSpaceWidth();
@@ -293,19 +343,21 @@ class EditorCoordinateMapper {
   }
 
   @NotNull
-  Point offsetToXY(int offset, boolean leanTowardsLargerOffsets) {
+  Point offsetToXY(int offset, boolean leanTowardsLargerOffsets, boolean beforeSoftWrap) {
     offset = Math.max(0, Math.min(myDocument.getTextLength(), offset));
     int logicalLine = myDocument.getLineNumber(offset);
-    int lineStartOffset = myDocument.getLineStartOffset(logicalLine);
-    int intraLineOffset = offset - lineStartOffset;
-    int y = visualLineToY(offsetToVisualLine(offset));
+    int visualLine = offsetToVisualLine(offset, beforeSoftWrap);
+    int visualLineStartOffset = visualLineToOffset(visualLine);
+    int y = visualLineToY(visualLine);
     float x = getStartX(logicalLine);
     if (myDocument.getTextLength() > 0) {
-      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset)) {
-        if (intraLineOffset == 0 && !leanTowardsLargerOffsets &&
-            fragment.getStartVisualColumn() == 0 && fragment.getStartLogicalLine() == logicalLine) {
+      boolean firstFragment = true;
+      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, beforeSoftWrap)) {
+        if (firstFragment && offset == visualLineStartOffset && !leanTowardsLargerOffsets) {
+          x = fragment.getStartX();
           break;
         }
+        firstFragment = false;
         int minOffset = fragment.getMinOffset();
         int maxOffset = fragment.getMaxOffset();
         if (offset > minOffset && offset < maxOffset ||
index 90cdf5ff723210091aec6edee8b49bde9cbc214e..9002f75829130908b49c088c731eecf25f099d11 100644 (file)
@@ -20,14 +20,13 @@ import com.intellij.openapi.editor.colors.EditorColors;
 import com.intellij.openapi.editor.colors.EditorFontType;
 import com.intellij.openapi.editor.ex.MarkupModelEx;
 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
-import com.intellij.openapi.editor.ex.util.EditorUtil;
 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
-import com.intellij.openapi.editor.impl.DocumentMarkupModel;
-import com.intellij.openapi.editor.impl.EditorComponentImpl;
-import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.editor.impl.*;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
 import com.intellij.openapi.editor.markup.*;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Couple;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.util.registry.Registry;
@@ -40,16 +39,19 @@ import com.intellij.util.Processor;
 import com.intellij.util.ui.JBUI;
 import com.intellij.util.ui.UIUtil;
 import gnu.trove.TFloatArrayList;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.*;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Renders editor contents.
  */
-class EditorPainter {
+class EditorPainter implements TextDrawingCallback {
   private static final Color CARET_LIGHT = Gray._255;
   private static final Color CARET_DARK = Gray._0;
   private static final Stroke IME_COMPOSED_TEXT_UNDERLINE_STROKE = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0,
@@ -81,8 +83,8 @@ class EditorPainter {
     
     int startLine = myView.yToVisualLine(Math.max(clip.y, 0));
     int endLine = myView.yToVisualLine(Math.max(clip.y + clip.height, 0));
-    int startOffset = myView.logicalPositionToOffset(myView.visualToLogicalPosition(new VisualPosition(startLine, 0)));
-    int endOffset = myView.logicalPositionToOffset(myView.visualToLogicalPosition(new VisualPosition(endLine + 1, 0, true)));
+    int startOffset = myView.visualPositionToOffset(new VisualPosition(startLine, 0));
+    int endOffset = myView.visualPositionToOffset(new VisualPosition(endLine + 1, 0, true));
     
     paintBackground(g, clip, startLine, endLine);
     paintRightMargin(g, clip);
@@ -131,6 +133,7 @@ class EditorPainter {
 
   private void paintBackground(Graphics2D g, Rectangle clip, int startVisualLine, int endVisualLine) {
     int lineCount = myEditor.getVisibleLineCount();
+    final Map<Integer, Couple<Integer>> virtualSelectionMap = createVirtualSelectionMap(startVisualLine, endVisualLine); 
     for (int visualLine = startVisualLine; visualLine <= endVisualLine; visualLine++) {
       int y = myView.visualLineToY(visualLine);
       LineLayout prefixLayout = myView.getPrefixLayout();
@@ -140,28 +143,105 @@ class EditorPainter {
       if (visualLine >= lineCount) break;
       paintLineFragments(g, clip, visualLine, y, new LineFragmentPainter() {
         @Override
+        public void paintBeforeLineStart(Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y) {
+          paintBackground(g, attributes, 0, y, xEnd);
+          paintSelectionOnSecondSoftWrapLineIfNecessary(g, columnEnd, xEnd, y);
+        }
+
+        @Override
         public void paint(Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, 
                           TextAttributes attributes, float xStart, float xEnd, int y) {
           paintBackground(g, attributes, xStart, y, xEnd - xStart);
         }
 
         @Override
-        public void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState it, float x, int y) {
-          x = paintAfterLineEndBackgroundSegments(g, it, x, y);
-          if (it.getEndOffset() < myDocument.getTextLength()) {
-            paintBackground(g, it.getPastLineEndBackgroundAttributes(), x, y, clip.x + clip.width - x);
+        public void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState it, int columnStart, float x, int y) {
+          paintBackground(g, it.getPastLineEndBackgroundAttributes(), x, y, clip.x + clip.width - x);
+          int offset = it.getEndOffset();
+          SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset);
+          if (softWrap == null) {
+            paintVirtualSelectionIfNecessary(g, virtualSelectionMap, columnStart, x, clip.x + clip.width, y);
           }
           else {
-            if (it.hasPastFileEndBackgroundSegments()) {
-              x = paintAfterLineEndBackgroundSegments(g, it, x, y);
-            }
-            paintBackground(g, it.getPastFileEndBackground(), x, y, clip.x + clip.width - x);
+            paintSelectionOnFirstSoftWrapLineIfNecessary(g, columnStart, x, clip.x + clip.width, y);
           }
         }
       });
     }
   }
 
+  private Map<Integer, Couple<Integer>> createVirtualSelectionMap(int startVisualLine, int endVisualLine) {
+    HashMap<Integer, Couple<Integer>> map = new HashMap<Integer, Couple<Integer>>();
+    for (Caret caret : myEditor.getCaretModel().getAllCarets()) {
+      if (caret.hasSelection()) {
+        VisualPosition selectionStart = caret.getSelectionStartPosition();
+        VisualPosition selectionEnd = caret.getSelectionEndPosition();
+        if (selectionStart.line == selectionEnd.line) {
+          int line = selectionStart.line;
+          if (line >= startVisualLine && line <= endVisualLine) {
+            map.put(line, Couple.of(selectionStart.column, selectionEnd.column));
+          }
+        }
+      }
+    }
+    return map;
+  }
+
+  private void paintVirtualSelectionIfNecessary(Graphics2D g,
+                                                Map<Integer, Couple<Integer>> virtualSelectionMap,
+                                                int columnStart,
+                                                float xStart,
+                                                float xEnd,
+                                                int y) {
+    int visualLine = myView.yToVisualLine(y);
+    Couple<Integer> selectionRange = virtualSelectionMap.get(visualLine);
+    if (selectionRange == null || selectionRange.second <= columnStart) return;
+    float startX = selectionRange.first <= columnStart ? xStart : 
+                   myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.first)).x;
+    float endX = Math.min(xEnd, myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.second)).x);
+    paintBackground(g, myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), startX, y, endX - startX);
+  }
+
+  private void paintSelectionOnSecondSoftWrapLineIfNecessary(Graphics2D g, int columnEnd, float xEnd, int y) {
+    VisualPosition selectionStartPosition = myEditor.getSelectionModel().getSelectionStartPosition();
+    VisualPosition selectionEndPosition = myEditor.getSelectionModel().getSelectionEndPosition();
+    int visualLine = myView.yToVisualLine(y);
+    
+    if (selectionStartPosition.equals(selectionEndPosition) || 
+        visualLine < selectionStartPosition.line || 
+        visualLine > selectionEndPosition.line || 
+        visualLine == selectionStartPosition.line && selectionStartPosition.column >= columnEnd) {
+      return;
+    }
+
+    float startX = (selectionStartPosition.line == visualLine && selectionStartPosition.column > 0) ? 
+                   myView.visualPositionToXY(selectionStartPosition).x : 0;
+    float endX = (selectionEndPosition.line == visualLine && selectionEndPosition.column < columnEnd) ? 
+                 myView.visualPositionToXY(selectionEndPosition).x : xEnd;
+    
+    paintBackground(g, myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), startX, y, endX - startX);
+  }
+
+  private void paintSelectionOnFirstSoftWrapLineIfNecessary(Graphics2D g, int columnStart, float xStart, float xEnd, int y) {
+    VisualPosition selectionStartPosition = myEditor.getSelectionModel().getSelectionStartPosition();
+    VisualPosition selectionEndPosition = myEditor.getSelectionModel().getSelectionEndPosition();
+    int visualLine = myView.yToVisualLine(y);
+
+    if (selectionStartPosition.equals(selectionEndPosition) || 
+        visualLine < selectionStartPosition.line || 
+        visualLine > selectionEndPosition.line || 
+        visualLine == selectionEndPosition.line && selectionEndPosition.column <= columnStart) {
+      return;
+    }
+
+    float startX = selectionStartPosition.line == visualLine && selectionStartPosition.column > columnStart ? 
+                   myView.visualPositionToXY(selectionStartPosition).x : xStart;
+    float endX = selectionEndPosition.line == visualLine ?
+                 myView.visualPositionToXY(selectionEndPosition).x : xEnd;
+
+    paintBackground(g, myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), startX, y, endX - startX);  
+  }
+  
   private void paintBackground(Graphics2D g, TextAttributes attributes, float x, int y, float width) {
     if (attributes == null) return;
     paintBackground(g, attributes.getBackgroundColor(), x, y, width);
@@ -176,16 +256,6 @@ class EditorPainter {
     g.fillRect((int)x, y, (int)width, myView.getLineHeight());
   }
 
-  private float paintAfterLineEndBackgroundSegments(Graphics2D g, IterationState it, float x, int y) {
-    while (it.hasPastLineEndBackgroundSegment()) {
-      int width = myView.getPlainSpaceWidth() * it.getPastLineEndBackgroundSegmentWidth();
-      paintBackground(g, it.getPastLineEndBackgroundAttributes(), x, y, width);
-      x += width;
-      it.advanceToNextPastLineEndBackgroundSegment();
-    }
-    return x;
-  }
-
   private void paintCustomRenderers(final Graphics2D g, final int startOffset, final int endOffset) {
     myEditor.getMarkupModel().processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() {
       @Override
@@ -227,7 +297,7 @@ class EditorPainter {
                                         ? marker.getStartOffset()
                                         : marker.getEndOffset());
     int visualLine = myView.logicalToVisualPosition(new LogicalPosition(line + (marker.getLineSeparatorPlacement() == 
-                                                                                SeparatorPlacement.TOP ? 0 : 1), 0)).line;
+                                                                                SeparatorPlacement.TOP ? 0 : 1), 0), false).line;
     int y = myView.visualLineToY(visualLine) - 1;
     int endShift = clip.x + clip.width;
     EditorSettings settings = myEditor.getSettings();
@@ -263,6 +333,14 @@ class EditorPainter {
       
       paintLineFragments(g, clip, visualLine, y, new LineFragmentPainter() {
         @Override
+        public void paintBeforeLineStart(Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y) {
+          SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel();
+          int symbolWidth = softWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
+          softWrapModel.paint(g, SoftWrapDrawingType.AFTER_SOFT_WRAP, 
+                              (int)xEnd - symbolWidth, y - myView.getAscent(), myView.getLineHeight());
+        }
+
+        @Override
         public void paint(Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, 
                           TextAttributes attributes, float xStart, float xEnd, int y) {
           if (attributes != null && attributes.getForegroundColor() != null) {
@@ -283,9 +361,16 @@ class EditorPainter {
         }
 
         @Override
-        public void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState iterationState, float x, int y) {
-          int logicalLine = myDocument.getLineNumber(iterationState.getStartOffset());
-          paintLineExtensions(g, logicalLine, x, y);
+        public void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState iterationState, int columnStart, float x, int y) {
+          int offset = iterationState.getEndOffset();
+          SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel();
+          if (softWrapModel.getSoftWrap(offset) == null) {
+            int logicalLine = myDocument.getLineNumber(offset);
+            paintLineExtensions(g, logicalLine, x, y);
+          }
+          else {
+            softWrapModel.paint(g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, (int)x, y - myView.getAscent(), myView.getLineHeight());
+          }
         }
       });
     }
@@ -414,7 +499,7 @@ class EditorPainter {
     int startOffset = highlighter.getStartOffset();
     int lineEndOffset = myDocument.getLineEndOffset(myDocument.getLineNumber(startOffset));
     if (myEditor.getFoldingModel().isOffsetCollapsed(lineEndOffset)) return;
-    Point lineEnd = myView.offsetToXY(lineEndOffset, true);
+    Point lineEnd = myView.offsetToXY(lineEndOffset, true, false);
     int x = lineEnd.x;
     int y = lineEnd.y;
     TextAttributes attributes = highlighter.getTextAttributes();
@@ -471,8 +556,8 @@ class EditorPainter {
     boolean rounded = attributes.getEffectType() == EffectType.ROUNDED_BOX;
     int lineHeight = myView.getLineHeight() - 1;
     g.setColor(attributes.getEffectColor());
-    VisualPosition startPosition = myView.offsetToVisualPosition(startOffset, true);
-    VisualPosition endPosition = myView.offsetToVisualPosition(endOffset, false);
+    VisualPosition startPosition = myView.offsetToVisualPosition(startOffset, true, false);
+    VisualPosition endPosition = myView.offsetToVisualPosition(endOffset, false, true);
     if (startPosition.line == endPosition.line) {
       int y = myView.visualLineToY(startPosition.line);
       TFloatArrayList ranges = adjustedLogicalRangeToVisualRanges(startOffset, endOffset);
@@ -490,8 +575,10 @@ class EditorPainter {
     else {
       int maxWidth = myView.getMaxWidthInLineRange(startPosition.line, endPosition.line) - 1;
       TFloatArrayList leadingRanges = adjustedLogicalRangeToVisualRanges(startOffset, 
-                                                                         EditorUtil.getNotFoldedLineEndOffset(myEditor, startOffset));
-      TFloatArrayList trailingRanges = adjustedLogicalRangeToVisualRanges(EditorUtil.getNotFoldedLineStartOffset(myEditor, endOffset), 
+                                                                         myView.visualPositionToOffset(new VisualPosition(
+                                                                           startPosition.line, Integer.MAX_VALUE, true)));
+      TFloatArrayList trailingRanges = adjustedLogicalRangeToVisualRanges(myView.visualPositionToOffset(new VisualPosition(endPosition.line,
+                                                                                                                           0)),
                                                                           endOffset);
       if (!leadingRanges.isEmpty() && !trailingRanges.isEmpty()) {
         boolean containsInnerLines = endPosition.line > startPosition.line + 1;
@@ -593,7 +680,7 @@ class EditorPainter {
   private TFloatArrayList logicalRangeToVisualRanges(int startOffset, int endOffset) {
     assert startOffset <= endOffset;
     TFloatArrayList result = new TFloatArrayList();
-    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset)) {
+    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false)) {
       int minOffset = fragment.getMinOffset();
       int maxOffset = fragment.getMaxOffset();
       if (startOffset == endOffset) {
@@ -627,8 +714,8 @@ class EditorPainter {
   private void paintComposedTextDecoration(Graphics2D g) {
     TextRange composedTextRange = myEditor.getComposedTextRange();
     if (composedTextRange != null) {
-      Point p1 = myView.offsetToXY(Math.min(composedTextRange.getStartOffset(), myDocument.getTextLength()), true);
-      Point p2 = myView.offsetToXY(Math.min(composedTextRange.getEndOffset(), myDocument.getTextLength()), false);
+      Point p1 = myView.offsetToXY(Math.min(composedTextRange.getStartOffset(), myDocument.getTextLength()), true, false);
+      Point p2 = myView.offsetToXY(Math.min(composedTextRange.getEndOffset(), myDocument.getTextLength()), false, true);
   
       int y = p1.y + myView.getAscent() + 1;
      
@@ -671,7 +758,8 @@ class EditorPainter {
         g.fillRect(startX, y, width, lineHeight - 1);
         if (myDocument.getTextLength() > 0 && caret != null) {
           int targetVisualColumn = caret.getVisualPosition().column;
-          for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, caret.getVisualLineStart())) {
+          for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, 
+                                                                                                  caret.getVisualLineStart(), false)) {
             int startVisualColumn = fragment.getStartVisualColumn();
             int endVisualColumn = fragment.getEndVisualColumn();
             if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn ||
@@ -703,15 +791,30 @@ class EditorPainter {
   
   private void paintLineFragments(Graphics2D g, Rectangle clip, int visualLine, int y, LineFragmentPainter painter) {
     float x = visualLine == 0 ? myView.getPrefixTextWidthInPixels() : 0;
-    LogicalPosition logicalPosition = myView.visualToLogicalPosition(new VisualPosition(visualLine, 0));
-    int offset = myDocument.getLineStartOffset(logicalPosition.line);
-    int visualLineEndOffset = EditorUtil.getNotFoldedLineEndOffset(myEditor, offset);
+    int offset = myView.visualPositionToOffset(new VisualPosition(visualLine, 0));
+    int visualLineEndOffset = myView.visualPositionToOffset(new VisualPosition(visualLine, Integer.MAX_VALUE, true));
     IterationState it = null;
     int prevEndOffset = -1;
-    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset)) {
+    boolean firstFragment = true;
+    int maxColumn = 0;
+    for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) {
       int fragmentStartOffset = fragment.getStartOffset();
       int start = fragmentStartOffset;
       int end = fragment.getEndOffset();
+      x = fragment.getStartX();
+      if (firstFragment) {
+        firstFragment = false;
+        SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset);
+        if (softWrap != null) {
+          prevEndOffset = offset;
+          it = new IterationState(myEditor, offset == 0 ? 0 : offset - 1, visualLineEndOffset, true, false, false, false);
+          if (it.getEndOffset() <= offset) {
+            it.advance();
+          }
+          painter.paintBeforeLineStart(g, it.getStartOffset() == offset ? it.getBeforeLineStartBackgroundAttributes() :
+                                          it.getMergedAttributes(), fragment.getStartVisualColumn(), fragment.getStartX(), y);
+        }
+      }
       FoldRegion foldRegion = fragment.getCurrentFoldRegion();
       if (foldRegion == null) {
         if (start != prevEndOffset) {
@@ -743,14 +846,17 @@ class EditorPainter {
         prevEndOffset = -1;
         it = null;
       }
+      maxColumn = fragment.getEndVisualColumn();
+    }
+    if (it == null || it.getEndOffset() != visualLineEndOffset) {
+      it = new IterationState(myEditor, visualLineEndOffset == offset ? visualLineEndOffset : visualLineEndOffset - 1, visualLineEndOffset, 
+                              true, false, false, false);
     }
-    if (it != null) {
-      assert !it.atEnd();
+    if (!it.atEnd()) {
       it.advance();
     }
-    painter.paintAfterLineEnd(g, clip, it != null && it.getEndOffset() == visualLineEndOffset ? it : 
-                                       new IterationState(myEditor, visualLineEndOffset, visualLineEndOffset, true, false, false, false),
-                              x, y);
+    assert it.atEnd();
+    painter.paintAfterLineEnd(g, clip, it, maxColumn, x, y);
   }
 
   private TextAttributes getFoldRegionAttributes(FoldRegion foldRegion) {
@@ -782,9 +888,17 @@ class EditorPainter {
                               primary.getFontType() == Font.PLAIN ? secondary.getFontType() : primary.getFontType());
   }
 
+  @Override
+  public void drawChars(@NotNull Graphics g, @NotNull char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) {
+    g.setFont(fontInfo.getFont());
+    g.setColor(color);
+    g.drawChars(data, start, end - start, x, y);
+  }
+
   interface LineFragmentPainter {
-    void paint(Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, 
-               float xStart, float xEnd, int y); 
-    void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState iterationState, float x, int y);
+    void paintBeforeLineStart(Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y);
+    void paint(Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes,
+               float xStart, float xEnd, int y);
+    void paintAfterLineEnd(Graphics2D g, Rectangle clip, IterationState iterationState, int columnStart, float x, int y);
   }
 }
index 882df8e34a7c47f08b76587e505870ffd59e1d45..ddddb691ec202e98297fc76bdb737a230beeeb17 100644 (file)
@@ -26,6 +26,8 @@ import com.intellij.openapi.editor.ex.FoldingListener;
 import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
 import com.intellij.openapi.editor.impl.EditorDocumentPriorities;
 import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.editor.impl.softwrap.mapping.IncrementalCacheUpdateEvent;
+import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapAwareDocumentParsingListenerAdapter;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import gnu.trove.TIntArrayList;
@@ -47,6 +49,13 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold
 
   private int myMaxLineWithExtensionWidth;
   private int myWidestLineWithExtension;
+
+  private final SoftWrapAwareDocumentParsingListenerAdapter mySoftWrapChangeListener = new SoftWrapAwareDocumentParsingListenerAdapter() {
+    @Override
+    public void onRecalculationEnd(@NotNull IncrementalCacheUpdateEvent event) {
+      onSoftWrapRecalculationEnd(event);
+    }
+  };
   
   EditorSizeManager(EditorView view) {
     myView = view;
@@ -54,10 +63,12 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold
     myDocument = myEditor.getDocument(); 
     myDocument.addDocumentListener(this, this);
     myEditor.getFoldingModel().addListener(this, this);
+    myEditor.getSoftWrapModel().getApplianceManager().addListener(mySoftWrapChangeListener);
   }
 
   @Override
   public void dispose() {
+    myEditor.getSoftWrapModel().getApplianceManager().removeListener(mySoftWrapChangeListener);
   }
 
   @Override
@@ -94,6 +105,10 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold
     foldingChangeEndOffset = Integer.MIN_VALUE;
   }
 
+  private void onSoftWrapRecalculationEnd(IncrementalCacheUpdateEvent event) {
+    invalidateRange(event.getStartOffset(), event.getActualEndOffset());
+  }
+
   Dimension getPreferredSize() {
     int width = getPreferredWidth();
     if (!myDocument.isInBulkUpdate()) {
@@ -152,8 +167,8 @@ class EditorSizeManager implements PrioritizedDocumentListener, Disposable, Fold
 
   void invalidateRange(int startOffset, int endOffset) {
     myWidthInPixels = -1;
-    int startVisualLine = myView.offsetToVisualLine(startOffset);
-    int endVisualLine = myView.offsetToVisualLine(endOffset);
+    int startVisualLine = myView.offsetToVisualLine(startOffset, false);
+    int endVisualLine = myView.offsetToVisualLine(endOffset, true);
     int lineDiff = myEditor.getVisibleLineCount() - myLineWidths.size();
     if (lineDiff > 0) {
       int[] newEntries = new int[lineDiff];
index 28c35b6b40aace4670fab4951055293b7fb67cf9..bc5c8b4b7acbc0b6327390e7bf65330242a18d00 100644 (file)
@@ -26,6 +26,9 @@ import com.intellij.openapi.editor.ex.DocumentEx;
 import com.intellij.openapi.editor.ex.util.EditorUIUtil;
 import com.intellij.openapi.editor.ex.util.EditorUtil;
 import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.editor.impl.FontInfo;
+import com.intellij.openapi.editor.impl.TextDrawingCallback;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Key;
@@ -42,7 +45,7 @@ import java.awt.image.BufferedImage;
  * 
  * Also contains a cache of several font-related quantities (line height, space width, etc).
  */
-public class EditorView implements Disposable {
+public class EditorView implements TextDrawingCallback, Disposable {
   private static Key<LineLayout> FOLD_REGION_TEXT_LAYOUT = Key.create("text.layout");
 
   private final EditorImpl myEditor;
@@ -79,8 +82,6 @@ public class EditorView implements Disposable {
     
     Disposer.register(this, myTextLayoutCache);
     Disposer.register(this, mySizeManager);
-    
-    reinitSettings();
   }
 
   EditorImpl getEditor() {
@@ -127,44 +128,57 @@ public class EditorView implements Disposable {
   }
 
   @NotNull
-  public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition pos) {
+  public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition pos, boolean beforeSoftWrap) {
     assertIsDispatchThread();
-    return myMapper.logicalToVisualPosition(pos);
+    myEditor.getSoftWrapModel().prepareToMapping();
+    return myMapper.logicalToVisualPosition(pos, beforeSoftWrap);
   }
 
   @NotNull
   public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition pos) {
     assertIsDispatchThread();
+    myEditor.getSoftWrapModel().prepareToMapping();
     return myMapper.visualToLogicalPosition(pos);
   }
 
   @NotNull
-  public VisualPosition offsetToVisualPosition(int offset, boolean leanTowardsLargerOffsets) {
+  public VisualPosition offsetToVisualPosition(int offset, boolean leanTowardsLargerOffsets, boolean beforeSoftWrap) {
     assertIsDispatchThread();
-    return myMapper.offsetToVisualPosition(offset, leanTowardsLargerOffsets);
+    myEditor.getSoftWrapModel().prepareToMapping();
+    return myMapper.offsetToVisualPosition(offset, leanTowardsLargerOffsets, beforeSoftWrap);
   }
 
-  public int offsetToVisualLine(int offset) {
+  public int visualPositionToOffset(VisualPosition visualPosition) {
     assertIsDispatchThread();
-    return myMapper.offsetToVisualLine(offset);
+    myEditor.getSoftWrapModel().prepareToMapping();
+    return myMapper.visualPositionToOffset(visualPosition);
+  }
+
+  public int offsetToVisualLine(int offset, boolean beforeSoftWrap) {
+    assertIsDispatchThread();
+    myEditor.getSoftWrapModel().prepareToMapping();
+    return myMapper.offsetToVisualLine(offset, beforeSoftWrap);
   }
 
   @NotNull
   public VisualPosition xyToVisualPosition(@NotNull Point p) {
     assertIsDispatchThread();
+    myEditor.getSoftWrapModel().prepareToMapping();
     return myMapper.xyToVisualPosition(p);
   }
 
   @NotNull
   public Point visualPositionToXY(@NotNull VisualPosition pos) {
     assertIsDispatchThread();
+    myEditor.getSoftWrapModel().prepareToMapping();
     return myMapper.visualPositionToXY(pos);
   }
 
   @NotNull
-  public Point offsetToXY(int offset, boolean leanTowardsLargerOffsets) {
+  public Point offsetToXY(int offset, boolean leanTowardsLargerOffsets, boolean beforeSoftWrap) {
     assertIsDispatchThread();
-    return myMapper.offsetToXY(offset, leanTowardsLargerOffsets);
+    myEditor.getSoftWrapModel().prepareToMapping();
+    return myMapper.offsetToXY(offset, leanTowardsLargerOffsets, beforeSoftWrap);
   }
 
   public void setPrefix(String prefixText, TextAttributes attributes) {
@@ -196,6 +210,7 @@ public class EditorView implements Disposable {
 
   public void paint(Graphics2D g) {
     assertIsDispatchThread();
+    myEditor.getSoftWrapModel().prepareToMapping();
     myPainter.paint(g);
   }
 
@@ -211,18 +226,23 @@ public class EditorView implements Disposable {
 
   public int getMaxWidthInRange(int startOffset, int endOffset) {
     assertIsDispatchThread();
-    return getMaxWidthInLineRange(offsetToVisualLine(startOffset), offsetToVisualLine(endOffset));
+    return getMaxWidthInLineRange(offsetToVisualLine(startOffset, false), offsetToVisualLine(endOffset, true));
   }
   
   int getMaxWidthInLineRange(int startVisualLine, int endVisualLine) {
     int maxWidth = 0;
     for (int i = startVisualLine; i <= endVisualLine; i++) {
-      int logicalLine = visualToLogicalPosition(new VisualPosition(i, 0)).line;
-      if (logicalLine >= myDocument.getLineCount()) break;
-      int startOffset = myDocument.getLineStartOffset(logicalLine);
+      LogicalPosition startPosition = visualToLogicalPosition(new VisualPosition(i, 0));
+      if (startPosition.line >= myDocument.getLineCount()) break;
+      int startOffset = logicalPositionToOffset(startPosition);
       float x = 0;
-      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset)) {
+      int maxOffset = 0;
+      for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset, false)) {
         x = fragment.getEndX();
+        maxOffset = Math.max(maxOffset, fragment.getMaxOffset());
+      }
+      if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) {
+        x += myEditor.getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
       }
       maxWidth = Math.max(maxWidth, (int) x);
     }
@@ -261,23 +281,35 @@ public class EditorView implements Disposable {
     myTextLayoutCache.resetToDocumentSize();
   }
   
-  public boolean isRtlLocation(int offset, boolean leanForward) {
+  public boolean isRtlLocation(@NotNull VisualPosition visualPosition) {
     assertIsDispatchThread();
     if (myDocument.getTextLength() == 0) return false;
+    LogicalPosition logicalPosition = visualToLogicalPosition(visualPosition);
+    int offset = logicalPositionToOffset(logicalPosition);
+    if (myEditor.getSoftWrapModel().getSoftWrap(offset) != null) {
+      VisualPosition beforeWrapPosition = offsetToVisualPosition(offset, true, true);
+      if (visualPosition.line == beforeWrapPosition.line && 
+          (visualPosition.column > beforeWrapPosition.column || 
+           visualPosition.column == beforeWrapPosition.column && visualPosition.leansRight)) {
+        return false;
+      }
+      VisualPosition afterWrapPosition = offsetToVisualPosition(offset, false, false);
+      if (visualPosition.line == afterWrapPosition.line &&
+          (visualPosition.column < afterWrapPosition.column ||
+           visualPosition.column == afterWrapPosition.column && !visualPosition.leansRight)) {
+        return false;
+      }
+    } 
     int line = myDocument.getLineNumber(offset);
     LineLayout layout = getLineLayout(line);
-    return layout.isRtlLocation(offset - myDocument.getLineStartOffset(line), leanForward);
+    return layout.isRtlLocation(offset - myDocument.getLineStartOffset(line), logicalPosition.leansForward);
   }
 
-  public boolean isDirectionBoundary(@NotNull VisualPosition visualPosition) {
+  public boolean isAtBidiRunBoundary(@NotNull VisualPosition visualPosition) {
     assertIsDispatchThread();
-    if (myDocument.getTextLength() == 0) return false;
-    LogicalPosition logicalPosition = visualToLogicalPosition(visualPosition);
-    int offset = logicalPositionToOffset(logicalPosition);
-    if (!visualPosition.equals(offsetToVisualPosition(offset, logicalPosition.leansForward))) return false;
-    int line = myDocument.getLineNumber(offset);
-    LineLayout layout = getLineLayout(line);
-    return layout.isDirectionBoundary(offset - myDocument.getLineStartOffset(line), logicalPosition.leansForward);
+    int offset = visualPositionToOffset(visualPosition);
+    int otherSideOffset = visualPositionToOffset(visualPosition.leanRight(!visualPosition.leansRight));
+    return offset != otherSideOffset;
   }
 
   /**
@@ -398,4 +430,9 @@ public class EditorView implements Disposable {
   private static void assertIsReadAccess() {
     ApplicationManager.getApplication().assertReadAccessAllowed();
   }
+
+  @Override
+  public void drawChars(@NotNull Graphics g, @NotNull char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) {
+    myPainter.drawChars(g, data, start, end, x, y, color, fontInfo);
+  }
 }
index ee31e70339cbdf7b13ef9ca4716a6af8926e8b60..4e59c74e54e1aee9b14de33849f41c678272e19e 100644 (file)
@@ -17,10 +17,7 @@ package com.intellij.openapi.editor.impl.view;
 
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.FoldRegion;
-import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.colors.EditorColors;
 import com.intellij.openapi.editor.ex.*;
 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
@@ -108,13 +105,9 @@ public class IterationState {
 
   private final int[] mySelectionStarts;
   private final int[] mySelectionEnds;
-  private final int[] myVirtualSelectionStarts;
-  private final int[] myVirtualSelectionEnds;
   private int myCurrentSelectionIndex = 0;
-  private int myCurrentVirtualSelectionIndex = 0;
-  private boolean myCurrentLineHasVirtualSelection;
-  private int myCurrentPastLineEndBackgroundSegment; // 0 - before selection, 1 - in selection, 2 - after selection
   private Color myCurrentBackgroundColor;
+  private Color myLastBackgroundColor;
 
   private final List<RangeHighlighterEx> myCurrentHighlighters = new ArrayList<RangeHighlighterEx>();
 
@@ -128,6 +121,8 @@ public class IterationState {
   private final Color myDefaultForeground;
   private final int myCaretRowStart;
   private final int myCaretRowEnd;
+  private final boolean myCaretRowStartsWithSoftWrap;
+  private final boolean myCaretRowEndsWithSoftWrap;
   private final List<TextAttributes> myCachedAttributesList = new ArrayList<TextAttributes>(5);
   private final DocumentEx myDocument;
   private final EditorEx myEditor;
@@ -152,32 +147,16 @@ public class IterationState {
     if (!useCaretAndSelection) {
       mySelectionStarts = ArrayUtilRt.EMPTY_INT_ARRAY;
       mySelectionEnds = ArrayUtilRt.EMPTY_INT_ARRAY;
-      myVirtualSelectionStarts = ArrayUtilRt.EMPTY_INT_ARRAY;
-      myVirtualSelectionEnds = ArrayUtilRt.EMPTY_INT_ARRAY;
     }
     else {
       List<Caret> carets = editor.getCaretModel().getAllCarets();
       int caretCount = carets.size();
       mySelectionStarts = new int[caretCount];
       mySelectionEnds = new int[caretCount];
-      if (iterateBackwards) {
-        myVirtualSelectionStarts = ArrayUtilRt.EMPTY_INT_ARRAY;
-        myVirtualSelectionEnds = ArrayUtilRt.EMPTY_INT_ARRAY;
-      }
-      else {
-        myVirtualSelectionStarts = new int[caretCount];
-        myVirtualSelectionEnds = new int[caretCount];
-      }
       for (int i = 0; i < caretCount; i++) {
         Caret caret = carets.get(i);
         mySelectionStarts[iterateBackwards ? caretCount - i - 1 : i] = caret.getSelectionStart();
         mySelectionEnds[iterateBackwards ? caretCount - i - 1 : i] = caret.getSelectionEnd();
-        if (!iterateBackwards && caret.hasSelection()) {
-          myVirtualSelectionStarts[i] = caret.getSelectionStartPosition().column -
-                                        editor.offsetToVisualPosition(mySelectionStarts[i], true).column;
-          myVirtualSelectionEnds[i] = caret.getSelectionEndPosition().column -
-                                      editor.offsetToVisualPosition(mySelectionEnds[i]).column;
-        }
       }
     }
 
@@ -193,7 +172,14 @@ public class IterationState {
     myDefaultForeground = editor.getColorsScheme().getDefaultForeground();
 
     myCaretRowStart = caretModel.getVisualLineStart();
-    myCaretRowEnd = caretModel.getVisualLineEnd();
+    int visualLineEnd = caretModel.getVisualLineEnd();
+    if (visualLineEnd == myDocument.getTextLength() && myDocument.getLineCount() > 0 && 
+        visualLineEnd > myDocument.getLineStartOffset(myDocument.getLineCount() - 1)) {
+      visualLineEnd++;
+    }
+    myCaretRowEnd = visualLineEnd;
+    myCaretRowStartsWithSoftWrap = editor.getSoftWrapModel().getSoftWrap(myCaretRowStart) != null;
+    myCaretRowEndsWithSoftWrap = editor.getSoftWrapModel().getSoftWrap(myCaretRowEnd) != null;
 
     MarkupModelEx editorMarkup = editor.getMarkupModel();
     myView = new HighlighterSweep(editorMarkup, start, myEnd, useOnlyFullLineHighlighters);
@@ -283,7 +269,6 @@ public class IterationState {
     myStartOffset = myEndOffset;
     advanceSegmentHighlighters();
     advanceCurrentSelectionIndex();
-    advanceCurrentVirtualSelectionIndex();
 
     if (!myUseOnlyFullLineHighlighters) {
       myCurrentFold = myFoldingModel == null ? null :
@@ -377,14 +362,6 @@ public class IterationState {
     }
   }
 
-  private void advanceCurrentVirtualSelectionIndex() {
-    if (myReverseIteration) return;
-    while (myCurrentVirtualSelectionIndex < mySelectionEnds.length
-           && (myStartOffset > mySelectionEnds[myCurrentVirtualSelectionIndex] || myVirtualSelectionEnds[myCurrentVirtualSelectionIndex] <= 0)) {
-      myCurrentVirtualSelectionIndex++;
-    }
-  }
-
   private int getSelectionEnd() {
     if (myCurrentSelectionIndex >= mySelectionStarts.length) {
       return myEnd;
@@ -486,17 +463,13 @@ public class IterationState {
   }
 
   private void reinit() {
-    if (myHighlighterIterator != null && myHighlighterIterator.atEnd()) {
-      return;
-    }
-
     boolean isInSelection = isInSelection();
-    boolean isInCaretRow = myStartOffset > myCaretRowStart && myStartOffset < myCaretRowEnd ||
-                           myStartOffset == (myReverseIteration ? myCaretRowEnd : myCaretRowStart);
+    boolean isInCaretRow = isInCaretRow(!myReverseIteration, myReverseIteration);
     boolean isInGuardedBlock = !myUseOnlyFullLineHighlighters &&
                                myDocument.getOffsetGuard(myReverseIteration ? myStartOffset - 1 : myStartOffset) != null;
 
-    TextAttributes syntax = myHighlighterIterator == null ? null : myHighlighterIterator.getTextAttributes();
+    TextAttributes syntax = myHighlighterIterator == null || myHighlighterIterator.atEnd() ? 
+                            null : myHighlighterIterator.getTextAttributes();
 
     TextAttributes selection = isInSelection ? mySelectionAttributes : null;
     TextAttributes caret = isInCaretRow ? myCaretRowAttributes : null;
@@ -521,15 +494,10 @@ public class IterationState {
     List<TextAttributes> cachedAttributes = myCachedAttributesList;
     cachedAttributes.clear();
 
-    int selectionAttributesIndex = -1; // a 'would-be' or real position of selection attributes in attributes list
-
     //noinspection ForLoopReplaceableByForEach
     for (int i = 0; i < size; i++) {
       RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
       if (highlighter.getLayer() < HighlighterLayer.SELECTION) {
-        if (selectionAttributesIndex < 0) {
-          selectionAttributesIndex = cachedAttributes.size();
-        }
         if (selection != null) {
           cachedAttributes.add(selection);
           selection = null;
@@ -562,9 +530,6 @@ public class IterationState {
       }
     }
 
-    if (selectionAttributesIndex < 0) {
-      selectionAttributesIndex = cachedAttributes.size();
-    }
     if (selection != null) cachedAttributes.add(selection);
     if (fold != null) cachedAttributes.add(fold);
     if (guard != null) cachedAttributes.add(guard);
@@ -577,8 +542,6 @@ public class IterationState {
     EffectType effectType = null;
     int fontType = 0;
 
-    boolean selectionBackgroundIsPotentiallyVisible = cachedAttributes.isEmpty();
-
     //noinspection ForLoopReplaceableByForEach
     for (int i = 0; i < cachedAttributes.size(); i++) {
       TextAttributes attrs = cachedAttributes.get(i);
@@ -588,9 +551,6 @@ public class IterationState {
       }
 
       if (back == null) {
-        if (isInSelection && i == selectionAttributesIndex || !isInSelection && i >= selectionAttributesIndex) {
-          selectionBackgroundIsPotentiallyVisible = true;
-        }
         back = ifDiffers(attrs.getBackgroundColor(), myDefaultBackground);
       }
 
@@ -610,17 +570,13 @@ public class IterationState {
 
     myMergedAttributes.setAttributes(fore, back, effect, null, effectType, fontType);
 
+    myLastBackgroundColor = myCurrentBackgroundColor;
     myCurrentBackgroundColor = back;
-    if (selectionBackgroundIsPotentiallyVisible &&
-        myCurrentVirtualSelectionIndex < myVirtualSelectionStarts.length &&
-        myStartOffset == mySelectionEnds[myCurrentVirtualSelectionIndex]) {
-      myCurrentLineHasVirtualSelection = true;
-      myCurrentPastLineEndBackgroundSegment = myVirtualSelectionStarts[myCurrentVirtualSelectionIndex] > 0 ? 0 : 1;
-    }
-    else {
-      myCurrentLineHasVirtualSelection = false;
-      myCurrentPastLineEndBackgroundSegment = 0;
-    }
+  }
+
+  private boolean isInCaretRow(boolean includeLineStart, boolean includeLineEnd) {
+    return myStartOffset > myCaretRowStart && myStartOffset < myCaretRowEnd ||
+           includeLineStart && myStartOffset == myCaretRowStart || includeLineEnd && myStartOffset == myCaretRowEnd;
   }
 
   @Nullable
@@ -646,73 +602,22 @@ public class IterationState {
     return myMergedAttributes;
   }
 
-  public FoldRegion getCurrentFold() {
-    return myCurrentFold;
-  }
-
-  public boolean hasPastLineEndBackgroundSegment() {
-    return myCurrentLineHasVirtualSelection && myCurrentPastLineEndBackgroundSegment < 2;
-  }
-
-  public int getPastLineEndBackgroundSegmentWidth() {
-    switch (myCurrentPastLineEndBackgroundSegment) {
-      case 0: return myVirtualSelectionStarts[myCurrentVirtualSelectionIndex];
-      case 1: return myVirtualSelectionEnds[myCurrentVirtualSelectionIndex] - myVirtualSelectionStarts[myCurrentVirtualSelectionIndex];
-      default: return 0;
-    }
-  }
-
   @NotNull
   public TextAttributes getPastLineEndBackgroundAttributes() {
-    myMergedAttributes.setBackgroundColor(myCurrentPastLineEndBackgroundSegment == 1 ? mySelectionAttributes.getBackgroundColor() : myCurrentBackgroundColor);
+    myMergedAttributes.setBackgroundColor(myEditor.getSoftWrapModel().getSoftWrap(myStartOffset) != null ? getBreakBackgroundColor(true) : 
+                                          myCurrentBackgroundColor);
     return myMergedAttributes;
   }
-
-  public void advanceToNextPastLineEndBackgroundSegment() {
-    myCurrentPastLineEndBackgroundSegment++;
-  }
-
-  public boolean hasPastFileEndBackgroundSegments() {
-    myCurrentLineHasVirtualSelection = myVirtualSelectionEnds.length > 0
-                                       && myVirtualSelectionEnds[myVirtualSelectionEnds.length - 1] > 0
-                                       && myEndOffset == myEnd
-                                       && mySelectionEnds[mySelectionStarts.length - 1] == myEndOffset;
-    if (myCurrentLineHasVirtualSelection) {
-      myCurrentVirtualSelectionIndex = myVirtualSelectionStarts.length - 1;
-      myCurrentPastLineEndBackgroundSegment = myVirtualSelectionStarts[myCurrentVirtualSelectionIndex] > 0 ? 0 : 1;
-      myCurrentBackgroundColor = myEndOffset >= myCaretRowStart ? myCaretRowAttributes.getBackgroundColor() : myDefaultBackground;
-    }
-    return myCurrentLineHasVirtualSelection;
+  
+  @NotNull
+  public TextAttributes getBeforeLineStartBackgroundAttributes() {
+    return new TextAttributes(null, getBreakBackgroundColor(false), null, null, 0);
   }
 
-  @Nullable
-  public Color getPastFileEndBackground() {
-    boolean isInCaretRow = myEditor.getCaretModel().getLogicalPosition().line >= myDocument.getLineCount() - 1;
-
-    Color caret = isInCaretRow && myCaretRowAttributes != null ? myCaretRowAttributes.getBackgroundColor() : null;
-
-    ContainerUtil.quickSort(myCurrentHighlighters, LayerComparator.INSTANCE);
-
-    //noinspection ForLoopReplaceableByForEach
-    for (int i = 0; i < myCurrentHighlighters.size(); i++) {
-      RangeHighlighterEx highlighter = myCurrentHighlighters.get(i);
-      if (caret != null && highlighter.getLayer() < HighlighterLayer.CARET_ROW) {
-        return caret;
-      }
-
-      if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE
-          || myDocument.getLineNumber(highlighter.getEndOffset()) < myDocument.getLineCount() - 1) {
-        continue;
-      }
-
-      TextAttributes textAttributes = highlighter.getTextAttributes();
-      if (textAttributes != null) {
-        Color backgroundColor = textAttributes.getBackgroundColor();
-        if (backgroundColor != null) return backgroundColor;
-      }
-    }
-
-    return caret;
+  private Color getBreakBackgroundColor(boolean lineEnd) {
+    return Comparing.equal(myCurrentBackgroundColor, myLastBackgroundColor) ? myCurrentBackgroundColor : 
+           isInCaretRow(!myCaretRowStartsWithSoftWrap || !lineEnd, myCaretRowEndsWithSoftWrap && lineEnd) ? 
+           myCaretRowAttributes.getBackgroundColor() : myDefaultBackground;
   }
 
   private static class LayerComparator implements Comparator<RangeHighlighterEx> {
index 3ade53a00dbd255d4685168c610e8bf31684708a..a837bd451af3d3b9fd3cfffc293a69b9c8f807e0 100644 (file)
@@ -33,6 +33,8 @@ interface LineFragment {
   
   int getVisualColumnCount(float startX);
   
+  int offsetToLogicalColumn(int startColumn, int offset);
+  
   // columns are visual
   int logicalToVisualColumn(float startX, int startColumn, int column);
 
index 8fe0ac4905fe2a20f6166bd493b64c7d03bbd00b..78138c956ef051968a928586bfe8de0a5231e93b 100644 (file)
@@ -220,20 +220,27 @@ class LineLayout {
     return new Iterable<VisualFragment>() {
       @Override
       public Iterator<VisualFragment> iterator() {
-        return new VisualOrderIterator(startX, 0, myBidiRunsInVisualOrder);
+        return new VisualOrderIterator(startX, 0, 0, 0, myBidiRunsInVisualOrder);
       }
     };
   }
 
-  Iterable<VisualFragment> getFragmentsInVisualOrder(final float startX, final int startVisualColumn, int startOffset, int endOffset) {
+  Iterable<VisualFragment> getFragmentsInVisualOrder(final float startX, 
+                                                     final int startVisualColumn, 
+                                                     final int startOffset, 
+                                                     int endOffset) {
     assert startOffset <= endOffset;
     final BidiRun[] runs;
+    int startLogicalColumn = 0;
     if (startOffset == endOffset) {
       runs = new BidiRun[0];
     }
     else {
       List<BidiRun> runList = new ArrayList<BidiRun>();
       for (BidiRun run : myBidiRunsInLogicalOrder) {
+        if (run.startOffset < startOffset) {
+          startLogicalColumn = run.getLogicalColumn(startLogicalColumn, Math.min(startOffset, run.endOffset));
+        }
         if (run.endOffset <= startOffset) continue;
         if (run.startOffset >= endOffset) break;
         runList.add(run.subRun(startOffset, endOffset));
@@ -241,10 +248,11 @@ class LineLayout {
       runs = runList.toArray(new BidiRun[runList.size()]);
       reorderRunsVisually(runs);
     }
+    final int finalStartLogicalColumn = startLogicalColumn;
     return new Iterable<VisualFragment>() {
       @Override
       public Iterator<VisualFragment> iterator() {
-        return new VisualOrderIterator(startX, startVisualColumn, runs);
+        return new VisualOrderIterator(startX, startVisualColumn, finalStartLogicalColumn, startOffset, runs);
       }
     };
   }
@@ -261,48 +269,32 @@ class LineLayout {
     return false;
   }
 
-  boolean isDirectionBoundary(int offset, boolean leanForward) {
-    boolean prevIsRtl = false;
-    boolean found = offset == 0 && !leanForward;
-    for (BidiRun run : myBidiRunsInVisualOrder) {
-      boolean curIsRtl = run.isRtl();
-      if (found || offset == (curIsRtl ? run.endOffset : run.startOffset)) return curIsRtl != prevIsRtl;
-      if (offset > run.startOffset && offset < run.endOffset) return false;
-      found = (offset == (curIsRtl ? run.startOffset : run.endOffset));
-      prevIsRtl = curIsRtl;
-    }
-    return prevIsRtl;
-  }
-
   int findNearestDirectionBoundary(int offset, boolean lookForward) {
     if (lookForward) {
-      boolean foundOrigin = false;
-      boolean originIsRtl = false;
+      byte originLevel = -1;
       for (BidiRun run : myBidiRunsInLogicalOrder) {
-        if (foundOrigin) {
-          if (run.isRtl() != originIsRtl) return run.startOffset;
+        if (originLevel >= 0) {
+          if (run.level != originLevel) return run.startOffset;
         }
         else if (run.endOffset > offset) {
-          foundOrigin = true;
-          originIsRtl = run.isRtl();
+          originLevel = run.level;
         }
       }
-      return originIsRtl ? myBidiRunsInLogicalOrder[myBidiRunsInLogicalOrder.length - 1].endOffset : -1;
+      return originLevel > 0 ? myBidiRunsInLogicalOrder[myBidiRunsInLogicalOrder.length - 1].endOffset : -1;
     }
     else {
-      boolean foundOrigin = false;
-      boolean originIsRtl = false;
+      byte originLevel = -1;
       for (int i = myBidiRunsInLogicalOrder.length - 1; i >= 0; i--) {
         BidiRun run = myBidiRunsInLogicalOrder[i];
-        if (foundOrigin) {
-          if (run.isRtl() != originIsRtl) return run.endOffset;
+        if (originLevel >= 0) {
+          if (run.level != originLevel) return run.endOffset;
         }
         else if (run.startOffset < offset) {
-          foundOrigin = true;
-          originIsRtl = run.isRtl();
+          originLevel = run.level;
+
         }
       }
-      return originIsRtl ? 0 : -1;
+      return originLevel > 0 ? 0 : -1;
     }
   }
 
@@ -342,6 +334,19 @@ class LineLayout {
       }
       return run;
     }
+
+    private int getLogicalColumn(int startLogicalColumn, int offset) {
+      assert offset >= startOffset;
+      assert offset <= endOffset;
+      int currentStartOffset = startOffset;
+      for (LineFragment fragment : fragments) {
+        int currentEndOffset = currentStartOffset + fragment.getLength();
+        startLogicalColumn = fragment.offsetToLogicalColumn(startLogicalColumn, Math.min(offset, currentEndOffset) - currentStartOffset);
+        currentStartOffset = currentEndOffset;
+        if (offset <= currentStartOffset) break;
+      }
+      return startLogicalColumn;
+    }
   }
 
   private static class VisualOrderIterator implements Iterator<VisualFragment> {
@@ -351,10 +356,12 @@ class LineLayout {
     private int myOffsetInsideRun = 0;
     private VisualFragment myFragment = new VisualFragment();
 
-    public VisualOrderIterator(float startX, int startVisualColumn, BidiRun[] runsInVisualOrder) {
+    private VisualOrderIterator(float startX, int startVisualColumn, int startLogicalColumn, int startOffset, BidiRun[] runsInVisualOrder) {
       myRuns = runsInVisualOrder;
       myFragment.startX = startX;
       myFragment.startVisualColumn = startVisualColumn;
+      myFragment.startLogicalColumn = startLogicalColumn;
+      myFragment.startOffset = startOffset;
     }
 
     @Override
@@ -369,7 +376,10 @@ class LineLayout {
       }
       BidiRun run = myRuns[myRunIndex];
 
-      if (myRunIndex > 0 || myFragmentIndex > 0) {
+      if (myRunIndex == 0 && myFragmentIndex == 0) {
+        myFragment.startLogicalColumn += (run.isRtl() ? run.endOffset : run.startOffset) - myFragment.startOffset; 
+      }
+      else {
         myFragment.startLogicalColumn = myFragment.getEndLogicalColumn();
         if (myFragmentIndex == 0) {
           myFragment.startLogicalColumn += (run.isRtl() ? run.endOffset : run.startOffset) - myFragment.getEndOffset();
@@ -382,10 +392,6 @@ class LineLayout {
       myFragment.delegate = run.fragments.get(run.isRtl() ? run.fragments.size() - 1 - myFragmentIndex : myFragmentIndex);
       myFragment.startOffset = run.isRtl() ? run.endOffset - myOffsetInsideRun : run.startOffset + myOffsetInsideRun;
       
-      if (myRunIndex == 0 && myFragmentIndex == 0) {
-        myFragment.startLogicalColumn = myFragment.startOffset;
-      }
-
       myOffsetInsideRun += myFragment.getLength();
       myFragmentIndex++;
       if (myFragmentIndex >= run.fragments.size()) {
index 1b2f2135572dde193327c4b799275a9d5ed53f72..201e3619f3035a60bf343c94729dde43debf0122 100644 (file)
@@ -51,6 +51,11 @@ class TabFragment implements LineFragment {
   }
 
   @Override
+  public int offsetToLogicalColumn(int startColumn, int offset) {
+    return startColumn + (offset == 0 ? 0 : getLogicalColumnCount(startColumn));
+  }
+
+  @Override
   public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) {
   }
 
index c11a07a8075cc9132908e17332550325884ac929..ab0d21cf48c7d4e7a4801aa7817866bfdfbcb9d9 100644 (file)
@@ -73,6 +73,11 @@ class TextFragment implements LineFragment {
   }
 
   @Override
+  public int offsetToLogicalColumn(int startColumn, int offset) {
+    return startColumn + offset;
+  }
+
+  @Override
   public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) {
     assert startColumn >= 0; 
     assert endColumn <= myCharPositions.length;
@@ -192,6 +197,11 @@ class TextFragment implements LineFragment {
     }
 
     @Override
+    public int offsetToLogicalColumn(int startColumn, int offset) {
+      return startColumn + offset;
+    }
+
+    @Override
     public int logicalToVisualColumn(float startX, int startColumn, int column) {
       return column;
     }
index 6d21e3fbfb983df8ba9bd8db197ddf2f71712bff..9aa923715efde8f54185748468a3310de5433c12 100644 (file)
@@ -17,11 +17,15 @@ package com.intellij.openapi.editor.impl.view;
 
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.FoldRegion;
+import com.intellij.openapi.editor.SoftWrap;
 import com.intellij.openapi.editor.ex.FoldingModelEx;
 import com.intellij.openapi.editor.ex.util.EditorUtil;
+import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
 
 import java.awt.*;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
@@ -30,11 +34,11 @@ import java.util.NoSuchElementException;
  */
 class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterator.Fragment> {
 
-  static Iterable<Fragment> create(final EditorView view, final int offset) {
+  static Iterable<Fragment> create(final EditorView view, final int offset, final boolean beforeSoftWrap) {
     return new Iterable<Fragment>() {
       @Override
       public Iterator<Fragment> iterator() {
-        return new VisualLineFragmentsIterator(view, offset);
+        return new VisualLineFragmentsIterator(view, offset, beforeSoftWrap);
       }
     };
   }
@@ -43,6 +47,7 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
   private final Document myDocument;
   private final FoldRegion[] myRegions;
   private final Fragment myFragment = new Fragment();
+  private final int myVisualLineStartOffset;
   
   private int mySegmentStartOffset;
   private int mySegmentEndOffset;
@@ -54,17 +59,46 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
   private FoldRegion myFoldRegion;
   private int myCurrentStartLogicalLine;
   private int myCurrentEndLogicalLine;
+  private int myNextWrapOffset;
 
-  private VisualLineFragmentsIterator(EditorView view, int offset) {
+  private VisualLineFragmentsIterator(EditorView view, int offset, boolean beforeSoftWrap) {
     myView = view;
-    myDocument = view.getEditor().getDocument();
-    FoldingModelEx foldingModel = view.getEditor().getFoldingModel();
+    EditorImpl editor = view.getEditor();
+    myDocument = editor.getDocument();
+    FoldingModelEx foldingModel = editor.getFoldingModel();
     FoldRegion[] regions = foldingModel.fetchTopLevel();
     myRegions = regions == null ? FoldRegion.EMPTY_ARRAY : regions;
-    mySegmentStartOffset = EditorUtil.getNotFoldedLineStartOffset(view.getEditor(), offset);
+    int visualLineStartOffset = EditorUtil.getNotFoldedLineStartOffset(editor, offset);
+
+    SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel();
+    List<? extends SoftWrap> softWraps = softWrapModel.getRegisteredSoftWraps();
+    int currentOrPrevWrapIndex = softWrapModel.getSoftWrapIndex(offset);
+    if (currentOrPrevWrapIndex < 0) {
+      currentOrPrevWrapIndex = - currentOrPrevWrapIndex - 2;
+    }
+    else if (beforeSoftWrap) {
+      currentOrPrevWrapIndex--;
+    }
+    SoftWrap currentOrPrevWrap = currentOrPrevWrapIndex < 0 || currentOrPrevWrapIndex >= softWraps.size() ? null :
+                                 softWraps.get(currentOrPrevWrapIndex);
+    SoftWrap followingWrap = (currentOrPrevWrapIndex + 1) >= softWraps.size() ? null : softWraps.get(currentOrPrevWrapIndex + 1);
+
+    if (currentOrPrevWrap != null && currentOrPrevWrap.getStart() > visualLineStartOffset) {
+      visualLineStartOffset = currentOrPrevWrap.getStart();
+    }
+    
+    myVisualLineStartOffset = mySegmentStartOffset = visualLineStartOffset;
+
     myCurrentFoldRegionIndex = foldingModel.getLastCollapsedRegionBefore(mySegmentStartOffset) + 1;
     myCurrentEndLogicalLine = myDocument.getLineNumber(mySegmentStartOffset);
-    myCurrentX = myCurrentEndLogicalLine == 0 ? myView.getPrefixTextWidthInPixels() : 0;
+    if (mySegmentStartOffset == 0) {
+      myCurrentX = myView.getPrefixTextWidthInPixels();
+    }
+    else if (currentOrPrevWrap != null && mySegmentStartOffset == currentOrPrevWrap.getStart()) {
+      myCurrentX = currentOrPrevWrap.getIndentInPixels();
+      myCurrentVisualColumn = currentOrPrevWrap.getIndentInColumns();
+    }
+    myNextWrapOffset = followingWrap == null ? Integer.MAX_VALUE : followingWrap.getStart();
     setFragmentIterator();
   }
 
@@ -72,7 +106,7 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
     mySegmentEndOffset = getCurrentFoldRegionStartOffset();
     if (mySegmentEndOffset > mySegmentStartOffset) {
       int line = myDocument.getLineNumber(mySegmentStartOffset);
-      mySegmentEndOffset = Math.min(mySegmentEndOffset, myDocument.getLineEndOffset(line));
+      mySegmentEndOffset = Math.min(myNextWrapOffset, Math.min(mySegmentEndOffset, myDocument.getLineEndOffset(line)));
       int lineStartOffset = myDocument.getLineStartOffset(line);
       myFragmentIterator = myView.getLineLayout(line).getFragmentsInVisualOrder(myCurrentX, myCurrentVisualColumn,
                                                                                 mySegmentStartOffset - lineStartOffset, 
@@ -81,7 +115,11 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
   }
 
   private int getCurrentFoldRegionStartOffset() {
-    return myCurrentFoldRegionIndex < myRegions.length ? myRegions[myCurrentFoldRegionIndex].getStartOffset() : Integer.MAX_VALUE;
+    if (myCurrentFoldRegionIndex >= myRegions.length) {
+      return Integer.MAX_VALUE;
+    }
+    int nextFoldingOffset = myRegions[myCurrentFoldRegionIndex].getStartOffset();
+    return nextFoldingOffset < myNextWrapOffset ? nextFoldingOffset : Integer.MAX_VALUE;
   }
   
   private float getFoldRegionWidthInPixels(FoldRegion foldRegion) {
@@ -157,6 +195,10 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
   }
 
   class Fragment {    
+    int getVisualLineStartOffset() {
+      return myVisualLineStartOffset;
+    }
+    
     boolean isCollapsedFoldRegion() {
       return myDelegate == null;
     }
@@ -193,6 +235,10 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
       return myCurrentEndLogicalLine;
     }
     
+    float getStartX() {
+      return myDelegate == null ? myCurrentX - getFoldRegionWidthInPixels(myFoldRegion) : myDelegate.getStartX();
+    }
+
     float getEndX() {
       return myCurrentX;
     }
@@ -214,7 +260,7 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
     // - second one is 1 if target location is closer to larger columns and 0 otherwise
     int[] xToVisualColumn(float x) {
       if (myDelegate == null) {
-        int[] column = getVisualColumnForXInsideFoldRegion(myFoldRegion, x - (myCurrentX - getFoldRegionWidthInPixels(myFoldRegion)));
+        int[] column = getVisualColumnForXInsideFoldRegion(myFoldRegion, x - getStartX());
         column[0] += getStartVisualColumn();
         return column;
       }
@@ -225,7 +271,7 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
 
     float visualColumnToX(int column) {
       return myDelegate == null ? 
-             myCurrentX - getFoldRegionWidthInPixels(myFoldRegion) + 
+             getStartX() +
              getXForVisualColumnInsideFoldRegion(myFoldRegion, column - myCurrentVisualColumn + getFoldRegionWidthInColumns(myFoldRegion)) :
              myDelegate.visualColumnToX(column);
     }
@@ -257,7 +303,7 @@ class VisualLineFragmentsIterator implements Iterator<VisualLineFragmentsIterato
     // offset is absolute
     float offsetToX(int offset) {
       return myDelegate == null ?
-             myCurrentX - getFoldRegionWidthInPixels(myFoldRegion) + getXForOffsetInsideFoldRegion(myFoldRegion, offset) :
+             getStartX() + getXForOffsetInsideFoldRegion(myFoldRegion, offset) :
              myDelegate.offsetToX(offset - myDocument.getLineStartOffset(myCurrentStartLogicalLine));
     }
 
index 482694255f8f1aef41b08f5d6df8c8459e42f9b9..f63c53dbf2702181cd006123a377b80c34678592 100644 (file)
@@ -204,7 +204,7 @@ public class TextComponentCaret extends UserDataHolderBase implements Caret {
   }
 
   @Override
-  public boolean isAtDirectionBoundary() {
+  public boolean isAtBidiRunBoundary() {
     return false;
   }
 
index 6fd2e848cb4888f0e5afaab65dcdd8d134ca76e3..6266a81b539fb90c884733ecdf8f7e765177c06d 100644 (file)
@@ -202,7 +202,7 @@ public class TextComponentEditor extends UserDataHolderBase implements Editor {
 
   @NotNull
   @Override
-  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward) {
+  public VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap) {
     return offsetToVisualPosition(offset);
   }
 
index e9ba379a0a0a168627dd6ea771f569ea775e4935..8b431a29c472e9f232e1de460cf2b52b608858cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 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.
@@ -53,7 +53,7 @@ public abstract class AbstractEditorTest extends LightPlatformCodeInsightTestCas
   @Override
   protected void setUp() throws Exception {
     super.setUp();
-    FontLayoutService.setInstance(new MockFontLayoutService(10, 10, 2));
+    FontLayoutService.setInstance(new MockFontLayoutService(10, 10, 2)); // char width matches the one in EditorTestUtil.configureSoftWraps
   }
 
   @Override
index 47ec82130c98ca8105c90967d0350953d58732bb..9de06799d9280fe3267e1b66f6f7e4ad52d5413b 100644 (file)
@@ -58,7 +58,7 @@ public class EditorImplTest extends AbstractEditorTest {
                           "\t}\n" +
                           "}</selection>");
     CodeFoldingManager.getInstance(ourProject).buildInitialFoldings(myEditor);
-    configureSoftWraps(32);
+    EditorTestUtil.configureSoftWraps(myEditor, 232, 7); // wrap after 32 characters
 
     // verify initial state
     assertEquals(4, EditorUtil.getTabSize(myEditor));
index 38bd7c2835d51091491fa06a01172949abbb2cf8..bbe2de51f9123c960d954b7a0e2797d881dba461 100644 (file)
@@ -19,8 +19,6 @@ import com.intellij.openapi.actionSystem.IdeActions;
 import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.LogicalPosition;
 import com.intellij.openapi.editor.VisualPosition;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.editor.impl.view.IterationState;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.testFramework.TestFileType;
 
@@ -61,8 +59,8 @@ public class EditorRtlTest extends AbstractEditorTest {
     checkLPConversions(3, 3, vR(3), vL(3)); 
     checkLPConversions(4, 4, vR(2), vR(4)); 
     checkLPConversions(5, 4, vL(5), vR(5)); 
-    checkLPConversions(l(1, 0, false), 4, v(1, 0, false)); 
-    checkLPConversions(l(1, 0, true), 4, v(1, 0, true)); 
+    checkLPConversions(lB(1, 0), 4, vL(1, 0));
+    checkLPConversions(lF(1, 0), 4, vR(1, 0));
     
     checkVPConversions(vL(0), lB(0), xy(0));
     checkVPConversions(vR(0), lF(0), xy(0));
@@ -76,8 +74,8 @@ public class EditorRtlTest extends AbstractEditorTest {
     checkVPConversions(vR(4), lF(4), xy(40));
     checkVPConversions(vL(5), lF(5), xy(50));
     checkVPConversions(vR(5), lF(5), xy(50));
-    checkVPConversions(v(1, 0, false), l(1, 0, false), xy(0, 10));
-    checkVPConversions(v(1, 0, true), l(1, 0, true), xy(0, 10));
+    checkVPConversions(vL(1, 0), lB(1, 0), xy(0, 10));
+    checkVPConversions(vR(1, 0), lF(1, 0), xy(0, 10));
     
     checkXYConversion(xy(0),  vL(0));
     checkXYConversion(xy(12), vR(1));
@@ -209,6 +207,71 @@ public class EditorRtlTest extends AbstractEditorTest {
     checkLPConversions(3, 3, vR(4), vR(5));
     checkLPConversions(4, 4, vL(6), vR(6));
   }
+
+  public void testPositionCalculationsWithSoftWraps() throws Exception {
+    prepareText("RR RR");
+    configureSoftWraps(3);
+
+    checkOffsetConversions(0, lB(0), vL(0), vL(3), xy(0), xy(30));
+    checkOffsetConversions(1, lB(1), vR(2), vL(2), xy(20));
+    checkOffsetConversions(2, lB(2), vR(1), vL(1), xy(10));
+    checkOffsetConversions(3, lB(3), vL(1, 1), vL(1, 3),  xy(10, 10), xy(30, 10));
+    checkOffsetConversions(4, lB(4), vR(1, 2), vL(1, 2),  xy(20, 10));
+    checkOffsetConversions(5, lB(5), vR(1, 1), vR(1, 3),  xy(10, 10), xy(30, 10));
+    checkOffsetConversions(6, lB(5), vR(1, 1), vR(1, 3),  xy(10, 10), xy(30, 10));
+
+    checkLPConversions(0, 0, vL(0), vL(3));
+    checkLPConversions(1, 1, vR(2), vL(2));
+    checkLPConversions(2, 2, vR(1), vL(1));
+    checkLPConversions(3, 3, vL(1, 1), vL(1, 3));
+    checkLPConversions(4, 4, vR(1, 2), vL(1, 2));
+    checkLPConversions(5, 5, vR(1, 1), vR(1, 3));
+    checkLPConversions(6, 5, vL(1, 4), vR(1, 4));
+    checkLPConversions(lB(1, 0), 5, vL(2, 0));
+    checkLPConversions(lF(1, 0), 5, vR(2, 0));
+    checkLPConversions(lB(1, 1), 5, vL(2, 1));
+    checkLPConversions(lF(1, 1), 5, vR(2, 1));
+
+    checkVPConversions(0, lB(0), lB(3), xy(0));
+    checkVPConversions(1, lF(2), lB(2), xy(10));
+    checkVPConversions(2, lF(1), lB(1), xy(20));
+    checkVPConversions(3, lF(0), lF(3), xy(30));
+    checkVPConversions(4, lF(3), lF(3), xy(40));
+    checkVPConversions(5, lF(3), lF(3), xy(50));
+    checkVPConversions(vL(1, 0), lB(3), xy(0, 10));
+    checkVPConversions(vR(1, 0), lB(3), xy(0, 10));
+    checkVPConversions(vL(1, 1), lB(3), xy(10, 10));
+    checkVPConversions(vR(1, 1), lB(5), xy(10, 10));
+    checkVPConversions(vL(1, 2), lF(4), xy(20, 10));
+    checkVPConversions(vR(1, 2), lB(4), xy(20, 10));
+    checkVPConversions(vL(1, 3), lF(3), xy(30, 10));
+    checkVPConversions(vR(1, 3), lF(5), xy(30, 10));
+    checkVPConversions(vL(1, 4), lF(6), xy(40, 10));
+    checkVPConversions(vR(1, 4), lF(6), xy(40, 10));
+
+    checkXYConversion(xy(0), vL(0));
+    checkXYConversion(xy(2), vR(0));
+    checkXYConversion(xy(9), vL(1));
+    checkXYConversion(xy(13), vR(1));
+    checkXYConversion(xy(18), vL(2));
+    checkXYConversion(xy(24), vR(2));
+    checkXYConversion(xy(30), vL(3));
+    checkXYConversion(xy(31), vR(3));
+    checkXYConversion(xy(39), vL(4));
+    checkXYConversion(xy(42), vR(4));
+    checkXYConversion(xy(47), vL(5));
+    checkXYConversion(xy(54), vR(5));
+    checkXYConversion(xy(0, 10), vL(1, 0));
+    checkXYConversion(xy(2, 10), vR(1, 0));
+    checkXYConversion(xy(9, 10), vL(1, 1));
+    checkXYConversion(xy(11, 10), vR(1, 1));
+    checkXYConversion(xy(18, 10), vL(1, 2));
+    checkXYConversion(xy(23, 10), vR(1, 2));
+    checkXYConversion(xy(30, 10), vL(1, 3));
+    checkXYConversion(xy(34, 10), vR(1, 3));
+    checkXYConversion(xy(37, 10), vL(1, 4));
+    checkXYConversion(xy(41, 10), vR(1, 4));
+  }
   
   public void testSelectingRtlLineByDraggingMouseFromLeftToRight() throws IOException {
     prepareText("R");
@@ -252,11 +315,11 @@ public class EditorRtlTest extends AbstractEditorTest {
     right();
     assertCaretPosition(vL(4));
     down();
-    assertCaretPosition(v(1, 4, false));
+    assertCaretPosition(vL(1, 4));
     left();
-    assertCaretPosition(v(1, 3, true));
+    assertCaretPosition(vR(1, 3));
     left();
-    assertCaretPosition(v(1, 2, true));
+    assertCaretPosition(vR(1, 2));
     up();
     assertCaretPosition(vR(2));
   }
@@ -292,6 +355,8 @@ public class EditorRtlTest extends AbstractEditorTest {
     right();
     assertVisualCaretLocation(3, true);
     right();
+    assertVisualCaretLocation(3, true);
+    right();
     assertVisualCaretLocation(4, true);
   }
   
@@ -424,20 +489,7 @@ public class EditorRtlTest extends AbstractEditorTest {
     assertVisualCaretLocation(2, false);
     checkResult("R<selection>R</selection>");
   }
-  
-  public void testIterationStateAfterLineEnd() throws Exception {
-    prepareText("RL");
-    myEditor.getCaretModel().moveToVisualPosition(new VisualPosition(0, 1, true));
 
-    IterationState it = new IterationState((EditorEx)myEditor, 1, 2, true, false, false, false);
-    assertFalse(it.atEnd());
-    assertEquals(1, it.getStartOffset());
-    assertEquals(2, it.getEndOffset());
-    it.advance();
-    assertTrue(it.atEnd());
-    assertFalse(it.hasPastLineEndBackgroundSegment());
-  }
-  
   public void testUsingLexerForBidiLayout() throws Exception {
     prepare("class Foo {\n  int<caret> R = 1;\n}", TestFileType.JAVA);
     right();
@@ -467,7 +519,38 @@ public class EditorRtlTest extends AbstractEditorTest {
   public void testMovingThroughBoundaryBetweenRunsWithNonadjacentLevels() throws Exception {
     prepareText("R=1");
     right();
+    assertVisualCaretLocation(0, false);
+    assertEquals(2, myEditor.getCaretModel().getOffset());
+    right();
     assertVisualCaretLocation(1, false);
+    assertEquals(3, myEditor.getCaretModel().getOffset());
+    right();
+    assertVisualCaretLocation(1, true);
+    assertEquals(2, myEditor.getCaretModel().getOffset());
+  }
+  
+  public void testMovingCaretThroughSoftWrap() throws Exception {
+    prepareText("RR RR");
+    configureSoftWraps(3);
+    
+    right();
+    assertVisualCaretLocation(0, true);
+    right();
+    assertVisualCaretLocation(1, true);
+    right();
+    assertVisualCaretLocation(2, true);
+    right();
+    assertVisualCaretLocation(3, true);
+    right();
+    assertVisualCaretLocation(3, false);
+    right();
+    assertVisualCaretLocation(1, 1, false);
+    right();
+    assertVisualCaretLocation(1, 1, true);
+    right();
+    assertVisualCaretLocation(1, 2, true);
+    right();
+    assertVisualCaretLocation(1, 3, true);
   }
   
   private void prepareText(String text) throws IOException {
@@ -498,11 +581,9 @@ public class EditorRtlTest extends AbstractEditorTest {
                                              Point xyTowardsLargerOffsets) {
     assertLogicalPositionsEqual("Wrong offset->logicalPosition calculation", logicalPosition, myEditor.offsetToLogicalPosition(offset));
     assertVisualPositionsEqual("Wrong beforeOffset->visualPosition calculation",
-                               visualPositionTowardsSmallerOffsets, myEditor.offsetToVisualPosition(offset, false));
-    assertEquals("Wrong beforeOffset->visualLine calculation", 
-                 visualPositionTowardsSmallerOffsets.line, ((EditorImpl)myEditor).offsetToVisualLine(offset));
+                               visualPositionTowardsSmallerOffsets, myEditor.offsetToVisualPosition(offset, false, false));
     assertVisualPositionsEqual("Wrong afterOffset->visualPosition calculation",
-                               visualPositionTowardsLargerOffsets, myEditor.offsetToVisualPosition(offset, true));
+                               visualPositionTowardsLargerOffsets, myEditor.offsetToVisualPosition(offset, true, false));
     assertEquals("Wrong afterOffset->visualLine calculation", 
                  visualPositionTowardsLargerOffsets.line, ((EditorImpl)myEditor).offsetToVisualLine(offset));
     assertEquals("Wrong beforeOffset->xy calculation", xyTowardsSmallerOffsets, ((EditorImpl)myEditor).offsetToXY(offset, false));
@@ -518,12 +599,18 @@ public class EditorRtlTest extends AbstractEditorTest {
   
   private static void checkLPConversions(LogicalPosition logicalPosition, int offset, VisualPosition visualPosition) {
     assertEquals("Wrong logicalPosition->offset calculation", offset, myEditor.logicalPositionToOffset(logicalPosition));
-    assertVisualPositionsEqual("Wrong beforeLogicalPosition->visualPosition calculation",
+    assertVisualPositionsEqual("Wrong logicalPosition->visualPosition calculation",
                                visualPosition, myEditor.logicalToVisualPosition(logicalPosition));
   }
 
+  private static void checkVPConversions(int visualColumn, LogicalPosition logicalPositionForLeftLeaningVp,
+                                         LogicalPosition logicalPositionForRightLeaningVp, Point xy) {
+    checkVPConversions(vL(visualColumn), logicalPositionForLeftLeaningVp, xy);
+    checkVPConversions(vR(visualColumn), logicalPositionForRightLeaningVp, xy);
+  }
+
   private static void checkVPConversions(VisualPosition visualPosition, LogicalPosition logicalPosition, Point xy) {
-    assertLogicalPositionsEqual("Wrong beforeVisualPosition->logicalPosition calculation",
+    assertLogicalPositionsEqual("Wrong visualPosition->logicalPosition calculation",
                                 logicalPosition, myEditor.visualToLogicalPosition(visualPosition));
     assertEquals("Wrong visualPosition->xy calculation", xy, myEditor.visualPositionToXY(visualPosition));
   }
@@ -548,8 +635,13 @@ public class EditorRtlTest extends AbstractEditorTest {
   }
 
   private static void assertVisualCaretLocation(int visualColumn, boolean reversedDirection) {
+    assertVisualCaretLocation(0, visualColumn, reversedDirection);
+  }
+  
+  private static void assertVisualCaretLocation(int visualLine, int visualColumn, boolean reversedDirection) {
     assertEquals(1, myEditor.getCaretModel().getCaretCount());
     Caret caret = myEditor.getCaretModel().getPrimaryCaret();
+    assertEquals(visualLine, caret.getVisualPosition().line);
     assertEquals(visualColumn, caret.getVisualPosition().column);
     assertEquals(reversedDirection, caret.isAtRtlLocation());
   }
@@ -559,13 +651,19 @@ public class EditorRtlTest extends AbstractEditorTest {
     return new LogicalPosition(0, column);
   }
   
+  // logical position leaning backward
+  private static LogicalPosition lB(int line, int column) {
+    return new LogicalPosition(line, column);
+  }
+
   // logical position leaning forward
   private static LogicalPosition lF(int column) {
     return new LogicalPosition(0, column, true);
   }
   
-  private static LogicalPosition l(int line, int column, boolean leanTowardsLargerColumns) {
-    return new LogicalPosition(line, column, leanTowardsLargerColumns);
+  // logical position leaning forward
+  private static LogicalPosition lF(int line, int column) {
+    return new LogicalPosition(line, column, true);
   }
  
   // visual position leaning to the left
@@ -573,15 +671,21 @@ public class EditorRtlTest extends AbstractEditorTest {
     return new VisualPosition(0, column);
   }
 
+  // visual position leaning to the left
+  private static VisualPosition vL(int line, int column) {
+    return new VisualPosition(line, column);
+  }
+
   // visual position leaning to the right
   private static VisualPosition vR(int column) {
     return new VisualPosition(0, column, true);
   }
-  
-  private static VisualPosition v(int line, int column, boolean leanTowardsLargerColumns) {
-    return new VisualPosition(line, column, leanTowardsLargerColumns);
+
+  // visual position leaning to the right
+  private static VisualPosition vR(int line, int column) {
+    return new VisualPosition(line, column, true);
   }
-  
+
   private static Point xy(int x) {
     return new Point(x, 0);
   }
index 8e1261e070a65ce4c5564c0bcf5c73942efffac6..0c726148b7d306af8707d5a795f203af107f98ea 100644 (file)
@@ -174,7 +174,7 @@ public class EditorTestUtil {
    */
   @TestOnly
   public static boolean configureSoftWraps(Editor editor, final int charCountToWrapAt) {
-    int charWidthInPixels = 7;
+    int charWidthInPixels = 10;
     // we're adding 1 to charCountToWrapAt, to account for wrap character width, and 1 to overall width to overcome wrapping logic subtleties
     return configureSoftWraps(editor, (charCountToWrapAt + 1) * charWidthInPixels + 1, charWidthInPixels);
   }
index a1b0d0f4f16f9393a906e515cbd685cb5dd3b04f..feed51aafca64fd039f8e7d69a640048234c6ac7 100644 (file)
@@ -527,10 +527,10 @@ new.css.schema.enabled=true
 html.prefer.short.notation.of.boolean.attributes=true
 
 editor.disable.rtl=false
-editor.disable.rtl.description=Disables RTL support in editor (which is broken now anyway)
+editor.disable.rtl.description=Disables RTL support in editor (all characters are displayed in LTR order)
 
 editor.new.rendering=false
-editor.new.rendering.description=Enables new editor rendering code, with support for RTL. Soft wraps are not supported yet.
+editor.new.rendering.description=Enables new editor rendering code, with support for RTL.
 
 decompiler.use.line.mapping=true
 decompiler.use.line.mapping.description=Maps original to decompiled line numbers when stepping in debugger.
@@ -635,4 +635,4 @@ editor.breadcrumbs.highlight.on.hover.description=Highlight corresponding ranges
 testDiscovery.enabled=false
 ruby.remote.debugger.supports.catchpoint.removal=true
 
-use.read.action.to.init.service=true
\ No newline at end of file
+use.read.action.to.init.service=true
index 6afbc181b98c8133fe59a807a4c000b6e6033856..22a0a45e7a8e79b2b409e2001de3defd3a2d9317 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 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.
@@ -37,6 +37,7 @@ import com.intellij.openapi.editor.CaretModel;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.LogicalPosition;
 import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.ex.util.EditorUtil;
 import com.intellij.openapi.editor.highlighter.FragmentedEditorHighlighter;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.fileEditor.FileEditorManager;
@@ -569,8 +570,8 @@ public class ChangesFragmentedDiffPanel implements Disposable {
     int cutEndLine = lp.line == 0 ? 0 : lp.line - 1;
 
     boolean commonPartOk = leftPixels == 0 || startLp.line == lp.line;
-    return new BeforeAfter<Integer>(commonPartOk && startLp.softWrapLinesOnCurrentLogicalLine == 0 ? startLp.line : curStartLine,
-                                    commonPartOk && lp.softWrapLinesOnCurrentLogicalLine == 0 ? lp.line : cutEndLine);
+    return new BeforeAfter<Integer>(commonPartOk && EditorUtil.getSoftWrapCountAfterLineStart(editor, startLp) == 0 ? startLp.line : curStartLine,
+                                    commonPartOk && EditorUtil.getSoftWrapCountAfterLineStart(editor, lp) == 0 ? lp.line : cutEndLine);
   }
 
   private static final int[] ourMarks = {1,2,4,8,-1};