Jsonalyzer Handlers

Jsonalyzer handlers are a specialized form of Language Handlers. They address one issue with language handlers: they are sometimes considered too much like a blank slate. They allow for a lot of freedom and are extremely powerful, but they require plugin developers to do all the hard work themselves.

To make it easier to implement language tooling, Cloud9 provides the Jsonalyzer. The Jsonalyzer is a layer on top of the normal language handler interface. It is actually built itself as a language handler, implementing the base_handler interface, and using simple JSON datastructures to describe analysis results (hence the name: we smashed together json and analyzer). These JSON structures for example describe all the classes and functions in a file. The Jsonalyzer consumes the structures to implement an outline view, and a basis for more advanced services like code completion and jump to definition.

Jsonalyzer Handlers

Jsonalyzer handlers implement the jsonalyzer_base_handler interface. They should be registered in a Cloud9 plugin as follows:

define(function(require, exports, module) {
    main.consumes = ["jsonalyzer"];
    main.provides = [];
    return main;
          
    function main(options, imports, register) {
        var jsonalyzer = imports.jsonalyzer;
          
        jsonalyzer.registerWorkerHandler('plugins/my.plugin/bar_handler');
          
        register(null, {});
    }
});

This registers a new jsonalyzer plugin in the web worker. Jsonalyzer plugins can also run on the server, if you register them with jsonalyzer.registerServerHandler() instead.

Below is an example client-side jsonalyzer handler for Python:

define(function(require, exports, module) {

var PluginBase = require("plugins/c9.ide.language.jsonalyzer/worker/jsonalyzer_base_handler");
var handler = module.exports = Object.create(PluginBase);
var util = require("plugins/c9.ide.language.jsonalyzer/worker/ctags/ctags_util");

handler.languages = ["py"];
handler.extensions = ["py"];

handler.analyzeCurrent = function(path, doc, ast, options, callback) {
    var structure = {};
    
    doc.replace(/(?:^|\n)\s*def\s+(?!_)([^ \(:]+)/g, function(fullMatch, name, offset) {
        results["_" + name] = [{
            row: util.getOffsetRow(doc, offset),
            kind: "method",
            guessFargs: true
        }];
    });
    
    var errors = [{
        pos: { sl: 0, sc: 0 },
        message: "This is a message for python users from a jsonalyzer handler",
        level: "info"
    }]

    return callback(null, { properties: structure }, errors);
};

});

The plugin above uses regular expressions to find all python definitions in a file (/something something def something something/). It creates a JavaScript object structure that describes the structure of the file based on this. This object is a map of the following form:

  • row the row of the definition
  • kind the kind of definition
  • properties any nested definitions inside the current definition; another map
  • guessFargs a boolean indicating if Jsonalyzer should try to guess the arguments of this function (usually, this should be true for anything that might take arguments)

The other thing the example above does is return a list of errors. This can be done with a normal language handler just as easily, but the main reason to chose for the jsonalyzer is that jsonalyzer handlers can also easily run on the server — that is, in the workspace of the user. This way you can call any server-side linter or analyzer from your plugin and display any errors it returns in the editor.

1570

We can have many handlers and handler functions per language, so this just adds to the standard JavaScript analyzer. Based on the above starting point, you can make your analyzer as sophisticated as you want, or integrate any existing analyzer. The base_handler interface also provides methods for doing code completion, showing an outline view, etc. that works in a similar fashion to what is shown above.

Client-side and Server-side handlers

Jsonalyzer handlers can run either in client or on the server. As seen above, client-side handlers can be registered using:

jsonalyzer.registerWorkerHandler("plugins/my.plugin/bar_handler");

🚧

Advanced: Server-Side Handlers

Working with server-side Jsonalyzer handlers is generally more tricky and error-prone than with handlers that run in the worker. As an alternative to running a handler fully in the server, Cloud9 also provides a new API for controlling server-side tools from the worker; see Using Existing Language Tools.

Server-side handlers can be registered using:

jsonalyzer.registerServerHandler("plugins/my.plugin/bar_handler");

to register a server-side handler. Examples of server-side handlers can be found in https://github.com/c9/c9.ide.language.jsonalyzer/tree/master/server/handlers. These handlers all call a server-side tool on the workspace of the user. For instance, the php handler uses the built-in linter of php by calling php -l on the command-line. It grabs all messages displayed and returns them as JavaScript objects to Cloud9.

Reference Implementation

The Python example of the Reference Implementations section has an example Jsonalyzer handler used to implement a Python outline: https://github.com/c9/c9.ide.language.python/blob/7aeaf4a/worker/python_jsonalyzer.js.