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.

This commit is contained in:
Julian Viereck 2011-04-29 11:08:19 +02:00
commit e871700e2e
3 changed files with 83 additions and 18 deletions

View file

@ -183,12 +183,14 @@ var Document = function(text) {
var firstLine = lines.splice(0, 1)[0]; var firstLine = lines.splice(0, 1)[0];
var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0]; var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
this._dispatchEvent("changeStart");
position = this.insertInLine(position, firstLine); position = this.insertInLine(position, firstLine);
if (lastLine !== null) { if (lastLine !== null) {
position = this.insertNewLine(position); // terminate first line position = this.insertNewLine(position); // terminate first line
position = this.insertLines(position.row, lines); position = this.insertLines(position.row, lines);
position = this.insertInLine(position, lastLine || ""); position = this.insertInLine(position, lastLine || "");
} }
this._dispatchEvent("changeEnd");
return position; return position;
}; };
@ -200,6 +202,7 @@ var Document = function(text) {
args.push.apply(args, lines); args.push.apply(args, lines);
this.$lines.splice.apply(this.$lines, args); this.$lines.splice.apply(this.$lines, args);
this._dispatchEvent("changeStart");
var range = new Range(row, 0, row + lines.length, 0); var range = new Range(row, 0, row + lines.length, 0);
var delta = { var delta = {
action: "insertLines", action: "insertLines",
@ -207,12 +210,15 @@ var Document = function(text) {
lines: lines lines: lines
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
return range.end; return range.end;
}, },
this.insertNewLine = function(position) { this.insertNewLine = function(position) {
position = this.$clipPosition(position); position = this.$clipPosition(position);
var line = this.$lines[position.row] || ""; var line = this.$lines[position.row] || "";
this._dispatchEvent("changeStart");
this.$lines[position.row] = line.substring(0, position.column); this.$lines[position.row] = line.substring(0, position.column);
this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length)); this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));
@ -227,6 +233,7 @@ var Document = function(text) {
text: this.getNewLineCharacter() text: this.getNewLineCharacter()
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
return end; return end;
}; };
@ -236,6 +243,8 @@ var Document = function(text) {
return position; return position;
var line = this.$lines[position.row] || ""; var line = this.$lines[position.row] || "";
this._dispatchEvent("changeStart");
this.$lines[position.row] = line.substring(0, position.column) + text this.$lines[position.row] = line.substring(0, position.column) + text
+ line.substring(position.column); + line.substring(position.column);
@ -250,6 +259,7 @@ var Document = function(text) {
text: text text: text
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
return end; return end;
}; };
@ -265,6 +275,7 @@ var Document = function(text) {
var firstRow = range.start.row; var firstRow = range.start.row;
var lastRow = range.end.row; var lastRow = range.end.row;
this._dispatchEvent("changeStart");
if (range.isMultiLine()) { if (range.isMultiLine()) {
var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1; var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
var lastFullRow = lastRow - 1; var lastFullRow = lastRow - 1;
@ -283,6 +294,7 @@ var Document = function(text) {
else { else {
this.removeInLine(firstRow, range.start.column, range.end.column); this.removeInLine(firstRow, range.start.column, range.end.column);
} }
this._dispatchEvent("changeEnd");
return range.start; return range.start;
}; };
@ -294,6 +306,7 @@ var Document = function(text) {
var line = this.getLine(row); var line = this.getLine(row);
var removed = line.substring(startColumn, endColumn); var removed = line.substring(startColumn, endColumn);
var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length); var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
this._dispatchEvent("changeStart");
this.$lines.splice(row, 1, newLine); this.$lines.splice(row, 1, newLine);
var delta = { var delta = {
@ -302,6 +315,7 @@ var Document = function(text) {
text: removed text: removed
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
return range.start; return range.start;
}; };
@ -313,6 +327,7 @@ var Document = function(text) {
* @return {String[]} The removed lines * @return {String[]} The removed lines
*/ */
this.removeLines = function(firstRow, lastRow) { this.removeLines = function(firstRow, lastRow) {
this._dispatchEvent("changeStart");
var range = new Range(firstRow, 0, lastRow + 1, 0); var range = new Range(firstRow, 0, lastRow + 1, 0);
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1); var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
@ -323,6 +338,7 @@ var Document = function(text) {
lines: removed lines: removed
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
return removed; return removed;
}; };
@ -333,6 +349,7 @@ var Document = function(text) {
var range = new Range(row, firstLine.length, row+1, 0); var range = new Range(row, firstLine.length, row+1, 0);
var line = firstLine + secondLine; var line = firstLine + secondLine;
this._dispatchEvent("changeStart");
this.$lines.splice(row, 2, line); this.$lines.splice(row, 2, line);
var delta = { var delta = {
@ -341,6 +358,7 @@ var Document = function(text) {
text: this.getNewLineCharacter() text: this.getNewLineCharacter()
}; };
this._dispatchEvent("change", { data: delta }); this._dispatchEvent("change", { data: delta });
this._dispatchEvent("changeEnd");
}; };
this.replace = function(range, text) { this.replace = function(range, text) {
@ -352,6 +370,7 @@ var Document = function(text) {
if (text == this.getTextRange(range)) if (text == this.getTextRange(range))
return range.end; return range.end;
this._dispatchEvent("changeStart");
this.remove(range); this.remove(range);
if (text) { if (text) {
var end = this.insert(range.start, text); var end = this.insert(range.start, text);
@ -359,6 +378,7 @@ var Document = function(text) {
else { else {
end = range.start; end = range.start;
} }
this._dispatchEvent("changeEnd");
return end; return end;
}; };

View file

@ -64,6 +64,7 @@ var EditSession = function(text, mode) {
}); });
return str; return str;
} }
this.$docChangeCounter = 0;
if (text instanceof Document) { if (text instanceof Document) {
this.setDocument(text); this.setDocument(text);
@ -89,26 +90,56 @@ var EditSession = function(text, mode) {
this.doc = doc; this.doc = doc;
doc.on("change", this.onChange.bind(this)); doc.on("change", this.onChange.bind(this));
doc.on("changeStart", this.onChangeStart.bind(this));
doc.on("changeEnd", this.onChangeEnd.bind(this));
}; };
this.getDocument = function() { this.getDocument = function() {
return this.doc; 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) { this.onChange = function(e) {
var delta = e.data; var delta = e.data;
this.$modified = true; this.$modified = true;
var removedFolds = this.$updateInternalDataOnChange(e); var removedFolds = this.$updateInternalDataOnChange(e);
if (!this.$fromUndo && this.$undoManager && !delta.ignore) { if (!this.$fromUndo && this.$undoManager && !delta.ignore) {
console.log("onChange", JSON.stringify(delta));
this.$deltas.push(delta); this.$deltas.push(delta);
if (removedFolds && removedFolds.length != 0) { if (removedFolds && removedFolds.length != 0) {
this.$deltas.push({ this.$deltaFolds.push({
action: "removeFolds", action: "removeFolds",
folds: removedFolds 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.setValue = function(text) {
this.doc.setValue(text); this.doc.setValue(text);
this.$deltas = []; this.$deltas = [];
this.$deltaFolds = [];
this.getUndoManager().reset(); this.getUndoManager().reset();
}; };
@ -142,6 +174,7 @@ var EditSession = function(text, mode) {
this.setUndoManager = function(undoManager) { this.setUndoManager = function(undoManager) {
this.$undoManager = undoManager; this.$undoManager = undoManager;
this.$deltas = []; this.$deltas = [];
this.$deltaFolds = [];
if (this.$informUndoManager) { if (this.$informUndoManager) {
this.$informUndoManager.cancel(); 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) { this.undoChanges = function(deltas) {
if (!deltas.length) if (!deltas.length)
return; return;
var docDeltas = filterDocActions(deltas);
var foldDeltas = filterFoldDeltas(deltas);
this.$fromUndo = true; this.$fromUndo = true;
this.doc.revertDeltas(docDeltas); var docDeltas = filterDocActions(deltas);
foldDeltas.forEach(function(foldDelta) { if (docDeltas.length == deltas.length) {
foldDelta.folds.forEach(function(fold) { this.doc.revertDeltas(deltas);
this.addFold(fold); } else {
}, this); var groupDocDeltas = [];
}, this); 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.$fromUndo = false;
this.$setUndoSelection(docDeltas, true); if (docDeltas.length == deltas.length) {
this.$setUndoSelection(docDeltas, true);
}
}, },
this.redoChanges = function(deltas) { this.redoChanges = function(deltas) {
@ -664,7 +704,6 @@ var EditSession = function(text, mode) {
return; return;
var docDeltas = filterDocActions(deltas); var docDeltas = filterDocActions(deltas);
var foldDeltas = filterFoldDeltas(deltas);
this.$fromUndo = true; this.$fromUndo = true;
this.doc.applyDeltas(docDeltas); this.doc.applyDeltas(docDeltas);
this.$fromUndo = false; this.$fromUndo = false;

View file

@ -334,6 +334,12 @@ function Folding() {
return fold; return fold;
}; };
this.addFolds = function(folds) {
folds.forEach(function(fold) {
this.addFold(fold);
}, this);
};
this.removeFold = function(fold) { this.removeFold = function(fold) {
var foldLine = fold.foldLine; var foldLine = fold.foldLine;
var startRow = foldLine.start.row; var startRow = foldLine.start.row;