IDEA-53596 Soft wrap for editors

1. 'Home' and 'End' keys typing processing is corrected;
2. Number of minor refactorings are introduced in order to make spell checker plugin happy (green code policy);
This commit is contained in:
Denis Zhdanov
2010-06-28 13:00:04 +04:00
parent 37e6d29972
commit c9f641faba
12 changed files with 408 additions and 146 deletions
@@ -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) {
@@ -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 <code>'soft wrapping'</code> to the editor.
* <p/>
@@ -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<TextChange> getSoftWrapsForLine(int documentLine);
/**
* Notifies current model that target document is about to be changed at location identified by the given visual position.
* <p/>
@@ -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,
@@ -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;
* <code>'-1'</code> 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<TextChange> 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;
* <code>'-1'</code> 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);
@@ -107,7 +107,7 @@ public interface EditorEx extends Editor {
void setFontSize(int fontSize);
Color getBackroundColor();
Color getBackgroundColor();
void setBackgroundColor(Color color);
@@ -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.
* <p/>
@@ -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
*/
@@ -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<TextChange> 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;
@@ -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 {
@@ -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();
@@ -116,6 +116,32 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
return i >= 0 ? myWraps.get(i) : null;
}
@Override
@NotNull
public List<TextChange> 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<TextChange> result = null;
for (; i < myWraps.size(); i++) {
TextChange softWrap = myWraps.get(i);
if (softWrap.getStart() >= end) {
break;
}
if (result == null) {
result = new ArrayList<TextChange>();
}
result.add(softWrap);
}
return result == null ? Collections.<TextChange>emptyList() : result;
}
/**
* Tries to find index of the target soft wrap stored at {@link #myWraps} collection. <code>'Target'</code> 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 {
@@ -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<TextChange> getSoftWrapsForLine(int documentLine) {
return Collections.emptyList();
}
@Override
public void beforeDocumentChange(@NotNull VisualPosition position) {
}
}
@@ -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);