better caching for screen to doc transforms

This commit is contained in:
nightwing 2012-05-24 12:23:49 +04:00
commit 7a51b207a5
3 changed files with 107 additions and 120 deletions

View file

@ -74,9 +74,10 @@ var EditSession = function(text, mode) {
this.$frontMarkers = {}; this.$frontMarkers = {};
this.$backMarkers = {}; this.$backMarkers = {};
this.$markerId = 1; this.$markerId = 1;
this.$rowCache = []; this.$resetRowCache(0);
this.$wrapData = []; this.$wrapData = [];
this.$foldData = []; this.$foldData = [];
this.$rowLengthCache = [];
this.$undoSelect = true; this.$undoSelect = true;
this.$foldData.toString = function() { this.$foldData.toString = function() {
var str = ""; var str = "";
@ -139,18 +140,37 @@ var EditSession = function(text, mode) {
* *
* *
**/ **/
this.$resetRowCache = function(row) { this.$resetRowCache = function(docRrow) {
if (row == 0) { if (!docRrow) {
this.$rowCache = []; this.$docRowCache = [];
this.$screenRowCache = [];
return; return;
} }
var rowCache = this.$rowCache;
for (var i = 0; i < rowCache.length; i++) { var i = this.$getRowCacheIndex(this.$docRowCache, docRrow) + 1;
if (rowCache[i].docRow >= row) { var l = this.$docRowCache.length;
rowCache.splice(i, rowCache.length); this.$docRowCache.splice(i, l);
return; 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;
}; };
/** /**
@ -296,7 +316,6 @@ var EditSession = function(text, mode) {
**/ **/
this.setUndoManager = function(undoManager) { this.setUndoManager = function(undoManager) {
this.$undoManager = undoManager; this.$undoManager = undoManager;
this.$resetRowCache(0);
this.$deltas = []; this.$deltas = [];
this.$deltasDoc = []; this.$deltasDoc = [];
this.$deltasFold = []; this.$deltasFold = [];
@ -407,6 +426,7 @@ var EditSession = function(text, mode) {
if (isNaN(tabSize) || this.$tabSize === tabSize) return; if (isNaN(tabSize) || this.$tabSize === tabSize) return;
this.$modified = true; this.$modified = true;
this.$rowLengthCache = [];
this.$tabSize = tabSize; this.$tabSize = tabSize;
this._emit("changeTabSize"); this._emit("changeTabSize");
}; };
@ -978,16 +998,6 @@ var EditSession = function(text, mode) {
return this.$scrollLeft; return this.$scrollLeft;
}; };
/**
* EditSession.getWidth() -> Number
*
* Returns the width of the document.
**/
this.getWidth = function() {
this.$computeWidth();
return this.width;
};
/** /**
* EditSession.getScreenWidth() -> Number * EditSession.getScreenWidth() -> Number
* *
@ -1002,38 +1012,33 @@ var EditSession = function(text, mode) {
if (this.$modified || force) { if (this.$modified || force) {
this.$modified = false; this.$modified = false;
if (this.$useWrapMode)
return this.screenWidth = this.$wrapLimit;
var lines = this.doc.getAllLines(); var lines = this.doc.getAllLines();
var longestLine = 0; var cache = this.$rowLengthCache;
var longestScreenLine = 0; 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++) { for (var i = 0; i < len; i++) {
var foldLine = this.getFoldLine(i), if (i > foldStart) {
line, len; i = foldLine.end.row + 1;
if (i >= len)
line = lines[i]; break
if (foldLine) { foldLine = this.$foldData[foldIndex++];
var end = foldLine.range.end; foldStart = foldLine ? foldLine.start.row : Infinity;
line = this.getFoldDisplayLine(foldLine);
// Continue after the foldLine.end.row. All the lines in
// between are folded.
i = end.row;
} }
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) { if (cache[i] == null)
this.screenWidth = this.$wrapLimit; cache[i] = this.$getStringScreenWidth(lines[i])[0];
} else {
this.screenWidth = longestScreenLine; if (cache[i] > longestScreenLine)
longestScreenLine = cache[i];
} }
this.screenWidth = longestScreenLine;
} }
}; };
@ -1616,7 +1621,7 @@ var EditSession = function(text, mode) {
if (len != 0) { if (len != 0) {
if (action.indexOf("remove") != -1) { if (action.indexOf("remove") != -1) {
useWrapMode && this.$wrapData.splice(firstRow, len); this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len);
var foldLines = this.$foldData; var foldLines = this.$foldData;
removedFolds = this.getFoldsInRange(e.data.range); removedFolds = this.getFoldsInRange(e.data.range);
@ -1650,6 +1655,10 @@ var EditSession = function(text, mode) {
args = [firstRow, 0]; args = [firstRow, 0];
for (var i = 0; i < len; i++) args.push([]); for (var i = 0; i < len; i++) args.push([]);
this.$wrapData.splice.apply(this.$wrapData, args); 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 // If some new line is added inside of a foldLine, then split
@ -1703,15 +1712,24 @@ var EditSession = function(text, mode) {
console.error("doc.getLength() and $wrapData.length have to be the same!"); 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; 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 /** internal, hide
* EditSession.$updateWrapData(firstRow, lastRow) * EditSession.$updateWrapData(firstRow, lastRow)
* *
*
**/ **/
this.$updateWrapData = function(firstRow, lastRow) { this.$updateWrapData = function(firstRow, lastRow) {
var lines = this.doc.getAllLines(); var lines = this.doc.getAllLines();
@ -1945,13 +1963,10 @@ var EditSession = function(text, mode) {
* *
**/ **/
this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) {
if (maxScreenColumn == 0) { if (maxScreenColumn == 0)
return [0, 0]; return [0, 0];
} if (maxScreenColumn == null)
if (maxScreenColumn == null) { maxScreenColumn = Infinity;
maxScreenColumn = screenColumn +
str.length * Math.max(this.getTabSize(), 2);
}
screenColumn = screenColumn || 0; screenColumn = screenColumn || 0;
var c, column; var c, column;
@ -2084,12 +2099,8 @@ var EditSession = function(text, mode) {
* *
**/ **/
this.screenToDocumentPosition = function(screenRow, screenColumn) { this.screenToDocumentPosition = function(screenRow, screenColumn) {
if (screenRow < 0) { if (screenRow < 0)
return { return {row: 0, column: 0};
row: 0,
column: 0
}
}
var line; var line;
var docRow = 0; var docRow = 0;
@ -2098,17 +2109,17 @@ var EditSession = function(text, mode) {
var row = 0; var row = 0;
var rowLength = 0; var rowLength = 0;
var rowCache = this.$rowCache; var rowCache = this.$screenRowCache;
for (var i = 0; i < rowCache.length; i++) { var i = this.$getRowCacheIndex(rowCache, screenRow);
if (rowCache[i].screenRow < screenRow) { var row1 = rowCache[i];
row = rowCache[i].screenRow; var docRow1 = this.$docRowCache[i];
docRow = rowCache[i].docRow; if (0 < i && i < rowCache.length) {
} var row = rowCache[i];
else { var docRow = this.$docRowCache[i];
break; 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 maxRow = this.getLength() - 1;
var foldLine = this.getNextFoldLine(docRow); var foldLine = this.getNextFoldLine(docRow);
@ -2128,10 +2139,8 @@ var EditSession = function(text, mode) {
} }
} }
if (doCache) { if (doCache) {
rowCache.push({ this.$docRowCache.push(docRow);
docRow: docRow, this.$screenRowCache.push(row);
screenRow: row
});
} }
} }
@ -2164,18 +2173,13 @@ var EditSession = function(text, mode) {
// We remove one character at the end so that the docColumn // We remove one character at the end so that the docColumn
// position returned is not associated to the next row on the screen. // 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; docColumn = column - 1;
}
if (foldLine) { if (foldLine)
return foldLine.idxToPosition(docColumn); return foldLine.idxToPosition(docColumn);
}
return { return {row: docRow, column: docColumn};
row: docRow,
column: docColumn
}
}; };
/** related to: EditSession.screenToDocumentPosition /** related to: EditSession.screenToDocumentPosition
@ -2199,20 +2203,6 @@ var EditSession = function(text, mode) {
docRow = pos.row; docRow = pos.row;
docColumn = pos.column; 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 screenRow = 0;
var foldStartRow = null; var foldStartRow = null;
var fold = null; var fold = null;
@ -2225,17 +2215,17 @@ var EditSession = function(text, mode) {
} }
var rowEnd, row = 0; var rowEnd, row = 0;
var rowCache = this.$rowCache;
for (var i = 0; i < rowCache.length; i++) {
if (rowCache[i].docRow < docRow) { var rowCache = this.$docRowCache;
screenRow = rowCache[i].screenRow; var i = this.$getRowCacheIndex(rowCache, docRow);
row = rowCache[i].docRow; if (0 < i && i < rowCache.length) {
} else { var row = rowCache[i];
break; 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 foldLine = this.getNextFoldLine(row);
var foldStart = foldLine ?foldLine.start.row :Infinity; var foldStart = foldLine ?foldLine.start.row :Infinity;
@ -2256,10 +2246,8 @@ var EditSession = function(text, mode) {
row = rowEnd; row = rowEnd;
if (doCache) { if (doCache) {
rowCache.push({ this.$docRowCache.push(row);
docRow: row, this.$screenRowCache.push(screenRow);
screenRow: screenRow
});
} }
} }
@ -2275,7 +2263,7 @@ var EditSession = function(text, mode) {
} }
// Clamp textLine if in wrapMode. // Clamp textLine if in wrapMode.
if (this.$useWrapMode) { if (this.$useWrapMode) {
var wrapRow = wrapData[foldStartRow]; var wrapRow = this.$wrapData[foldStartRow];
var screenRowOffset = 0; var screenRowOffset = 0;
while (textLine.length >= wrapRow[screenRowOffset]) { while (textLine.length >= wrapRow[screenRowOffset]) {
screenRow ++; screenRow ++;

View file

@ -340,6 +340,8 @@ function Folding() {
if (this.$useWrapMode) if (this.$useWrapMode)
this.$updateWrapData(foldLine.start.row, foldLine.start.row); this.$updateWrapData(foldLine.start.row, foldLine.start.row);
else
this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row);
// Notify that fold data has changed. // Notify that fold data has changed.
this.$modified = true; this.$modified = true;
@ -395,9 +397,10 @@ function Folding() {
newFoldLine.start.column = folds[0].start.column; newFoldLine.start.column = folds[0].start.column;
} }
if (this.$useWrapMode) { if (this.$useWrapMode)
this.$updateWrapData(startRow, endRow); this.$updateWrapData(startRow, endRow);
} else
this.$updateRowLengthCache(startRow, endRow);
// Notify that fold data has changed. // Notify that fold data has changed.
this.$modified = true; this.$modified = true;

View file

@ -423,22 +423,18 @@ module.exports = {
"test get longest line" : function() { "test get longest line" : function() {
var session = new EditSession(["12"]); var session = new EditSession(["12"]);
session.setTabSize(4); session.setTabSize(4);
assert.equal(session.getWidth(), 2);
assert.equal(session.getScreenWidth(), 2); assert.equal(session.getScreenWidth(), 2);
session.doc.insertNewLine(0); session.doc.insertNewLine({row: 0, column: Infinity});
session.doc.insertLines(1, ["123"]); session.doc.insertLines(1, ["123"]);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 3); assert.equal(session.getScreenWidth(), 3);
session.doc.insertNewLine(0); session.doc.insertNewLine({row: 0, column: Infinity});
session.doc.insertLines(1, ["\t\t"]); session.doc.insertLines(1, ["\t\t"]);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 8); assert.equal(session.getScreenWidth(), 8);
session.setTabSize(2); session.setTabSize(2);
assert.equal(session.getWidth(), 3);
assert.equal(session.getScreenWidth(), 4); assert.equal(session.getScreenWidth(), 4);
}, },