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 c172d60c64ca..c0accddd92fd 100644
--- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
+++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2011 JetBrains s.r.o.
+ * Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -560,7 +560,7 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx {
int end = myDocumentWindow.getLineEndOffset(lineNumber);
CharSequence text = myDocumentWindow.getCharsSequence();
- return EditorUtil.calcOffset(this, text, lineStartOffset, end, col, EditorUtil.getTabSize(myDelegate));
+ return EditorUtil.calcOffset(this, text, lineStartOffset, end, col, EditorUtil.getTabSize(myDelegate), null);
}
@Override
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 ad2fe4b161c4..2e7322c59bf1 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
@@ -32,6 +32,7 @@ import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import org.intellij.lang.annotations.JdkConstants;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.MouseWheelEvent;
@@ -205,11 +206,18 @@ public class EditorUtil {
* @param end end offset of the logical line that holds target column (exclusive)
* @param columnNumber target column number
* @param tabSize number of desired visual columns to use for tabulation representation
+ * @param debugBuffer buffer to hold debug info during the processing (if any)
* @return given text offset that identifies the same position that is pointed by the given visual column
*/
- public static int calcOffset(EditorEx editor, CharSequence text, int start, int end, int columnNumber, int tabSize) {
+ public static int calcOffset(EditorEx editor, CharSequence text, int start, int end, int columnNumber, int tabSize,
+ @Nullable StringBuilder debugBuffer)
+ {
assert start >= 0 : "start (" + start + ") must not be negative. end (" + end + ")";
assert end >= start : "start (" + start + ") must not be greater than end (" + end + ")";
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Starting calcOffset(). Start=%d, end=%d, column number=%d, tab size=%d%n",
+ start, end, columnNumber, tabSize));
+ }
final int maxScanIndex = Math.min(start + columnNumber + 1, end);
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
List extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex);
@@ -221,7 +229,8 @@ public class EditorUtil {
if (currentColumn[0] >= columnNumber) {
return startToUse;
}
- int result = calcSoftWrapUnawareOffset(editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn);
+ int result
+ = calcSoftWrapUnawareOffset(editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn, debugBuffer);
if (result >= 0) {
return result;
}
@@ -235,13 +244,17 @@ public class EditorUtil {
return startToUse;
}
- int result = calcSoftWrapUnawareOffset(editor, text, startToUse, end, columnNumber, tabSize, x, currentColumn);
+ int result = calcSoftWrapUnawareOffset(editor, text, startToUse, end, columnNumber, tabSize, x, currentColumn, debugBuffer);
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.
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Returning %d as no match has been found for the target column (%d) at the target range [%d;%d)",
+ end, columnNumber, start, end));
+ }
return end;
}
@@ -256,12 +269,18 @@ public class EditorUtil {
* @param tabSize user-defined desired number of columns to use for tabulation symbol representation
* @param x 'x' coordinate that corresponds to the given 'start' offset
* @param currentColumn logical column that corresponds to the given 'start' offset
+ * @param debugBuffer buffer to hold debug info during the processing (if any)
* @return target offset that belongs to the [start; end) range and points to the target logical
* column if any; -1 otherwise
*/
private static int calcSoftWrapUnawareOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize, int x,
- int[] currentColumn)
- {
+ int[] currentColumn, @Nullable StringBuilder debugBuffer) {
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format(
+ "Starting calcSoftWrapUnawareOffset(). Target range: [%d; %d), target column number to map: %d, tab size: %d, "
+ + "x: %d, current column: %d%n", start, end, columnNumber, tabSize, x, currentColumn[0]));
+ }
+
// 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
@@ -270,17 +289,26 @@ public class EditorUtil {
boolean hasNonTabs = false;
boolean hasTabs = false;
for (int i = start; i < end; i++) {
- if (text.charAt(i) == '\t') {
+ char c = text.charAt(i);
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Found symbol '%c' at the offset %d%n", c, i));
+ }
+ if (c == '\t') {
hasTabs = true;
if (hasNonTabs) {
useOptimization = false;
break;
}
- } else {
+ }
+ else {
hasNonTabs = true;
}
}
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Has tabs: %b, use optimisation: %b%n", hasTabs, useOptimization));
+ }
+
// 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) {
@@ -291,6 +319,9 @@ public class EditorUtil {
}
else {
currentColumn[0] += end - start;
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Incrementing 'current column' by %d (new value is %d)%n", end - start, currentColumn[0]));
+ }
return -1;
}
}
@@ -300,14 +331,26 @@ public class EditorUtil {
int shift = 0;
int offset = start;
int prevX = x;
+ if (debugBuffer != null) {
+ debugBuffer.append("Processing a string that contains only tabs\n");
+ }
for (; offset < end && offset + shift + currentColumn[0] < start + columnNumber; offset++) {
- if (text.charAt(offset) == '\t') {
+ final char c = text.charAt(offset);
+ if (c == '\t') {
int nextX = nextTabStop(prevX, editor, tabSize);
- shift += columnsNumber(nextX - prevX, getSpaceWidth(Font.PLAIN, editor)) - 1;
+ final int columnsShift = columnsNumber(nextX - prevX, getSpaceWidth(Font.PLAIN, editor)) - 1;
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format(
+ "Processing tabulation symbol at the offset %d. Current X: %d, new X: %d, current columns shift: %d, new column shift: %d%n",
+ offset, prevX, nextX, shift, shift + columnsShift
+ ));
+ }
+ shift += columnsShift;
prevX = nextX;
}
}
int diff = start + columnNumber - offset - shift - currentColumn[0];
+ if (debugBuffer != null) debugBuffer.append(String.format("Resulting diff: %d%n", diff));
if (diff < 0) {
return offset - 1;
}
@@ -315,7 +358,11 @@ public class EditorUtil {
return offset;
}
else {
- currentColumn[0] += offset - start + shift;
+ final int inc = offset - start + shift;
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format("Incrementing 'current column' by %d (new value is %d)%n", inc, currentColumn[0] + inc));
+ }
+ currentColumn[0] += inc;
return -1;
}
}
@@ -339,11 +386,25 @@ public class EditorUtil {
char c = text.charAt(offset);
if (c == '\t') {
int prevX = x;
- x = nextTabStop(x, editorImpl);
- column += columnsNumber(x - prevX, spaceSize);
+ final int newX = nextTabStop(x, editorImpl);
+ final int columns = columnsNumber(x - prevX, spaceSize);
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format(
+ "Processing tabulation at the offset %d. Current X: %d, new X: %d, current column: %d, new column: %d%n",
+ offset, x, newX, column, column + columns
+ ));
+ }
+ x = newX;
+ column += columns;
}
else {
- x += charWidth(c, fontType, editorImpl);
+ final int width = charWidth(c, fontType, editorImpl);
+ if (debugBuffer != null) {
+ debugBuffer.append(String.format(
+ "Processing symbol '%c' at the offset %d. Current X: %d, new X: %d%n", c, offset, x, x + width
+ ));
+ }
+ x += width;
column++;
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
index f292d06c58cf..0263f6332cd8 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
@@ -182,7 +182,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
myVisualLineStart = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
myVisualLineEnd = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
- ((FoldingModelImpl)myEditor.getFoldingModel()).flushCaretPosition();
+ myEditor.getFoldingModel().flushCaretPosition();
myEditor.setLastColumnNumber(myVisibleCaret.column);
myEditor.updateCaretCursor();
@@ -220,13 +220,15 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
final DocumentEx document = myEditor.getDocument();
int textEnd = Math.min(document.getTextLength() - 1, Math.max(offset, myOffset) + 1);
CharSequence text = document.getCharsSequence().subSequence(textStart, textEnd);
+ StringBuilder positionToOffsetTrace = new StringBuilder();
+ int inverseOffset = myEditor.logicalPositionToOffset(logicalPosition, positionToOffsetTrace);
LogMessageEx.error(
- LOG, "caret moved to wrong offset",
+ LOG, "caret moved to wrong offset. Please submit a dedicated ticket and attach current editor's text to it.",
String.format(
"Requested: offset=%d, logical position='%s' but actual: offset=%d, logical position='%s' (%s). %s%n"
- + "interested text [%d;%d): '%s'%n debug trace: %s",
+ + "interested text [%d;%d): '%s'%n debug trace: %s%nLogical position -> offset ('%s'->'%d') trace: %s",
offset, logicalPosition, myOffset, myLogicalCaret, positionByOffsetAfterMove, myEditor.dumpState(),
- textStart, textEnd, text, debugBuffer
+ textStart, textEnd, text, debugBuffer, logicalPosition, inverseOffset, positionToOffsetTrace
));
}
if (event != null) {
@@ -493,7 +495,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
}
}
- ((FoldingModelImpl)myEditor.getFoldingModel()).flushCaretPosition();
+ myEditor.getFoldingModel().flushCaretPosition();
VerticalInfo oldInfo = myCaretInfo;
LogicalPosition oldCaretPosition = myLogicalCaret;
@@ -522,7 +524,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
Runnable runnable = new Runnable() {
@Override
public void run() {
- FoldRegion[] allCollapsedAt = ((FoldingModelImpl)myEditor.getFoldingModel()).fetchCollapsedAt(offset);
+ FoldRegion[] allCollapsedAt = myEditor.getFoldingModel().fetchCollapsedAt(offset);
for (FoldRegion foldRange : allCollapsedAt) {
foldRange.setExpanded(true);
}
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 fa6a7ba84451..28e18587875e 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
@@ -3312,6 +3312,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@Override
public int logicalPositionToOffset(@NotNull LogicalPosition pos) {
+ return logicalPositionToOffset(pos, null);
+ }
+
+
+ public int logicalPositionToOffset(@NotNull LogicalPosition pos, @Nullable StringBuilder debugBuffer) {
assertReadAccess();
if (myDocument.getLineCount() == 0) return 0;
@@ -3328,7 +3333,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
CharSequence text = myDocument.getCharsNoThreadCheck();
- return EditorUtil.calcOffset(this, text, start, end, pos.column, EditorUtil.getTabSize(this));
+ return EditorUtil.calcOffset(this, text, start, end, pos.column, EditorUtil.getTabSize(this), debugBuffer);
}
@Override