I started writing my own transaction manager for Diavolo because of the problem I outlined in my previous article. For those of you who did not understand the problem let me explain :

  • in the normal plaintext editor, if you type "foo > bar", the whole thing is aggregated into two transactions only : first create a text node, then insert the whole text inside that text node. The last transaction is in fact updated each time you add a character at the end of the text node. And both transactions are batched into a single container transaction so one Undo only removes the whole text
  • in the source editor, when you type the first "f", the editor calls the tokeniser and then tries to resolve the token in the current grammar context. So the source editor creates a span with attributes specifying the token, the context, the role, and the expected context ; the original text node is moved as first child of that span. Then "oo" are added to that text node. The first whitespace is another token so the editor splits the text node in two and the right result is inserted inside a new span for the whitespace token. And so on for the new tokens ">" " " and "bar".

So aggregating "insert text" transactions is much harder in the latter case than in the former one, and that's why I started my own transaction manager. The way the existing nsTransactionManager works did not seem to me enough for the quality of Undo/Redo I want for Diavolo.

Even if I had written a new editor on top of nsEditor dealing only with text insertion and deletion based on start and end offsets in the text, I would still have to deal with coalescing text transactions in the transaction manager...

So I wrote a large part of my own transaction manager and six editor transactions until I tried something I did not think of with the existing txrmgr. And it worked. Sort of. Undo and Redo almost work correctly now BUT if you type "foo", Undo does not delete the whole token yet ; it undoes the last char only. Of course, when you paste/cut/delete/move text, it works fine. I'll try to improve the code but it'll be difficult. Let me call it a good compromise for the time being.

When that's done, I'll add auto-indent and I'll prepare everything needed to add the <sourceeditor> tag to the XUL toolkit. Line numbering is out of scope for the first release but I keep it in mind.

In terms of speed and performance, I am super glad ! It's really fast. It's also reasonably light since the whole thing is only 1286 lines of JS, excluding the grammar and the associated stylesheet but with comments, empty lines for readability, braces standing alone on a line and so on. I'll run the source through a JS optimizer, just to have an idea of what could be the target size of v1.0. Stay tuned :-)