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