Fix bugs in undo/redo seleciton handling

This commit is contained in:
Julian Viereck 2011-04-29 12:16:55 +02:00
commit 007125025f

View file

@ -107,10 +107,22 @@ var EditSession = function(text, mode) {
this.$docChangeCounter --; this.$docChangeCounter --;
console.log("<<< onChangeEnd", this.$docChangeCounter); console.log("<<< onChangeEnd", this.$docChangeCounter);
if (this.$docChangeCounter == 0 if (this.$docChangeCounter == 0
&& this.$deltaFolds && this.$deltaFolds.length) && !this.$fromUndo && this.$undoManager)
{ {
this.$deltas = this.$deltaFolds.concat(this.$deltas); if (this.$deltasFold.length) {
this.$deltaFolds = []; this.$deltas.push({
group: "fold",
deltas: this.$deltasFold
});
this.$deltasFold = [];
}
if (this.$deltasDoc) {
this.$deltas.push({
group: "doc",
deltas: this.$deltasDoc
});
this.$deltasDoc = [];
}
this.$informUndoManager.schedule(); this.$informUndoManager.schedule();
} }
}; };
@ -122,24 +134,14 @@ var EditSession = function(text, mode) {
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)); console.log("onChange", JSON.stringify(delta));
this.$deltas.push(delta); this.$deltasDoc.push(delta);
if (removedFolds && removedFolds.length != 0) { if (removedFolds && removedFolds.length != 0) {
this.$deltaFolds.push({ this.$deltasFold.push({
action: "removeFolds", action: "removeFolds",
folds: removedFolds folds: removedFolds
}); });
console.log("onChangeFold", removedFolds[0].toString()); 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();
}
} }
@ -150,7 +152,8 @@ 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.$deltasDoc = [];
this.$deltasFold = [];
this.getUndoManager().reset(); this.getUndoManager().reset();
}; };
@ -174,7 +177,8 @@ 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 = []; this.$deltasDoc = [];
this.$deltasFold = [];
if (this.$informUndoManager) { if (this.$informUndoManager) {
this.$informUndoManager.cancel(); this.$informUndoManager.cancel();
@ -655,106 +659,98 @@ var EditSession = function(text, mode) {
return this.doc.remove(range); return this.doc.remove(range);
}; };
var docActions = [
"insertLines",
"insertText",
"removeLines",
"removeText"
];
function filterDocActions(deltas) {
return deltas.filter(function(delta) {
return docActions.indexOf(delta.action) != -1;
});
}
this.undoChanges = function(deltas) { this.undoChanges = function(deltas) {
if (!deltas.length) if (!deltas.length)
return; return;
this.$fromUndo = true; this.$fromUndo = true;
var docDeltas = filterDocActions(deltas); var lastUndoRange = null;
if (docDeltas.length == deltas.length) {
this.doc.revertDeltas(deltas);
} else {
var groupDocDeltas = [];
for (var i = deltas.length - 1; i != -1; i--) { for (var i = deltas.length - 1; i != -1; i--) {
delta = deltas[i]; delta = deltas[i];
if (docActions.indexOf(delta.action) != -1) { if (delta.group == "doc") {
groupDocDeltas.push(delta); this.doc.revertDeltas(delta.deltas);
lastUndoRange =
this.$setUndoSelection(delta.deltas, true, lastUndoRange);
} else { } else {
groupDocDeltas.reverse() delta.deltas.forEach(function(foldDelta) {
this.doc.revertDeltas(groupDocDeltas); this.addFolds(foldDelta.folds);
this.$setUndoSelection(groupDocDeltas, true); }, this);
groupDocDeltas = [];
this.addFolds(delta.folds);
} }
} }
this.doc.revertDeltas(groupDocDeltas);
}
this.$fromUndo = false; this.$fromUndo = false;
if (docDeltas.length == deltas.length) {
this.$setUndoSelection(docDeltas, true);
}
}, },
this.redoChanges = function(deltas) { this.redoChanges = function(deltas) {
if (!deltas.length) if (!deltas.length)
return; return;
var docDeltas = filterDocActions(deltas);
this.$fromUndo = true; this.$fromUndo = true;
this.doc.applyDeltas(docDeltas); var lastUndoRange = null;
for (var i = 0; i < deltas.length; i++) {
delta = deltas[i];
if (delta.group == "doc") {
this.doc.applyDeltas(delta.deltas);
lastUndoRange =
this.$setUndoSelection(delta.deltas, false, lastUndoRange);
}
}
this.$fromUndo = false; this.$fromUndo = false;
this.$setUndoSelection(docDeltas, false);
}, },
this.$setUndoSelection = function(deltas, isUndo) { this.$setUndoSelection = function(deltas, isUndo, lastUndoRange) {
// invert deltas is they are an undo function isInsert(delta) {
if (isUndo) var insert =
deltas = deltas.map(function(delta) { delta.action == "insertText" || delta.action == "insertLines";
var d = { return isUndo ? !insert : insert;
range: delta.range
} }
if (delta.action == "insertText" || delta.action == "insertLines")
d.action = "removeText"
else
d.action = "insertText"
return d;
}).reverse();
var delta = deltas[0];
var actions = [{}]; var range;
var lastDeltaIsInsert = false;
// collapse insert and remove operations if (isInsert(delta)) {
for (var i=0; i<deltas.length; i++) { range = delta.range.clone();
var delta = deltas[i]; lastDeltaIsInsert = true;
var isInsert = delta.action == "insertText" || delta.action == "insertLines"; } else {
var action = actions[actions.length-1]; range = Range.fromPoints(delta.range.start, delta.range.start);
if (action.isInsert !== isInsert) { lastDeltaIsInsert = false;
actions.push({
isInsert: isInsert,
start: isInsert ? delta.range.start : delta.range.end,
end: isInsert ? delta.range.end : delta.range.start
})
} }
else {
if (isInsert) for (var i = 1; i < deltas.length; i++) {
action.end = delta.range.end; delta = deltas[i];
else if (isInsert(delta)) {
action.start = delta.range.start; if (range.compare(delta.range.start) == -1) {
range.setStart(delta.range.start);
}
if (range.compare(delta.range.end) == 1) {
range.setEnd(delta.range.end);
}
lastDeltaIsInsert = true;
} else {
if (range.compare(delta.range.start) == -1) {
range =
Range.fromPoints(delta.range.start, delta.range.start);
}
lastDeltaIsInsert = false;
} }
} }
// update selection based on last operation // Check if this range and the last undo range has something in common.
this.selection.clearSelection(); // If true, merge the ranges.
var action = actions[actions.length-1]; if (lastUndoRange != null) {
if (action.isInsert) var cmp = lastUndoRange.compareRange(range);
this.selection.setSelectionRange(Range.fromPoints(action.start, action.end)); if (cmp == 1) {
else range.setStart(lastUndoRange.start);
this.selection.moveCursorToPosition(action.end); } else if (cmp == -1) {
range.setEnd(lastUndoRange.end);
}
}
if (!range.isEmpty()) {
this.selection.setSelectionRange(range);
} else {
this.selection.moveCursorToPosition(range.start);
}
return range;
}, },
this.replace = function(range, text) { this.replace = function(range, text) {