{"_id":"54deb134c4a3570d009d4a60","category":{"_id":"54e68e62a43fe13500db3879","__v":0,"project":"54d53c7b23010a0d001aca0c","pages":[],"version":"54d5635532d98b0d00384afb","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-02-20T01:31:13.999Z","from_sync":false,"order":5,"slug":"editor-plugins","title":"Editor Plugins"},"user":"54cfa8e1a8a4fd0d00b7fd1d","__v":68,"parentDoc":null,"project":"54d53c7b23010a0d001aca0c","version":{"_id":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","__v":10,"forked_from":"54d53c7c23010a0d001aca0f","createdAt":"2015-02-07T00:59:01.934Z","releaseDate":"2015-02-07T00:59:01.934Z","categories":["54d5635632d98b0d00384afc","54d5635632d98b0d00384afd","54d5635632d98b0d00384afe","54d5635632d98b0d00384aff","54d5635632d98b0d00384b00","54d5635632d98b0d00384b01","54d5635632d98b0d00384b02","54d652097e05890d006f153e","54dd1315ca1e5219007e9daa","54e21e2b22de1c230094b147","54e68e62a43fe13500db3879","54fa1d3fe7a0ba2f00306309","551c453a23a1ee190034d19a","551df586e52a0b23000c62b6","551f39be6886f8230055f02a","55a6720751457325000e4d97"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"0.1.0","version":"0.1"},"updates":[],"next":{"pages":[],"description":""},"createdAt":"2015-02-14T02:21:40.871Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"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.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Creating UndoManagers\",\n  \"body\": \"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.\"\n}\n[/block]\n# Object structure\n\nBesides 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.\n\n- [Pane](https://docs.c9.io/api/#!/api/Pane) - Represent a single pane, housing multiple tabs\n  - [Editor](https://docs.c9.io/api/#!/api/Editor) - The editor responsible for displaying the document in the tab\n  - [Tab](https://docs.c9.io/api/#!/api/Tab) - A single tab (button) in a pane\n    - [Document](https://docs.c9.io/api/#!/api/Document) - The representation of a file in the tab\n      - [Session](https://docs.c9.io/api/#!/api/Session) - The session information of the editor\n      - [UndoManager](https://docs.c9.io/api/#!/api/UndoManager) - The object that manages the undo stack for this document\n\nIt 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.\n\n# Undo() and Redo()\n\nThe 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.\n\nThe following example opens a file in the [ace editor](http://ace.c9.io/#nav=howto) and adds a change and then calls `undo()` and `redo()`.\n\n```javascript\ntabManager.open({ path: \"/file.js\", focus: true }, function(err, tab){\n    if (err) return console.error(err);\n    \n    var doc = tab.document;\n    var ace = tab.editor.ace;\n    \n    // Make a change\n    ace.insert(\"Something cool\");\n    \n    // Undo that change\n    doc.undoManager.undo();\n    \n    // Redo that change\n    doc.undoManager.redo();\n});\n```\n\n# Change event\n\nThe `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](http://cloud9-sdk.readme.io/v0.1/docs/documents#changed) to check if a document is changed.*\n\n```\ndoc.undoManager.on(\"change\", function(){\n    console.log(\"a change occurred\");\n});\n```\n\n# The Undo Stack\n\nThe `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.\n\n```\nvar undo = doc.undoManager;\n\nconsole.log(\"Current length\", undo.length); // 10 - Index 0 to 9\nconsole.log(\"Current position\", undo.position); // 9 - No reverted items\n\nundo.undo();\n\nconsole.log(\"Current length\", undo.length); // 10 - Index 0 to 9\nconsole.log(\"Current position\", undo.position); // 8 - 1 reverted items\n```\n\nNote that if there are 0 items on the stack, the `position` is `-1` and length will be `0`.\n\nClear all the reverted items.\n```\ndoc.undoManager.clearUndo();\n```\n\nReset the the undo manager to having a `length` of `0` and `position` of `-1`.\n```\ndoc.undoManager.reset();\n```\n\n# Bookmark\n\nA 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.\n\n```\ndoc.undoManager.bookmark();\ndoc.undoManager.bookmark(somePosition);\n```\n\nCheck whether the current `position` is at the bookmark.\n```\nvar isChanged = doc.undoManager.isAtBookmark();\n```\n\n# Items\n\nUndo 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.\n\nWhen implementing a custom editor plugin that supports undo/redo implement your own undo items. An undo item *must* implement the following methods.\n[block:html]\n{\n  \"html\": \"<table style=\\\"width:300px\\\">\\n  <tr><th>undo()</th><td>Revert a change</td></tr>\\n  <tr><th>redo()</th><td>Redo a change</td></tr>\\n  <tr><th>getState()</th><td>Serialize a change</td></tr>\\n</table>\"\n}\n[/block]\nIt'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.\n\nThe next example implements an undo item that manipulates an array.\n```javascript\nvar data = [];\n\nfunction Item(info, idx) {\n    this.getState = function(){ \n        return [ info, idx ] \n    };\n    this.undo = function(){ \n        data.splice(idx, 1) \n    };\n    this.redo = function(){ \n        data[idx || (idx = data.length)] = info; \n        return this;\n    };\n}\n\nvar undo = doc.undoManager;\nundo.add(new Item(\"a\").redo()); // data = [\"a\"]\nundo.add(new Item(\"b\").redo()); // data = [\"a\", \"b\"]\n\nundo.undo(); // data = [\"a\"];\nundo.undo(); // data = [];\nundo.redo(); // data = [\"a\"];\n```\n\nWhen 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.\n\n```\ndoc.undoManager.on(\"itemFind\", function(e){\n    return new Item(e.state.[0], e.state[1]);\n});\n```","excerpt":"","slug":"the-undomanager","type":"basic","title":"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. [block:callout] { "type": "warning", "title": "Creating UndoManagers", "body": "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." } [/block] # 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](https://docs.c9.io/api/#!/api/Pane) - Represent a single pane, housing multiple tabs - [Editor](https://docs.c9.io/api/#!/api/Editor) - The editor responsible for displaying the document in the tab - [Tab](https://docs.c9.io/api/#!/api/Tab) - A single tab (button) in a pane - [Document](https://docs.c9.io/api/#!/api/Document) - The representation of a file in the tab - [Session](https://docs.c9.io/api/#!/api/Session) - The session information of the editor - [UndoManager](https://docs.c9.io/api/#!/api/UndoManager) - The object that manages the undo stack for this document 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](http://ace.c9.io/#nav=howto) and adds a change and then calls `undo()` and `redo()`. ```javascript 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](http://cloud9-sdk.readme.io/v0.1/docs/documents#changed) 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. [block:html] { "html": "<table style=\"width:300px\">\n <tr><th>undo()</th><td>Revert a change</td></tr>\n <tr><th>redo()</th><td>Redo a change</td></tr>\n <tr><th>getState()</th><td>Serialize a change</td></tr>\n</table>" } [/block] 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. ```javascript 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]); }); ```