The purpose of this page is to give you an overview of what an analyzer plugin is and what it can do.
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.
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.
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.
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.
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.
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.
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 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.
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 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 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 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 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.