Determine if JavaScript syntax is valid in change handler of ACE

Phrogz picture Phrogz · Feb 29, 2012 · Viewed 13.3k times · Source

I'm using the ACE editor for interactive JavaScript editing. When I set the editor to JavaScript mode, ACE automatically determines if the code is valid or not, with an error message and line number highlighted when it's not.

During the change event handler, I want to detect if ACE thinks the code is valid or not before I attempt to eval() it. The only way I thought that I might do it is:

var jsMode = require("ace/mode/javascript").Mode;
var editor = ace.edit('mycode'), edEl = document.querySelector('#mycode');
editor.getSession().setMode(new jsMode);
editor.getSession().on('change',function(){
  // bail out if ACE thinks there's an error
  if (edEl.querySelector('div.ace_gutter-cell.ace_error')) return;
  try{
    eval(editor.getSession().getValue());
  }catch(e){}
});

However:

  1. Leaning on the presence of an element in the UI with a particular class seems awfully fragile, but more importantly,
  2. The visual update for parsing occurs after the change callback occurs.

Thus, I actually have to wait more than 500ms (the delay before the JavaScript worker kicks in):

editor.getSession().on('change',function(){
  setTimeout(function(){
    // bail out if ACE thinks there's an error
    if (edEl.querySelector('div.ace_gutter-cell.ace_error')) return;
    try{
      eval(editor.getSession().getValue());
    }catch(e){}
  },550); // Must be longer than timeout delay in javascript_worker.js
});

Is there a better way, something in an undocumented API for the JS mode, to ask whether there are any errors or not?

Answer

hkrish picture hkrish · May 19, 2012

The current session fires onChangeAnnotation event when annotations change.

after that the new set of annotations can be retrieved as follows

var annotations = editor.getSession().getAnnotations();

seems to do the trick. It returns a JSON object which has the row as key and an array as value. The value array may have more than one object, depending on whether there are more than one annotation for each row.

the structure is as follows (copied from firebug –for a test script that I wrote)

// annotations would look like
({

82:[
    {/*annotation*/
        row:82, 
        column:22, 
        text:"Use the array literal notation [].", 
        type:"warning", 
        lint:{/*raw output from jslint*/}
    }
],

rownumber : [ {anotation1}, {annotation2} ],

...

});

so..

editor.getSession().on("changeAnnotation", function(){

    var annot = editor.getSession().getAnnotations();

    for (var key in annot){
        if (annot.hasOwnProperty(key))
            console.log("[" + annot[key][0].row + " , " + annot[key][0].column + "] - \t" + annot[key][0].text);
    }

});

// thanks http://stackoverflow.com/a/684692/1405348 for annot.hasOwnProperty(key) :)

should give you a list of all annotations in the current Ace edit session, when the annotations change!

Hope this helps!