Orion includes a simple but powerful infrastructure for implementing content assist for your favorite language. This article will describe how to create your own content assist plugin by working through a simple example. As with any Orion plugin, the starting point is an HTML file declaring your plugin. You can find a complete example here, but the body of the plugin is a simple script like this:
window.onload = function() { var provider = new eclipse.PluginProvider(); provider.registerServiceProvider("orion.edit.contentAssist", new RubyContentAssistProvider(), {name: "Ruby content assist", pattern: "\\.(ru|rb)$"}); provider.connect(); };
This script registers an implementation of the orion.edit.contentAssist service. The service parameters indicate that it should be associated with filenames ending in “.ru”, or “.rb”. The content assist implementation is found in the RubyContentAssistProvider object defined in a separate script, rubyContentAssist.js. This script defines a service implementation object declaring the computeProposals method. The completion reference for the arguments and return value of this method are provided in the orion.edit.contentAssist documentation.
Example 1: Simple keywords
Let’s start with a simple example that offers completion on a subset of Ruby’s reserved keywords.
function computeProposals(prefix) { return ["alias","and","begin", "break","case","class"]; }
We are given a string argument that contains the sequence of word characters immediately preceding the current cursor position. We didn’t use the argument here but we could have used it to only compute the proposals matching that prefix. Since a simple keyword list is trivial to compute we didn’t bother – the Orion content assist implementation will only show proposals matching that prefix anyway. Here’s a shot of our current content assist implementation in action:
Example 2: Advanced completion features
Now let’s look at a more interesting example:
function computeProposals(prefix, buffer, selection) { var proposals = ["alias","and","begin", "break","case","class"]; var prefixStart = selection.offset - prefix.length; var precedingChar = buffer.charAt(prefixStart - 1); if (precedingChar === '=' && "begin".indexOf(prefix) === 0) { //suggest writing a block comment proposals.push({ proposal: "begin\n\n=end", description: "=begin - block comment", escapePosition: prefixStart+6 }); } return proposals; }
We have added two more arguments to the computeProposals function: buffer, and selection. These arguments contain the current editor buffer and selection at the time content assist was invoked. We are using this information to check if the user is attempting to create a Ruby block comment, which starts with “=begin”.
Instead of returning a simple string in this example, we are returning a proposal object. This object allows us to provide more details back to the caller such as a description of the proposal, and the cursor position the editor should have after the completion has been inserted. In this case we want the cursor to end up inside the newly created block comment.
Also note that the text of the proposal does not include the equals character. The completion engine will only replace the provided prefix word, which only includes normal word characters (letters, numbers, or the underscore character). We already know that the character immediately before the prefix is an equals character already, so we don’t need to insert another one. Here is a shot of our enhanced content assist in action:
Note that both the begin keyword and the block comment proposal are included here. The description we provided for the block comment proposal helps the user understand the difference in this case. A more complete example could do more sophisticated analysis of the source to rule out the possibility of a keyword appearing at this location, and only show the block comment proposal.
Example 3: Linked mode completion
We’ll use one final example to illustrate Orion’s linked mode completion. This works exactly like some of the content assist in the Eclipse Java development tools, where after selecting a proposal the user uses the tab key to iterate through a set of fields that need to be filled in. Once all the fields are filled in, the user presses enter to finish the insertion process and exit linked mode. In Orion we indicate a linked mode completion by adding a positions property to a returned proposal. This property is an array of position objects indicating the offset and length of each linked region that requires user completion. In this example we use linked mode for an “if block” template where the user must provide the conditional expression:
function computeProposals(prefix, buffer, selection) { var proposals = []; var prefixStart = selection.offset - prefix.length; if ("if".indexOf(prefix) === 0) { //suggest writing a block comment proposals.push({ proposal: "if condition\n\t\nend", description: "if - if block", positions: [{offset: prefixStart + 3, length: 9}] escapePosition: prefixStart+14 }); } return proposals; }
When this proposal is accepted, the proposal text is inserted and the editor selection moves to the first position, which is the “condition” field. Below is a screenshot of what the editor looks like after this proposal has been selected. Note that the message area in the Orion toolbar indicates to the user that they have entered into a linked mode completion.
After editing the condition, the user hits Enter to finish the insertion, and the cursor will move to the indicated escape position. Note that the escapePosition property doesn’t need to account for the fact that the user might add or remove characters in the linked fields before they are done. The content assist engine will automatically adjust this value to account for any typing, so you just need to record the final cursor position under the assumption that the user will not modify any of the linked fields.
This concludes our overview of Orion’s content assist infrastructure. As these examples illustrate, it is very easy to get started with writing your own content assist service for Orion. Writing a truly powerful content assist service requires much more in depth source code analysis, but Orion takes care of all the basic editing infrastructure to let you focus on the language analysis. Check out the complete Ruby editor example and Developer guide for the most up to date details.
Pingback: Google I/O 2011: Android + App Engine: A Developer’s Dream Combination | Androidmarket