Merge pull request #771 from ajaxorg/screen2doc
performance improvements for large documents
This commit is contained in:
commit
31cfc0a921
4 changed files with 124 additions and 135 deletions
|
|
@ -74,9 +74,10 @@ var EditSession = function(text, mode) {
|
|||
this.$frontMarkers = {};
|
||||
this.$backMarkers = {};
|
||||
this.$markerId = 1;
|
||||
this.$rowCache = [];
|
||||
this.$resetRowCache(0);
|
||||
this.$wrapData = [];
|
||||
this.$foldData = [];
|
||||
this.$rowLengthCache = [];
|
||||
this.$undoSelect = true;
|
||||
this.$foldData.toString = function() {
|
||||
var str = "";
|
||||
|
|
@ -86,7 +87,7 @@ var EditSession = function(text, mode) {
|
|||
return str;
|
||||
}
|
||||
|
||||
if (typeof text == "object") {
|
||||
if (typeof text == "object" && text.getLine) {
|
||||
this.setDocument(text);
|
||||
} else {
|
||||
this.setDocument(new Document(text));
|
||||
|
|
@ -139,18 +140,37 @@ var EditSession = function(text, mode) {
|
|||
*
|
||||
*
|
||||
**/
|
||||
this.$resetRowCache = function(row) {
|
||||
if (row == 0) {
|
||||
this.$rowCache = [];
|
||||
this.$resetRowCache = function(docRrow) {
|
||||
if (!docRrow) {
|
||||
this.$docRowCache = [];
|
||||
this.$screenRowCache = [];
|
||||
return;
|
||||
}
|
||||
var rowCache = this.$rowCache;
|
||||
for (var i = 0; i < rowCache.length; i++) {
|
||||
if (rowCache[i].docRow >= row) {
|
||||
rowCache.splice(i, rowCache.length);
|
||||
return;
|
||||
}
|
||||
|
||||
var i = this.$getRowCacheIndex(this.$docRowCache, docRrow) + 1;
|
||||
var l = this.$docRowCache.length;
|
||||
this.$docRowCache.splice(i, l);
|
||||
this.$screenRowCache.splice(i, l);
|
||||
|
||||
};
|
||||
|
||||
this.$getRowCacheIndex = function(cacheArray, val) {
|
||||
var low = 0;
|
||||
var hi = cacheArray.length - 1;
|
||||
|
||||
while (low <= hi) {
|
||||
var mid = (low + hi) >> 1;
|
||||
var c = cacheArray[mid];
|
||||
|
||||
if (val > c)
|
||||
low = mid + 1;
|
||||
else if (val < c)
|
||||
hi = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
|
||||
return low && low -1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -295,7 +315,6 @@ var EditSession = function(text, mode) {
|
|||
**/
|
||||
this.setUndoManager = function(undoManager) {
|
||||
this.$undoManager = undoManager;
|
||||
this.$resetRowCache(0);
|
||||
this.$deltas = [];
|
||||
this.$deltasDoc = [];
|
||||
this.$deltasFold = [];
|
||||
|
|
@ -406,6 +425,7 @@ var EditSession = function(text, mode) {
|
|||
if (isNaN(tabSize) || this.$tabSize === tabSize) return;
|
||||
|
||||
this.$modified = true;
|
||||
this.$rowLengthCache = [];
|
||||
this.$tabSize = tabSize;
|
||||
this._emit("changeTabSize");
|
||||
};
|
||||
|
|
@ -977,16 +997,6 @@ var EditSession = function(text, mode) {
|
|||
return this.$scrollLeft;
|
||||
};
|
||||
|
||||
/**
|
||||
* EditSession.getWidth() -> Number
|
||||
*
|
||||
* Returns the width of the document.
|
||||
**/
|
||||
this.getWidth = function() {
|
||||
this.$computeWidth();
|
||||
return this.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* EditSession.getScreenWidth() -> Number
|
||||
*
|
||||
|
|
@ -1001,38 +1011,33 @@ var EditSession = function(text, mode) {
|
|||
if (this.$modified || force) {
|
||||
this.$modified = false;
|
||||
|
||||
if (this.$useWrapMode)
|
||||
return this.screenWidth = this.$wrapLimit;
|
||||
|
||||
var lines = this.doc.getAllLines();
|
||||
var longestLine = 0;
|
||||
var cache = this.$rowLengthCache;
|
||||
var longestScreenLine = 0;
|
||||
var foldIndex = 0;
|
||||
var foldLine = this.$foldData[foldIndex];
|
||||
var foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||
var len = lines.length;
|
||||
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
var foldLine = this.getFoldLine(i),
|
||||
line, len;
|
||||
|
||||
line = lines[i];
|
||||
if (foldLine) {
|
||||
var end = foldLine.range.end;
|
||||
line = this.getFoldDisplayLine(foldLine);
|
||||
// Continue after the foldLine.end.row. All the lines in
|
||||
// between are folded.
|
||||
i = end.row;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (i > foldStart) {
|
||||
i = foldLine.end.row + 1;
|
||||
if (i >= len)
|
||||
break
|
||||
foldLine = this.$foldData[foldIndex++];
|
||||
foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||
}
|
||||
len = line.length;
|
||||
longestLine = Math.max(longestLine, len);
|
||||
if (!this.$useWrapMode) {
|
||||
longestScreenLine = Math.max(
|
||||
longestScreenLine,
|
||||
this.$getStringScreenWidth(line)[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
this.width = longestLine;
|
||||
|
||||
if (this.$useWrapMode) {
|
||||
this.screenWidth = this.$wrapLimit;
|
||||
} else {
|
||||
this.screenWidth = longestScreenLine;
|
||||
if (cache[i] == null)
|
||||
cache[i] = this.$getStringScreenWidth(lines[i])[0];
|
||||
|
||||
if (cache[i] > longestScreenLine)
|
||||
longestScreenLine = cache[i];
|
||||
}
|
||||
this.screenWidth = longestScreenLine;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1615,7 +1620,7 @@ var EditSession = function(text, mode) {
|
|||
|
||||
if (len != 0) {
|
||||
if (action.indexOf("remove") != -1) {
|
||||
useWrapMode && this.$wrapData.splice(firstRow, len);
|
||||
this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len);
|
||||
|
||||
var foldLines = this.$foldData;
|
||||
removedFolds = this.getFoldsInRange(e.data.range);
|
||||
|
|
@ -1649,6 +1654,10 @@ var EditSession = function(text, mode) {
|
|||
args = [firstRow, 0];
|
||||
for (var i = 0; i < len; i++) args.push([]);
|
||||
this.$wrapData.splice.apply(this.$wrapData, args);
|
||||
} else {
|
||||
args = Array(len);
|
||||
args.unshift(firstRow, 0);
|
||||
this.$rowLengthCache.splice.apply(this.$rowLengthCache, args);
|
||||
}
|
||||
|
||||
// If some new line is added inside of a foldLine, then split
|
||||
|
|
@ -1702,15 +1711,24 @@ var EditSession = function(text, mode) {
|
|||
console.error("doc.getLength() and $wrapData.length have to be the same!");
|
||||
}
|
||||
|
||||
useWrapMode && this.$updateWrapData(firstRow, lastRow);
|
||||
if (useWrapMode)
|
||||
this.$updateWrapData(firstRow, lastRow);
|
||||
else
|
||||
this.$updateRowLengthCache(firstRow, lastRow);
|
||||
|
||||
return removedFolds;
|
||||
};
|
||||
|
||||
this.$updateRowLengthCache = function(firstRow, lastRow, b) {
|
||||
//console.log(firstRow, lastRow, b)
|
||||
this.$rowLengthCache[firstRow] = null;
|
||||
this.$rowLengthCache[lastRow] = null;
|
||||
//console.log(this.$rowLengthCache)
|
||||
};
|
||||
|
||||
/** internal, hide
|
||||
* EditSession.$updateWrapData(firstRow, lastRow)
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.$updateWrapData = function(firstRow, lastRow) {
|
||||
var lines = this.doc.getAllLines();
|
||||
|
|
@ -1944,13 +1962,10 @@ var EditSession = function(text, mode) {
|
|||
*
|
||||
**/
|
||||
this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) {
|
||||
if (maxScreenColumn == 0) {
|
||||
if (maxScreenColumn == 0)
|
||||
return [0, 0];
|
||||
}
|
||||
if (maxScreenColumn == null) {
|
||||
maxScreenColumn = screenColumn +
|
||||
str.length * Math.max(this.getTabSize(), 2);
|
||||
}
|
||||
if (maxScreenColumn == null)
|
||||
maxScreenColumn = Infinity;
|
||||
screenColumn = screenColumn || 0;
|
||||
|
||||
var c, column;
|
||||
|
|
@ -2083,12 +2098,8 @@ var EditSession = function(text, mode) {
|
|||
*
|
||||
**/
|
||||
this.screenToDocumentPosition = function(screenRow, screenColumn) {
|
||||
if (screenRow < 0) {
|
||||
return {
|
||||
row: 0,
|
||||
column: 0
|
||||
}
|
||||
}
|
||||
if (screenRow < 0)
|
||||
return {row: 0, column: 0};
|
||||
|
||||
var line;
|
||||
var docRow = 0;
|
||||
|
|
@ -2097,17 +2108,17 @@ var EditSession = function(text, mode) {
|
|||
var row = 0;
|
||||
var rowLength = 0;
|
||||
|
||||
var rowCache = this.$rowCache;
|
||||
for (var i = 0; i < rowCache.length; i++) {
|
||||
if (rowCache[i].screenRow < screenRow) {
|
||||
row = rowCache[i].screenRow;
|
||||
docRow = rowCache[i].docRow;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
var rowCache = this.$screenRowCache;
|
||||
var i = this.$getRowCacheIndex(rowCache, screenRow);
|
||||
var row1 = rowCache[i];
|
||||
var docRow1 = this.$docRowCache[i];
|
||||
if (0 < i && i < rowCache.length) {
|
||||
var row = rowCache[i];
|
||||
var docRow = this.$docRowCache[i];
|
||||
var doCache = screenRow > row || (screenRow == row && i == rowCache.length - 1);
|
||||
} else {
|
||||
var doCache = true;
|
||||
}
|
||||
var doCache = !rowCache.length || i == rowCache.length;
|
||||
|
||||
var maxRow = this.getLength() - 1;
|
||||
var foldLine = this.getNextFoldLine(docRow);
|
||||
|
|
@ -2127,10 +2138,8 @@ var EditSession = function(text, mode) {
|
|||
}
|
||||
}
|
||||
if (doCache) {
|
||||
rowCache.push({
|
||||
docRow: docRow,
|
||||
screenRow: row
|
||||
});
|
||||
this.$docRowCache.push(docRow);
|
||||
this.$screenRowCache.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2163,18 +2172,13 @@ var EditSession = function(text, mode) {
|
|||
|
||||
// We remove one character at the end so that the docColumn
|
||||
// position returned is not associated to the next row on the screen.
|
||||
if (this.$useWrapMode && docColumn >= column) {
|
||||
if (this.$useWrapMode && docColumn >= column)
|
||||
docColumn = column - 1;
|
||||
}
|
||||
|
||||
if (foldLine) {
|
||||
if (foldLine)
|
||||
return foldLine.idxToPosition(docColumn);
|
||||
}
|
||||
|
||||
return {
|
||||
row: docRow,
|
||||
column: docColumn
|
||||
}
|
||||
return {row: docRow, column: docColumn};
|
||||
};
|
||||
|
||||
/** related to: EditSession.screenToDocumentPosition
|
||||
|
|
@ -2198,20 +2202,6 @@ var EditSession = function(text, mode) {
|
|||
docRow = pos.row;
|
||||
docColumn = pos.column;
|
||||
|
||||
var wrapData;
|
||||
// Special case in wrapMode if the doc is at the end of the document.
|
||||
if (this.$useWrapMode) {
|
||||
wrapData = this.$wrapData;
|
||||
if (docRow > wrapData.length - 1) {
|
||||
return {
|
||||
row: this.getScreenLength(),
|
||||
column: wrapData.length == 0
|
||||
? 0
|
||||
: (wrapData[wrapData.length - 1].length - 1)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var screenRow = 0;
|
||||
var foldStartRow = null;
|
||||
var fold = null;
|
||||
|
|
@ -2224,17 +2214,17 @@ var EditSession = function(text, mode) {
|
|||
}
|
||||
|
||||
var rowEnd, row = 0;
|
||||
var rowCache = this.$rowCache;
|
||||
|
||||
for (var i = 0; i < rowCache.length; i++) {
|
||||
if (rowCache[i].docRow < docRow) {
|
||||
screenRow = rowCache[i].screenRow;
|
||||
row = rowCache[i].docRow;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
var rowCache = this.$docRowCache;
|
||||
var i = this.$getRowCacheIndex(rowCache, docRow);
|
||||
if (0 < i && i < rowCache.length) {
|
||||
var row = rowCache[i];
|
||||
var screenRow = this.$screenRowCache[i];
|
||||
var doCache = docRow > row || (docRow == row && i == rowCache.length - 1);
|
||||
} else {
|
||||
var doCache = true;
|
||||
}
|
||||
var doCache = !rowCache.length || i == rowCache.length;
|
||||
|
||||
var foldLine = this.getNextFoldLine(row);
|
||||
var foldStart = foldLine ?foldLine.start.row :Infinity;
|
||||
|
|
@ -2255,10 +2245,8 @@ var EditSession = function(text, mode) {
|
|||
row = rowEnd;
|
||||
|
||||
if (doCache) {
|
||||
rowCache.push({
|
||||
docRow: row,
|
||||
screenRow: screenRow
|
||||
});
|
||||
this.$docRowCache.push(row);
|
||||
this.$screenRowCache.push(screenRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2274,7 +2262,7 @@ var EditSession = function(text, mode) {
|
|||
}
|
||||
// Clamp textLine if in wrapMode.
|
||||
if (this.$useWrapMode) {
|
||||
var wrapRow = wrapData[foldStartRow];
|
||||
var wrapRow = this.$wrapData[foldStartRow];
|
||||
var screenRowOffset = 0;
|
||||
while (textLine.length >= wrapRow[screenRowOffset]) {
|
||||
screenRow ++;
|
||||
|
|
|
|||
|
|
@ -340,6 +340,8 @@ function Folding() {
|
|||
|
||||
if (this.$useWrapMode)
|
||||
this.$updateWrapData(foldLine.start.row, foldLine.start.row);
|
||||
else
|
||||
this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row);
|
||||
|
||||
// Notify that fold data has changed.
|
||||
this.$modified = true;
|
||||
|
|
@ -395,9 +397,10 @@ function Folding() {
|
|||
newFoldLine.start.column = folds[0].start.column;
|
||||
}
|
||||
|
||||
if (this.$useWrapMode) {
|
||||
if (this.$useWrapMode)
|
||||
this.$updateWrapData(startRow, endRow);
|
||||
}
|
||||
else
|
||||
this.$updateRowLengthCache(startRow, endRow);
|
||||
|
||||
// Notify that fold data has changed.
|
||||
this.$modified = true;
|
||||
|
|
|
|||
|
|
@ -423,22 +423,18 @@ module.exports = {
|
|||
"test get longest line" : function() {
|
||||
var session = new EditSession(["12"]);
|
||||
session.setTabSize(4);
|
||||
assert.equal(session.getWidth(), 2);
|
||||
assert.equal(session.getScreenWidth(), 2);
|
||||
|
||||
session.doc.insertNewLine(0);
|
||||
session.doc.insertNewLine({row: 0, column: Infinity});
|
||||
session.doc.insertLines(1, ["123"]);
|
||||
assert.equal(session.getWidth(), 3);
|
||||
assert.equal(session.getScreenWidth(), 3);
|
||||
|
||||
session.doc.insertNewLine(0);
|
||||
session.doc.insertNewLine({row: 0, column: Infinity});
|
||||
session.doc.insertLines(1, ["\t\t"]);
|
||||
|
||||
assert.equal(session.getWidth(), 3);
|
||||
assert.equal(session.getScreenWidth(), 8);
|
||||
|
||||
session.setTabSize(2);
|
||||
assert.equal(session.getWidth(), 3);
|
||||
assert.equal(session.getScreenWidth(), 4);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -50,34 +50,36 @@ var assert = require("./test/assertions");
|
|||
module.exports = {
|
||||
"test: screen2text the column should be rounded to the next character edge" : function() {
|
||||
var el = document.createElement("div");
|
||||
|
||||
|
||||
if (!el.getBoundingClientRect) {
|
||||
console.log("Skipping test: This test only runs in the browser");
|
||||
return;
|
||||
}
|
||||
|
||||
el.style.left = "0px";
|
||||
el.style.top = "0px";
|
||||
el.style.width = "100px";
|
||||
|
||||
el.style.left = "20px";
|
||||
el.style.top = "30px";
|
||||
el.style.width = "300px";
|
||||
el.style.height = "100px";
|
||||
document.body.style.margin = "0px";
|
||||
document.body.style.padding = "0px";
|
||||
document.body.appendChild(el);
|
||||
|
||||
var renderer = new VirtualRenderer(el);
|
||||
renderer.setPadding(0);
|
||||
renderer.setSession(new EditSession("1234"));
|
||||
|
||||
var r = renderer.scroller.getBoundingClientRect();
|
||||
function testPixelToText(x, y, row, column) {
|
||||
assert.position(renderer.screenToTextCoordinates(x+r.left, y+r.top), row, column);
|
||||
}
|
||||
|
||||
renderer.characterWidth = 10;
|
||||
renderer.lineHeight = 15;
|
||||
|
||||
assert.position(renderer.screenToTextCoordinates(0, 0), 0, 0);
|
||||
assert.position(renderer.screenToTextCoordinates(4, 0), 0, 0);
|
||||
assert.position(renderer.screenToTextCoordinates(5, 0), 0, 1);
|
||||
assert.position(renderer.screenToTextCoordinates(9, 0), 0, 1);
|
||||
assert.position(renderer.screenToTextCoordinates(10, 0), 0, 1);
|
||||
assert.position(renderer.screenToTextCoordinates(14, 0), 0, 1);
|
||||
assert.position(renderer.screenToTextCoordinates(15, 0), 0, 2);
|
||||
testPixelToText(4, 0, 0, 0);
|
||||
testPixelToText(5, 0, 0, 1);
|
||||
testPixelToText(9, 0, 0, 1);
|
||||
testPixelToText(10, 0, 0, 1);
|
||||
testPixelToText(14, 0, 0, 1);
|
||||
testPixelToText(15, 0, 0, 2);
|
||||
document.body.removeChild(el);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue