Fix move to first/last character in line if in wrap mode. Fixes some other bugs on the way and add some simple unit tests.
This commit is contained in:
parent
40472b71fc
commit
28ea247f17
8 changed files with 171 additions and 51 deletions
|
|
@ -800,9 +800,12 @@ var Document = function(text, mode) {
|
|||
this.$useWrapMode = false;
|
||||
|
||||
this.setUseWrapMode = function(useWrapMode) {
|
||||
this.$useWrapMode = useWrapMode;
|
||||
this.$updateWrapData(0, this.lines.length - 1);
|
||||
this._dispatchEvent("changeWrapMode");
|
||||
if (useWrapMode != this.$useWrapMode) {
|
||||
this.$useWrapMode = useWrapMode;
|
||||
this.$updateWrapData(0, this.lines.length - 1);
|
||||
this._dispatchEvent("changeWrapMode");
|
||||
this.fireChangeEvent(0);
|
||||
}
|
||||
};
|
||||
|
||||
this.getUseWrapMode = function() {
|
||||
|
|
@ -810,7 +813,10 @@ var Document = function(text, mode) {
|
|||
};
|
||||
|
||||
this.setWrapLimit = function(wrapLimit) {
|
||||
this.$wrapLimit = wrapLimit;
|
||||
if (wrapLimit != this.$wrapLimit) {
|
||||
this.$wrapLimit = wrapLimit;
|
||||
this.$updateWrapData(0, this.lines.length - 1);
|
||||
}
|
||||
};
|
||||
|
||||
this.getWrapLimit = function() {
|
||||
|
|
@ -1008,21 +1014,67 @@ var Document = function(text, mode) {
|
|||
return rows * config.lineHeight;
|
||||
};
|
||||
|
||||
this.getScreenRowLength = function(screenRow) {
|
||||
|
||||
/**
|
||||
* Calculates the width of the a string on the screen while assuming that
|
||||
* the string starts at the first column on the screen.
|
||||
*
|
||||
* @param string str String to calculate the screen width of
|
||||
* @return int number of columns for str on screen.
|
||||
*/
|
||||
this.$getStringScreenWidth = function(str) {
|
||||
var len = str.length;
|
||||
str.replace("\t", function(m) {
|
||||
len += tabSize-1;
|
||||
return m;
|
||||
});
|
||||
return len;
|
||||
}
|
||||
|
||||
this.getScreenLastRowColumn = function(screenRow, returnDocPosition) {
|
||||
if (!this.$useWrapMode) {
|
||||
return this.getLine(screenRow).length;
|
||||
return this.$getStringScreenWidth(this.getLine(screenRow));
|
||||
}
|
||||
|
||||
var rowData = this.$screenToDocumentRow(screenRow);
|
||||
var docRow = rowData[0],
|
||||
row = rowData[1];
|
||||
|
||||
var start, end;
|
||||
if (this.$wrapData[docRow][row]) {
|
||||
return this.$wrapData[docRow][row] - (this.$wrapData[docRow][row - 1] || 0);
|
||||
start = (this.$wrapData[docRow][row - 1] || 0);
|
||||
end = this.$wrapData[docRow][row];
|
||||
returnDocPosition && end--;
|
||||
} else {
|
||||
return this.lines[docRow].length -
|
||||
(this.$wrapData[docRow][row - 1] || 0) ;
|
||||
end = this.lines[docRow].length;
|
||||
start = (this.$wrapData[docRow][row - 1] || 0);
|
||||
}
|
||||
if (!returnDocPosition) {
|
||||
return this.$getStringScreenWidth(this.getLine(docRow).substring(start, end));
|
||||
} else {
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
this.getDocumentLastRowColumn = function(docRow, docColumn) {
|
||||
if (!this.$useWrapMode) {
|
||||
return this.getLine(docRow).length;
|
||||
}
|
||||
|
||||
var screenRow = this.documentToScreenRow(docRow, docColumn);
|
||||
return this.getScreenLastRowColumn(screenRow, true);
|
||||
}
|
||||
|
||||
this.getScreenFirstRowColumn = function(screenRow) {
|
||||
if (!this.$useWrapMode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var rowData = this.$screenToDocumentRow(screenRow);
|
||||
var docRow = rowData[0],
|
||||
row = rowData[1];
|
||||
|
||||
return this.$wrapData[docRow][row - 1] || 0;
|
||||
};
|
||||
|
||||
this.getRowSplitData = function(row) {
|
||||
|
|
@ -1033,6 +1085,12 @@ var Document = function(text, mode) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns array
|
||||
* - array[0]: The documentRow aquivalent.
|
||||
* - array[1]: The screenRowOffset to the first documentRow on the screen.
|
||||
*/
|
||||
this.$screenToDocumentRow = function(row) {
|
||||
if (!this.$useWrapMode) {
|
||||
return [row, 0];
|
||||
|
|
@ -1063,13 +1121,14 @@ var Document = function(text, mode) {
|
|||
var remaining = column;
|
||||
if (!this.$useWrapMode) {
|
||||
docRow = row;
|
||||
row = 0;
|
||||
docColumn = 0;
|
||||
line = this.getLine(docRow).split("\t");
|
||||
} else {
|
||||
var wrapData = this.$wrapData, linesCount = this.lines.length;
|
||||
|
||||
var rowData = this.$screenToDocumentRow(row);
|
||||
var row = rowData[1];
|
||||
row = rowData[1];
|
||||
docRow = rowData[0];
|
||||
|
||||
if (docRow >= this.lines.length) {
|
||||
|
|
@ -1101,7 +1160,12 @@ var Document = function(text, mode) {
|
|||
|
||||
// Clamp docColumn.
|
||||
if (docRow < linesCount && wrapData[docRow][row]) {
|
||||
docColumn = Math.min(docColumn, wrapData[docRow][row]);
|
||||
if (docColumn >= wrapData[docRow][row]) {
|
||||
// We remove one character at the end such that the docColumn
|
||||
// position returned is not associated to the next row on the
|
||||
// screen.
|
||||
docColumn = wrapData[docRow][row] - 1;
|
||||
}
|
||||
} else if (this.lines[docRow]) {
|
||||
docColumn = Math.min(docColumn, this.lines[docRow].length);
|
||||
}
|
||||
|
|
@ -1116,33 +1180,57 @@ var Document = function(text, mode) {
|
|||
return this.documentToScreenPosition(row, docColumn).column;
|
||||
};
|
||||
|
||||
this.documentToScreenRow = function(row) {
|
||||
/**
|
||||
*
|
||||
* @return array[2]
|
||||
* - array[0]: The number of the row on the screen (aka screenRow)
|
||||
* - array[1]: The number of rows from the first docRow on the screen
|
||||
* (aka screenRowOffset);
|
||||
*/
|
||||
this.$documentToScreenRow = function(docRow, docColumn) {
|
||||
if (!this.$useWrapMode) {
|
||||
return row;
|
||||
return [docRow, 0];
|
||||
}
|
||||
|
||||
var wrapData = this.$wrapData;
|
||||
var screenRow = 0;
|
||||
|
||||
// Handle special case where the row is outside of the range of lines.
|
||||
if (row > wrapData.length - 1) {
|
||||
for (row = 0; row < wrapData.length; row ++) {
|
||||
screenRow += wrapData[row].length + 1;
|
||||
}
|
||||
return screenRow;
|
||||
if (docRow > wrapData.length - 1) {
|
||||
return [this.getScreenLength(),
|
||||
this.wrapData[this.wrapData.length - 1].length - 1];
|
||||
}
|
||||
|
||||
for (var i = 0; i < row; i++) {
|
||||
for (var i = 0; i < docRow; i++) {
|
||||
screenRow += wrapData[i].length + 1;
|
||||
}
|
||||
|
||||
return screenRow;
|
||||
var screenRowOffset = 0;
|
||||
while (docColumn >= wrapData[docRow][screenRowOffset]) {
|
||||
screenRow ++;
|
||||
screenRowOffset++;
|
||||
}
|
||||
|
||||
return [screenRow, screenRowOffset];
|
||||
}
|
||||
|
||||
this.documentToScreenPosition = function(row, column) {
|
||||
this.documentToScreenRow = function(docRow, docColumn) {
|
||||
return this.$documentToScreenRow(docRow, docColumn)[0];
|
||||
}
|
||||
|
||||
this.documentToScreenPosition = function(pos, column) {
|
||||
var str;
|
||||
var tabSize = this.getTabSize();
|
||||
|
||||
// Normalize the passed in arguments.
|
||||
var row;
|
||||
if (column != null) {
|
||||
row = pos;
|
||||
} else {
|
||||
row = pos.row;
|
||||
column = pos.column;
|
||||
}
|
||||
|
||||
if (!this.$useWrapMode) {
|
||||
str = this.getLine(row).substring(0, column);
|
||||
column += (str.split("\t").length - 1) * (tabSize - 1);
|
||||
|
|
@ -1151,26 +1239,24 @@ var Document = function(text, mode) {
|
|||
column: column
|
||||
};
|
||||
}
|
||||
var screenRow = this.documentToScreenRow(row);
|
||||
if (row >= this.lines.length) {
|
||||
return {
|
||||
row: screenRow,
|
||||
column: 0
|
||||
};
|
||||
}
|
||||
var screenColumn = column;
|
||||
var wrapRowData = this.$wrapData[row];
|
||||
var split;
|
||||
for (split = 0; wrapRowData && split < wrapRowData.length; split++) {
|
||||
if (column > wrapRowData[split]) {
|
||||
screenColumn = column - wrapRowData[split];
|
||||
screenRow ++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
str = this.getLine(row).substring(wrapRowData[split - 1] || 0, column);
|
||||
var split;
|
||||
var wrapRowData = this.$wrapData[row];
|
||||
var screenRow, screenRowOffset;
|
||||
var screenColumn;
|
||||
var rowData = this.$documentToScreenRow(row, column);
|
||||
screenRow = rowData[0];
|
||||
screenRowOffset = rowData[1];
|
||||
screenColumn = column - (wrapRowData[screenRowOffset - 1] || 0);
|
||||
|
||||
str = this.getLine(row).substring(
|
||||
wrapRowData[screenRowOffset - 1] || 0, column);
|
||||
screenColumn += (str.split("\t").length - 1) * (tabSize - 1);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ var Cursor = function(parentEl) {
|
|||
|
||||
this.setCursor = function(position, overwrite) {
|
||||
this.position =
|
||||
this.doc.documentToScreenPosition(position.row, position.column);
|
||||
this.doc.documentToScreenPosition(position);
|
||||
if (overwrite) {
|
||||
dom.addCssClass(this.cursor, "ace_overwrite");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ var Marker = function(parentEl) {
|
|||
// selection start
|
||||
var row = range.start.row;
|
||||
var lineRange = new Range(row, range.start.column,
|
||||
row, this.doc.getScreenRowLength(row));
|
||||
row, this.doc.getScreenLastRowColumn(row));
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig);
|
||||
|
||||
// selection end
|
||||
|
|
@ -122,7 +122,7 @@ var Marker = function(parentEl) {
|
|||
for (var row = range.start.row + 1; row < range.end.row; row++) {
|
||||
lineRange.start.row = row;
|
||||
lineRange.end.row = row;
|
||||
lineRange.end.column = this.doc.getScreenRowLength(row);
|
||||
lineRange.end.column = this.doc.getScreenLastRowColumn(row);
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,9 +147,9 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
|
|||
|
||||
this.toScreenRange = function(doc) {
|
||||
var screenPosStart =
|
||||
doc.documentToScreenPosition(this.start.row, this.start.column);
|
||||
doc.documentToScreenPosition(this.start);
|
||||
var screenPosEnd =
|
||||
doc.documentToScreenPosition(this.end.row, this.end.column);
|
||||
doc.documentToScreenPosition(this.end);
|
||||
return new Range(
|
||||
screenPosStart.row, screenPosStart.column,
|
||||
screenPosEnd.row, screenPosEnd.column
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ var Selection = function(doc) {
|
|||
this.$updateDesiredColumn = function() {
|
||||
var cursor = this.getCursor();
|
||||
if (cursor) {
|
||||
this.$desiredColumn = this.doc.documentToScreenPosition(cursor.row, cursor.column).column;
|
||||
this.$desiredColumn = this.doc.documentToScreenColumn(cursor.row, cursor.column);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -311,19 +311,27 @@ var Selection = function(doc) {
|
|||
this.moveCursorLineStart = function() {
|
||||
var row = this.selectionLead.row;
|
||||
var column = this.selectionLead.column;
|
||||
var beforeCursor = this.doc.getLine(row).slice(0, column);
|
||||
var screenRow = this.doc.documentToScreenRow(row, column);
|
||||
var firstRowColumn = this.doc.getScreenFirstRowColumn(screenRow);
|
||||
var beforeCursor = this.doc.getLine(row).slice(firstRowColumn, column);
|
||||
var leadingSpace = beforeCursor.match(/^\s*/);
|
||||
if (leadingSpace[0].length == 0)
|
||||
this.moveCursorTo(row, this.doc.getLine(row).match(/^\s*/)[0].length);
|
||||
else if (leadingSpace[0].length >= column)
|
||||
this.moveCursorTo(row, 0);
|
||||
else
|
||||
this.moveCursorTo(row, leadingSpace[0].length);
|
||||
if (leadingSpace[0].length == 0) {
|
||||
var lastRowColumn = this.doc.getDocumentLastRowColumn(row, column);
|
||||
leadingSpace = this.doc.getLine(row).
|
||||
substring(firstRowColumn, lastRowColumn).
|
||||
match(/^\s*/);
|
||||
this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);
|
||||
} else if (leadingSpace[0].length >= column) {
|
||||
this.moveCursorTo(row, firstRowColumn);
|
||||
} else {
|
||||
this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);
|
||||
}
|
||||
};
|
||||
|
||||
this.moveCursorLineEnd = function() {
|
||||
this.moveCursorTo(this.selectionLead.row,
|
||||
this.doc.getLine(this.selectionLead.row).length);
|
||||
var selLead = this.selectionLead;
|
||||
this.moveCursorTo(selLead.row,
|
||||
this.doc.getDocumentLastRowColumn(selLead.row, selLead.column));
|
||||
};
|
||||
|
||||
this.moveCursorFileEnd = function() {
|
||||
|
|
|
|||
|
|
@ -321,6 +321,31 @@ var Test = {
|
|||
|
||||
// Ignore spaces/tabs at beginning of split.
|
||||
computeAndAssert("foo \t \t \t \t bar", [ 14]);
|
||||
},
|
||||
|
||||
"test: documentToScreen": function() {
|
||||
var tabSize = 4;
|
||||
var wrapLimit = 12;
|
||||
var doc = new Document(["foo bar foo bar"]);
|
||||
doc.setUseWrapMode(true);
|
||||
doc.setWrapLimit(12);
|
||||
|
||||
assert.position(doc.documentToScreenPosition(0, 11), 0, 11);
|
||||
assert.position(doc.documentToScreenPosition(0, 12), 1, 0);
|
||||
},
|
||||
|
||||
"test: screenToDocument": function() {
|
||||
var tabSize = 4;
|
||||
var wrapLimit = 12;
|
||||
var doc = new Document(["foo bar foo bar"]);
|
||||
doc.setUseWrapMode(true);
|
||||
doc.setWrapLimit(12);
|
||||
|
||||
assert.position(doc.screenToDocumentPosition(1, 0), 0, 12);
|
||||
assert.position(doc.screenToDocumentPosition(0, 11), 0, 11);
|
||||
// Check if the position is clamped the right way.
|
||||
assert.position(doc.screenToDocumentPosition(0, 12), 0, 11);
|
||||
assert.position(doc.screenToDocumentPosition(0, 20), 0, 11);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -585,7 +585,7 @@ var VirtualRenderer = function(container, theme) {
|
|||
var row = Math.floor((pageY + this.scrollTop - canvasPos.top)
|
||||
/ this.lineHeight);
|
||||
|
||||
return this.doc.screenToDocumentPosition(row, col);
|
||||
return this.doc.screenToDocumentPosition(row, Math.max(col, 0));
|
||||
};
|
||||
|
||||
this.textToScreenCoordinates = function(row, column) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue