blob: 2be0e4df44f8999e1df02bc230fd3cc7d5b5c05a [file] [log] [blame] [view]
# 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.
If you mix in the class `DartAssistsMixin`, then the request will be an instance
of `DartAssistRequest`, which also has analysis results.
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 =
AssistKind('wrapInIf', 100, "Wrap in an 'if' statement");
DartAssistRequest request;
AssistCollector collector;
AnalysisSession get session => request.result.session;
@override
Future<void> computeAssists(DartAssistRequest request, AssistCollector collector) async {
this.request = request;
this.collector = collector;
await _wrapInIf();
await _wrapInWhile();
// ...
}
Future<void> _wrapInIf() async {
ChangeBuilder builder = ChangeBuilder(session: session);
await changeBuilder.addDartFileEdit(path,
(DartFileEditBuilder fileEditBuilder) {
// TODO Build the edit to wrap the selection in a 'if' statement.
});
addAssist(wrapInIf, builder);
}
Future<void> _wrapInWhile() async {
// ...
}
}
```
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(String path) {
return <AssistContributor>[MyAssistContributor()];
}
}
```
[creatingEdits]: creating_edits.md