mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
Initial documentation for the plugin package
R=devoncarew@google.com Review-Url: https://codereview.chromium.org/2973753003 .
This commit is contained in:
parent
6323bedc58
commit
0484f28335
|
@ -4,6 +4,18 @@ A framework for building plugins for the analysis server.
|
|||
|
||||
## Usage
|
||||
|
||||
**Note:** The plugin support is not currently available for general use.
|
||||
|
||||
Plugins are written in Dart and are run in the same VM as the analysis server.
|
||||
The analysis server runs each plugin in a separate isolate and communicates with
|
||||
the plugin using a [plugin API][pluginapi]. This API is similar to the API used
|
||||
by the analysis server to communicate with clients.
|
||||
|
||||
Plugins are automatically discovered and run by the analysis server.
|
||||
|
||||
This package contains support code to make it easier to write a plugin. There is
|
||||
a [tutorial][tutorial] describing how to use the support in this package.
|
||||
|
||||
## Support
|
||||
|
||||
Post issues and feature requests on the [issue tracker][issues].
|
||||
|
@ -19,3 +31,4 @@ See the [LICENSE] file.
|
|||
[LICENSE]: https://github.com/dart-lang/sdk/blob/master/pkg/analyzer/LICENSE
|
||||
[list]: https://groups.google.com/a/dartlang.org/forum/#!forum/analyzer-discuss
|
||||
[pluginapi]: https://htmlpreview.github.io/?https://github.com/dart-lang/sdk/blob/master/pkg/analyzer_plugin/doc/api.html
|
||||
[tutorial]: doc/tutorial/tutorial.md
|
||||
|
|
106
pkg/analyzer_plugin/doc/tutorial/assists.md
Normal file
106
pkg/analyzer_plugin/doc/tutorial/assists.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
# Providing Quick Assists
|
||||
|
||||
A quick assist is used by clients to provide a set of possible changes to code
|
||||
that are based on the structure of the code. Quick assists are intended to help
|
||||
users safely make local changes to code when those changes do not require any
|
||||
user interaction. (Modifications that require interaction with users or that
|
||||
touch multiple files are usually implemented as refactorings.)
|
||||
|
||||
For example, if the user has a function whose body consists of a single return
|
||||
statement in a block, server will provide an assist to convert the function body
|
||||
from a block to an expression (`=>`).
|
||||
|
||||
Assists have a priority associated with them. The priority allows the client to
|
||||
display the assists that are most likely to be of use closer to the top of the
|
||||
list when there are multiple assists available.
|
||||
|
||||
## Implementation details
|
||||
|
||||
When appropriate, the analysis server will send your plugin an `edit.getAssists`
|
||||
request. The request includes the `file`, `offset` and `length` associated with
|
||||
the selected region of code.
|
||||
|
||||
When an `edit.getAssists` request is received, the method `handleEditGetAssists`
|
||||
will be invoked. This method is responsible for returning a response that
|
||||
contains the available assists.
|
||||
|
||||
The easiest way to implement this method is by adding the classes `AssistsMixin`
|
||||
and `DartAssistsMixin` (from `package:analyzer_plugin/plugin/assist_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: `getAssistContributors`.
|
||||
That method is responsible for returning a list of `AssistContributor`s. It is
|
||||
the assist contributors that produce the actual assists. (Most plugins will only
|
||||
need a single assist contributor.)
|
||||
|
||||
To write an assist contributor, create a class that implements
|
||||
`AssistContributor`. The interface defines a single method named
|
||||
`computeAssists`. The method has two arguments: an `AssistRequest` that
|
||||
describes the location at which assists were requested and an `AssistCollector`
|
||||
through which assists are to be added.
|
||||
|
||||
The class `AssistContributorMixin` defines a support method that makes it easier
|
||||
to implement `computeAssists`.
|
||||
|
||||
## Example
|
||||
|
||||
Start by creating a class that implements `AssistContributor` and that mixes in
|
||||
the class `AssistContributorMixin`, then implement the method `computeAssists`.
|
||||
This method is typically implemented as a sequence of invocations of methods
|
||||
that check to see whether a given assist is appropriate in the context of the
|
||||
request
|
||||
|
||||
To learn about the support available for creating the edits, see
|
||||
[Creating Edits][creatingEdits].
|
||||
|
||||
For example, your contributor might look something like the following:
|
||||
|
||||
```dart
|
||||
class MyAssistContributor extends Object
|
||||
with AssistContributorMixin
|
||||
implements AssistContributor {
|
||||
static AssistKind wrapInIf =
|
||||
new AssistKind('wrapInIf', 100, "Wrap in an 'if' statement");
|
||||
|
||||
DartAssistRequest request;
|
||||
|
||||
AssistCollector collector;
|
||||
|
||||
AnalysisSession get session => request.result.session;
|
||||
|
||||
@override
|
||||
void computeAssists(DartAssistRequest request, AssistCollector collector) {
|
||||
this.request = request;
|
||||
this.collector = collector;
|
||||
_wrapInIf();
|
||||
_wrapInWhile();
|
||||
// ...
|
||||
}
|
||||
|
||||
void _wrapInIf() {
|
||||
ChangeBuilder builder = new DartChangeBuilder(session);
|
||||
// TODO Build the edit to wrap the selection in a 'if' statement.
|
||||
addAssist(wrapInIf, builder);
|
||||
}
|
||||
|
||||
void _wrapInWhile() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Given a contributor like the one above, you can implement your plugin similar to
|
||||
the following:
|
||||
|
||||
```dart
|
||||
class MyPlugin extends ServerPlugin with AssistsMixin, DartAssistsMixin {
|
||||
// ...
|
||||
|
||||
@override
|
||||
List<AssistContributor> getAssistContributors(
|
||||
covariant AnalysisDriver driver) {
|
||||
return <AssistContributor>[new MyAssistContributor()];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[creatingEdits]: creating_edits.md
|
81
pkg/analyzer_plugin/doc/tutorial/completion.md
Normal file
81
pkg/analyzer_plugin/doc/tutorial/completion.md
Normal file
|
@ -0,0 +1,81 @@
|
|||
# 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 `CompletionContributor`s. 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.
|
||||
|
||||
## 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:
|
||||
|
||||
```dart
|
||||
class MyCompletionContributor implements CompletionContributor {
|
||||
@override
|
||||
Future<Null> computeSuggestions(covariant DartCompletionRequest request,
|
||||
CompletionCollector collector) async {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Given a contributor like the one above, you can implement your plugin similar to
|
||||
the following:
|
||||
|
||||
```dart
|
||||
class MyPlugin extends ServerPlugin with CompletionMixin, DartCompletionMixin {
|
||||
// ...
|
||||
|
||||
@override
|
||||
List<CompletionContributor> getCompletionContributors(
|
||||
covariant AnalysisDriverGeneric driver) {
|
||||
return <CompletionContributor>[new MyCompletionContributor()];
|
||||
}
|
||||
}
|
||||
```
|
205
pkg/analyzer_plugin/doc/tutorial/creating_edits.md
Normal file
205
pkg/analyzer_plugin/doc/tutorial/creating_edits.md
Normal file
|
@ -0,0 +1,205 @@
|
|||
# Creating `SourceChange`s
|
||||
|
||||
Several of the response objects take a `SourceChange` (specifically, assists,
|
||||
fixes, and refactorings). Because `SourceChange` is a structured object that
|
||||
can be difficult to create correctly, this package provides a set of utility
|
||||
classes to help you build those structures.
|
||||
|
||||
Using these classes will not only simplify the work you need to do to implement
|
||||
your plugin, but will ensure a consistent user experience in terms of the code
|
||||
being generated by the analysis server.
|
||||
|
||||
## `DartChangeBuilder`
|
||||
|
||||
The class used to create a `SourceChange` is `DartChangeBuilder`, defined in
|
||||
`package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart`.
|
||||
You can create a `DartChangeBuilder` with the following:
|
||||
|
||||
```dart
|
||||
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
|
||||
```
|
||||
|
||||
The constructor required an instance of the class `AnalysisSession`. How you get
|
||||
the correct instance depends on where the constructor is being invoked.
|
||||
|
||||
A `SourceChange` can contain edits that are to be applied to multiple files. The
|
||||
edits for a single file are created by invoking the method `addFileEdit`, as
|
||||
illustrated by the following:
|
||||
|
||||
```dart
|
||||
changeBuilder.addFileEdit(path, (DartFileEditBuilder fileEditBuilder) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
where the `path` is the path to the file to which the edits will be applied.
|
||||
|
||||
## `DartFileEditBuilder`
|
||||
|
||||
The class `DartFileEditBuilder` defines methods for creating three kinds of
|
||||
edits: deletions, insertions, and replacements.
|
||||
|
||||
For deletions, you pass in the range of code to be deleted as a `SourceRange`.
|
||||
In addition to the constructor for `SourceRange`, there are a set of functions
|
||||
defined in `package:analyzer_plugin/utilities/range_factory.dart` that can be
|
||||
used to build a `SourceRange` from tokens, AST nodes, and elements.
|
||||
|
||||
For example, if you need to remove the text in a given `range`, you could write:
|
||||
|
||||
```dart
|
||||
fileEditBuilder.addDeletion(range);
|
||||
```
|
||||
|
||||
In the case of insertions and replacements, there are two styles of method. The
|
||||
first takes the string that is to be inserted; the second takes a closure in
|
||||
which the string can be composed. Insertions take the offset of the insertion,
|
||||
while replacements take a `SourceRange` indicating the location of the text to
|
||||
be replaced.
|
||||
|
||||
For example, if you need to insert `text` at offset `offset`, you could write
|
||||
|
||||
```dart
|
||||
fileEditBuilder.addSimpleInsertion(offset, text);
|
||||
```
|
||||
|
||||
The forms that take a closure are useful primarily because they give you access
|
||||
to a `DartEditBuilder`, which is described below.
|
||||
|
||||
For example, to replace a given `range` of text with some yet to be constructed
|
||||
text, you could write:
|
||||
|
||||
```dart
|
||||
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In addition, `DartFileEditBuilder` has some methods that allow you to build some
|
||||
common sets of edits more easily. For example, `importLibraries` allows you to
|
||||
pass in the `Source`s for one or more libraries and will create one or more
|
||||
edits to insert `import` directives in the correct locations.
|
||||
|
||||
## `DartEditBuilder`
|
||||
|
||||
A `DartEditBuilder` allows you to compose source code by writing the individual
|
||||
pieces, much like a `StringSink`. It also provides additional methods to compose
|
||||
more complex code. For example, if you need to write a type annotation, the
|
||||
method `writeType` will handle writing all of the type arguments and will add
|
||||
import directives as needed. There are also methods to write class declarations
|
||||
and to write various members within a class.
|
||||
|
||||
For example, if you're implementing a quick assist to insert a template for a
|
||||
class declaration, the code to create the insertion edit could look like the
|
||||
following:
|
||||
|
||||
```dart
|
||||
String className = 'NewClass';
|
||||
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
|
||||
editBuilder.writeClassDeclaration(className, memberWriter: () {
|
||||
editBuilder.writeConstructorDeclaration(className);
|
||||
editBuilder.writeOverrideOfInheritedMember(
|
||||
typeProvider.objectType.getMethod('toString'));
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Linked Edits
|
||||
|
||||
Many clients support a style of editing in which multiple regions of text can be
|
||||
edited simultaneously. Server refers to these as "linked" edit groups. Many
|
||||
clients also support having multiple groups associated with the edits in a file
|
||||
and allow users to tab from one group to the next. Essentially, these edit
|
||||
groups mark placeholders for text that users might want to change after the
|
||||
edits are applied.
|
||||
|
||||
The class `DartEditBuilder` provides support for creating linked edits through
|
||||
the method `addLinkedEdit`. As with the insertion and replacement methods
|
||||
provided by `DartFileEditBuilder` (see above), there are both a "simple" and a
|
||||
closure-based version of this method.
|
||||
|
||||
For example, if you're implementing a quick assist to insert a for loop, you
|
||||
should add the places where the loop variable name appears to a linked edit
|
||||
group. You should also add the name of the list being iterated over to a
|
||||
different group. The code to create the insertion edit could look like the
|
||||
following:
|
||||
|
||||
```dart
|
||||
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
|
||||
String listName = 'list';
|
||||
String listGroup = 'list_variable';
|
||||
String variableName = 'i';
|
||||
String variableGroup = 'loop_variable';
|
||||
|
||||
editBuilder.write('for (int ');
|
||||
editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
|
||||
editBuilder.write(' = 0; ');
|
||||
editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
|
||||
editBuilder.write(' < ');
|
||||
editBuilder.addSimpleLinkedEdit(listGroup, listName);
|
||||
editBuilder.write('.length; ');
|
||||
editBuilder.addSimpleLinkedEdit(variableGroup, variableName);
|
||||
editBuilder.write('++) {}');
|
||||
}
|
||||
```
|
||||
|
||||
One of the advantages of the closure-based form of `addLinkedEdit` is that you
|
||||
can specify suggested replacements for the values of each group. You do that by
|
||||
invoking either `addSuggestion` or `addSuggestions`. In the example above, you
|
||||
might choose to suggest `j` and `k` as other likely loop variable names. You
|
||||
could do that by replacing one of the places where the variable name is written
|
||||
with code like the following:
|
||||
|
||||
```dart
|
||||
editBuilder.addLinkedEdit(variableGroup, (LinkedEditBuilder linkedEditBuilder) {
|
||||
linkedEditBuilder.write(variableName);
|
||||
linkedEditBuilder.addSuggestions(['j', 'k']);
|
||||
});
|
||||
```
|
||||
|
||||
A more interesting use of this feature would be to find the names of all of the
|
||||
list-valued variables within scope and suggest those names as alternatives for
|
||||
the name of the list.
|
||||
|
||||
That said, most of the methods on `DartEditBuilder` that help you generate Dart
|
||||
code take one or more optional arguments that allow you to create linked edit
|
||||
groups for appropriate pieces of text and even to specify the suggestions for
|
||||
those groups.
|
||||
|
||||
## Post-edit Selection
|
||||
|
||||
A `SourceChange` also allows you to specify where the cursor should be placed
|
||||
after the edits are applied. There are two ways to specify this.
|
||||
|
||||
The first is by invoking the method `setSelection` on a `DartChangeBuilder`.
|
||||
The method takes a `Position`, which encapsulates an offset in a particular
|
||||
file. This can be difficult to get right because the offset is required to be
|
||||
the offset *after* all of the edits for that file have been applied.
|
||||
|
||||
The second, and easier, way is by invoking the method `selectHere` on a
|
||||
`DartEditBuilder`. This method does not require any arguments; it computes the
|
||||
offset for the position based on the edits that have previously been created.
|
||||
It does require that all of the edits that apply to text before the desired
|
||||
cursor location have been created before the method is invoked.
|
||||
|
||||
For example, if you're implementing a quick assist to insert a to-do comment at
|
||||
the cursor location, the code to create the insertion edit could look like the
|
||||
following:
|
||||
|
||||
```dart
|
||||
fileEditBuilder.addReplacement(range, (DartEditBuilder editBuilder) {
|
||||
editBuilder.write('/* TODO ');
|
||||
editBuilder.selectHere();
|
||||
editBuilder.write(' */');
|
||||
}
|
||||
```
|
||||
|
||||
This will cause the cursor to be placed between the two spaces inside the
|
||||
comment.
|
||||
|
||||
## Non-Dart Files
|
||||
|
||||
All of the classes above are subclasses of more general classes (just drop the
|
||||
prefix "Dart" from the subclass names). If you are editing files that do not
|
||||
contain Dart code, the more general classes might be a better choice. These
|
||||
classes are defined in
|
||||
`package:analyzer_plugin/utilities/change_builder/change_builder_core.dart`.
|
121
pkg/analyzer_plugin/doc/tutorial/fixes.md
Normal file
121
pkg/analyzer_plugin/doc/tutorial/fixes.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
# Providing Quick Fixes
|
||||
|
||||
A quick fix is used by clients to provide a set of possible changes to code that
|
||||
are based on diagnostics reported against the code. Quick fixes are intended to
|
||||
help users resolve the issue being reported.
|
||||
|
||||
If your plugin generates any diagnostics then you should consider providing
|
||||
support for automatically fixing those diagnostics. There is often more than one
|
||||
potential way of fixing a given problem, so it is possible for your plugin to
|
||||
provide multiple fixes for a single problem.
|
||||
|
||||
For example, if an undefined identifier is used in the code, you might return
|
||||
a fix to create an appropriate definition for the identifier. If there is a
|
||||
similar identifier that is already defined, you might also return a second fix
|
||||
to replace the undefined identifier with the defined identifier.
|
||||
|
||||
The latter example illustrates that fixes can be conditionally returned. You
|
||||
will produce a better UX if only those fixes that actually make sense in the
|
||||
given context are returned. If a lot of work is required to determine which
|
||||
fixes make sense, it is possible to improve performance by generating different
|
||||
diagnostics for the same issue, depending on the context in which the issue
|
||||
occurs.
|
||||
|
||||
In addition, fixes have a priority associated with them. The priority allows the
|
||||
client to display the fixes that are most likely to be of use closer to the top
|
||||
of the list when there are multiple fixes available.
|
||||
|
||||
## Implementation details
|
||||
|
||||
When appropriate, the analysis server will send your plugin an `edit.getFixes`
|
||||
request. The request includes the `file` and `offset` associated with the
|
||||
diagnostics for which fixes should be generated. Fixes are typically produced
|
||||
for all of the diagnostics on a given line of code. Your plugin should only
|
||||
return fixes associated with the errors that it produced earlier.
|
||||
|
||||
When an `edit.getFixes` request is received, the method `handleEditGetFixes`
|
||||
will be invoked. This method is responsible for returning a response that
|
||||
contains the available fixes.
|
||||
|
||||
The easiest way to implement this method is by adding the classes `FixesMixin`
|
||||
and `DartFixesMixin` (from `package:analyzer_plugin/plugin/fix_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: `getFixContributors`. That
|
||||
method is responsible for returning a list of `FixContributor`s. It is the fix
|
||||
contributors that produce the actual fixes. (Most plugins will only need a
|
||||
single fix contributor.)
|
||||
|
||||
To write a fix contributor, create a class that implements `FixContributor`. The
|
||||
interface defines a single method named `computeFixes`. The method has two
|
||||
arguments: a `FixesRequest` that describes the errors that should be fixed and a
|
||||
`FixCollector` through which fixes are to be added. (If you use the mixins above
|
||||
then the list of errors available through the request object will only include
|
||||
the errors for which fixes should be returned.)
|
||||
|
||||
The class `FixContributorMixin` defines a simple implementation of this method
|
||||
that captures the two arguments in fields, iterates through the errors, and
|
||||
invokes a method named `computeFixesForError` for each of the errors for which
|
||||
fixes are to be computed.
|
||||
|
||||
## Example
|
||||
|
||||
Start by creating a class that implements `FixContributor` and that mixes in the
|
||||
class `FixContributorMixin`, then implement the method `computeFixesForError`.
|
||||
This method is typically implemented by a series of `if` statements that test
|
||||
the error code and invoke individual methods that compute the actual fixes to be
|
||||
proposed. (In addition to keeping the method `computeFixesForError` shorter,
|
||||
this also allows some fixes to be used for multiple error codes.)
|
||||
|
||||
To learn about the support available for creating the edits, see
|
||||
[Creating Edits][creatingEdits].
|
||||
|
||||
For example, your contributor might look something like the following:
|
||||
|
||||
```dart
|
||||
class MyFixContributor extends Object
|
||||
with FixContributorMixin
|
||||
implements FixContributor {
|
||||
static FixKind defineComponent =
|
||||
new FixKind('defineComponent', 100, "Define a component named {0}");
|
||||
|
||||
AnalysisSession get session => request.result.session;
|
||||
|
||||
@override
|
||||
void computeFixesForError(AnalysisError error) {
|
||||
ErrorCode code = error.errorCode;
|
||||
if (code == MyErrorCode.undefinedComponent) {
|
||||
_defineComponent(error);
|
||||
_useExistingComponent(error);
|
||||
}
|
||||
}
|
||||
|
||||
void _defineComponent(AnalysisError error) {
|
||||
// TODO Get the name from the source code.
|
||||
String componentName = null;
|
||||
ChangeBuilder builder = new DartChangeBuilder(session);
|
||||
// TODO Build the edit to insert the definition of the component.
|
||||
addFix(error, defineComponent, builder, args: [componentName]);
|
||||
}
|
||||
|
||||
void _useExistingComponent(AnalysisError error) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Given a contributor like the one above, you can implement your plugin similar to
|
||||
the following:
|
||||
|
||||
```dart
|
||||
class MyPlugin extends ServerPlugin with FixesMixin, DartFixesMixin {
|
||||
// ...
|
||||
|
||||
@override
|
||||
List<FixContributor> getFixContributors(
|
||||
covariant AnalysisDriverGeneric driver) {
|
||||
return <FixContributor>[new MyFixContributor()];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[creatingEdits]: creating_edits.md
|
39
pkg/analyzer_plugin/doc/tutorial/getting_started.md
Normal file
39
pkg/analyzer_plugin/doc/tutorial/getting_started.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Getting Started
|
||||
|
||||
## Creating a Minimal Plugin
|
||||
|
||||
To implement a plugin, start by creating a simple package and create a class
|
||||
that is a subclass of `ServerPlugin`. This class will need to implement a
|
||||
constructor, three getters, and two methods. The getters provide some basic
|
||||
information about your plugin: the name and version, both of which are included
|
||||
in error messages if there is a problem encountered, and a list of glob patterns
|
||||
for the files that the plugin cares about. The methods ...
|
||||
|
||||
Here's an example of what a minimal plugin might look like.
|
||||
|
||||
```dart
|
||||
class MyPlugin extends ServerPlugin {
|
||||
MyPlugin(ResourceProvider provider) : super(provider);
|
||||
|
||||
@override
|
||||
List<String> get fileGlobsToAnalyze => <String>['**/*.dart'];
|
||||
|
||||
@override
|
||||
String get name => 'My fantastic plugin';
|
||||
|
||||
@override
|
||||
String get version => '1.0.0';
|
||||
|
||||
@override
|
||||
AnalysisDriverGeneric createAnalysisDriver(ContextRoot contextRoot) {
|
||||
// TODO: implement createAnalysisDriver
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void sendNotificationsForSubscriptions(
|
||||
Map<String, List<AnalysisService>> subscriptions) {
|
||||
// TODO: implement sendNotificationsForSubscriptions
|
||||
}
|
||||
}
|
||||
```
|
30
pkg/analyzer_plugin/doc/tutorial/tutorial.md
Normal file
30
pkg/analyzer_plugin/doc/tutorial/tutorial.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# 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.
|
||||
|
||||
## Pages
|
||||
|
||||
The following is a list of the pages available in this tutorial.
|
||||
|
||||
[Getting Started][gettingStarted] -
|
||||
How to write a minimal plugin.
|
||||
|
||||
[Creating Edits][creatingEdits] -
|
||||
How to compose the edits used in assists, fixes, and refactorings.
|
||||
|
||||
[Providing Quick Assists][assists] -
|
||||
How to provide quick assists.
|
||||
|
||||
[Providing Quick Fixes][fixes] -
|
||||
How to provide quick fixes associated with errors.
|
||||
|
||||
[Providing Code Completions][completion] -
|
||||
How to provide code completion suggestions.
|
||||
|
||||
[assists]: assists.md
|
||||
[completion]: completion.md
|
||||
[creatingEdits]: creating_edits.md
|
||||
[fixes]: fixes.md
|
||||
[gettingStarted]: getting_started.md
|
Loading…
Reference in a new issue