Sunday, October 14, 2012

ACE and the Disappearing Text

‹prev | My Chain | next›

I had the privilege of running my first Gaming JavaScript hackathon today. I made the risky decision to switch to the ACE Editor for the hackathon, but in the end it paid off. The girls did not have any difficulty working with it and it held up well. There was, however, a glitch.

When resizing the viewport, which often occurs when the JavaScript console is used, the code has the unfortunate tendency to disappear:


This is not quite as bad as the way that CodeMirror disappeared chunks of code when highlighting text, but it is definitely annoying.

It is also easier to restore the missing ACE code, a simple scroll and the code returns:


Mr Doob's code editor, from whence my fork came, already handles page resize. I add a console.log() to it to make sure that it is actually being called when I raise and lower the JavaScript console:
window.addEventListener( 'resize', function ( event ) {
  console.log("resize!");

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
And indeed, it is:


So this seems like a good place to add a hook to account for the disappearing code. At first glance, I do not find anything in ACE that suggests a means to force a redraw, but I do find centerSelection(). It turns out that invoking this method on resize does the trick:
window.addEventListener( 'resize', function ( event ) {
  ace.centerSelection();

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
Since that could end up centering text that was not even in the viewport prior to the resize, I will file that away as plan B.

And, after digging through a few ACE issues, I come across the onResize() method (which is a method, not an event callback). It seems that this is used in various places of the code when the window is resized. I am unsure why this does not seem to have the desired effect under normal circumstances, but I believe that I can eliminate the disappearing code with a force onResize() call in my resize listener:
window.addEventListener( 'resize', function ( event ) {
  ace.renderer.onResize(true);

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
That was a worrisome little bug—especially to run across it in the midst of a hackathon. It is possible that this is special to the manner in which ACE is being employed in this code-editor. Regardless, the fix was small and not too hard to find.


Day #539

Saturday, October 13, 2012

ACE Editor Syntax Checking Simple Mixed HTML

‹prev | My Chain | next›

Up today, I would like to see if I can get syntax checking working in the ACE editor. Actually, syntax checking is a built-in feature of ACE for certain modes. And it seems to work pretty well with JavaScript—except when JavaScript is embedded in an HTML document. Of course, all of the code in Gaming JavaScript is embedded inside HTML documents.

My initial strategy to get ACE to work with mixed HTML and JavaScript is to strip out the HTML, leaving behind only JavaScript. I cannot simply strip everything between HTML tags because <script> tags tend to live in between <html> and </html> tags.

Significantly complicating things is the need to retain the same number of lines. I cannot strip everything inside the <body> tag:
<body>
<h1>foo</h1>
<p>Bob went fishing while I slept.</p>
</body>
Because then I would be left with
<body></body>
Subsequent lines in the file would then have errors reported on the wrong line.

Instead of trying to solve all problems, I will start with the problems directly facing me in the book. The HTML in my templates looks simply like:
<body></body>
<script src="http://gamingJS.com/Three.js"></script>
<script src="http://gamingJS.com/ChromeFixes.js"></script>
<script>
  // This is where stuff in our game will happen:
  // ...
</script>
If all I need to do is remove empty <body> tags and <script> tags, well... I can do that.

After a bit of digging, I find that ACE has a JavaScript worker that is responsible for running JSHint against the code. Deep within that worker code is the line that gets the current value of the editor contents and parses it with JSHint:
oop.inherits(JavaScriptWorker, Mirror);

(function() {

    this.onUpdate = function() {
        var value = this.doc.getValue();
        value = value.replace(/^#!.*\n/, "\n");
        try {
            parser.parse(value);
        } catch(e) { /* ... */ }
    }
// ...
});
Interestingly, even though this is a JavaScript worker, it is replacing shebang lines. I can add on to that to replace my lines:
this.onUpdate = function() {
        var value = this.doc.getValue();
        value = value.replace(/^#!.*\n/, "\n");
        value = value.replace(/^<(\w+).*?>\s*<\/\1>/gm, "");
        value = value.replace(/<\/?script>/g, "");
        try {
            parser.parse(value);
        } catch(e) { /* ... */ }
   }
With my two replace() lines, I am replacing all lines with empty HTML pair tags with a newline. I am also stripping out <script> tags.

Thus, JSHint only sees the following when it parses my templates:
  // This is where stuff in our game will happen:
  // ...

As long as // ... is replaced with valid JavaScript, JSHint should see no errors or warnings.

And that seems to do the trick. If I add some intentionally broken code, I get warnings in the line number gutter of ACE. And if I mouse over them, then I am told what went wrong:


That is pretty darn nice. I might ask for simpler error messages that might help kids, but this is a start. If I can explain the most common error messages in the book and how to fix them, hopefully that will be good enough -- or at least no worse than having no warnings at all.



Day #538

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

Thursday, October 11, 2012

ACE JavaScript Lint

‹prev | My Chain | next›

Last night I was able to get the ACE editor working fairly well with my fork of Mr Doob's code editor.

There is still one thing that bugs me about ACE. Tab markers:


They are not even tab markers since I have disabled tabs with setUseSoftTabs(true). They are more extra indent markers and they seem unnecessary.

To turn them off, I eventually find setDisplayIndentGuides():
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);
With that, I am ready to look at something a little more interesting: JavaScript syntax checking.

In Gaming JavaScript, I had planned to introduce kids to the joys of the Chrome JavaScript console. But ACE supports syntax checking that might not make that necessary. Actually, I'll almost certainly include that anyway because I think it a fun introduction to hacking, but syntax checking can only help.

But, if I set the editor mode to JavaScript:
ace.getSession().setMode("ace/mode/javascript");
I am not seeing anything—even if I intentionally add an error.

Eventually, I notice the following in the JavaScript console:


Well, that is easy enough to fix. I copy the JavaScript worker from the ACE source that I downloaded the other day:
➜  code-editor git:(ace) ✗ cp ../ace-builds/src/worker-javascript.js js/ace 
With that, I see an error. Only not the right one:


Darn. I really want to include HTML tags in the Gaming JavaScript editor. I think that I helps to give a better perspective of how all of this fits together. But unfortunately ACE in JavaScript mode sees that HTML as a JavaScript syntax error.

I try switching to HTML/mixed mode instead:
var ace = ace.edit("editor");
ace.setTheme("ace/theme/chrome");
ace.getSession().setMode("ace/mode/html");
But now there are no errors at all:


I do not know how important this really is to me. I may fiddle with the JavaScript worker to see if I can convince it to ignore HTML. I may also investigate Esprima a bit as an alternative. Tomorrow.

Day #537

Wednesday, October 10, 2012

Finishing with ACE

‹prev | My Chain | next›

Last night I was able to get the ACE code editor up and running (with Emacs support, which is all that really matters in life). Tonight, I hope to completely integrate it with my fork of Mr Doob's awesome code editor.

It turns out that ACE and the CodeMirror editor (which is currently used by the code-editor) share a similar API. Updating the contents of both is accomplished with a setValue() method and retrieving the contents is done via a similarly named getValue(). So I perform a quick search and replace of code.getValue / code.setValue for ace.getValue / ace.setValue.

With that, and a little CSS work, I get the code stored in code-editor's localStorage to show up in ACE and the preview to show "behind" it:



That was surprisingly easy. But I am not quite done yet.

Although the code and preview are working, updates are not working. When the programmer updates the code, the preview should be updated to reflect the code changes. Nothing is happening right now. The explanation is simple enough: I removed the editor's on-change handler. Now I need to figure out how to get the ACE equivalent.

Like many of the other ACE settings that I have used so far, the on-change handler is part of the edit session:
ace.getSession().on( "change", function () {
  save();

  if ( documents[ 0 ].autoupdate === false ) return;

  clearTimeout( interval );
  interval = setTimeout( update, 300 );
});
That does the trick. When I update code in ACE, the contents are immediately saved to localStorage. Then, if the auto-update checkbox is enabled, the preview is updated. There is a 0.3 second delay in case the programmer is still typing.

All that remains now is minor clean-up and tweaks. The iframe used to preview the editor contents had scrollbars. I am unsure why this happens now, but the fix is easy enough: set the document margin to 0 pixels.

It seems that the API between CodeMirror and ACE is similar, but not exactly the same. ACE defaults the setValue() method to select all of the contents of the editor. This seems a recipe for deleting everything. Fortunately, there is a second argument to setValue() that specifies cursor position. -1 means at the beginning of the contents:
  ace.setValue( documents[ 0 ].code, -1 );
A few other settings for the editor are:
ace.getSession().setUseWrapMode(true);
ace.setPrintMarginColumn(false);
The first setting means that code will wrap and thus not create horizontal scrolling. The editor is the size of the window, so it is silly to accommodate long horizontal lines of code. I'll break kids of that bad habit before it begins. The last setting removes the visible print margin. I may reinstate that and tell kids not to code past that line. For now, though I remove it as it may be confusing.

I am still unsure whether I prefer CodeMirror or ACE. The default behavior of setValue() in ACE is annoying, but easily addressed in the 3 places that the code-editor uses it. It is nice that ACE does not appear to suffer from the Chrome redraw bug. I may switch for that reason alone.

Day #535

Tuesday, October 9, 2012

Getting Started with ACE in the Code Editor

‹prev | My Chain | next›

I think that I have worked through the most pressing of the issues in my fork of Mr Doob's code editor. At the least, I have enough to continue writing and making screenshots for Gaming JavaScript.

Tonight, I am going to continue following along behind the great work by Mr Doob. After I forked his code-editor, Mr Doob experimented with the ACE Code Editor instead of the current CodeMirror editor. I do not have an opinion (yet) on which is better. Both seem to be in wide use with a number of prominent users.

In a new "ace" branch, I remove all of the CodeMirror code. I could have left it, but I want to be sure that I eliminate stray dependencies. Next, I grab the ace source:
➜  repos  git clone https://github.com/ajaxorg/ace-builds.git
Cloning into 'ace-builds'...
I could use the CDN version, but I need to evaluate this locally since it would ultimately be run as part of an HTML appcache.

Back in the code-editor local repository, I copy the ace files that I think I will need:
➜  code-editor git:(gamingjs) ✗ cd js
➜  js git:(ace) ✗ mkdir ace
➜  js git:(ace) ✗ cd !$
➜  js git:(ace) ✗ cd ace
➜  ace git:(ace) ✗ cp ~/repos/ace-builds/src/ace.js .
➜  ace git:(ace) ✗ cp ~/repos/ace-builds/src/keybinding-emacs.js .
➜  ace git:(ace) ✗ cp ~/repos/ace-builds/src/mode-{css.js,html.js,javascript.js} .
➜  ace git:(ace) ✗ cp ~/repos/ace-builds/src/theme-chrome.js .
In the application's index.html, I pull in this new ace.js file:
<!DOCTYPE html>
<html>
 <head>
  <title>code editor</title>
    <meta charset="utf-8">
  <link rel="stylesheet" href="editor.css">
    <script src="js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
  <script src="js/rawinflate.js"></script>
  <script src="js/rawdeflate.js"></script>
  <script src="js/appcache.js"></script>
 </head>
 <body>
 </body>
  <script src="js/editor.js">
  </script>
</html>
And, in the editor.js file, into which I have moved all of the code-editor specific code, I replace the CodeMirror setup with a simliar ACE setup:
// editor

var interval;

var editor = document.createElement( 'div' );
editor.id = "editor";
editor.style.position = 'absolute';
editor.style.left = '0px';
editor.style.top = '0px';
editor.style.width = window.innerWidth + 'px';
editor.style.height = window.innerHeight + 'px';
document.body.appendChild( editor );

var ace = ace.edit("editor");
ace.setTheme("ace/theme/chrome");
ace.getSession().setMode("ace/mode/html");
With that, I get the ACE code editor:


I tweak the settings a bit to get a large enough font size and to get Emacs keybindings (very important):
var ace = ace.edit("editor");
ace.setTheme("ace/theme/chrome");
ace.getSession().setMode("ace/mode/html");
ace.setFontSize('16px');
var emacs = require("ace/keyboard/emacs").handler;
ace.setKeyboardHandler(emacs);
It took me a long while to figure out how to enable the Emacs keybindings. Maybe it is just me, but that was not obvious. In particular, in addition to requiring the emacs keybindings, I am forced to reference the source in the HTML:
...
    <script src="js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/ace/keybinding-emacs.js"></script>
...
I am not going to dwell on that too much. At least it is working. I call it a night here. I will pick back up integrating into the code-editor tomorrow.


Day #534

Monday, October 8, 2012

Code Editor Focus

‹prev | My Chain | next›

After last night, I have one significant issue outstanding in the code editor for Gaming JavaScript. These are not so much defects in the original implementation of Mr Doob's code editor—more usability tweaks needed for kids to be able to use it well.

The last substantive issue is that, when switching from code + preview to just the preview, the renderer does not have keyboard focus. Hopefully, unlike last night, this will be a relatively quick fix. I have writing to do.

To test this out, I copy my retrograde motion simulation into my local development editor:


When the code is hidden, I should be able to switch between cameras by typing "1" or "2". That is not working right now.

Both preview and code are absolutely positioned, full viewport <div> tags:
// preview

var preview = document.createElement( 'div' );
// ...
document.body.appendChild( preview );

// editor

var code = CodeMirror( document.body, { /* ... */ } );
var codeElement = code.getWrapperElement();
// ...
document.body.appendChild( codeElement );
So my first thought is to focus() these <div> tags when toggling the display:
var toggle = function () {
  if (codeElement.style.display == '') {
    shortCodeToolbar();
    codeElement.style.display = 'none';
    preview.focus();
  } 
  else {
    codeToolbar();
    codeElement.style.display = '';
    codeElement.focus();
  }
};
Unfortunately, neither ends up working.

The preview <div> hold a frame element, which is what actually needs focus. Even simpler, the CodeMirror editor exposes a focus() method so I can treat it as a black box:
var toggle = function () {
  if (codeElement.style.display == '') {
    shortCodeToolbar();
    codeElement.style.display = 'none';
    preview.children[0].focus();
  }
  else {
    codeToolbar();
    codeElement.style.display = '';
    code.focus();
  }
};
And that does the trick. I can now toggle the code display and have the editor or the preview focused as needed. That should make things much easier for kids. Now back to writing...


Day #533