mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-56987 Throwable at EditorImpl.logicalToVisualPosition() on opening a database console
Soft wrap-aware mapping rules are corrected
This commit is contained in:
@@ -195,7 +195,7 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FoldingModel getFoldingModel() {
|
||||
public FoldingModelEx getFoldingModel() {
|
||||
return myFoldingModelWindow;
|
||||
}
|
||||
|
||||
@@ -283,6 +283,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition offsetToLogicalPosition(final int offset) {
|
||||
return offsetToLogicalPosition(offset, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition offsetToLogicalPosition(final int offset, boolean softWrapAware) {
|
||||
assert isValid();
|
||||
int lineNumber = myDocumentWindow.getLineNumber(offset);
|
||||
int lineStartOffset = myDocumentWindow.getLineStartOffset(lineNumber);
|
||||
@@ -465,6 +470,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull final VisualPosition pos) {
|
||||
return visualToLogicalPosition(pos, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull final VisualPosition pos, boolean softWrapAware) {
|
||||
assert isValid();
|
||||
return new LogicalPosition(pos.line, pos.column);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ import com.intellij.ide.DeleteProvider;
|
||||
import com.intellij.ide.PasteProvider;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.FoldingModel;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.impl.TextDrawingCallback;
|
||||
@@ -124,4 +127,13 @@ public interface EditorEx extends Editor {
|
||||
int calcColumnNumber(int offset, int lineIndex);
|
||||
|
||||
TextDrawingCallback getTextDrawingCallback();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
FoldingModelEx getFoldingModel();
|
||||
|
||||
@NotNull
|
||||
LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos, boolean softWrapAware);
|
||||
|
||||
@NotNull LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware);
|
||||
}
|
||||
|
||||
@@ -35,26 +35,22 @@ import java.util.List;
|
||||
public interface SoftWrapModelEx extends SoftWrapModel {
|
||||
|
||||
/**
|
||||
* Asks current model to adjust logical position for the given visual position if necessary.
|
||||
* <p/>
|
||||
* Given logical position is allowed to be non-soft wrap aware, i.e. the one calculated as there are no soft wraps at the moment.
|
||||
* Asks current model to map given visual position to logical.
|
||||
*
|
||||
* @param defaultLogical default logical position that corresponds to the given visual position
|
||||
* @param visual target visual position for which logical position should be adjusted if necessary
|
||||
* @param visual target visual position for which logical position should be mapped
|
||||
* @return logical position that corresponds to the given visual position
|
||||
*/
|
||||
@NotNull
|
||||
LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual);
|
||||
LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual);
|
||||
|
||||
/**
|
||||
* Asks current model to adjust logical position for the given document offset.
|
||||
* Asks current model to map given document offset to logical position.
|
||||
*
|
||||
* @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);
|
||||
LogicalPosition offsetToLogicalPosition(int offset);
|
||||
|
||||
/**
|
||||
* Asks current model to adjust visual position that corresponds to the given logical position if necessary.
|
||||
@@ -103,7 +99,15 @@ public interface SoftWrapModelEx extends SoftWrapModel {
|
||||
* @param drawingType target drawing type
|
||||
* @return width in pixels required for the painting of the given type
|
||||
*/
|
||||
int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType);
|
||||
int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType);
|
||||
|
||||
/**
|
||||
* Allows to ask for the minimal width in columns required for painting of the given type.
|
||||
*
|
||||
* @param drawingType target drawing type
|
||||
* @return width in columns required for the painting of the given type
|
||||
*/
|
||||
int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType);
|
||||
|
||||
/**
|
||||
* Registers given listener within the current model
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class EditorUtil {
|
||||
private EditorUtil() { }
|
||||
@@ -178,54 +179,69 @@ public class EditorUtil {
|
||||
*/
|
||||
public static int calcOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize) {
|
||||
final int maxScanIndex = Math.min(start + columnNumber + 1, end);
|
||||
if (editor == null) {
|
||||
return calcSoftWrapUnawareOffset(text, start, maxScanIndex, columnNumber, tabSize);
|
||||
}
|
||||
|
||||
EditorEx editorImpl = (EditorEx)editor;
|
||||
int offset = start;
|
||||
IterationState state = new IterationState(editorImpl, offset, false);
|
||||
int fontType = state.getMergedAttributes().getFontType();
|
||||
int column = 0;
|
||||
int x = 0;
|
||||
int spaceSize = getSpaceWidth(fontType, editorImpl);
|
||||
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
|
||||
while (column < columnNumber) {
|
||||
TextChange softWrap = softWrapModel.getSoftWrap(offset);
|
||||
if (softWrap != null) {
|
||||
x = softWrapModel.getSoftWrapIndentWidthInPixels(softWrap);
|
||||
List<? extends TextChange> softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex);
|
||||
int startToUse = start;
|
||||
int x = 0;
|
||||
AtomicInteger currentColumn = new AtomicInteger();
|
||||
for (TextChange softWrap : softWraps) {
|
||||
// There is a possible case that target column points inside soft wrap-introduced virtual space.
|
||||
if (currentColumn.get() >= columnNumber) {
|
||||
return startToUse;
|
||||
}
|
||||
if (offset >= state.getEndOffset()) {
|
||||
state.advance();
|
||||
fontType = state.getMergedAttributes().getFontType();
|
||||
int result = calcSoftWrapUnawareOffset(editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
char c = offset < end ? text.charAt(offset++) : ' ';
|
||||
int prevX = x;
|
||||
if (c == '\t') {
|
||||
x = nextTabStop(x, editorImpl);
|
||||
}
|
||||
else {
|
||||
x += charWidth(c, fontType, editorImpl);
|
||||
}
|
||||
column += columnsNumber(c, x, prevX, spaceSize);
|
||||
startToUse = softWrap.getStart();
|
||||
x = softWrapModel.getSoftWrapIndentWidthInPixels(softWrap);
|
||||
}
|
||||
//if (column == columnNumber && offset < end && text.charAt(offset) == '\t' && (nextTabStop(x, editorImpl) - x) / spaceSize == 0) {
|
||||
// offset++;
|
||||
//}
|
||||
if (column > columnNumber) offset--;
|
||||
|
||||
return offset;
|
||||
// There is a possible case that target column points inside soft wrap-introduced virtual space.
|
||||
if (currentColumn.get() >= columnNumber) {
|
||||
return startToUse;
|
||||
}
|
||||
|
||||
int result = calcSoftWrapUnawareOffset(editor, text, startToUse, end, columnNumber, tabSize, x, currentColumn);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// We assume that given column points to the virtual space after the line end if control flow reaches this place,
|
||||
// hence, just return end of line offset then.
|
||||
return end;
|
||||
}
|
||||
|
||||
private static int calcSoftWrapUnawareOffset(CharSequence text, int start, int end, int columnNumber, int tabSize) {
|
||||
/**
|
||||
* Tries to match given logical column to the document offset assuming that it's located at <code>[start; end)</code> region.
|
||||
*
|
||||
* @param editor editor that is used to represent target document
|
||||
* @param text target document text
|
||||
* @param start start offset to check (inclusive)
|
||||
* @param end end offset to check (exclusive)
|
||||
* @param columnNumber target logical column number
|
||||
* @param tabSize user-defined desired number of columns to use for tabulation symbol representation
|
||||
* @param x <code>'x'</code> coordinate that corresponds to the given <code>'start'</code> offset
|
||||
* @param currentColumn logical column that corresponds to the given <code>'start'</code> offset
|
||||
* @return target offset that belongs to the <code>[start; end)</code> range and points to the target logical
|
||||
* column if any; <code>-1</code> otherwise
|
||||
*/
|
||||
private static int calcSoftWrapUnawareOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize, int x,
|
||||
AtomicInteger currentColumn)
|
||||
{
|
||||
// The main problem in a calculation is that target text may contain tabulation symbols and every such symbol may take different
|
||||
// number of logical columns to represent. E.g. it takes two columns if tab size is four and current column is two; three columns
|
||||
// if tab size is four and current column is one etc. So, first of all we check if there are tabulation symbols at the target
|
||||
// text fragment.
|
||||
boolean useOptimization = true;
|
||||
boolean hasNonTabs = false;
|
||||
boolean hasTabs = false;
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
if (text.charAt(i) == '\t') {
|
||||
hasTabs = true;
|
||||
if (hasNonTabs) {
|
||||
useOptimization = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -233,20 +249,77 @@ public class EditorUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTabs) return Math.min(start + columnNumber, end);
|
||||
// Perform optimized processing if possible. 'Optimized' here means the processing when we exactly know how many logical
|
||||
// columns are occupied by tabulation symbols.
|
||||
if (editor == null || useOptimization) {
|
||||
if (!hasTabs) {
|
||||
int result = start + columnNumber - currentColumn.get();
|
||||
if (result < end) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
currentColumn.addAndGet(end - start);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int shift = 0;
|
||||
int offset = start;
|
||||
for (; offset < end && offset + shift < start + columnNumber; offset++) {
|
||||
if (text.charAt(offset) == '\t') {
|
||||
shift += getTabLength(offset + shift - start, tabSize) - 1;
|
||||
int shift = 0;
|
||||
int offset = start;
|
||||
int prevX = x;
|
||||
for (; offset < end && offset + shift + currentColumn.get() < start + columnNumber; offset++) {
|
||||
if (text.charAt(offset) == '\t') {
|
||||
int nextX = nextTabStop(prevX, editor, tabSize);
|
||||
shift += columnsNumber(nextX - prevX, getSpaceWidth(Font.PLAIN, editor));
|
||||
prevX = nextX;
|
||||
}
|
||||
}
|
||||
int diff = start + columnNumber - offset - shift - currentColumn.get();
|
||||
if (diff < 0) {
|
||||
return offset - 1;
|
||||
}
|
||||
else if (diff == 0) {
|
||||
return offset;
|
||||
}
|
||||
else {
|
||||
currentColumn.addAndGet(offset - start + shift);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (offset + shift > start + columnNumber) {
|
||||
offset--;
|
||||
|
||||
// It means that there are tabulation symbols that can't be explicitly mapped to the occupied logical columns number,
|
||||
// hence, we need to perform special calculations to get know that.
|
||||
EditorEx editorImpl = (EditorEx)editor;
|
||||
int offset = start;
|
||||
IterationState state = new IterationState(editorImpl, offset, false);
|
||||
int fontType = state.getMergedAttributes().getFontType();
|
||||
int column = currentColumn.get();
|
||||
int spaceSize = getSpaceWidth(fontType, editorImpl);
|
||||
for (; column <= columnNumber && offset < end; offset++) {
|
||||
if (offset >= state.getEndOffset()) {
|
||||
state.advance();
|
||||
fontType = state.getMergedAttributes().getFontType();
|
||||
}
|
||||
|
||||
char c = text.charAt(offset);
|
||||
if (c == '\t') {
|
||||
int prevX = x;
|
||||
x = nextTabStop(x, editorImpl);
|
||||
column += columnsNumber(x - prevX, spaceSize);
|
||||
}
|
||||
else {
|
||||
x += charWidth(c, fontType, editorImpl);
|
||||
column++;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
if (column == columnNumber) {
|
||||
return offset;
|
||||
}
|
||||
if (column > columnNumber && text.charAt(offset) == '\t') {
|
||||
return offset - 1;
|
||||
}
|
||||
currentColumn.set(column);
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int getTabLength(int colNumber, int tabSize) {
|
||||
@@ -328,7 +401,10 @@ public class EditorUtil {
|
||||
if (tabSize <= 0) {
|
||||
tabSize = 1;
|
||||
}
|
||||
return nextTabStop(x, editor, tabSize);
|
||||
}
|
||||
|
||||
public static int nextTabStop(int x, Editor editor, int tabSize) {
|
||||
tabSize *= getSpaceWidth(Font.PLAIN, editor);
|
||||
|
||||
int nTabs = x / tabSize;
|
||||
@@ -371,6 +447,21 @@ public class EditorUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer how many visual columns are occupied by the given width.
|
||||
*
|
||||
* @param width target width
|
||||
* @param spaceSize width of the single space symbol within the target editor
|
||||
* @return number of visual columns are occupied by the given width
|
||||
*/
|
||||
public static int columnsNumber(int width, int spaceSize) {
|
||||
int result = width / spaceSize;
|
||||
if (width % spaceSize > 0) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer what width in pixels is required to draw fragment of the given char array from <code>[start; end)</code> interval
|
||||
* at the given editor.
|
||||
@@ -390,10 +481,10 @@ public class EditorUtil {
|
||||
* from <code>[1; tab size]</code> (check {@link #nextTabStop(int, Editor)} for more details)
|
||||
* @return width in pixels required for target text representation
|
||||
*/
|
||||
public static int textWidth(@NotNull Editor editor, char[] text, int start, int end, int fontType, int x) {
|
||||
public static int textWidth(@NotNull Editor editor, CharSequence text, int start, int end, int fontType, int x) {
|
||||
int result = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = text[i];
|
||||
char c = text.charAt(i);
|
||||
if (c != '\t') {
|
||||
FontInfo font = fontForChar(c, fontType, editor);
|
||||
result += font.charWidth(c, editor.getContentComponent());
|
||||
|
||||
@@ -470,24 +470,27 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
|
||||
|
||||
private VerticalInfo createVerticalInfo(LogicalPosition position) {
|
||||
Document document = myEditor.getDocument();
|
||||
int line = position.line;
|
||||
int logicalLine = position.line;
|
||||
|
||||
// There is a possible case that active logical line is represented on multiple lines due to soft wraps processing.
|
||||
// We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for the logical line start
|
||||
// and height in accordance with the number of occupied visual lines.
|
||||
int y = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line))).y;
|
||||
LogicalPosition logicalPosition = myEditor.offsetToLogicalPosition(document.getLineStartOffset(logicalLine));
|
||||
VisualPosition visualPosition = myEditor.logicalToVisualPosition(logicalPosition);
|
||||
int y = myEditor.visualPositionToXY(visualPosition).y;
|
||||
int height = myEditor.getLineHeight();
|
||||
if (line < document.getLineCount() - 1) {
|
||||
int nextLineY = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line + 1))).y;
|
||||
int heightCandidate = nextLineY - y;
|
||||
|
||||
// There is a possible case that active line is the one that ends with folding, so, 'y' position
|
||||
// of its next logical line is the same as the previous. We explicitly check that in order to use non-standard
|
||||
// line height only in case of visible soft-wrapped line.
|
||||
if (heightCandidate > height) {
|
||||
height = heightCandidate;
|
||||
int visualLine = visualPosition.line + 1;
|
||||
while (true) {
|
||||
LogicalPosition logical = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0));
|
||||
if (logical.line == logicalLine) {
|
||||
height += myEditor.getLineHeight();
|
||||
visualLine++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new VerticalInfo(y, height);
|
||||
}
|
||||
|
||||
|
||||
+11
-8
@@ -17,6 +17,9 @@ package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
@@ -31,17 +34,17 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x) {
|
||||
public int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x) {
|
||||
return EditorUtil.textWidthInColumns(myEditor, text, start, end, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int charWidth(char c, int x, int fontType) {
|
||||
if (c == '\t') {
|
||||
return EditorUtil.nextTabStop(x, myEditor) - x;
|
||||
}
|
||||
else {
|
||||
return EditorUtil.charWidth(c, fontType, myEditor);
|
||||
}
|
||||
public int toVisualColumnSymbolsNumber(int width) {
|
||||
return EditorUtil.columnsNumber(width, EditorUtil.getSpaceWidth(Font.PLAIN, myEditor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int textWidth(@NotNull CharSequence text, int start, int end, int x) {
|
||||
return EditorUtil.textWidth(myEditor, text, start, end, Font.PLAIN, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FoldingModel getFoldingModel() {
|
||||
public FoldingModelEx getFoldingModel() {
|
||||
return myFoldingModel;
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
// Process 'after soft wrap' sign.
|
||||
prevX = x;
|
||||
charWidth = mySoftWrapModel.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP);
|
||||
charWidth = mySoftWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
|
||||
x += charWidth;
|
||||
if (x >= px) {
|
||||
break outer;
|
||||
@@ -983,16 +983,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return offsetToLogicalPosition(offset, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware) {
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.offsetToLogicalPosition(offset);
|
||||
}
|
||||
int line = calcLogicalLineNumber(offset, false);
|
||||
int column = calcColumnNumber(offset, line, false);
|
||||
LogicalPosition position = new LogicalPosition(line, column);
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.adjustLogicalPosition(position, offset);
|
||||
}
|
||||
else {
|
||||
return position;
|
||||
}
|
||||
return new LogicalPosition(line, column);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -1055,7 +1054,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
if (i >= 0) {
|
||||
start = i + 1;
|
||||
}
|
||||
return new Point(EditorUtil.textWidth(this, softWrapChars, start, column + 1, Font.PLAIN, 0), y);
|
||||
return new Point(EditorUtil.textWidth(this, softWrap.getText(), start, column + 1, Font.PLAIN, 0), y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1319,6 +1318,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
void paint(Graphics g) {
|
||||
//TODO den remove
|
||||
System.out.printf("EditorImpl.paint(): (%d; %d) - [%d; %d]%n", g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height);
|
||||
startOptimizedScrolling();
|
||||
|
||||
if (myCursorUpdater != null) {
|
||||
@@ -2667,10 +2668,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos) {
|
||||
assertReadAccess();
|
||||
if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) {
|
||||
return new LogicalPosition(visiblePos.line, visiblePos.column);
|
||||
return visualToLogicalPosition(visiblePos, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos, boolean softWrapAware) {
|
||||
if (softWrapAware) {
|
||||
return mySoftWrapModel.visualToLogicalPosition(visiblePos);
|
||||
}
|
||||
assertReadAccess();
|
||||
if (!myFoldingModel.isFoldingEnabled()) return new LogicalPosition(visiblePos.line, visiblePos.column);
|
||||
|
||||
int line = visiblePos.line;
|
||||
int column = visiblePos.column;
|
||||
@@ -2678,35 +2685,23 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
FoldRegion lastCollapsedBefore = getLastCollapsedBeforePosition(visiblePos);
|
||||
|
||||
if (lastCollapsedBefore != null) {
|
||||
LogicalPosition softWrapAwareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset());
|
||||
VisualPosition softWrapAwareVisFoldEnd = logicalToVisualPosition(softWrapAwareLogFoldEnd);
|
||||
if (softWrapAwareVisFoldEnd.line == visiblePos.line) {
|
||||
if (visiblePos.column == softWrapAwareVisFoldEnd.column) {
|
||||
return softWrapAwareLogFoldEnd;
|
||||
}
|
||||
else if (visiblePos.column > softWrapAwareVisFoldEnd.column) {
|
||||
int columnToUse = softWrapAwareLogFoldEnd.column + visiblePos.column - softWrapAwareVisFoldEnd.column;
|
||||
return new LogicalPosition(
|
||||
softWrapAwareLogFoldEnd.line, columnToUse, softWrapAwareLogFoldEnd.softWrapLinesBeforeCurrentLogicalLine,
|
||||
softWrapAwareLogFoldEnd.softWrapLinesOnCurrentLogicalLine, visiblePos.column - columnToUse - softWrapAwareLogFoldEnd.foldingColumnDiff,
|
||||
softWrapAwareLogFoldEnd.foldedLines, softWrapAwareLogFoldEnd.foldingColumnDiff
|
||||
);
|
||||
LogicalPosition logFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset(), false);
|
||||
VisualPosition visFoldEnd = logicalToVisualPosition(logFoldEnd, false);
|
||||
|
||||
line = logFoldEnd.line + (visiblePos.line - visFoldEnd.line);
|
||||
if (visFoldEnd.line == visiblePos.line) {
|
||||
if (visiblePos.column >= visFoldEnd.column) {
|
||||
column = logFoldEnd.column + (visiblePos.column - visFoldEnd.column);
|
||||
}
|
||||
else {
|
||||
return offsetToLogicalPosition(lastCollapsedBefore.getStartOffset());
|
||||
return offsetToLogicalPosition(lastCollapsedBefore.getStartOffset(), false);
|
||||
}
|
||||
}
|
||||
|
||||
LogicalPosition softWrapUnawareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset(), false);
|
||||
VisualPosition softWrapUnawareVisFoldEnd = logicalToVisualPosition(softWrapUnawareLogFoldEnd, false);
|
||||
line = softWrapUnawareLogFoldEnd.line + (visiblePos.line - softWrapUnawareVisFoldEnd.line);
|
||||
}
|
||||
|
||||
if (column < 0) column = 0;
|
||||
line = Math.min(line, myDocument.getLineCount() - 1);
|
||||
|
||||
LogicalPosition softWrapUnawareResult = new LogicalPosition(line, column);
|
||||
return mySoftWrapModel.adjustLogicalPosition(softWrapUnawareResult, visiblePos);
|
||||
return new LogicalPosition(line, column);
|
||||
}
|
||||
|
||||
private int calcLogicalLineNumber(int offset) {
|
||||
@@ -4844,7 +4839,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
TextChange softWrap = getSoftWrapModel().getSoftWrap(i);
|
||||
if (softWrap != null) {
|
||||
column++; // For 'after soft wrap' drawing.
|
||||
x = getSoftWrapModel().getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP);
|
||||
x = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
|
||||
}
|
||||
|
||||
char c = text.charAt(i);
|
||||
|
||||
+22
-8
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Strategy interface for various utility methods used for representing document text at the editor.
|
||||
* <p/>
|
||||
@@ -37,16 +39,28 @@ public interface EditorTextRepresentationHelper {
|
||||
* @param x <code>'x'</code> offset from the visual line start
|
||||
* @return number of visual columns necessary for the target text sub-sequence representation
|
||||
*/
|
||||
int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x);
|
||||
int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x);
|
||||
|
||||
/**
|
||||
* Allows to retrieve width (in pixels) necessary to represent given symbol at the given <code>'x'</code> offset from
|
||||
* visual line start using given font type.
|
||||
* Allows to answer how many visual columns is necessary for representing text of the given width.
|
||||
*
|
||||
* @param c target symbol which width should be calculated
|
||||
* @param x current <code>'x'</code> of the visual line start to use for the target symbol representation
|
||||
* @param fontType font type to use for representing given symbol
|
||||
* @return number of pixels necessary for the given symbol representation
|
||||
* @param width target width
|
||||
* @return number of visual columns necessary for representation of the text with the given width
|
||||
*/
|
||||
int charWidth(char c, int x, int fontType);
|
||||
int toVisualColumnSymbolsNumber(int width);
|
||||
|
||||
/**
|
||||
* Allows to retrieve width (in pixels) necessary to represent given region (<code>[start; end)</code>) starting
|
||||
* at the given <code>'x'</code> offset from visual line start using given font type.
|
||||
* <p/>
|
||||
* <b>Note:</b> target region is allows to contain line feeds, the width is calculated as a difference between <code>'x'</code>
|
||||
* coordinates of the last and first symbols.
|
||||
*
|
||||
* @param text target text holder
|
||||
* @param start start offset of the target text sub-sequence (inclusive)
|
||||
* @param end end offset of the target text sub-sequence (exclusive)
|
||||
* @param x <code>'x'</code> offset from the visual line start
|
||||
* @return width in pixels necessary for the target text sub-sequence representation
|
||||
*/
|
||||
int textWidth(@NotNull CharSequence text, int start, int end, int x);
|
||||
}
|
||||
|
||||
+31
-17
@@ -43,7 +43,7 @@ import java.util.List;
|
||||
*/
|
||||
public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
|
||||
private final SoftWrapDataMapper myDataAdjuster;
|
||||
private final SoftWrapDataMapper myDataMapper;
|
||||
private final SoftWrapsStorage myStorage;
|
||||
private final SoftWrapPainter myPainter;
|
||||
private final SoftWrapApplianceManager myApplianceManager;
|
||||
@@ -60,20 +60,20 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
public SoftWrapModelImpl(@NotNull final EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter) {
|
||||
this(
|
||||
editor, storage, painter, new DefaultSoftWrapApplianceManager(storage, editor, painter),
|
||||
new SoftWrapDataMapper(editor, storage, painter, new DefaultEditorTextRepresentationHelper(editor)),
|
||||
new SoftWrapDataMapper(editor, storage, new DefaultEditorTextRepresentationHelper(editor)),
|
||||
new SoftWrapDocumentChangeManager(editor, storage)
|
||||
);
|
||||
}
|
||||
|
||||
public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
|
||||
@NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataAdjuster,
|
||||
@NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
|
||||
@NotNull SoftWrapDocumentChangeManager documentChangeManager)
|
||||
{
|
||||
myEditor = editor;
|
||||
myStorage = storage;
|
||||
myPainter = painter;
|
||||
myApplianceManager = applianceManager;
|
||||
myDataAdjuster = dataAdjuster;
|
||||
myDataMapper = dataMapper;
|
||||
myDocumentChangeManager = documentChangeManager;
|
||||
}
|
||||
|
||||
@@ -184,25 +184,39 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) {
|
||||
public int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType) {
|
||||
return myPainter.getMinDrawingWidth(drawingType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType) {
|
||||
return myPainter.getMinDrawingWidth(drawingType) > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
|
||||
@Override
|
||||
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) {
|
||||
if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
|
||||
return defaultLogical;
|
||||
return myEditor.visualToLogicalPosition(visual, false);
|
||||
}
|
||||
|
||||
if (defaultLogical.visualPositionAware) {
|
||||
return defaultLogical;
|
||||
}
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
return myDataAdjuster.adjustLogicalPosition(defaultLogical, visual);
|
||||
return myDataMapper.visualToLogical(visual);
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
finally {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
|
||||
return myEditor.offsetToLogicalPosition(offset, false);
|
||||
}
|
||||
myActive++;
|
||||
try {
|
||||
return myDataMapper.offsetToLogicalPosition(offset);
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +229,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
return myDataAdjuster.offsetToLogicalPosition(offset);
|
||||
return myDataMapper.offsetToLogicalPosition(offset);
|
||||
} finally {
|
||||
myActive--;
|
||||
}
|
||||
@@ -229,7 +243,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
|
||||
myActive++;
|
||||
try {
|
||||
return myDataAdjuster.adjustVisualPosition(logical, defaultVisual);
|
||||
return myDataMapper.adjustVisualPosition(logical, defaultVisual);
|
||||
}
|
||||
finally {
|
||||
myActive--;
|
||||
@@ -292,7 +306,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
|
||||
}
|
||||
|
||||
if (start < end) {
|
||||
result += EditorUtil.textWidth(myEditor, chars, start, end, Font.PLAIN, 0);
|
||||
result += EditorUtil.textWidth(myEditor, softWrap.getText(), start, end, Font.PLAIN, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
+3
-1
@@ -29,6 +29,8 @@ import gnu.trove.TIntArrayList;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
/**
|
||||
* Default {@link SoftWrapApplianceManager} implementation that is built with the following design guide lines:
|
||||
* <pre>
|
||||
@@ -214,7 +216,7 @@ public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager
|
||||
int x = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
|
||||
int prevSoftWrapOffset = start;
|
||||
for (int i = start; i < end; i++) {
|
||||
int symbolWidth = EditorUtil.textWidth(myEditor, text, i, i + 1, fontType, x);
|
||||
int symbolWidth = EditorUtil.textWidth(myEditor, CharBuffer.wrap(text), i, i + 1, fontType, x);
|
||||
if (x + symbolWidth >= myVisibleAreaWidth) {
|
||||
int offset = calculateSoftWrapOffset(text, i - 1, prevSoftWrapOffset, end);
|
||||
if (offset >= end || offset <= prevSoftWrapOffset) {
|
||||
|
||||
+400
-525
File diff suppressed because it is too large
Load Diff
+119
-35
@@ -3,10 +3,13 @@ package com.intellij.openapi.editor.impl.softwrap;
|
||||
import com.intellij.mock.MockFoldRegion;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx;
|
||||
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
|
||||
import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
@@ -18,7 +21,6 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -88,7 +90,8 @@ public class SoftWrapDataMapperTest {
|
||||
private EditorEx myEditor;
|
||||
private Document myDocument;
|
||||
private SoftWrapsStorage myStorage;
|
||||
private FoldingModel myFoldingModel;
|
||||
private SoftWrapModelEx mySoftWrapModel;
|
||||
private FoldingModelEx myFoldingModel;
|
||||
private MockEditorTextRepresentationHelper myRepresentationHelper;
|
||||
|
||||
@Before
|
||||
@@ -100,7 +103,8 @@ public class SoftWrapDataMapperTest {
|
||||
myEditor = myMockery.mock(EditorEx.class);
|
||||
myDocument = myMockery.mock(Document.class);
|
||||
myStorage = new SoftWrapsStorage();
|
||||
myFoldingModel = myMockery.mock(FoldingModel.class);
|
||||
mySoftWrapModel = myMockery.mock(SoftWrapModelEx.class);
|
||||
myFoldingModel = myMockery.mock(FoldingModelEx.class);
|
||||
final EditorSettings settings = myMockery.mock(EditorSettings.class);
|
||||
final Project project = myMockery.mock(Project.class);
|
||||
final SoftWrapPainter painter = myMockery.mock(SoftWrapPainter.class);
|
||||
@@ -140,6 +144,23 @@ public class SoftWrapDataMapperTest {
|
||||
allowing(settings).isWhitespacesShown();will(returnValue(true));
|
||||
allowing(myEditor).getProject();will(returnValue(project));
|
||||
|
||||
// Soft wraps.
|
||||
allowing(myEditor).getSoftWrapModel(); will(returnValue(mySoftWrapModel));
|
||||
allowing(mySoftWrapModel).getSoftWrapIndentWidthInColumns(with(any(TextChange.class)));
|
||||
will(new CustomAction("getSoftWrapIndentWidthInColumns") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getSoftWrapIndentWidthInColumns((TextChange)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(mySoftWrapModel).getSoftWrapIndentWidthInPixels(with(any(TextChange.class)));
|
||||
will(new CustomAction("getSoftWrapIndentWidthInPixels") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return getSoftWrapIndentWidthInPixels((TextChange)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
|
||||
// Folding.
|
||||
allowing(myEditor).getFoldingModel();will(returnValue(myFoldingModel));
|
||||
allowing(myFoldingModel).isOffsetCollapsed(with(any(int.class))); will(new CustomAction("isOffsetCollapsed()") {
|
||||
@@ -155,7 +176,7 @@ public class SoftWrapDataMapperTest {
|
||||
return getCollapsedFoldRegion((Integer)invocation.getParameter(0));
|
||||
}
|
||||
});
|
||||
allowing(myFoldingModel).getAllFoldRegions(); will(new CustomAction("getAllFoldRegions()") {
|
||||
allowing(myFoldingModel).fetchTopLevel(); will(new CustomAction("fetchTopLevel()") {
|
||||
@Override
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
return myFoldRegions.toArray(new FoldRegion[myFoldRegions.size()]);
|
||||
@@ -188,7 +209,7 @@ public class SoftWrapDataMapperTest {
|
||||
|
||||
myRepresentationHelper = new MockEditorTextRepresentationHelper();
|
||||
|
||||
myAdjuster = new SoftWrapDataMapper(myEditor, myStorage, painter, myRepresentationHelper, new MockFontTypeProvider());
|
||||
myAdjuster = new SoftWrapDataMapper(myEditor, myStorage, myRepresentationHelper/*, new MockFontTypeProvider()*/);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -196,6 +217,16 @@ public class SoftWrapDataMapperTest {
|
||||
myMockery.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noSoftWrapsAndFolding() {
|
||||
String document =
|
||||
"class Test { \n" +
|
||||
" public void foo() {} \n" +
|
||||
" \n" +
|
||||
"}";
|
||||
test(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void softWrapHasSymbolBeforeFirstLineFeed() {
|
||||
String document =
|
||||
@@ -216,13 +247,13 @@ public class SoftWrapDataMapperTest {
|
||||
String document =
|
||||
"public class Test {\n" +
|
||||
" public void foo(int[] data) {\n" +
|
||||
" bar(data[0], data[1], <WRAP>\n" +
|
||||
" </WRAP>data[2], data[3], <WRAP> \n" +
|
||||
" </WRAP>data[4], data[5], \n" +
|
||||
" data[6], data[7], \n" +
|
||||
" data[8], data[9], <WRAP>\n" +
|
||||
" </WRAP>data[10], data[11], <WRAP> \n" +
|
||||
" </WRAP>data[12], data[13]); \n" +
|
||||
" bar(data[0], <WRAP>\n" +
|
||||
" </WRAP>data[1] <WRAP> \n" +
|
||||
" </WRAP>data[2] \n" +
|
||||
" data[3], \n" +
|
||||
" data[4], <WRAP>\n" +
|
||||
" </WRAP>data[5] <WRAP> \n" +
|
||||
" </WRAP>data[6]); \n" +
|
||||
" }\n" +
|
||||
" public void bar(int ... i) {\n" +
|
||||
" }\n" +
|
||||
@@ -345,6 +376,34 @@ public class SoftWrapDataMapperTest {
|
||||
test(document);
|
||||
}
|
||||
|
||||
private static int getSoftWrapIndentWidthInColumns(TextChange softWrap) {
|
||||
int result = 0;
|
||||
CharSequence text = softWrap.getText();
|
||||
for (int i = text.length() - 1; i >= 0; i--) {
|
||||
if (text.charAt(i) == '\n') {
|
||||
break;
|
||||
}
|
||||
result++;
|
||||
}
|
||||
if (SOFT_WRAP_DRAWING_WIDTH > 0) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getSoftWrapIndentWidthInPixels(TextChange softWrap) {
|
||||
int result = 0;
|
||||
CharSequence text = softWrap.getText();
|
||||
for (int i = text.length() - 1; i >= 0; i--) {
|
||||
if (text.charAt(i) == '\n') {
|
||||
break;
|
||||
}
|
||||
result += myRepresentationHelper.textWidth(text, i, i + 1, 0);
|
||||
}
|
||||
result += SOFT_WRAP_DRAWING_WIDTH;
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getLineNumber(int offset) {
|
||||
int line = 0;
|
||||
for (TextRange range : myLineRanges) {
|
||||
@@ -430,10 +489,12 @@ public class SoftWrapDataMapperTest {
|
||||
for (DataEntry data : myExpectedData) {
|
||||
|
||||
// Check logical by visual.
|
||||
LogicalPosition actualLogicalByVisual = myAdjuster.adjustLogicalPosition(toSoftWrapUnawareLogicalByVisual(data), data.visual);
|
||||
LogicalPosition actualLogicalByVisual = myAdjuster.visualToLogical(data.visual);
|
||||
// We don't want to perform the check for logical positions that correspond to the folded space because all of them relate to
|
||||
// the same logical position of the folding start.
|
||||
if (!data.foldedSpace && !data.insideTab && !equals(data.logical, actualLogicalByVisual)) {
|
||||
//TODO den remove
|
||||
myAdjuster.visualToLogical(data.visual);
|
||||
throw new AssertionError(
|
||||
String.format("Detected unmatched logical position by visual (%s). Expected: '%s', actual: '%s'. Calculation was performed "
|
||||
+ "against soft wrap-unaware logical: '%s'",
|
||||
@@ -446,6 +507,8 @@ public class SoftWrapDataMapperTest {
|
||||
// We don't to perform the check for the data that points to soft wrap location here. The reason is that it shares offset
|
||||
// with the first document symbol after soft wrap, hence, examination always fails.
|
||||
if (!data.virtualSpace && !data.insideTab && !equals(data.logical, actualLogicalByOffset)) {
|
||||
//TODO den remove
|
||||
myAdjuster.offsetToLogicalPosition(data.offset);
|
||||
throw new AssertionError(
|
||||
String.format("Detected unmatched logical position by offset. Expected: '%s', actual: '%s'. Calculation was performed "
|
||||
+ "against offset: '%d' and soft wrap-unaware logical: '%s'",
|
||||
@@ -635,7 +698,7 @@ public class SoftWrapDataMapperTest {
|
||||
}
|
||||
else if (c == '\t') {
|
||||
int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
|
||||
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
|
||||
x += MockEditorTextRepresentationHelper.charWidth(c, x);
|
||||
|
||||
// There is a possible case that single tabulation symbols is shown in more than one visual column at IntelliJ editor.
|
||||
// We store data entry only for the first tab column without 'inside tab' flag then.
|
||||
@@ -652,7 +715,7 @@ public class SoftWrapDataMapperTest {
|
||||
} else {
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
|
||||
x += MockEditorTextRepresentationHelper.charWidth(c, x);
|
||||
foldingColumnDiff--;
|
||||
}
|
||||
return;
|
||||
@@ -685,7 +748,7 @@ public class SoftWrapDataMapperTest {
|
||||
visualColumn++;
|
||||
softWrapColumnDiff++;
|
||||
softWrapSymbolsOnCurrentVisualLine++;
|
||||
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
|
||||
x += MockEditorTextRepresentationHelper.charWidth(c, x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -706,7 +769,7 @@ public class SoftWrapDataMapperTest {
|
||||
}
|
||||
else if (c == '\t') {
|
||||
int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
|
||||
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
|
||||
x += MockEditorTextRepresentationHelper.charWidth(c, x);
|
||||
|
||||
// There is a possible case that single tabulation symbols is shown in more than one visual column at IntelliJ editor.
|
||||
// We store data entry only for the first tab column without 'inside tab' flag then.
|
||||
@@ -726,7 +789,7 @@ public class SoftWrapDataMapperTest {
|
||||
visualColumn++;
|
||||
logicalColumn++;
|
||||
offset++;
|
||||
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
|
||||
x += MockEditorTextRepresentationHelper.charWidth(c, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,10 +845,10 @@ public class SoftWrapDataMapperTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x) {
|
||||
public int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x) {
|
||||
int result = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
int width = charWidth(text.charAt(i), x, Font.PLAIN);
|
||||
int width = charWidth(text.charAt(i), x);
|
||||
result += width / SPACE_SIZE;
|
||||
if (width % SPACE_SIZE > 0) {
|
||||
result++;
|
||||
@@ -796,7 +859,28 @@ public class SoftWrapDataMapperTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int charWidth(char c, int x, int fontType) {
|
||||
public int toVisualColumnSymbolsNumber(int width) {
|
||||
int result = width / SPACE_SIZE;
|
||||
if (width % SPACE_SIZE > 0) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int textWidth(@NotNull CharSequence text, int start, int end, int x) {
|
||||
int result = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = text.charAt(i);
|
||||
switch (c) {
|
||||
case '\n': result = 0; break;
|
||||
default: result += charWidth(c, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int charWidth(char c, int x) {
|
||||
if (c == '\t') {
|
||||
int tabWidth = SPACE_SIZE * TAB_SIZE;
|
||||
int tabsNumber = x / tabWidth;
|
||||
@@ -808,18 +892,18 @@ public class SoftWrapDataMapperTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockFontTypeProvider implements SoftWrapDataMapper.FontTypeProvider {
|
||||
@Override
|
||||
public void init(int start) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFontType(int offset) {
|
||||
return Font.PLAIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
}
|
||||
}
|
||||
//private static class MockFontTypeProvider implements SoftWrapDataMapper.FontTypeProvider {
|
||||
// @Override
|
||||
// public void init(int start) {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getFontType(int offset) {
|
||||
// return Font.PLAIN;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void cleanup() {
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -1236,6 +1236,20 @@ public class StringUtil {
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if target symbol is contained at given char sequence at <code>[start; end)</code> interval.
|
||||
*
|
||||
* @param s target char sequence to check
|
||||
* @param start start offset to use within the given char sequence (inclusive)
|
||||
* @param end end offset to use within the given char sequence (exclusive)
|
||||
* @param c target symbol to check
|
||||
* @return <code>true</code> if given symbol is contained at the target range of the given char sequence;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean contains(CharSequence s, int start, int end, char c) {
|
||||
return indexOf(s, c, start, end) >= 0;
|
||||
}
|
||||
|
||||
public static int indexOf(@NotNull CharSequence s, char c) {
|
||||
return indexOf(s, c, 0, s.length());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user