diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
index c47b95fb2640..a107bb1d9691 100644
--- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
+++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
@@ -398,8 +398,8 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
myDelegate.setBackgroundColor(color);
}
- public Color getBackroundColor() {
- return myDelegate.getBackroundColor();
+ public Color getBackgroundColor() {
+ return myDelegate.getBackgroundColor();
}
public int getMaxWidthInRange(final int startOffset, final int endOffset) {
diff --git a/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java b/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java
index 67bd7828d84c..ae447fe5ee2f 100644
--- a/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java
+++ b/platform/platform-api/src/com/intellij/openapi/editor/SoftWrapModel.java
@@ -18,6 +18,8 @@ package com.intellij.openapi.editor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.List;
+
/**
* Defines contract for the services that bring 'soft wrapping' to the editor.
*
@@ -85,6 +87,15 @@ public interface SoftWrapModel {
@Nullable
TextChange getSoftWrap(int offset);
+ /**
+ * Allows to ask current model about all soft wraps registered for the given document line.
+ *
+ * @param documentLine target document line
+ * @return all soft wraps registered for the given document line
+ */
+ @NotNull
+ List getSoftWrapsForLine(int documentLine);
+
/**
* Notifies current model that target document is about to be changed at location identified by the given visual position.
*
diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
index 41decf45c2c5..2da522b899e7 100644
--- a/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
+++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/EditorFragmentComponent.java
@@ -160,7 +160,7 @@ public class EditorFragmentComponent extends JPanel {
int endLine,
boolean showFolding, boolean showGutter) {
final EditorEx editorEx = (EditorEx)editor;
- final Color old = editorEx.getBackroundColor();
+ final Color old = editorEx.getBackgroundColor();
Color backColor = getBackgroundColor(editor);
editorEx.setBackgroundColor(backColor);
EditorFragmentComponent fragmentComponent = new EditorFragmentComponent(editorEx, startLine, endLine,
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java
index aff8e62e1515..841a909719df 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java
@@ -24,6 +24,11 @@
*/
package com.intellij.openapi.editor.actions;
+import com.intellij.ide.ui.customization.CustomActionsSchema;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPlaces;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseEventArea;
@@ -31,17 +36,20 @@ import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
-import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.EditorPopupHandler;
-import com.intellij.ide.ui.customization.CustomActionsSchema;
import java.awt.*;
import java.awt.event.MouseEvent;
+import java.util.List;
public class EditorActionUtil {
protected static final Object EDIT_COMMAND_GROUP = Key.create("EditGroup");
protected static final Object DELETE_COMMAND_GROUP = Key.create("DeleteGroup");
+ private EditorActionUtil() {
+ }
+
public static void scrollRelatively(Editor editor, int lineShift) {
if (lineShift != 0) {
editor.getScrollingModel().scrollVertically(
@@ -69,10 +77,10 @@ public class EditorActionUtil {
int columnShift,
int lineShift,
boolean withSelection) {
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
+ Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
VisualPosition pos = editor.getCaretModel().getVisualPosition();
Point caretLocation = editor.visualPositionToXY(pos);
- int caretVShift = caretLocation.y - viewRect.y;
+ int caretVShift = caretLocation.y - visibleArea.y;
editor.getCaretModel().moveCaretRelatively(columnShift, lineShift, withSelection, false, false);
@@ -82,6 +90,7 @@ public class EditorActionUtil {
editor.getScrollingModel().scrollVertically(caretLocation2.y - caretVShift);
}
+ @SuppressWarnings({"AssignmentToForLoopParameter"})
public static void indentLine(Project project, Editor editor, int lineNumber, int indent) {
EditorSettings editorSettings = editor.getSettings();
Document document = editor.getDocument();
@@ -172,21 +181,21 @@ public class EditorActionUtil {
char current = text.charAt(offset);
char next = offset + 1 < text.length() ? text.charAt(offset + 1) : 0;
- final boolean firstIsIdentifiePart = Character.isJavaIdentifierPart(prev);
+ final boolean firstIsIdentifierPart = Character.isJavaIdentifierPart(prev);
final boolean secondIsIdentifierPart = Character.isJavaIdentifierPart(current);
- if (firstIsIdentifiePart && !secondIsIdentifierPart) {
+ if (firstIsIdentifierPart && !secondIsIdentifierPart) {
return true;
}
if (isCamel) {
- if (firstIsIdentifiePart && secondIsIdentifierPart &&
+ if (firstIsIdentifierPart && secondIsIdentifierPart &&
(Character.isLowerCase(prev) && Character.isUpperCase(current) || prev != '_' && current == '_' ||
Character.isUpperCase(prev) && Character.isUpperCase(current) && Character.isLowerCase(next))) {
return true;
}
}
- return !Character.isWhitespace(prev) && !firstIsIdentifiePart &&
+ return !Character.isWhitespace(prev) && !firstIsIdentifierPart &&
(Character.isWhitespace(current) || secondIsIdentifierPart);
}
@@ -198,98 +207,197 @@ public class EditorActionUtil {
LogicalPosition blockSelectionStart = selectionModel.hasBlockSelection()
? selectionModel.getBlockStart()
: caretModel.getLogicalPosition();
-
- int columnNumber = editor.getCaretModel().getLogicalPosition().column;
- int lineNumber = editor.getCaretModel().getLogicalPosition().line;
-
- VisualPosition visCaret = editor.getCaretModel().getVisualPosition();
- while (lineNumber >= 0 && editor.logicalToVisualPosition(new LogicalPosition(lineNumber, 0)).line == visCaret.line) lineNumber--;
- lineNumber++;
-
EditorSettings editorSettings = editor.getSettings();
- if (!editorSettings.isSmartHome()) {
- LogicalPosition pos = new LogicalPosition(lineNumber, 0);
- editor.getCaretModel().moveToLogicalPosition(pos);
+ int logCaretLine = caretModel.getLogicalPosition().line;
+ VisualPosition currentVisCaret = caretModel.getVisualPosition();
+ VisualPosition caretLogLineStartVis = editor.offsetToVisualPosition(document.getLineStartOffset(logCaretLine));
+
+ if (currentVisCaret.line > caretLogLineStartVis.line) {
+ // Caret is located not at the first visual line of soft-wrapped logical line.
+ moveCaretToStartOfSoftWrappedLine(editor, currentVisCaret);
setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
return;
}
- int offset = editor.getCaretModel().getOffset();
- if (lineNumber >= document.getLineCount() || offset >= document.getTextLength()) {
- int newColNumber = 0;
- if (columnNumber == 0) {
- newColNumber = findSmartIndent(editor, offset);
+ // Skip folded lines.
+ int logLineToUse = logCaretLine - 1;
+ while (logLineToUse >= 0 && editor.offsetToVisualPosition(document.getLineEndOffset(logLineToUse)).line == currentVisCaret.line) {
+ logLineToUse--;
+ }
+ logLineToUse++;
+
+ if (logLineToUse >= document.getLineCount()) {
+ editor.getCaretModel().moveToVisualPosition(new VisualPosition(logLineToUse, 0));
+ }
+ else if (logLineToUse == logCaretLine) {
+ int line = currentVisCaret.line;
+ int column = 0;
+ if (currentVisCaret.column == 0) {
+ column = findSmartIndentColumn(editor, currentVisCaret.line);
}
- LogicalPosition pos = new LogicalPosition(lineNumber, newColNumber);
- editor.getCaretModel().moveToLogicalPosition(pos);
-
- setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
- editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
- return;
+ caretModel.moveToVisualPosition(new VisualPosition(line, column));
}
-
- int start = findFirstNonspaceOffsetOnTheLine(document, lineNumber);
- int lineEnd = document.getLineEndOffset(lineNumber);
- if (lineNumber > 0 && lineEnd == start && columnNumber == 0) {
- int newColNumber = findSmartIndent(editor, offset);
- LogicalPosition pos = new LogicalPosition(lineNumber, newColNumber);
- editor.getCaretModel().moveToLogicalPosition(pos);
- setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
- editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
- return;
+ else {
+ LogicalPosition logLineEndLog = editor.offsetToLogicalPosition(document.getLineEndOffset(logLineToUse));
+ VisualPosition logLineEndVis = editor.logicalToVisualPosition(logLineEndLog);
+ if (logLineEndLog.isOnSoftWrappedLine()) {
+ moveCaretToStartOfSoftWrappedLine(editor, logLineEndVis);
+ }
+ else {
+ int line = logLineEndVis.line;
+ int column = 0;
+ if (currentVisCaret.column == 0 && editorSettings.isSmartHome()) {
+ findSmartIndentColumn(editor, line);
+ }
+ caretModel.moveToVisualPosition(new VisualPosition(line, column));
+ }
}
- int lineStart = document.getLineStartOffset(lineNumber);
- int newOffset = lineStart;
- if (start < offset || columnNumber == 0) {
- newOffset = start;
- }
- editor.getCaretModel().moveToOffset(newOffset);
- editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
-
setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
+ editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
- private static int findSmartIndent(Editor editor, int offset) {
- int nonSpaceLineNumber = findFirstNonspaceLineBack(editor.getDocument(), offset);
- if (nonSpaceLineNumber >= 0) {
- int newOffset = findFirstNonspaceOffsetOnTheLine(editor.getDocument(), nonSpaceLineNumber);
- return editor.offsetToLogicalPosition(newOffset).column;
+ private static void moveCaretToStartOfSoftWrappedLine(Editor editor, VisualPosition currentVisual) {
+ CaretModel caretModel = editor.getCaretModel();
+ int line = currentVisual.line;
+ int column = 0;
+
+ if (currentVisual.column == 0) {
+ line--;
+ int nonSpaceColumn = findFirstNonSpaceColumnOnTheLine(editor, line);
+ column = nonSpaceColumn >= 0 ? nonSpaceColumn : 0;
+ }
+ else {
+ int nonSpaceColumn = findFirstNonSpaceColumnOnTheLine(editor, currentVisual.line);
+ if (nonSpaceColumn > 0 /* current visual line is not empty */ && nonSpaceColumn != currentVisual.column) {
+ column = nonSpaceColumn;
+ }
+ }
+
+ caretModel.moveToVisualPosition(new VisualPosition(line, column));
+ }
+
+ private static int findSmartIndentColumn(Editor editor, int visualLine) {
+ for (int i = visualLine; i >= 0; i--) {
+ int column = findFirstNonSpaceColumnOnTheLine(editor, visualLine);
+ if (column >= 0) {
+ return column;
+ }
}
return 0;
}
- private static int findFirstNonspaceLineBack(Document document, int offset) {
- CharSequence text = document.getCharsSequence();
- int foundOffset = offset - 1;
- for (; foundOffset > 0; foundOffset--) {
- char c = text.charAt(foundOffset);
- if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
- break;
+ /**
+ * Tries to find visual column that points to the first non-white space symbol at the visual line at the given editor.
+ *
+ * @param editor target editor
+ * @param visualLineNumber target visual line
+ * @return visual column that points to the first non-white space symbol at the target visual line if the one exists;
+ * '-1' otherwise
+ */
+ public static int findFirstNonSpaceColumnOnTheLine(Editor editor, int visualLineNumber) {
+ Document document = editor.getDocument();
+ VisualPosition visLine = new VisualPosition(visualLineNumber, 0);
+ int logLine = editor.visualToLogicalPosition(visLine).line;
+ int logLineStartOffset = document.getLineStartOffset(logLine);
+ int logLineEndOffset = document.getLineEndOffset(logLine);
+ LogicalPosition logLineStart = editor.offsetToLogicalPosition(logLineStartOffset);
+ VisualPosition visLineStart = editor.logicalToVisualPosition(logLineStart);
+
+ boolean softWrapIntroducedLine = visLineStart.line != visualLineNumber;
+ if (!softWrapIntroducedLine) {
+ int offset = findFirstNonSpaceOffsetInRange(document.getCharsSequence(), logLineStartOffset, logLineEndOffset);
+ if (offset >= 0) {
+ return EditorUtil.calcColumnNumber(editor, document.getCharsSequence(), logLineStartOffset, offset);
+ }
+ else {
+ return -1;
}
}
- if (foundOffset == -1) {
- return -1;
- }
- else {
- return document.getLineNumber(foundOffset);
+
+ int lineFeedsToSkip = visualLineNumber - visLineStart.line;
+ List softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(logLine);
+ for (TextChange softWrap : softWraps) {
+ int softWrapLineFeedsNumber = StringUtil.countNewLines(softWrap.getText());
+
+ if (softWrapLineFeedsNumber < lineFeedsToSkip) {
+ lineFeedsToSkip -= softWrapLineFeedsNumber;
+ continue;
+ }
+
+ // Point to the first non-white space symbol at the target soft wrap visual line or to the first non-white space symbol
+ // of document line that follows it if possible.
+ CharSequence softWrapText = softWrap.getText();
+ int softWrapTextLength = softWrapText.length();
+ boolean skip = true;
+ for (int j = 0; j < softWrapTextLength; j++) {
+ if (softWrapText.charAt(j) == '\n') {
+ skip = --lineFeedsToSkip > 0;
+ continue;
+ }
+ if (skip) {
+ continue;
+ }
+
+ int nextSoftWrapLineFeedOffset = StringUtil.indexOf(softWrapText, '\n', j, softWrapTextLength);
+
+ int end = findFirstNonSpaceOffsetInRange(softWrapText, j, softWrapTextLength);
+ if (end >= 0) {
+ // Non space symbol is contained at soft wrap text after offset that corresponds to the target visual line start.
+ if (nextSoftWrapLineFeedOffset < 0 || end < nextSoftWrapLineFeedOffset) {
+ return EditorUtil.calcColumnNumber(editor, softWrapText, j, end);
+ }
+ else {
+ return -1;
+ }
+ }
+
+ if (nextSoftWrapLineFeedOffset >= 0) {
+ // There are soft wrap-introduced visual lines after the target one
+ return -1;
+ }
+
+ end = findFirstNonSpaceOffsetInRange(document.getCharsSequence(), softWrap.getStart(), logLineEndOffset);
+ if (end >= 0) {
+ // Width of soft wrap virtual text that is located at the target visual line.
+ int result = EditorUtil.calcColumnNumber(editor, softWrapText, j, softWrapTextLength);
+ result += EditorUtil.calcColumnNumber(editor, document.getCharsSequence(), softWrap.getStart(), end);
+ return result;
+ }
+ else {
+ return -1;
+ }
+ }
}
+ return -1;
}
- public static int findFirstNonspaceOffsetOnTheLine(Document document, int lineNumber) {
+ public static int findFirstNonSpaceOffsetOnTheLine(Document document, int lineNumber) {
int lineStart = document.getLineStartOffset(lineNumber);
int lineEnd = document.getLineEndOffset(lineNumber);
- CharSequence text = document.getCharsSequence();
- int start = lineStart;
- for (; start < lineEnd; start++) {
+ int result = findFirstNonSpaceOffsetInRange(document.getCharsSequence(), lineStart, lineEnd);
+ return result >= 0 ? result : lineEnd;
+ }
+
+ /**
+ * Tries to find non white space symbol at the given range at the given document.
+ *
+ * @param text text to be inspected
+ * @param start target start offset (inclusive)
+ * @param end target end offset (exclusive)
+ * @return index of the first non-white space character at the given document at the given range if the one is found;
+ * '-1' otherwise
+ */
+ public static int findFirstNonSpaceOffsetInRange(CharSequence text, int start, int end) {
+ for (; start < end; start++) {
char c = text.charAt(start);
if (c != ' ' && c != '\t') {
return start;
}
}
- return lineEnd;
+ return -1;
}
public static void moveCaretToLineEnd(Editor editor, boolean isWithSelection) {
@@ -309,10 +417,33 @@ public class EditorActionUtil {
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
return;
}
- VisualPosition visCaret = editor.getCaretModel().getVisualPosition();
- visCaret = new VisualPosition(visCaret.line, EditorUtil.getLastVisualLineColumnNumber(editor, visCaret.line));
+ VisualPosition currentVisualCaret = editor.getCaretModel().getVisualPosition();
+ VisualPosition visualEndOfLineWithCaret
+ = new VisualPosition(currentVisualCaret.line, EditorUtil.getLastVisualLineColumnNumber(editor, currentVisualCaret.line));
- LogicalPosition logLineEnd = editor.visualToLogicalPosition(visCaret);
+ // There is a possible case that the caret is already located at the visual end of line and the line is soft wrapped.
+ // We want to move the caret to the end of the next visual line then.
+ if (currentVisualCaret.equals(visualEndOfLineWithCaret)) {
+ LogicalPosition logical = editor.visualToLogicalPosition(visualEndOfLineWithCaret);
+ int offset = editor.logicalPositionToOffset(logical);
+ if (offset < editor.getDocument().getTextLength()) {
+ SoftWrapModel softWrapModel = editor.getSoftWrapModel();
+ TextChange softWrap = softWrapModel.getSoftWrap(offset);
+ if (softWrap == null) {
+ // Same offset may correspond to positions on different visual lines in case of soft wraps presence
+ // (all soft-wrap introduced virtual text is mapped to the same offset as the first document symbol after soft wrap).
+ // Hence, we check for soft wraps presence at two offsets.
+ softWrap = softWrapModel.getSoftWrap(offset + 1);
+ }
+ if (softWrap != null) {
+ int line = currentVisualCaret.line + 1;
+ int column = EditorUtil.getLastVisualLineColumnNumber(editor, line);
+ visualEndOfLineWithCaret = new VisualPosition(line, column);
+ }
+ }
+ }
+
+ LogicalPosition logLineEnd = editor.visualToLogicalPosition(visualEndOfLineWithCaret);
int offset = editor.logicalPositionToOffset(logLineEnd);
lineNumber = logLineEnd.line;
int newOffset = offset;
@@ -325,11 +456,17 @@ public class EditorActionUtil {
newOffset = i;
}
- if (newOffset == editor.getCaretModel().getOffset()) {
- newOffset = offset;
+ // Check if there are white space symbols between the current caret position and visual line end.
+ if (newOffset == offset) {
+ caretModel.moveToVisualPosition(visualEndOfLineWithCaret);
+ }
+ else {
+ if (newOffset == caretModel.getOffset()) {
+ newOffset = offset;
+ }
+ caretModel.moveToOffset(newOffset);
}
- editor.getCaretModel().moveToOffset(newOffset);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
@@ -416,9 +553,9 @@ public class EditorActionUtil {
public static void moveCaretPageUp(Editor editor, boolean isWithSelection) {
((EditorEx)editor).stopOptimizedScrolling();
int lineHeight = editor.getLineHeight();
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
- int linesIncrement = viewRect.height / lineHeight;
- editor.getScrollingModel().scrollVertically(viewRect.y - viewRect.y % lineHeight - linesIncrement * lineHeight);
+ Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
+ int linesIncrement = visibleArea.height / lineHeight;
+ editor.getScrollingModel().scrollVertically(visibleArea.y - visibleArea.y % lineHeight - linesIncrement * lineHeight);
int lineShift = -linesIncrement;
editor.getCaretModel().moveCaretRelatively(0, lineShift, isWithSelection, editor.isColumnMode(), true);
}
@@ -426,11 +563,11 @@ public class EditorActionUtil {
public static void moveCaretPageDown(Editor editor, boolean isWithSelection) {
((EditorEx)editor).stopOptimizedScrolling();
int lineHeight = editor.getLineHeight();
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
- int linesIncrement = viewRect.height / lineHeight;
- int allowedBottom = ((EditorEx)editor).getContentSize().height - viewRect.height;
+ Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
+ int linesIncrement = visibleArea.height / lineHeight;
+ int allowedBottom = ((EditorEx)editor).getContentSize().height - visibleArea.height;
editor.getScrollingModel().scrollVertically(
- Math.min(allowedBottom, viewRect.y - viewRect.y % lineHeight + linesIncrement * lineHeight));
+ Math.min(allowedBottom, visibleArea.y - visibleArea.y % lineHeight + linesIncrement * lineHeight));
editor.getCaretModel().moveCaretRelatively(0, linesIncrement, isWithSelection, editor.isColumnMode(), true);
}
@@ -442,9 +579,9 @@ public class EditorActionUtil {
LogicalPosition blockSelectionStart = selectionModel.hasBlockSelection()
? selectionModel.getBlockStart()
: caretModel.getLogicalPosition();
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
- int lineNumber = viewRect.y / lineHeight;
- if (viewRect.y % lineHeight > 0) {
+ Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
+ int lineNumber = visibleArea.y / lineHeight;
+ if (visibleArea.y % lineHeight > 0) {
lineNumber++;
}
VisualPosition pos = new VisualPosition(lineNumber, editor.getCaretModel().getVisualPosition().column);
@@ -460,8 +597,8 @@ public class EditorActionUtil {
LogicalPosition blockSelectionStart = selectionModel.hasBlockSelection()
? selectionModel.getBlockStart()
: caretModel.getLogicalPosition();
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
- int lineNumber = (viewRect.y + viewRect.height) / lineHeight - 1;
+ Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
+ int lineNumber = (visibleArea.y + visibleArea.height) / lineHeight - 1;
VisualPosition pos = new VisualPosition(lineNumber, editor.getCaretModel().getVisualPosition().column);
editor.getCaretModel().moveToVisualPosition(pos);
setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart);
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
index 9860fb8bdefc..d57e1cfb1dc5 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
@@ -107,7 +107,7 @@ public interface EditorEx extends Editor {
void setFontSize(int fontSize);
- Color getBackroundColor();
+ Color getBackgroundColor();
void setBackgroundColor(Color color);
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java
index a819f812971e..e67356537f17 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java
@@ -43,6 +43,16 @@ public interface SoftWrapModelEx extends SoftWrapModel {
@NotNull
LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual);
+ /**
+ * Asks current model to adjust logical position for the given document offset.
+ *
+ * @param defaultLogical default logical position that corresponds to the given document offset
+ * @param offset target editor document offset
+ * @return logical position for the given editor document offset
+ */
+ @NotNull
+ LogicalPosition adjustLogicalPosition(LogicalPosition defaultLogical, int offset);
+
/**
* Asks current model to adjust visual position that corresponds to the given logical position if necessary.
*
@@ -55,15 +65,6 @@ public interface SoftWrapModelEx extends SoftWrapModel {
@NotNull
VisualPosition adjustVisualPosition(@NotNull LogicalPosition logical, @NotNull VisualPosition defaultVisual);
- /**
- * Asks current model to calculate logical position for the given editor document offset.
- *
- * @param offset target editor document offset
- * @return logical position for the given editor document offset
- */
- @NotNull
- LogicalPosition offsetToLogicalPosition(int offset);
-
/**
* @return unmodifiable collection of soft wraps currently registered within the current model
*/
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java
index 4106dfa79e74..fb514e5382fd 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java
@@ -17,55 +17,115 @@ package com.intellij.openapi.editor.ex.util;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorModificationUtil;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.editor.VisualPosition;
+import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry;
import com.intellij.openapi.editor.impl.FontInfo;
import com.intellij.openapi.editor.impl.IterationState;
+import com.intellij.openapi.util.text.StringUtil;
import java.awt.*;
+import java.util.List;
public class EditorUtil {
private EditorUtil() { }
public static int getLastVisualLineColumnNumber(Editor editor, int line) {
+ Document document = editor.getDocument();
+ int lastLine = document.getLineCount() - 1;
+ if (lastLine < 0) {
+ return 0;
+ }
+
+ // Filter all lines that are not shown because of collapsed folding region.
VisualPosition visStart = new VisualPosition(line, 0);
LogicalPosition logStart = editor.visualToLogicalPosition(visStart);
int lastLogLine = logStart.line;
- while (lastLogLine < editor.getDocument().getLineCount() - 1) {
+ while (lastLogLine < document.getLineCount() - 1) {
logStart = new LogicalPosition(logStart.line + 1, logStart.column);
VisualPosition tryVisible = editor.logicalToVisualPosition(logStart);
if (tryVisible.line != visStart.line) break;
lastLogLine = logStart.line;
}
- int lastLine = editor.getDocument().getLineCount() - 1;
- if (lastLine < 0) {
- return 0;
+ int resultLogLine = Math.min(lastLogLine, lastLine);
+ VisualPosition resVisStart = editor.offsetToVisualPosition(document.getLineStartOffset(resultLogLine));
+ VisualPosition resVisEnd = editor.offsetToVisualPosition(document.getLineEndOffset(resultLogLine));
+
+ // Target logical line is not soft wrap affected.
+ if (resVisStart.line == resVisEnd.line) {
+ return resVisEnd.column;
}
- return editor.offsetToVisualPosition(editor.getDocument().getLineEndOffset(Math.min(lastLogLine, lastLine))).column;
+
+ int visualLinesToSkip = line - resVisStart.line;
+ List softWraps = editor.getSoftWrapModel().getSoftWrapsForLine(resultLogLine);
+ for (int i = 0; i < softWraps.size(); i++) {
+ TextChange softWrap = softWraps.get(i);
+ int softWrapLineFeeds = StringUtil.countNewLines(softWrap.getText());
+ if (softWrapLineFeeds < visualLinesToSkip) {
+ visualLinesToSkip -= softWrapLineFeeds;
+ continue;
+ }
+
+ // Target visual column is the one just before line feed introduced by the next line feed.
+ if (softWrapLineFeeds == visualLinesToSkip) {
+ if (i >= softWraps.size() - 1) {
+ return resVisEnd.column;
+ }
+ // We need to find visual column for line feed of the next soft wrap.
+ TextChange nextSoftWrap = softWraps.get(i + 1);
+ int result = editor.offsetToVisualPosition(nextSoftWrap.getStart() - 1).column;
+
+ // We need to add '1' because current column points to the last symbol before the next soft wrap;
+ result++;
+
+ int lineFeedIndex = StringUtil.indexOf(nextSoftWrap.getText(), '\n');
+ result += calcColumnNumber(editor, nextSoftWrap.getText(), 0, lineFeedIndex);
+ return result;
+ }
+
+ // Target visual column is the one before line feed introduced by the current soft wrap.
+ int softWrapStartOffset = 0;
+ int softWrapEndOffset = 0;
+ int softWrapTextLength = softWrap.getText().length();
+ while (visualLinesToSkip-- > 0) {
+ softWrapStartOffset = softWrapEndOffset + 1;
+ if (softWrapStartOffset >= softWrapTextLength) {
+ assert false;
+ return resVisEnd.column;
+ }
+ softWrapEndOffset = StringUtil.indexOf(softWrap.getText(), '\n', softWrapStartOffset, softWrapTextLength);
+ if (softWrapEndOffset < 0) {
+ assert false;
+ return resVisEnd.column;
+ }
+ }
+ int result = editor.offsetToVisualPosition(softWrap.getStart() - 1).column;
+ result += calcColumnNumber(editor, softWrap.getText(), softWrapStartOffset, softWrapEndOffset);
+ return result;
+ }
+
+ assert false;
+ return resVisEnd.column;
}
public static float calcVerticalScrollProportion(Editor editor) {
- Rectangle viewRect = editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
- if (viewRect.height == 0) {
+ Rectangle viewArea = editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
+ if (viewArea.height == 0) {
return 0;
}
LogicalPosition pos = editor.getCaretModel().getLogicalPosition();
Point location = editor.logicalPositionToXY(pos);
- return (location.y - viewRect.y) / (float) viewRect.height;
+ return (location.y - viewArea.y) / (float) viewArea.height;
}
public static void setVerticalScrollProportion(Editor editor, float proportion) {
- Rectangle viewRect = editor.getScrollingModel().getVisibleArea();
+ Rectangle viewArea = editor.getScrollingModel().getVisibleArea();
LogicalPosition caretPosition = editor.getCaretModel().getLogicalPosition();
Point caretLocation = editor.logicalPositionToXY(caretPosition);
int yPos = caretLocation.y;
- yPos -= viewRect.height * proportion;
+ yPos -= viewArea.height * proportion;
editor.getScrollingModel().scrollVertically(yPos);
}
@@ -163,6 +223,10 @@ public class EditorUtil {
return tabSize - colNumber % tabSize;
}
+ public static int calcColumnNumber(Editor editor, CharSequence text, int start, int offset) {
+ return calcColumnNumber(editor, text, start, offset, getTabSize(editor));
+ }
+
public static int calcColumnNumber(Editor editor, CharSequence text, int start, int offset, int tabSize) {
boolean useOptimization = true;
boolean hasNonTabs = false;
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
index 8160cff3ef07..b540e898fe13 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
@@ -239,7 +239,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
else {
g.setColor(Color.white);
int x = getWhitespaceSeparatorOffset() - 1;
- UIUtil.drawVDottedLine((Graphics2D)g, x, clip.y, clip.y + clip.height, myEditor.getBackroundColor(), getFoldingColor(false));
+ UIUtil.drawVDottedLine((Graphics2D)g, x, clip.y, clip.y + clip.height, myEditor.getBackgroundColor(), getFoldingColor(false));
}
}
@@ -693,7 +693,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
int lineX = getWhitespaceSeparatorOffset();
paintBackground(g, clip, getFoldingAreaOffset(), getFoldingAreaWidth());
- g.setColor(myEditor.getBackroundColor());
+ g.setColor(myEditor.getBackgroundColor());
g.fillRect(lineX, clip.y, getFoldingAreaWidth(), clip.height);
paintCaretRowBackground(g, lineX, getFoldingAnchorWidth());
@@ -705,7 +705,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
if (!isFoldingOutlineShown()) return;
Rectangle clip = g.getClipBounds();
- UIUtil.drawVDottedLine(g, getWhitespaceSeparatorOffset(), clip.y, clip.y + clip.height, myEditor.getBackroundColor(), getFoldingColor(false));
+ UIUtil.drawVDottedLine(g, getWhitespaceSeparatorOffset(), clip.y, clip.y + clip.height, myEditor.getBackgroundColor(), getFoldingColor(false));
int anchorX = getFoldingAreaOffset();
int width = getFoldingAnchorWidth();
@@ -814,7 +814,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
int[] yPoints = {y, y, y + baseHeight, y + height, y + baseHeight};
if (paintBackground) {
- g.setColor(myEditor.getBackroundColor());
+ g.setColor(myEditor.getBackgroundColor());
g.fillPolygon(xPoints, yPoints, 5);
}
@@ -850,7 +850,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
boolean active,
boolean paintBackground) {
if (paintBackground) {
- g.setColor(myEditor.getBackroundColor());
+ g.setColor(myEditor.getBackgroundColor());
g.fillRect(anchorX, y, width, width);
}
else {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
index b34d3e458373..356202ea343a 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
@@ -959,13 +959,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
public LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware) {
- if (softWrapAware) {
- return mySoftWrapModel.offsetToLogicalPosition(offset);
- }
-
int line = calcLogicalLineNumber(offset, false);
int column = calcColumnNumber(offset, line, false);
- return new LogicalPosition(line, column);
+ LogicalPosition position = new LogicalPosition(line, column);
+ if (softWrapAware) {
+ return mySoftWrapModel.adjustLogicalPosition(position, offset);
+ }
+ else {
+ return position;
+ }
}
@NotNull
@@ -1284,8 +1286,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return;
}
- Rectangle viewRect = getScrollingModel().getVisibleArea();
- if (viewRect == null) {
+ Rectangle visibleArea = getScrollingModel().getVisibleArea();
+ if (visibleArea == null) {
return;
}
@@ -1374,7 +1376,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return myScheme.getDefaultForeground();
}
- public Color getBackroundColor() {
+ public Color getBackgroundColor() {
if (myForcedBackground != null) return myForcedBackground;
return getBackgroundIgnoreForced();
@@ -1382,7 +1384,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private Color getBackgroundColor(final TextAttributes attributes) {
final Color attrColor = attributes.getBackgroundColor();
- return attrColor == myScheme.getDefaultBackground() ? getBackroundColor() : attrColor;
+ return attrColor == myScheme.getDefaultBackground() ? getBackgroundColor() : attrColor;
}
private Color getBackgroundIgnoreForced() {
@@ -1501,7 +1503,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@SuppressWarnings({"StatementWithEmptyBody"})
private void paintBackgrounds(Graphics g, Rectangle clip) {
- Color defaultBackground = getBackroundColor();
+ Color defaultBackground = getBackgroundColor();
g.setColor(defaultBackground);
g.fillRect(clip.x, clip.y, clip.width, clip.height);
@@ -2140,7 +2142,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
g.setColor(savedColor);
}
else if (effectType == EffectType.BOLD_DOTTED_LINE) {
- final Color bgColor = getBackroundColor();
+ final Color bgColor = getBackgroundColor();
final int dottedAt = SystemInfo.isMac ? y : y + 1;
UIUtil.drawBoldDottedLine((Graphics2D)g, xStart, xEnd, dottedAt, bgColor, effectColor, false);
}
@@ -2598,12 +2600,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
throw new IndexOutOfBoundsException("Wrong offset: " + offset + " textLength: " + textLength);
}
+ int lineIndex = myDocument.getLineNumber(offset);
+ LOG.assertTrue(lineIndex >= 0 && lineIndex < myDocument.getLineCount());
+
if (softWrapAware) {
- return mySoftWrapModel.offsetToLogicalPosition(offset).line;
+ int column = calcColumnNumber(offset, lineIndex, false);
+ return mySoftWrapModel.adjustLogicalPosition(new LogicalPosition(lineIndex, column), offset).line;
}
else {
- int lineIndex = myDocument.getLineNumber(offset);
- LOG.assertTrue(lineIndex >= 0 && lineIndex < myDocument.getLineCount());
return lineIndex;
}
}
@@ -2615,14 +2619,17 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
public int calcColumnNumber(int offset, int lineIndex, boolean softWrapAware) {
if (myDocument.getTextLength() == 0) return 0;
+ CharSequence text = myDocument.getCharsSequence();
+ int start = myDocument.getLineStartOffset(lineIndex);
+ if (start == offset) return 0;
+ int column = EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
+
if (softWrapAware) {
- return mySoftWrapModel.offsetToLogicalPosition(offset).column;
+ int line = calcLogicalLineNumber(offset, false);
+ return mySoftWrapModel.adjustLogicalPosition(new LogicalPosition(line, column), offset).column;
}
else {
- CharSequence text = myDocument.getCharsSequence();
- int start = myDocument.getLineStartOffset(lineIndex);
- if (start == offset) return 0;
- return EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
+ return column;
}
}
@@ -3148,7 +3155,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
else {
Color background = myScheme.getColor(EditorColors.CARET_ROW_COLOR);
- if (background == null) background = getBackroundColor();
+ if (background == null) background = getBackgroundColor();
g.setXORMode(background);
g.fillRect(x, y, myWidth, lineHeight - 1);
@@ -3668,6 +3675,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return r[0];
}
+ @Nullable
public AttributedCharacterIterator cancelLatestCommittedText(AttributedCharacterIterator.Attribute[] attributes) {
return null;
}
@@ -3707,6 +3715,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return r;
}
+ @Nullable
public TextHitInfo getLocationOffset(int x, int y) {
if (composedText != null) {
Point p = getContentComponent().getLocationOnScreen();
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
index 8af6f3f0600d..ef66bff68aa5 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
@@ -116,6 +116,32 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
return i >= 0 ? myWraps.get(i) : null;
}
+ @Override
+ @NotNull
+ public List getSoftWrapsForLine(int documentLine) {
+ Document document = myEditor.getDocument();
+ int start = document.getLineStartOffset(documentLine);
+ int end = document.getLineEndOffset(documentLine);
+
+ int i = getSoftWrapIndex(start);
+ if (i < 0) {
+ i = -i - 1;
+ }
+
+ List result = null;
+ for (; i < myWraps.size(); i++) {
+ TextChange softWrap = myWraps.get(i);
+ if (softWrap.getStart() >= end) {
+ break;
+ }
+ if (result == null) {
+ result = new ArrayList();
+ }
+ result.add(softWrap);
+ }
+ return result == null ? Collections.emptyList() : result;
+ }
+
/**
* Tries to find index of the target soft wrap stored at {@link #myWraps} collection. 'Target' soft wrap is the one
* that starts at the given offset.
@@ -453,21 +479,21 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
}
@NotNull
- public LogicalPosition offsetToLogicalPosition(int offset) {
+ public LogicalPosition adjustLogicalPosition(LogicalPosition defaultLogical, int offset) {
if (myActive > 0 || !isSoftWrappingEnabled()) {
- return myEditor.offsetToLogicalPosition(offset, false);
+ return defaultLogical;
}
myActive++;
try {
- return doOffsetToLogicalPosition(offset);
+ return offsetToLogicalPosition(offset);
} finally {
myActive--;
}
}
@SuppressWarnings({"AssignmentToForLoopParameter"})
- private LogicalPosition doOffsetToLogicalPosition(int offset) {
+ private LogicalPosition offsetToLogicalPosition(int offset) {
DocumentImpl document = (DocumentImpl)myEditor.getDocument();
CharSequence chars = document.getCharsNoThreadCheck();
@@ -759,7 +785,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
}
private int toVisualColumnSymbolsNumber(CharSequence text, int start, int end) {
- return EditorUtil.calcColumnNumber(myEditor, text, start, end, EditorUtil.getTabSize(myEditor));
+ return EditorUtil.calcColumnNumber(myEditor, text, start, end);
}
private class Context {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java
index 39e0fa40ed8c..682b7a75a17c 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentSoftWrapModel.java
@@ -19,6 +19,10 @@ import com.intellij.openapi.editor.SoftWrapModel;
import com.intellij.openapi.editor.TextChange;
import com.intellij.openapi.editor.VisualPosition;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
/**
* @author Denis Zhdanov
@@ -26,14 +30,24 @@ import org.jetbrains.annotations.NotNull;
*/
public class TextComponentSoftWrapModel implements SoftWrapModel {
+ @Override
public boolean isSoftWrappingEnabled() {
return false;
}
+ @Nullable
+ @Override
public TextChange getSoftWrap(int offset) {
return null;
}
+ @NotNull
+ @Override
+ public List getSoftWrapsForLine(int documentLine) {
+ return Collections.emptyList();
+ }
+
+ @Override
public void beforeDocumentChange(@NotNull VisualPosition position) {
}
}
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java
index 36ef084e473d..ee988600c8d7 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java
@@ -784,7 +784,7 @@ public class LineStatusTracker {
JComponent toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, group, true).getComponent();
- final Color background = ((EditorEx)editor).getBackroundColor();
+ final Color background = ((EditorEx)editor).getBackgroundColor();
final Color foreground = editor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
toolbar.setBackground(background);