diff --git a/demo/demo.js b/demo/demo.js index 14a661a1..5bb3e576 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -150,13 +150,13 @@ 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(2, 20, 2, 25), "bar..."); - docs.js.addFold(new Range(1, 10, 2, 10), "foo..."); + docs.js.addFold("args...", new Range(0, 13, 0, 18)); + docs.js.addFold("bar...", new Range(2, 20, 2, 25)); + docs.js.addFold("foo...", new Range(1, 10, 2, 10)); - docs.svg.addFold(new Range(1, 0, 7, 0), "fold..."); + docs.svg.addFold("fold...", new Range(1, 0, 7, 0)); - docs.plain.addFold(new Range(0, 90, 2, 30), "fold"); + docs.plain.addFold("fold", new Range(0, 90, 2, 30)); window.s = docs.plain; window.e = env.editor; setTimeout(function() { @@ -481,7 +481,7 @@ exports.launch = function(env) { range = env.editor.selection.getRange(), placeHolder = session.getTextRange(range).substring(0, 3) + "..."; - session.addFold(range, placeHolder); + session.addFold(placeHolder, range); } }); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 6df87f11..4a824832 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -1006,6 +1006,7 @@ var EditSession = function(text, mode) { var foldLine; var row = firstRow; + lastRow = Math.min(lastRow, lines.length - 1); while (row <= lastRow) { foldLine = this.getFoldLine(row); if (!foldLine) { @@ -1030,8 +1031,14 @@ var EditSession = function(text, mode) { tokens = tokens.concat(walkTokens); }.bind(this), foldLine.end.row, - lines[foldLine.end.row].length + lines[foldLine.end.row].length + 1 ); + // Remove spaces/tabs from the back of the token array. + while (tokens.length != 0 + && tokens[tokens.length - 1] >= SPACE) + { + tokens.pop(); + } } wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); @@ -1096,24 +1103,9 @@ var EditSession = function(text, mode) { } // === ELSE === - // Search for the first non space/tab token backwards. - for (split; split != lastSplit - 1; split--) { - if (tokens[split] >= SPACE) { - split++; - break; - } - } - // If we found one, then add the split. - if (split > lastSplit) { - addSplit(split); - continue; - } - - // === ELSE === - split = lastSplit + wrapLimit; - // No space or tab around? Well, force a split then. - // Check if the split position is inside of a placeholder. - // Placeholder are not splitable! + // Check if split is inside of a placeholder. Placeholder are + // not splitable. Therefore, seek the beginning of the placeholder + // and try to place the split beofre the placeholder's start. if (tokens[split] == PLACEHOLDER_START || tokens[split] == PLACEHOLDER_BODY) { @@ -1140,8 +1132,7 @@ var EditSession = function(text, mode) { // placeholder. So, let's seek for the end of the placeholder. split = lastSplit + wrapLimit; for (split; split < tokens.length; split++) { - if (tokens[split] != PLACEHOLDER_START && - tokens[split] != PLACEHOLDER_BODY) + if (tokens[split] != PLACEHOLDER_BODY) { break; } @@ -1159,8 +1150,23 @@ var EditSession = function(text, mode) { } // === ELSE === - // The split is inside of a CHAR or CHAR_EXT token. Forcing - // a split here is all right. + // Search for the first non space/tab/placeholder token backwards. + for (split; split != lastSplit - 1; split--) { + if (tokens[split] >= PLACEHOLDER_START) { + split++; + break; + } + } + // If we found one, then add the split. + if (split > lastSplit) { + addSplit(split); + continue; + } + + // === ELSE === + split = lastSplit + wrapLimit; + // The split is inside of a CHAR or CHAR_EXT token and no space + // around -> force a split. addSplit(lastSplit + wrapLimit); } return splits; diff --git a/lib/ace/edit_session/fold_line.js b/lib/ace/edit_session/fold_line.js index 8939287d..1a5cb7a3 100644 --- a/lib/ace/edit_session/fold_line.js +++ b/lib/ace/edit_session/fold_line.js @@ -123,22 +123,19 @@ function FoldLine(foldData, folds) { for (var i = 0; i < folds.length; i++) { fold = folds[i]; - comp = fold.range.compareEnd(endRow, endColumn); + comp = fold.range.compareStart(endRow, endColumn); // This fold is after the endRow/Column. if (comp == -1) { callback(null, endRow, endColumn, lastEnd, isNewRow); return; } - // The endRow/Column is inside of the current fold. - else if (comp == 0) { - callback(null, endRow, fold.start.column, lastEnd, isNewRow); - return; - } stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow); - stop = stop || callback(fold.placeholder, null, null, lastEnd); + stop = !stop && callback(fold.placeholder, null, null, lastEnd); - if (stop) { + // If the user requested to stop the walk or endRow/endColumn is + // inside of this fold (comp == 0), then end here. + if (stop || comp == 0) { return; } diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 033f6a85..ec2d16f1 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -38,6 +38,7 @@ define(function(require, exports, module) { +var Range = require("ace/range").Range; var FoldLine = require("ace/edit_session/fold_line").FoldLine; /** @@ -212,11 +213,19 @@ function Folding() { /** * Adds a new fold. */ - this.addFold = function(range, placeholder) { - var startRow = range.start.row, - endRow = range.end.row, - foldData = this.$foldData, - foldRow = null; + this.addFold = function(placeholder, startRow, startColumn, endRow, endColumn) { + var range; + // Normalize parameters. + if (!(startRow instanceof Range)) { + range = new Range(startRow, startColumn, endRow, endColumn); + } else { + range = startRow; + startRow = range.start.row; + endRow = range.end.row; + } + + var foldData = this.$foldData; + var foldRow = null; var foldLine; var fold = new Fold(range, placeholder); diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index 2cefd40c..6f65c54e 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -61,9 +61,9 @@ function createFoldTestSession() { ]; var session = new EditSession(lines.join("\n")); session.setUndoManager(new UndoManager()); - session.addFold(new Range(0, 13, 0, 18), "args..."); - session.addFold(new Range(1, 10, 2, 10), "foo..."); - session.addFold(new Range(2, 20, 2, 25), "bar..."); + session.addFold("args...", new Range(0, 13, 0, 18)); + session.addFold("foo...", new Range(1, 10, 2, 10)); + session.addFold("bar...", new Range(2, 20, 2, 25)); return session; } @@ -697,7 +697,7 @@ module.exports = { function assertArray(a, b) { assert.ok(a.length == b.length); for (var i = 0; i < a.length; i++) { - assert.ok(a[i] == b[i]); + assert.equal(a[i], b[i]); } } @@ -707,6 +707,11 @@ module.exports = { line2 && assertArray(wrapData[2], line2); } + function removeFoldAssertWrap(docRow, docColumn, line0, line1, line2) { + session.removeFold(session.getFoldAt(docRow, docColumn)); + assertWrap(line0, line1, line2); + } + var lines = [ "foo bar foo bar", "foo bar foo bar", @@ -714,19 +719,78 @@ module.exports = { ]; var session = new EditSession(lines.join("\n")); - var wrapData = session.$wrapData; session.setUseWrapMode(true); session.$wrapLimit = 7; session.$updateWrapData(0, 2); + var wrapData = session.$wrapData; - assertWrap([7], [7], [7]); - session.addFold(new Range(0, 13, 0, 18), "args..."); + // Do a simple assertion without folds to check basic functionallity. + assertWrap([8], [8], [8]); + // --- Do in line folding --- + + // Adding a fold. The split position is inside of the fold. As placeholder + // are not splitable, the split should be before the split. + session.addFold("woot", 0, 4, 0, 16); + assertWrap([4], [8], [8]); + + // Remove the fold again which should reset the wrapData. + removeFoldAssertWrap(0, 4, [8], [8], [8]); + + session.addFold("woot", 0, 6, 0, 9); + assertWrap([6, 13], [8], [8]); + removeFoldAssertWrap(0, 6, [8], [8], [8]); + + // The fold fits into the wrap limit - no split expected. + session.addFold("woot", 0, 3, 0, 16); + assertWrap([], [8], [8]); + removeFoldAssertWrap(0, 4, [8], [8], [8]); + + // Fold after split position should be all fine. + session.addFold("woot", 0, 8, 0, 16); + assertWrap([8], [8], [8]); + removeFoldAssertWrap(0, 8, [8], [8], [8]); + + // Fold's placeholder is far too long for wrapSplit. + session.addFold("woot0123456789", 0, 8, 0, 16); + assertWrap([8], [8], [8]); + removeFoldAssertWrap(0, 8, [8], [8], [8]); + + // Fold's placeholder is far too long for wrapSplit + // + content at the end of the line + session.addFold("woot0123456789", 0, 7, 0, 8); + assertWrap([7, 21], [8], [8]); + removeFoldAssertWrap(0, 8, [8], [8], [8]); + + session.addFold("woot0123456789", 0, 7, 0, 8); + session.addFold("woot0123456789", 0, 8, 0, 9); + assertWrap([7, 21, 35], [8], [8]); + session.removeFold(session.getFoldAt(0, 7)); + removeFoldAssertWrap(0, 8, [8], [8], [8]); + + session.addFold("woot0123456789", 0, 7, 0, 8); + session.addFold("woot0123456789", 0, 14, 0, 16); + assertWrap([7, 21, 27], [8], [8]); + session.removeFold(session.getFoldAt(0, 7)); + removeFoldAssertWrap(0, 14, [8], [8], [8]); + + // --- Do some multiline folding --- + + // Add a fold over two lines. Note, that the wrapData[1] stays the + // same. This is an implementation detail and expected behavior. + session.addFold("woot", 0, 8, 1, 16); + assertWrap([8], [8 /* See comments */], [8]); + removeFoldAssertWrap(0, 8, [8], [8], [8]); + + session.addFold("woot", 0, 9, 1, 11); + assertWrap([8, 14], [8 /* See comments */], [8]); + removeFoldAssertWrap(0, 9, [8], [8], [8]); + + session.addFold("woot", 0, 9, 1, 16); + assertWrap([8], [8 /* See comments */], [8]); + removeFoldAssertWrap(0, 9, [8], [8], [8]); - session.addFold(new Range(1, 10, 2, 10), "foo..."); - session.addFold(new Range(2, 20, 2, 25), "bar..."); return session; - } }; diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index e157032b..5cb8e78b 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -66,7 +66,7 @@ var UndoManager = function() { this.$undoStack.push(deltas); } }; - + this.reset = function() { this.$undoStack = []; this.$redoStack = [];