From 7b58e782a7fc3b18bb2e87902fbc6910a76417dd Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Sat, 23 Apr 2011 21:51:39 +0200 Subject: [PATCH] Rewrote the handling of . Now there is one FoldLine per folded line/row as it's displayed on the screen. --- lib/ace/edit_session.js | 304 +++++++++++++++++++---------------- lib/ace/edit_session_test.js | 3 + lib/ace/layer/gutter.js | 2 +- lib/ace/layer/text.js | 29 ++-- 4 files changed, 180 insertions(+), 158 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index a82b050d..db03e9a7 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -896,9 +896,6 @@ var EditSession = function(text, mode) { if (useWrapMode && this.$wrapData.length != this.doc.$lines.length) { console.error("The length of doc.$lines and $wrapData have to be the same!"); } - if (this.$foldData.length != this.doc.$lines.length) { - console.error("The length of doc.$lines and $foldData have to be the same!"); - } // TODO: // this.$updateFoldData(firstRow, lastRow); @@ -1242,65 +1239,45 @@ var EditSession = function(text, mode) { return this.documentToScreenPosition(docRow, docColumn).row; } - this.$buildFoldedTextLine = function(row, endRow, endColumn) { + this.$buildFoldedTextLine = function(foldLine, endRow, endColumn) { var textLine = ""; // Build a one line string for the current fold sequence that starts // at foldStartRow. + var lastEnd = 0, + line = this.getLine(foldLine.start.row), + comp, + folds = foldLine.folds, + fold; - while_loop: - while (true) { - var folds = this.getFoldData(row); - var line = this.getLine(row); + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; - var lastEnd = 0; - var startIdx = 0; - if (folds[0].start.row != row) { - startIdx = 1; - lastEnd = folds[0].end.column; + comp = fold.compare(endRow, endColumn); + // This fold is after the endRow/Column. + if (comp == -1) { + textLine += line.substring(lastEnd, endColumn); + return textLine; } - fold = null; - for (var i = startIdx; i < folds.length; i++) { - fold = folds[i]; - // STOP - if (row == endRow) { - if (endColumn <= fold.start.column) { - textLine += line.substring(lastEnd, endColumn); - break while_loop; - } else if (!fold.sameLine || endColumn < fold.end.column) { - textLine += line.substring(lastEnd, fold.start.column); - break while_loop; - } - } + // The endRow/Column is inside of the current fold. + else if (comp == 0) { textLine += line.substring(lastEnd, fold.start.column); + return textLine; + } - // 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; - } else { - row = fold.end.row; - // STOP - if (row == endRow && endColumn <= fold.end.column) { - break while_loop; - } - } - } - if (row > endRow) { - break; - } - if (row == endRow && (!fold || fold.sameLine)) { - if (fold) { - textLine += line.substring(lastEnd, endColumn); - } - break; + textLine += line.substring(lastEnd, fold.start.column); + textLine += fold.placeholder; + + if (fold.sameLine) { + lastEnd = fold.end.column; + } else { + row = fold.end.row; + + line = this.getLine(row); + lastEnd = fold.end.column; } } + textLine += line.substring(lastEnd, endColumn); return textLine; } @@ -1308,40 +1285,53 @@ var EditSession = function(text, mode) { var screenRow = 0, screenColumn = 0, foldStartRow = null, - fold = null; + fold = null, + folds, + comp, + foldLine = null; // Clamp the docRow position in case it's inside of a folded block. - if (!this.isRowVisible(docRow)) { - // As the line is not visible, getFoldData will return only a - // single fold and also not an array. - var lfold = this.getFoldData(docRow); - docRow = lfold.start.row; - docColumn = lfold.start.column; + foldLine = this.getFoldLine(docRow); + if (foldLine) { + folds = foldLine.folds; + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; + comp = fold.compare(docRow, docColumn); + if (comp == 0) { + docRow = fold.start.row; + docColumn = fold.start.column; + break; + } else if (comp == -1) { + break; + } + } + foldLine = null; } for (var row = 0; row < docRow; row++) { - var fold = this.getRowLastFold(row); - if (fold && !fold.sameLine) { - if (foldStartRow == null) { - foldStartRow = row; + foldLine = this.getFoldLine(row); + if (foldLine) { + if (foldLine.end.row >= docRow) { + break; } - row = fold.end.row - 1; + row = foldLine.end.row; + screenRow += foldLine.getRowLength(); } else { screenRow += this.getRowLength(row); - foldStartRow = null; } } // Calculate the text line that is displayed in docRow on the screen. var textLine = ""; + foldLine = this.getFoldLine(docRow); // Check if the final row we want to reach is inside of a fold. - if (!this.isRowFolded(docRow)) { + if (!foldLine) { textLine = this.getLine(docRow).substring(0, docColumn); foldStartRow = docRow; } else { textLine = this.$buildFoldedTextLine( - (foldStartRow != null ? foldStartRow : docRow), + foldLine, docRow, docColumn); } @@ -1453,67 +1443,87 @@ 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 = []; + Fold.prototype.contains = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return false; + } + return this.range.contains(row, column); + } + + Fold.prototype.compare = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } + return this.range.compare(row, column); + } + + function FoldLine(fold) { + this.folds = [fold]; + this.range = fold.range.clone(); + this.start = this.range.start; + this.end = this.range.end; } (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"; + if (fold.start.row < this.startRow || fold.endRow > this.endRow) { + throw "Can't add a fold to this FoldLine as it has no connection"; } - this.linkOut = fold; - } else if (fold.end.row == this.row) { - if (this.linkIn) { - throw "There is already a linkIn fold"; - } - this.linkIn = fold; + this.folds.push(fold); + this.folds.sort(function(a, b) { + if (a.sameLine && b.sameLine) { + if (a.start.row == b.start.row) { + return a.start.colun - b.start.column; + } else { + return a.start.row - b.start.row; + } + } else if (!a.sameLine && !b.sameLine) { + return a.end.row - b.start.row; + } else if (!a.sameLine) { + return a.end.row - b.start.row; + } else if (!b.sameLine) { + return b.end.row - a.start.row; + } + }); + } else if (fold.start.row == this.endRow) { + this.folds.push(fold); + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (fold.end.row == this.startRow) { + this.folds.unshift(fold); + this.start.row = fold.start.row; + this.start.column = fold.start.column; } else { throw "Trying to add fold to FoldRow that doesn't have a matching row"; } } - }).call(FoldRow.prototype); - this.getFoldRow = function(docRow, startFoldRow) { + this.getRowLength = function() { + // TODO: Add support for wrapped lines here. + return 1; + } + + this.getSplitData = function() { + // TODO: Add support for wrapped lines here. + return undefined; + } + }).call(FoldLine.prototype); + + this.getFoldLine = function(docRow, startFoldLine) { 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) { + var i = Math.max(foldData.indexOf(startFoldLine), 0); + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.start.row >= docRow || foldLine.end.row >= docRow) { + return foldLine; + } else if (foldLine.end.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. */ @@ -1523,16 +1533,46 @@ var EditSession = function(text, mode) { foldData = this.$foldData, foldRow = null; - foldRow = this.getFoldRowOrCreate(startRow); - var fold = new Fold(range, placeholder); - foldRow.addFold(fold); + var added = false; - // If this fold folds more then one line, then add this fold as the - // linkOut of the FoldRow at endRow. - if (!fold.sameLine) { - foldRow = this.getFoldRowOrCreate(endRow, foldRow); - foldRow.addFold(fold); + // For now we assume that no two folds are created for the same range! + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (endRow == foldLine.start.row) { + foldLine.addFold(fold); + added = true; + break; + } else if (startRow == foldLine.end.row) { + foldLine.addFold(fold); + added = true; + if (!fold.sameLine) { + // Check if we might have to merge two FoldLines. + foldLineNext = foldData[i + 1]; + if (foldLineNext && foldLineNext.start.row == endRow) { + // We need to merge! + var nextFolds = foldLineNext.folds; + for (var i = 0; i < nextFolds.length; i++) { + foldLine.addFold(nextFolds[i]); + } + // Remove the foldLineNext - no longer needed, as + // it's merged now with foldLine. + foldData.splice(foldData.indexOf(foldLineNext), 1); + break; + } + } + break; + } else if (endRow <= foldLine.start.row) { + break; + } + } + + if (!added) { + foldLine = new FoldLine(fold); + foldData.push(foldLine); + foldData.sort(function(a, b) { + return a.start.row - b.start.row; + }); } // TODO: Recalculate wrapData @@ -1548,36 +1588,16 @@ var EditSession = function(text, mode) { * folded parts such that some parts of the line is still visible. **/ this.isRowFolded = function(docRow, startFoldRow) { - return !!this.getFoldRow(docRow, startFoldRow); + return !!this.getFoldLine(docRow, startFoldRow); }; this.getRowFoldEnd = function(docRow, startFoldRow) { - var foldRow = this.getFoldRow(docRow, startFoldRow); - return (foldRow - ? (foldRow.linkOut ? foldRow.linkOut.end.row : docRow) - : docRow); + var foldLine = this.getFoldLine(docRow, startFoldRow); + return (foldLine + ? foldLine.end.row + : docRow) }; - 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!"; - } - - var foldData = this.$foldData; - for (var i = foldData.indexOf(foldRow); foldRow; i++) { - if (foldRow.linkIn) { - folds.push(foldRow.linkIn); - } - - foldRow = foldRow.linkOut; - } - } - // this.findRowFoldedForward = function(start, limit) { // if (limit == null) { // limit = this.getLength(); diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index d9d6caf9..42cb4536 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -407,6 +407,9 @@ module.exports = { assertDoc2Screen(2, 25, 1, 32); assertDoc2Screen(2, 26, 1, 33); assertDoc2Screen(2, 99, 1, 40); + + // Test one position after the folds. Should be all like normal. + assertDoc2Screen(3, 0, 2, 0); } }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index e5fb1c9c..84a36ca2 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -21,7 +21,7 @@ * * Contributor(s): * Fabian Jakobs - * Julian Viereck + * 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 diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 3a5abb8c..eac5dc0e 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -21,7 +21,7 @@ * * Contributor(s): * Fabian Jakobs - * Julian Viereck + * Julian Viereck * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of @@ -401,9 +401,9 @@ var Text = function(parentEl) { // Nothing to do if the entire line is folded. // TODO: Remove this, once the folding feature is done. Only for // developing stuff at the moment. - if (!this.session.isRowVisible(row)) { - throw "Calling renderLine on folded line doesn't make sense?"; - } +// if (!this.session.isRowVisible(row)) { +// throw "Calling renderLine on folded line doesn't make sense?"; +// } // Check if the line to render is folded or not. If not, things are // simple, otherwise, we need to fake some things... @@ -417,7 +417,8 @@ var Text = function(parentEl) { this.$renderFoldLine = function(stringBuilder, row, tokens) { var session = this.session, - folds = session.getFoldData(row); + foldLine = session.getFoldLine(row), + folds = foldLine.folds; var renderTokens = []; @@ -461,9 +462,11 @@ var Text = function(parentEl) { } } - var lastCol = 0; + var lastCol = 0, + fold = null; + for (var i = 0; i < folds.length; i++) { - var fold = folds[i]; + fold = folds[i]; addTokens(tokens, lastCol, fold.start.column); renderTokens.push({ type: "fold", @@ -471,21 +474,17 @@ var Text = function(parentEl) { }); if (fold.start.row != fold.end.row) { - row = fold.end.row; - tokens = this.tokenizer.getTokens(row, row)[0].tokens; - folds = session.getFoldData(row); - // Skip the first fold as it's only referencing to the current - // fold that is already rendered. - i = 0; + tokens = this.tokenizer.getTokens( + fold.end.row, fold.end.row)[0].tokens; } lastCol = fold.end.column; } // Add the rest of the line - addTokens(tokens, lastCol, session.getLine(row).length); + addTokens(tokens, lastCol, session.getLine(foldLine.end.row).length); // TODO: Build a fake splits array! - var splits = false; + var splits = foldLine.getSplitData(); this.$renderLineCore(stringBuilder, row, renderTokens, splits); };