From e871700e2eb9e7d831807c059feb6061f543ef95 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Fri, 29 Apr 2011 11:08:19 +0200 Subject: [PATCH] Fix bug in undo/redo + folds: The folds have to get onto the undo queue after the changeStack is empty. New events changeStart/End added. --- lib/ace/document.js | 20 +++++++++ lib/ace/edit_session.js | 75 +++++++++++++++++++++++++-------- lib/ace/edit_session/folding.js | 6 +++ 3 files changed, 83 insertions(+), 18 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 09cf0488..2a9b7778 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -183,12 +183,14 @@ var Document = function(text) { var firstLine = lines.splice(0, 1)[0]; var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0]; + this._dispatchEvent("changeStart"); position = this.insertInLine(position, firstLine); if (lastLine !== null) { position = this.insertNewLine(position); // terminate first line position = this.insertLines(position.row, lines); position = this.insertInLine(position, lastLine || ""); } + this._dispatchEvent("changeEnd"); return position; }; @@ -200,6 +202,7 @@ var Document = function(text) { args.push.apply(args, lines); this.$lines.splice.apply(this.$lines, args); + this._dispatchEvent("changeStart"); var range = new Range(row, 0, row + lines.length, 0); var delta = { action: "insertLines", @@ -207,12 +210,15 @@ var Document = function(text) { lines: lines }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); return range.end; }, this.insertNewLine = function(position) { position = this.$clipPosition(position); var line = this.$lines[position.row] || ""; + + this._dispatchEvent("changeStart"); this.$lines[position.row] = line.substring(0, position.column); this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length)); @@ -227,6 +233,7 @@ var Document = function(text) { text: this.getNewLineCharacter() }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); return end; }; @@ -236,6 +243,8 @@ var Document = function(text) { return position; var line = this.$lines[position.row] || ""; + + this._dispatchEvent("changeStart"); this.$lines[position.row] = line.substring(0, position.column) + text + line.substring(position.column); @@ -250,6 +259,7 @@ var Document = function(text) { text: text }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); return end; }; @@ -265,6 +275,7 @@ var Document = function(text) { var firstRow = range.start.row; var lastRow = range.end.row; + this._dispatchEvent("changeStart"); if (range.isMultiLine()) { var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1; var lastFullRow = lastRow - 1; @@ -283,6 +294,7 @@ var Document = function(text) { else { this.removeInLine(firstRow, range.start.column, range.end.column); } + this._dispatchEvent("changeEnd"); return range.start; }; @@ -294,6 +306,7 @@ var Document = function(text) { var line = this.getLine(row); var removed = line.substring(startColumn, endColumn); var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length); + this._dispatchEvent("changeStart"); this.$lines.splice(row, 1, newLine); var delta = { @@ -302,6 +315,7 @@ var Document = function(text) { text: removed }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); return range.start; }; @@ -313,6 +327,7 @@ var Document = function(text) { * @return {String[]} The removed lines */ this.removeLines = function(firstRow, lastRow) { + this._dispatchEvent("changeStart"); var range = new Range(firstRow, 0, lastRow + 1, 0); var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1); @@ -323,6 +338,7 @@ var Document = function(text) { lines: removed }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); return removed; }; @@ -333,6 +349,7 @@ var Document = function(text) { var range = new Range(row, firstLine.length, row+1, 0); var line = firstLine + secondLine; + this._dispatchEvent("changeStart"); this.$lines.splice(row, 2, line); var delta = { @@ -341,6 +358,7 @@ var Document = function(text) { text: this.getNewLineCharacter() }; this._dispatchEvent("change", { data: delta }); + this._dispatchEvent("changeEnd"); }; this.replace = function(range, text) { @@ -352,6 +370,7 @@ var Document = function(text) { if (text == this.getTextRange(range)) return range.end; + this._dispatchEvent("changeStart"); this.remove(range); if (text) { var end = this.insert(range.start, text); @@ -359,6 +378,7 @@ var Document = function(text) { else { end = range.start; } + this._dispatchEvent("changeEnd"); return end; }; diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index caa4e685..570e374b 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -64,6 +64,7 @@ var EditSession = function(text, mode) { }); return str; } + this.$docChangeCounter = 0; if (text instanceof Document) { this.setDocument(text); @@ -89,26 +90,56 @@ var EditSession = function(text, mode) { this.doc = doc; doc.on("change", this.onChange.bind(this)); + doc.on("changeStart", this.onChangeStart.bind(this)); + doc.on("changeEnd", this.onChangeEnd.bind(this)); }; this.getDocument = function() { return this.doc; }; + this.onChangeStart = function() { + console.log(">>> onChangeStart", this.$docChangeCounter); + this.$docChangeCounter ++; + }; + + this.onChangeEnd = function() { + this.$docChangeCounter --; + console.log("<<< onChangeEnd", this.$docChangeCounter); + if (this.$docChangeCounter == 0 + && this.$deltaFolds && this.$deltaFolds.length) + { + this.$deltas = this.$deltaFolds.concat(this.$deltas); + this.$deltaFolds = []; + this.$informUndoManager.schedule(); + } + }; + this.onChange = function(e) { var delta = e.data; this.$modified = true; var removedFolds = this.$updateInternalDataOnChange(e); if (!this.$fromUndo && this.$undoManager && !delta.ignore) { + console.log("onChange", JSON.stringify(delta)); this.$deltas.push(delta); if (removedFolds && removedFolds.length != 0) { - this.$deltas.push({ + this.$deltaFolds.push({ action: "removeFolds", folds: removedFolds }); + console.log("onChangeFold", removedFolds[0].toString()); + } + // Only inform the undoManager about new deltas if there + // are no deltaFolds. As there are some deltasFolds, the + // undoManager get notified by the onChangeEnd once the + // change stack is empty. Calling this.$informUndoManager here + // might cause the current deltas get flushed to the undoManager + // before the change stack is empty and as such the deltaFolds might + // get added at the wrong position. + else if (!this.$deltaFolds.length) { + this.$informUndoManager.schedule(); } - this.$informUndoManager.schedule(); } @@ -119,6 +150,7 @@ var EditSession = function(text, mode) { this.setValue = function(text) { this.doc.setValue(text); this.$deltas = []; + this.$deltaFolds = []; this.getUndoManager().reset(); }; @@ -142,6 +174,7 @@ var EditSession = function(text, mode) { this.setUndoManager = function(undoManager) { this.$undoManager = undoManager; this.$deltas = []; + this.$deltaFolds = []; if (this.$informUndoManager) { this.$informUndoManager.cancel(); @@ -635,28 +668,35 @@ var EditSession = function(text, mode) { }); } - function filterFoldDeltas(deltas) { - return deltas.filter(function(delta) { - return delta.action == "removeFolds"; - }); - } - this.undoChanges = function(deltas) { if (!deltas.length) return; - var docDeltas = filterDocActions(deltas); - var foldDeltas = filterFoldDeltas(deltas); this.$fromUndo = true; - this.doc.revertDeltas(docDeltas); - foldDeltas.forEach(function(foldDelta) { - foldDelta.folds.forEach(function(fold) { - this.addFold(fold); - }, this); - }, this); + var docDeltas = filterDocActions(deltas); + if (docDeltas.length == deltas.length) { + this.doc.revertDeltas(deltas); + } else { + var groupDocDeltas = []; + for (var i = deltas.length - 1; i != -1; i--) { + delta = deltas[i]; + if (docActions.indexOf(delta.action) != -1) { + groupDocDeltas.push(delta); + } else { + groupDocDeltas.reverse() + this.doc.revertDeltas(groupDocDeltas); + this.$setUndoSelection(groupDocDeltas, true); + groupDocDeltas = []; + this.addFolds(delta.folds); + } + } + this.doc.revertDeltas(groupDocDeltas); + } this.$fromUndo = false; - this.$setUndoSelection(docDeltas, true); + if (docDeltas.length == deltas.length) { + this.$setUndoSelection(docDeltas, true); + } }, this.redoChanges = function(deltas) { @@ -664,7 +704,6 @@ var EditSession = function(text, mode) { return; var docDeltas = filterDocActions(deltas); - var foldDeltas = filterFoldDeltas(deltas); this.$fromUndo = true; this.doc.applyDeltas(docDeltas); this.$fromUndo = false; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index bbf0717b..8a2ce937 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -334,6 +334,12 @@ function Folding() { return fold; }; + this.addFolds = function(folds) { + folds.forEach(function(fold) { + this.addFold(fold); + }, this); + }; + this.removeFold = function(fold) { var foldLine = fold.foldLine; var startRow = foldLine.start.row;