Providing Code Completions

A code completion is used by clients to provide a set of possible completions to partially entered code. Completions are intended to address two use cases: to help users enter code with less effort and to help users discover the behavior of an object.

For example, if the user has typed o.toSt and then requested completions, one suggestion might be toString.

That said, the completion suggestions that your plugin returns should include all of the options that would be valid if the partial identifier did not exist. The reason is that most clients are implemented such that they send a single request for completions when the dialog with the user begins and cannot send any subsequent requests. If the user presses the backspace key during the dialog the client needs to have already received the expanded list of options that now match the prefix (or all options if the prefix has completely been deleted). Clients will filter the list of suggestions displayed as appropriate.

Hence, in the example above, plugins should return suggestions as if the user had requested completions after typing o.;

Implementation details

When appropriate, the analysis server will send your plugin a completion.getSuggestions request. The request includes the file and offset at which completions are being requested.

When a completion.getSuggestions request is received, the method handleCompletionGetSuggestions will be invoked. This method is responsible for returning a response that contains the available suggestions.

The easiest way to implement this method is by adding the classes CompletionMixin and DartCompletionMixin (from package:analyzer_plugin/plugin/completion_mixin.dart) to the list of mixins for your subclass of ServerPlugin. This will leave you with one abstract method that you need to implement: getCompletionContributors. That method is responsible for returning a list of CompletionContributors. It is the completion contributors that produce the actual completion suggestions. (Most plugins will only need a single completion contributor.)

To write a completion contributor, create a class that implements CompletionContributor. The interface defines a single method named computeSuggestions. The method has two arguments: a CompletionRequest that describes the where completions are being requested and a CompletionCollector through which suggestions are to be added.

If you mix in the class DartCompletionMixin, then the request will be an instance of DartCompletionRequest, which also has analysis results.

Example

Start by creating a class that implements CompletionContributor, then implement the method computeSuggestions. Your contributor should invoke the method checkAborted, defined on the CompletionRequest object, before starting any slow work. This allows the computation of completion suggestions to be preempted if the client no longer needs the results.

For example, your contributor might look something like the following:

class MyCompletionContributor implements CompletionContributor {
  @override
  Future<void> computeSuggestions(DartCompletionRequest request,
      CompletionCollector collector) async {
    // ...
  }
}

Given a contributor like the one above, you can implement your plugin similar to the following:

class MyPlugin extends ServerPlugin with CompletionMixin, DartCompletionMixin {
  // ...

  @override
  List<CompletionContributor> getCompletionContributors(
      AnalysisDriverGeneric driver) {
    return <CompletionContributor>[new MyCompletionContributor()];
  }
}