Our product requires an online code editor. So, we decided to experiment with two of the leading contenders – Ace and CodeMirror. Both of these are open sourced and can be easily integrated with Witty. In fact, we managed the integration is about an hour each.
I’ll skip the details on how to use Ace and CodeMirror as these can be gleaned from their friendly documentation. They’re also both compatible in so many ways.
However, I will try to show how it can be easily integrated with Witty and also some general comments. All we need is to load the Javascript, invoke the appropriate functions with the appropriate DOM object to use as the editor, and set the onChange event trigger.
For Ace:
Wt::WApplication::instance()->require("ace/src-min-noconflict/ace.js");
this->doJavaScript("var e" + this->id() + " = ace.edit(" + this->jsRef() +");");
this->doJavaScript("e" + this->id() + ".getSession().on('change', function(e) {" + _change.createCall("e" + this->id() + ".getValue()") + "});");
For CodeMirror:
Wt::WApplication::instance()->require("codemirror/lib/codemirror.js");
Wt::WApplication::instance()->useStyleSheet("codemirror/lib/codemirror.css");
this->doJavaScript("var cm" + this->id() + " = CodeMirror(" + this->jsRef() + ");");
this->doJavaScript("cm" + this->id() + ".on('change', function(e,o) {" + _change.createCall("JSON.stringify(o)") + "});");
As you can see from the above, integrating either one with Witty is pretty simple and straight-forward. There are of course a lot more other powerful features of each editor to play with but to just get the editor to appear in a widget and return changes to the server, is just that simple.
The key methods to use are jsRef()
to generate the Javascript to identify the DOM object to use, id()
to return a unique ID to be used as an identifier, and createCall()
to generate the appropriate Javascript that will trigger a JSignal on the server with the required data.
The Witty widget would need to have a JSignal tied to the onChange() event for each editor to send changes to the server. I won’t go into the specifics except to show the header definitions for both Ace and CodeMirror (they’re exactly the same).
Wt::JSignal _change; ///< change signal
Both will return a string to the server on edits. But this is where things become a little different as both editors take a slightly different approach as to what they return on edits.
The Ace editor uses the object.getValue() method to return the entire editor contents to the server. This technique can also be used in CodeMirror to provide exactly the same feature. But CodeMirror also provides an object that contains the delta changes made. However, this object had to be converted to a JSON string before being sent to the server.
Then on the server end, the entire CodeMirror object that was converted into a JSON string is then parsed into a JSON object using Witty’s built-in JSON parser. I’m so happy that Witty comes with all these useful little helpers.
void Editor::onChange(const std::string code) {
try {
// Parse JSON object
Wt::Json::Object obj;
Wt::Json::parse(code, obj);
std::cout < < ":" << obj.size() << std::endl; // object size.
} catch (Wt::Json::ParseError e) {
Wt::log("error") << e.what() << code;
}
}
Of course, we would then need to build a specialised parser to handle the Delta objects to maintain consistency between the edited document on the client side with the original document on the server side. But this seems quite trivial to do as the change instructions are pretty specific.
All this took just about an hour or two to implement with Witty.
Just for extra credit, the delta change object returned is in the following format:
{"from":{"line":0,"ch":7},"to":{"line":0,"ch":11},"text":["g"],"removed":["ddfl"],"origin":"+input"}
Now, to the question of which editor to choose for our product, there are other experiences out there on the internet. We do not have enough experience with either one to make a definitive judgement.
But on first impression, I like the idea of a delta-change object as it may reduce the amount of traffic for large documents. Also, CodeMirror has the ability to batch multiple delta changes together into an object array and send those out at a go. These powerful capabilities make it attractive as a live code editor to reduce bandwidth usage.
But for simplicity in our initial MVP, I think that we’ll stick with returning the full edited text on each change. As this can be done with the same code for both Ace and CodeMirror, we will probably standardise on CodeMirror instead of Ace.
2 Comments
yuqi79 · 2016-06-14 at 08:46
Nice explanation. I am finding with CodeMirror loading text to the TextArea via a submit Wt::PushButton does not work i.e. the text isn’t updated. If I manually TextArea>setText(code) it works; but never, from an event.
JointJS with Wt | AESTE · 2015-02-15 at 22:32
[…] of integrating JointJS with Witty code was fairly simple, and very similar to the integration of Witty with Ace and CodeMirror. I am not going to go through all the details here, but highlighting the important parts. First, I […]