Add more documentation for plugins

R=devoncarew@google.com

Review-Url: https://codereview.chromium.org/2992843002 .
This commit is contained in:
Brian Wilkerson 2017-08-02 07:45:43 -07:00
parent 39e43776d7
commit 4e1644f12a
6 changed files with 309 additions and 8 deletions

View file

@ -96,8 +96,7 @@ class MyPlugin extends ServerPlugin with AssistsMixin, DartAssistsMixin {
// ...
@override
List<AssistContributor> getAssistContributors(
covariant AnalysisDriver driver) {
List<AssistContributor> getAssistContributors(AnalysisDriver driver) {
return <AssistContributor>[new MyAssistContributor()];
}
}

View file

@ -58,7 +58,7 @@ For example, your contributor might look something like the following:
```dart
class MyCompletionContributor implements CompletionContributor {
@override
Future<Null> computeSuggestions(covariant DartCompletionRequest request,
Future<Null> computeSuggestions(DartCompletionRequest request,
CompletionCollector collector) async {
// ...
}
@ -74,7 +74,7 @@ class MyPlugin extends ServerPlugin with CompletionMixin, DartCompletionMixin {
@override
List<CompletionContributor> getCompletionContributors(
covariant AnalysisDriverGeneric driver) {
AnalysisDriverGeneric driver) {
return <CompletionContributor>[new MyCompletionContributor()];
}
}

View file

@ -112,7 +112,7 @@ class MyPlugin extends ServerPlugin with FixesMixin, DartFixesMixin {
@override
List<FixContributor> getFixContributors(
covariant AnalysisDriverGeneric driver) {
AnalysisDriverGeneric driver) {
return <FixContributor>[new MyFixContributor()];
}
}

View file

@ -0,0 +1,218 @@
# 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][pluginapi] 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][pluginapi], 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][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][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][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][assists].
[assists]: assists.md
[completion]: completion.md
[fixes]: fixes.md
[navigation]: navigation.md
[pluginapi]: https://htmlpreview.github.io/?https://github.com/dart-lang/sdk/blob/master/pkg/analyzer_plugin/doc/api.html

View file

@ -0,0 +1,79 @@
# Providing Navigation
Navigation information is used by clients to allow users to navigate to the
location at which an identifier is defined.
## Implementation details
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.
When an `analysis.getNavigation` request is received, the method
`handleAnalysisGetNavigation` will be invoked. This method is responsible for
returning a response that contains the available navigation information.
The easiest way to implement the method `handleAnalysisGetNavigation` is by
adding the classes `NavigationMixin` and `DartNavigationMixin` (from
`package:analyzer_plugin/plugin/navigation_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: `getNavigationContributors`. That method is
responsible for returning a list of `NavigationContributor`s. It is the
navigation contributors that produce the actual navigation information. (Most
plugins will only need a single navigation contributor.)
To write a navigation contributor, create a class that implements
`NavigationContributor`. The interface defines a single method named
`computeNavigation`. The method has two arguments: a `NavigationRequest` that
describes the region of the file for which navigation is being requested and a
`NavigationCollector` through which navigation information is to be added.
## Example
Start by creating a class that implements `NavigationContributor`, then
implement the method `computeNavigation`. This method is typically implemented
by creating a visitor (such as an AstVisitor) that can visit the results of the
analysis (such as a CompilationUnit) and extract the navigation information from
the analysis result.
For example, your contributor might look something like the following:
```dart
class MyNavigationContributor implements NavigationContributor {
@override
void computeNavigation(
NavigationRequest request, NavigationCollector collector) {
if (request is DartNavigationRequest) {
NavigationVisitor visitor = new NavigationVisitor(collector);
request.result.unit.accept(visitor);
}
}
}
class NavigationVisitor extends RecursiveAstVisitor {
final NavigationCollector collector;
NavigationVisitor(this.collector);
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
// ...
}
}
```
Given a contributor like the one above, you can implement your plugin similar to
the following:
```dart
class MyPlugin extends ServerPlugin with NavigationMixin, DartNavigationMixin {
// ...
@override
List<NavigationContributor> getNavigationContributors(String path) {
return <NavigationContributor>[new MyNavigationContributor()];
}
}
```

View file

@ -1,13 +1,17 @@
# Building a Plugin
This is the introduction page to a set of pages that describe how to implement a
plugin. You should probably read the [Getting Started][gettingStarted] page
first, but there is no specific order for the remaining pages.
This is the table of contents for a set of pages that describe how to implement
a plugin. You should probably read the [Introduction][introduction] and
[Getting Started][gettingStarted] pages first, but the remaining pages can be
read as you attempt to implement the described functionality.
## Pages
The following is a list of the pages available in this tutorial.
[Introduction][introduction] -
What is a plugin and what can it do?
[Getting Started][gettingStarted] -
How to write a minimal plugin.
@ -28,3 +32,4 @@ How to provide code completion suggestions.
[creatingEdits]: creating_edits.md
[fixes]: fixes.md
[gettingStarted]: getting_started.md
[introduction]: introduction.md