Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if JavaScript syntax is valid in change handler of ACE

Tags:

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?

like image 351
Phrogz Avatar asked Feb 29 '12 20:02

Phrogz


2 Answers

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!

like image 61
hkrish Avatar answered Sep 29 '22 01:09

hkrish


I found a solution that is probably faster than traversing the DOM. The editor's session has a getAnnotations method you can use. Each annotation has a type that shows whether they are an error or not.

Here is how I set my callback for the on 'change'

function callback() {     var annotation_lists = window.aceEditor.getSession().getAnnotations();     var has_error = false;      // Unfortunately, you get back a list of lists. However, the first list is     //   always length one (but not always index 0)     go_through:     for (var l in annotation_lists) {         for (var a in annotation_lists[l]) {             var annotation = annotation_lists[l][a];             console.log(annotation.type);             if (annotation.type === "error") {                 has_error = true;                 break go_through;             }         }     }      if (!has_error) {         try {             eval(yourCodeFromTextBox);             prevCode = yourCodeFromTextBox;         }         catch (error) {             eval(prevCode);         }     } } 

As far as I know, there are two other types for annotations: "warning" and "info", just in case you'd like to check for those as well.

I kept track of the pervious code that worked in a global (well, outside the scope of the callback function) because often there would be errors in the code but not in the list of annotations. In that case, when eval'ing the errored code, it would be code and eval the older code instead.

Although it seems like two evals would be slower, it seems to me like the performance is no that bad, thus far.

like image 27
AndrewKS Avatar answered Sep 29 '22 02:09

AndrewKS