diff --git a/demo/demo.js b/demo/demo.js index eb227809..1291db1a 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -149,6 +149,7 @@ exports.launch = function(env) { // BEGING TESTING var Range = require("ace/range").Range; + docs.js.addFold(new Range(0, 13, 0, 18), "args..."); docs.js.addFold(new Range(1, 10, 2, 10), "foo..."); docs.js.addFold(new Range(2, 20, 2, 25), "bar..."); window.s = docs.js; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 117ae04b..a82b050d 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -22,6 +22,7 @@ * Contributor(s): * Fabian Jakobs * Mihai Sucan + * Julian Viereck * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -55,6 +56,7 @@ var EditSession = function(text, mode) { this.$backMarkers = {}; this.$markerId = 1; this.$wrapData = []; + this.$foldData = []; if (text instanceof Document) { this.setDocument(text); @@ -875,8 +877,8 @@ var EditSession = function(text, mode) { if (len != 0) { if (action.indexOf("remove") != -1) { useWrapMode && this.$wrapData.splice(firstRow, len); - // TODO: More checking needed here. - this.$foldData.splice(firstRow, len); + // TODO: Remove no longer needed folds here. + // TODO: Update row data on folds. lastRow = firstRow; } else { var args; @@ -886,10 +888,8 @@ var EditSession = function(text, mode) { this.$wrapData.splice.apply(this.$wrapData, args); } - args = [firstRow, 0]; - for (var i = 0; i < len; i++) args.push(false); - // TODO: More checking needed here. - this.$foldData.splice.apply(this.$foldData, args); + // TODO: Expand folds here if needed. + // TODO: Update row data on folds. } } @@ -1041,7 +1041,7 @@ var EditSession = function(text, mode) { screenColumn += 1; } } - + return screenColumn; } @@ -1242,7 +1242,7 @@ var EditSession = function(text, mode) { return this.documentToScreenPosition(docRow, docColumn).row; } - this.$buildWrappedTextLine = function(row, endRow, endColumn) { + this.$buildFoldedTextLine = function(row, endRow, endColumn) { var textLine = ""; // Build a one line string for the current fold sequence that starts @@ -1273,6 +1273,13 @@ var EditSession = function(text, mode) { } } textLine += line.substring(lastEnd, fold.start.column); + + // Stop if the end position is inside of this fold. + if (endRow < fold.end.row || + endRow == fold.end.row && endColumn < fold.end.column + ) { + break while_loop; + } textLine += fold.placeholder; if (fold.sameLine) { lastEnd = fold.end.column; @@ -1333,8 +1340,10 @@ var EditSession = function(text, mode) { textLine = this.getLine(docRow).substring(0, docColumn); foldStartRow = docRow; } else { - textLine = this.$buildWrappedTextLine( - (foldStartRow != null ? foldStartRow : docRow), docRow, docColumn); + textLine = this.$buildFoldedTextLine( + (foldStartRow != null ? foldStartRow : docRow), + docRow, + docColumn); } // Clamp textLine if in wrapMode. @@ -1381,7 +1390,13 @@ var EditSession = function(text, mode) { } } - screenRows -= this.getFoldedRowLength(0, length - 1); + var foldData = this.$foldData; + for (var i = 0; i < foldData.length; i++) { + var foldRow = foldData[i]; + if (foldRow.linkOut) { + screenRows -= foldRow.linkOut.end.row - foldRow.linkOut.start.row; + } + } return screenRows; } @@ -1438,35 +1453,86 @@ var EditSession = function(text, mode) { this.sameLine = range.start.row == range.end.row; } + function FoldRow(row) { + this.row = row; + this.linkIn = null; + this.linkOut = null; + this.inRow = []; + } + + (function() { + this.addInRowFold = function(fold) { + if (!fold.sameLine || fold.start.row != this.row) { + throw "NOPE: Can't add fold here!"; + } + this.inRow.push(fold); + this.inRow.sort(function(a, b) { + return a.start.column - b.start.column; + }); + } + + this.addFold = function(fold) { + if (fold.sameLine) { + this.addInRowFold(fold) + } else if (fold.start.row == this.row) { + if (this.linkOut) { + throw "There is already a linkOut fold"; + } + this.linkOut = fold; + } else if (fold.end.row == this.row) { + if (this.linkIn) { + throw "There is already a linkIn fold"; + } + this.linkIn = fold; + } else { + throw "Trying to add fold to FoldRow that doesn't have a matching row"; + } + } + }).call(FoldRow.prototype); + + this.getFoldRow = function(docRow, startFoldRow) { + var foldData = this.$foldData; + for (var i = foldData.indexOf(startFoldRow) || 0; i < foldData.length; i++) { + var foldRow = foldData[i]; + if (foldRow.row == docRow) { + return foldRow; + } else if (foldRow.row > docRow) { + return null; + } + } + return null; + } + + this.getFoldRowOrCreate = function(docRow, startFoldRow) { + var foldRow = this.getFoldRow(docRow, startFoldRow); + if (!foldRow) { + foldRow = new FoldRow(startRow); + this.$foldData.push(foldRow); + this.$foldData.sort(function(a, b) { + return a.row - b.row; + }); + } + } + /** * Adds a new fold. */ this.addFold = function(range, placeholder) { var startRow = range.start.row, endRow = range.end.row, - foldData = this.$foldData; + foldData = this.$foldData, + foldRow = null; - // In case there is no fold data for the start row yet. - if (!Array.isArray(foldData[startRow])) { - foldData[startRow] = []; - } + foldRow = this.getFoldRowOrCreate(startRow); var fold = new Fold(range, placeholder); - // TODO: The folds have to be ordered by range.start! - foldData[startRow].push(fold); + foldRow.addFold(fold); - // Mark all lines folded by this fold as folded. - for (var row = startRow + 1; row < endRow; row++) { - foldData[row] = fold; - } - - // If this fold folds more then one line, then add the fold at the - // beginning of the foldData[endRow] as well. + // If this fold folds more then one line, then add this fold as the + // linkOut of the FoldRow at endRow. if (!fold.sameLine) { - if (!Array.isArray(foldData[endRow])) { - foldData[endRow] = []; - } - foldData[endRow].splice(0, 0, fold); + foldRow = this.getFoldRowOrCreate(endRow, foldRow); + foldRow.addFold(fold); } // TODO: Recalculate wrapData @@ -1481,104 +1547,60 @@ var EditSession = function(text, mode) { * Checks if a given documentRow is folded. This is true if there are some * folded parts such that some parts of the line is still visible. **/ - this.isRowFolded = function(docRow) { - return !!this.$foldData[docRow]; + this.isRowFolded = function(docRow, startFoldRow) { + return !!this.getFoldRow(docRow, startFoldRow); }; - this.getRowLastFold = function(docRow) { - var fold = this.$foldData[docRow]; - if (!fold) { - return null; - } else if (Array.isArray(fold)) { - return fold[fold.length - 1]; - } else { - return fold; - } - }; - - this.getRowFirstFold = function(docRow, skipLastWrap) { - var fold = this.$foldData[docRow]; - if (!fold) { - return null; - } else if (Array.isArray(fold)) { - if (skipLastWrap) { - if (fold[0].row.start != docRow) { - return fold[1] || null; - } - } - return fold[0]; - } else { - return fold; - } - }; - - this.getRowFoldEnd = function(docRow) { - return (this.$foldData[docRow] - ? this.getRowLastFold(docRow).end.row + this.getRowFoldEnd = function(docRow, startFoldRow) { + var foldRow = this.getFoldRow(docRow, startFoldRow); + return (foldRow + ? (foldRow.linkOut ? foldRow.linkOut.end.row : docRow) : docRow); }; - /** - * Checks if a given documentRow is visible or not. Not beeing visible means - * it's folded completly. - **/ - this.isRowVisible = function(docRow) { - var fold = this.$foldData[docRow]; - return !fold || Array.isArray(fold); - }; + this.buildFoldRowLine = function(docRow, startFoldRow) { + var foldRow = this.getFoldRow(docRow, startFoldRow); + var folds = []; + if (!foldRow) { + throw "Passed in docRow doesn't have a foldRow"; + } + if (foldRow.linkIn) { + throw "Can't build a single fold line starting at a FoldRow that is linked in!"; + } - this.getFoldData = function(docRow) { - return this.$foldData[docRow]; + var foldData = this.$foldData; + for (var i = foldData.indexOf(foldRow); foldRow; i++) { + if (foldRow.linkIn) { + folds.push(foldRow.linkIn); + } + + foldRow = foldRow.linkOut; + } } - /** - * Returns the number of folded lines between start and end row. - */ - this.getFoldedRowLength = function(start, end) { - var row = start; - var count = 0; - while (row <= end) { - row = this.findRowFoldedForward(row, end); - if (row != null) { - var fold = this.getRowLastFold(row); - if (!fold.sameLine) { - if (row == start) { - count += fold.end.row - start; - } else { - count += fold.end.row - fold.start.row; - } - row = fold.end.row + 1; - } - } else { - break; - } - } - return count; - }; - - this.findRowFoldedForward = function(start, limit) { - if (limit == null) { - limit = this.getLength(); - } - for (var row = start; row != limit; row++) { - if (this.isRowFolded(row)) { - return row; - } - } - return null; - } - - this.findRowFoldedBackwards = function(start, limit) { - if (limit == null) { - limit = -1; - } - for (var row = start; row != limit; row--) { - if (this.isRowFolded(row)) { - return row; - } - } - return null; - } +// this.findRowFoldedForward = function(start, limit) { +// if (limit == null) { +// limit = this.getLength(); +// } +// for (var row = start; row != limit; row++) { +// if (this.isRowFolded(row)) { +// return row; +// } +// } +// return null; +// } +// +// this.findRowFoldedBackwards = function(start, limit) { +// if (limit == null) { +// limit = -1; +// } +// for (var row = start; row != limit; row--) { +// if (this.isRowFolded(row)) { +// return row; +// } +// } +// return null; +// } }).call(EditSession.prototype); diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index 948802a3..d9d6caf9 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -20,6 +20,7 @@ * * Contributor(s): * Fabian Jakobs + * Julian Viereck * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -49,6 +50,21 @@ var MockRenderer = require("ace/test/mockrenderer"); var Range = require("ace/range").Range; var assert = require("ace/test/assertions"); +function createFoldTestSession() { + var lines = [ + "function foo(items) {", + " for (var i=0; i"; } - + node.innerHTML = msg; log.appendChild(node);