Introduction

The purpose of this page is to give you an overview of what an analyzer plugin is and what it can do.

What is a plugin?

An analyzer plugin is a piece of code that communicates with the analysis server to provide additional analysis support. The additional support is often specific to a package or set of packages. For example, there is a plugin that provides analysis specific to the Angular framework. Plugins are not required to be specific to a package, but if the additional analysis is general enough, we would urge you to consider contributing it back to the Dart project so that everyone can more easily benefit from your work.

Plugins are written in Dart. They are executed by the analysis server by running them in the same VM as the analysis server, but each plugin is run in a separate isolate. The analysis server communicates with the plugins using a wire protocol that is specified in the plugin API document. This API is similar to the API used by the analysis server to communicate with clients.

The API consists of three kinds of communication. When the analysis server needs information from the plugin, or needs to pass information to the plugin, it sends a request. The plugin is required to answer every request with a response. If the request was a request for information, then the response will contain the requested information. Otherwise, the response is merely an acknowledgement that the request was received. In addition, the plugin can send a notification to the server to provide information to the server.

What can plugins do?

The scope of what a plugin can do is defined by the plugin API, but it's useful to start with a high level overview.

Lifecycle management

The API includes support for managing the lifecycle of a plugin. There is no guarantee about when plugins will be started or stopped relative to either the server or to each other.

When a plugin is first started, the analysis server will send a plugin.versionCheck request to the plugin to verify that the plugin is using the same version of the API as the server and therefore can communicate with the server. This exchange also serves to communicate some other information between the two participants.

When the server is asked to shut down, it will send a plugin.shutdown request to the plugin to shut it down. This gives the plugin an opportunity to release system resources or perform any other necessary actions. If a plugin encounters an error that causes it to need to shut down, it should send a plugin.error notification to the server to indicate that it is doing so.

Managing analysis

The API includes support for managing which files are analyzed. There is no requirement for when a plugin should perform the analysis, but to optimize the user experience plugins should provide information to the server as quickly as possible.

The analysis server sends an analysis.setContextRoots request to plugins to tell them which files to analyze. Each ContextRoot indicates the root directory containing the files to be analyzed (included) and any files and directories within the root that should not be analyzed (excluded). Plugins can read and use excluded files in the process of analyzing included files, but should not report results for excluded files.

In order to improve the user experience, the analysis server will send an analysis.setPriorityFiles request to specify which files should take priority over other files. These are typically the files that are open and visible in the client.

The analysis server will send an analysis.handleWatchEvents request to the plugin when one or more files (within a context root) have been modified. The plugin is expected to re-analyze those files in order to update the results for those files.

The analysis server will send an analysis.updateContent request when the user has edited a file but the edited content has not yet been written to disk. This allows the plugin to provide analysis results as the user is typing.

Requesting Analysis Results

In order to accommodate the workflow of clients, there are two ways for the server to request analysis results from a plugin.

First, the server can send a request to request specific results for a specific file. This is typically used for client-side functionality that the user has to explicitly request and that clients will not retain long term. For example, there is a request to get code completion suggestions. These requests are discussed below.

For functionality that is always available, or for results that can change without the client being aware that new data should be requested, there is a subscription model. The server will send an analysis.setSubscriptions request to the plugin. This request tells the plugin which results should be sent to the server when the results become available. The plugin does not send any results in the response to the request, but instead is expected to send a notification to the server when the results have been computed. The notifications that can be requested are also discussed below.

If the server has explicitly requested results, either by a request or by a subscription, the plugin should provide those results even if the file is an excluded file. This exception to the general rule does not apply to the implicit subscription for diagnostics.

Plugins should not send analysis results that duplicate the information computed by the analysis server itself. The expectation is for plugins to extend this information, not replicate it.

Diagnostics

Plugins can generate diagnostics to make users aware of problems in the code that they have written. Diagnostics are typically displayed in the editor region and might also be displayed in a separate diagnostics view or as decorations on a directory structure view.

Plugins are expected to send any diagnostics that they generate for any of the analyzed files (files that are included in a context root and not excluded). Essentially, there is an implicit subscription for errors for all (non-excluded) files. The plugin should send the errors in an analysis.errors notification.

Semantic highlighting

Highlight information is used to color the content of the editor view.

If the server has subscribed for highlighting information in some set of files, then the plugin should send the information in an analysis.highlights notification whenever the information needs to be updated.

Navigation

Navigation information is used by clients to allow users to navigate to the location at which an identifier is defined.

Navigation information can be requested both by an analysis.getNavigation request and by a subscription. If the server has subscribed for navigation information in some set of files, the the plugin should send the information in an analysis.navigation notification whenever the information needs to be updated.

There is a tutorial explaining how to implement navigation.

Mark occurrences

Occurrences information is used by clients to highlight (or mark) all uses within a given file of a single identifier when the user selects one use of that identifier.

If the server has subscribed for occurrences information in some set of files, then the plugin should send the information in an analysis.occurrences notification whenever the information needs to be updated.

Outline

Outline information is typically used by clients to provide a tree indicating the nesting structure of declarations within the code.

If the server has subscribed for outline information in some set of files, then the plugin should send the information in an analysis.outline notification whenever the information needs to be updated.

Folding

Folding information is used to allow users to collapse regions of text.

If the server has subscribed for folding information in some set of files, then the plugin should send the information in an analysis.folding notification whenever the information needs to be updated.

Code completion

Code completion suggestions are used to provide possible completions at some point in the text.

When the client request completion suggestions, the server will send a completion.getSuggestions request. The plugin should only send suggestions that would not also be returned by the server.

There is a tutorial explaining how to implement code completion.

Fixes and assists

Fixes and assists are a set of edits that users can choose to have applied to the code. They differ from a refactoring in that they cannot request additional information from the user. For example, a rename refactoring needs to know the new name, which requires prompting the user, and hence could not be implemented as either a fix or an assist.

Fixes are associated with specific diagnostics, and hence should only be generated if the diagnostics with which they are associated have been generated. For example, if a diagnostic has been produced to indicate that a required semicolon is missing, a fix might be generated to insert a semicolon.

The analysis server will request fixes by sending an edit.getFixes request.

Plugins should provide fixes for as many of the diagnostics they generate as possible, but only when those fixes provide value to the user. (For example, a fix to insert a semicolon is arguably harder to use than simply typing the semicolon would be, and therefore is of questionable value.)

There is a tutorial explaining how to implement fixes.

Assists are generally context-specific and hence should only be generated if the cursor is in the right context. For example, if there is an assist to convert an expression-style function body (one introduced by =>) into a block-style function body, it should only be generated if the cursor is within an expression-style function body.

The analysis server will request assists by sending an edit.getAssists request.

There is a tutorial explaining how to implement assists.