Send to Kindle

Friday, October 12, 2012

ACE UndoManager and setValue()

‹prev | My Chain | next›

While working with the ACE Editor, I noticed a terrifying feature. They call it "undo".

OK so maybe it's not that terrifying, but it does have a terrifying behavior—it undos back beyond when a new project is started. That is, it undos back to when the editor was empty. So, hitting multiple Ctrl-Z's will eventually land you, not on the original version of the project, but on a blank page. Since I am auto-saving my work in localStorage, I very quickly have a blank project with no obvious means to restore my work.

That's frustrating for me, but would be horrifying for a kid working through Gaming JavaScript.

I do not see any obvious way to remove an entry from ACE's UndoManager. I do see that EditSession has a setUnderManager() method—perhaps I can use that to initialize the undo at the point just after the editor is updated with setValue().

So I do just that:
var ace = ace.edit("editor");
ace.setTheme("ace/theme/chrome");
ace.getSession().setMode("ace/mode/javascript");
ace.getSession().setUseWrapMode(true);
ace.getSession().setUseSoftTabs(true);
ace.setPrintMarginColumn(false);
ace.setDisplayIndentGuides(false);
ace.setFontSize('18px');
var emacs = require("ace/keyboard/emacs").handler;
ace.setKeyboardHandler(emacs);
ace.setValue((documents.length > 0) ? documents[ 0 ].code : templates[ 0 ].code, -1);

ace.getSession().setUndoManager(new UndoManager());
I will have to do the same anywhere else that setValue() changes the contents (or generalize a code editor setValue function). But first, I need to make sure this works.

It does not. In the JavaScript console, I see:
Uncaught ReferenceError: UndoManager is not defined 
Ah, it seems that I will have to require.js it into my current namespace. I try:
var UndoManager = require("ace/ace").UndoManager;
But that does not work. It seems that UndoManager is not a property of the ACE constructor.

The define() statement for UndoManager is:
define('ace/undomanager', /* ... */);
This suggests that the following might work:
var UndoManager = require("ace/undomanager");
editor.getSession().setUndoManager(new UndoManager());
However, I still seem to lack a constructor:
Uncaught TypeError: object is not a function
If I check this out in the JavaScript console, it seems that I need the UndoManger property of that require object:


And indeed, the following does the trick:
// ...
ace.setValue(/* ... */);

var UndoManager = require("ace/undomanager").UndoManager;
ace.getSession().setUndoManager(new UndoManager());
Not only so the page load without error, but undo works as desired. I can type a bunch of junk in my current project, Ctrl-Z to my heart's content, but never move back to the blank page before the first setValue().

I believe that removes any objection that I might have had to replacing CodeMirror with ACE. They both seem very similar—both in features and quality. The only real difference is that I had to implement a very hacky fix to a CodeMirror+Chrome paint issue. Any issues that I have had with ACE, I have been able to resolve via the API. I do think that I will take some time tomorrow to see if I can get JavaScript syntax checking working under ACE when mixing HTML and JavaScript. That would be very nice.

Day #537

1 comment:

  1. Hello! Thank you for you post. It helped me to solve the same problem.

    But i found a much more easy solution:

    var undo_manager = ace.getSession().getUndoManager();
    undo_manager.reset();
    ace.getSession().setUndoManager(undo_manager);

    ReplyDelete