Customizing Code Completers

Cloud9 offers a lot of ways to customize your code completer, making it more responsive and showing richer results. This page highlights some of the features provided by the base_handler interface.

📘

Looking for customizing JavaScript completions?

See Code Completion For JavaScript Libraries for more information about customizing JavaScript completions for custom libraries or language extensions.

Overview

Look & Feel

Icons

Cloud9 provides the following icons for code completion. These look different depending on the theme used. Add an icon to your completions using the icon property:

handler.complete = function(doc, ast, pos, options, callback) {
    ...
    callback(null, [
        {
            name: "foo()",
            replaceText: "foo",
            icon: "method",
            ...
        }
    ]);
};
IconNameDescription
"event"An event (or, sometimes, an HTML element).
"method"A method or function.
"method2"A method or function that is less significant (e.g. a private method). This is icon is also used for dynamic analyzers when it's not 100% certain if a method exists or not.
"package"A package, class, or module.
"property"A property, variable, or field.
"property2"A property, variable, or field that is considered less significant (e.g. a private field). This is icon is also used for dynamic analyzers when it's not 100% certain if a property exists or not.
"unknown"Something else, or unkown
"unknown2"Something else, or unknown. Not as significant as unknown.
(no icon)undefinedOther completions that are not significant to the users, such as word completions. These don't stand out as much visually as other completions.

Tooltips

Tooltips can be implemented using base_handler.tooltip(). But in case you haven't implemented this abstract method, Cloud9 may still be able to guess tooltips based on the last completion provided. Simply set the guessCompletions property with your completion.

handler.complete = function(doc, ast, pos, options, callback) {
    var completions = ...;
    callback(null, completions.map(function(c) {
        c.guessTooltip = true;
    });
}

Continuous Code Completion

The getCompletionRegex() function is used to set a character sequence for which to start code completion. For example, for a Java-like language where you would want to complete whenever a user types ".", use:

handler.getCompletionRegex = function() {
    return /^\.$/;
}

If a completion regex is specified, continuous completion is enabled for that language. This means that whenever a user types a character, completion is attempted with a short delay. If a character sequence matching the completion regex is typed, completion is performed immediately.

Generic and Contextual Code Completions

Cloud9 provides generic code completions that work for any language based on words used in files. It also provides generic completions based on Jsonalyzer Handlers and CTags. These generic completions are generally visually distinguished by using no icon or an insignificant icon. They are also marked as generic by setting the isGeneric property of base_handler.complete() to true.

For languages that implement a "smart" code completer using a static analyzer it can be useful to hide generic completions. We also these "smart" completions contextual completions. By marking all contextual completions using the isContextual property, all generic completions are automatically hidden when there are any contextual ones available:

handler.complete = function(doc, ast, pos, options, callback) {
    var completions = ...;
    callback(null, completions.map(function(c) {
        c.isContextual = true;
    });
}

Performance

Code Completion Pre-Caching and Predictions

Cloud9 plugins can pre-cache the next code completion for performance,
or even show it to the user to improve the look & feel:

938938

These predictions can be implemented using base_handler.predictNextCompletion(). An example implementation for JavaScript returns the current
completion plus a dot:

handler.predictNextCompletion = function(doc, ast, pos, options, callback) {
    // Look at the current completion proposals.
    // Only predict if we have exactly one available completion.
    if (options.matches.length !== 1)
        return callback();
    // Return that completion plus a dot
    callback(null, { predicted: predicted[0] + ".", showEarly: true });
};

To understand how the above works, consider the again the scenario

function Foo() {
    this.myProp = 3;
    this.foo = 3
    th
}

Normal code completion can tell us that th will expand to this. With the predictor function above we can determine that the next code completion will likely be for this.. That means that Cloud9 can go ahead and determine all completions for this.. By setting the showEarly property to true, it can even show those completions to users.

Below is a more sophisticated example which filters the current completions
before making a prediction:

handler.predictNextCompletion = function(doc, ast, pos, options, callback) {
    // We look at all current completion proposals, but first filter for
    // contextual completions and ignore any keyword predictions
    var predicted = options.matches.filter(function(m) {
        return m.isContextual && !m.replaceText.match(KEYWORD_REGEX);
    });
    // Let's predict only if we have exactly one proposal left over to
    // make a prediction for (e.g., we know the user is going to type "foo")
    if (predicted.length !== 1)
        return callback();
    // Predict that the current user is going to type this identifier
    // followed by a dot (e.g., "foo.")
    callback(null, { predicted: predicted[0] + "." });
};

Optimizing Documentation Fetching

Normally the Cloud9 code completion interface expects documentation for all completion suggestions. For certain completers it can be hamper performance to retrieve and send these documentation blocks with every completion. base_handler.complete() provides a noDoc option that allows language handlers to leave out documentation until it is actually demanded by the UI.

The example below returns a single completion, and only retrieves documentation for it when it is actually needed:

handler.complete = function(doc, ast, pos, options, callback) {
    var result = {
        name: "foo()",
        replaceText: "foo",
        icon: "method",
        ...
    };
    if (options.noDoc)
        result.noDoc = true; // just tell Cloud9 documentation will come later
    }
    else {
        result.doc = ...; // ok, documentation was requested, let's get it
    }
    callback(null, [result]);
}

Asynchronous Code Completion

To cope with slow code completion engines, it can be useful to use asynchronous code completion: compute basic completions early, and get additional completions in the background. The worker_util.completeUpdate() function makes this possible. Simply get the additional completions in the background and call completeUpdate() when ready:

var asyncResult = {};

handler.complete = function(doc, ast, pos, options, callback) {
    var completions = ...;
    if (asyncResult && asyncResult.doc === doc
        && asyncResult.pos.row === pos.row && asyncResult.pos.column === pos.column) {
        completions = completions.concat(asyncResult.matches)
    } else {
        fetchAsyncCompletions();
    }
    callback(null, completions);
}

function fetchAsyncCompletions(doc, pos, line) {
    // Get additional completions, e.g. by calling some remote
    // API or doing type inference or what not
    var matches = ...;
    asyncResult = { pos: pos, doc: doc, matches: matches };
    // Once done, retrigger completion
    worker_util.completeUpdate(pos, line);
}

The completeUpdate() method takes the current position and line as a sanity measure, to make sure the completion is still relevant as the user may have changed the document in the mean time.