UndoManagers
UndoManagers manage the change history of the contents of a document. An UndoManager is completely customizable and enables an editor implementation to implement undo in a straight forward way. This article is geared primarily towards working with the UndoManager in custom editors.
Creating UndoManagers
We recommend using the UndoManager class to create undoManagers when you need an abstract representation of change history. In most cases you won't need to create an undoManager as this is done automatically when a document is created. However there is no reason you couldn't create an undoManager and use it for other purposes.
Object structure
Besides tabs and panes there are several other objects involved in displaying content and making it editable. The following hierarchy describes the relationships between those objects.
- Pane - Represent a single pane, housing multiple tabs
- Editor - The editor responsible for displaying the document in the tab
- Tab - A single tab (button) in a pane
- Document - The representation of a file in the tab
- Session - The session information of the editor
- UndoManager - The object that manages the undo stack for this document
- Document - The representation of a file in the tab
It is important to note that the Editor
plugin(s) are related to the pane, not the tab. There's only one instance of a certain editor for each pane. The editor is responsible for displaying the content of a document based on the tab that is active. There can be a maximum of one tab active per pane.
Undo() and Redo()
The most common operations a user triggers on the undo manager of a document are undo and redo. Undo will revert the last done change and redo will undo a recent revert, if any.
The following example opens a file in the ace editor and adds a change and then calls undo()
and redo()
.
tabManager.open({ path: "/file.js", focus: true }, function(err, tab){
if (err) return console.error(err);
var doc = tab.document;
var ace = tab.editor.ace;
// Make a change
ace.insert("Something cool");
// Undo that change
doc.undoManager.undo();
// Redo that change
doc.undoManager.redo();
});
Change event
The change
event is called each time the undo stack changes. Operations like undo, redo, reset and bookmark all trigger the change
event. Note that you should use the changed
property of a document to check if a document is changed.
doc.undoManager.on("change", function(){
console.log("a change occurred");
});
The Undo Stack
The length
and position
properties give you access to the undoManager's state. All the items on the stack that have an index the same or less than position
have caused a change to the current state of the document's contents. All the items on the stack greater
than the position
have been reverted.
var undo = doc.undoManager;
console.log("Current length", undo.length); // 10 - Index 0 to 9
console.log("Current position", undo.position); // 9 - No reverted items
undo.undo();
console.log("Current length", undo.length); // 10 - Index 0 to 9
console.log("Current position", undo.position); // 8 - 1 reverted items
Note that if there are 0 items on the stack, the position
is -1
and length will be 0
.
Clear all the reverted items.
doc.undoManager.clearUndo();
Reset the the undo manager to having a length
of 0
and position
of -1
.
doc.undoManager.reset();
Bookmark
A bookmark is a way to set a place holder at a position on the undo stack. The save plugin uses this feature to keep track of which change state was saved to disk. Set the bookmark using the bookmark()
method.
doc.undoManager.bookmark();
doc.undoManager.bookmark(somePosition);
Check whether the current position
is at the bookmark.
var isChanged = doc.undoManager.isAtBookmark();
Items
Undo items are the unit of the stack and are the objects that implement actual changes to the document contents. The undo manager by itself only manages the process and calls upon these item objects to make changes.
When implementing a custom editor plugin that supports undo/redo implement your own undo items. An undo item must implement the following methods.
undo() | Revert a change |
---|---|
redo() | Redo a change |
getState() | Serialize a change |
It's a good idea to implement the constructor in a way that the object can be created with the serialized data that getState()
returns. Any JSON serializable object is accepted.
The next example implements an undo item that manipulates an array.
var data = [];
function Item(info, idx) {
this.getState = function(){
return [ info, idx ]
};
this.undo = function(){
data.splice(idx, 1)
};
this.redo = function(){
data[idx || (idx = data.length)] = info;
return this;
};
}
var undo = doc.undoManager;
undo.add(new Item("a").redo()); // data = ["a"]
undo.add(new Item("b").redo()); // data = ["a", "b"]
undo.undo(); // data = ["a"];
undo.undo(); // data = [];
undo.redo(); // data = ["a"];
When the IDE reloads, the serialized undo state is loaded from a file's metadata. When undo()
is called and an undo item is not present, the undo manager calls the itemFind
event which allows your editor to create an item based on the serialized state.
doc.undoManager.on("itemFind", function(e){
return new Item(e.state.[0], e.state[1]);
});
Updated less than a minute ago