diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 7300546e..23fc3fcd 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -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 ++; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 187bd0e6..3a113c84 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -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; diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index e2bc7fae..0f7edd9e 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -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); }, diff --git a/lib/ace/virtual_renderer_test.js b/lib/ace/virtual_renderer_test.js index 59d5b089..191666c3 100644 --- a/lib/ace/virtual_renderer_test.js +++ b/lib/ace/virtual_renderer_test.js @@ -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); }