blob: c211d83b2503c3af9aba3f075b6d0ed93fce820d [file] [log] [blame] [view] [edit]
# Writing assists
This package gives analyzer plugin authors the ability to write "quick assists,"
which can make local changes to source code, like small refactorings, from a
developer's IDE. This document describes briefly how to write such an assist,
and how to register it in an analyzer plugin.
## The ResolvedCorrectionProducer class
A quick assist is specified by subclassing the ResolvedCorrectionProducer class.
Here is our example:
```dart
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
class RemoveAwait extends ResolvedCorrectionProducer {
static const _removeAwaitKind = AssistKind(
'dart.assist.removeAwait', 30 /* default */, "Remove the 'await' keyword");
RemoveAwait({required super.context});
@override
CorrectionApplicability get applicability =>
CorrectionApplicability.singleLocation;
@override
AssistKind get assistKind => _removeAwaitKind;
@override
Future<void> compute(ChangeBuilder builder) async {
var awaitExpression = node;
if (awaitExpression is AwaitExpression) {
var awaitToken = awaitExpression.awaitKeyword;
await builder.addDartFileEdit(file, (builder) {
builder.addDeletion(range.startStart(awaitToken, awaitToken.next!));
});
}
}
}
```
Let's look at each declaration individually:
* `class RemoveAwait` - A quick assist is a class that extends
`ResolvedCorrectionProducer`. The name of the base class indicates that an
instance of this class can produce "corrections" (a set of edits) for a
resolved library.
* `static const _removeAwaitKind = AssistKind(...)` - Each quick assist must
have an associated `AssistKind` which has a unique `id`
(`'dart.assist.removeAwait'`), a priority (`DartFixKindPriority.standard` is a
fine default), and a message which is displayed in the IDE
(`"Remove the 'await' keyword"`).
* `RemoveAwait({required super.context});` - A standard constructor that accepts
a `CorrectionProducerContext` and passes it up to the super-constructor.
* `CorrectionApplicability get applicability =>` - the applicability field
describes how widely an assist can be applied safely and sensibly. Currently,
only `CorrectionApplicability.singleLocation` should be used.
* `AssistKind get assistKind => _removeAwaitKind;` - each instance of this class
can refer to the static field for it's `assistKind`.
* `Future<void> compute(ChangeBuilder builder)` - This method is called when the
client has requested quick assists at a specific location, and we want a
possible correction from this correction producer. This is the code that
looks at the `node` field and surrounding code and determines what correction
to offer, if any.
* `await builder.addDartFileEdit(...)` - Once we have determined that we want
to offer an assist, we call this method, and specify code deletions,
insertions, and/or replacements inside the callback function. If there are
cases where this correction producer will not offer any quick assists (such
as the source code having certain properties), then those cases should be
checked so that we don't call this method in such cases.
* `builder.addDeletion(...)` - For this assist (removing an `await` keyword),
we can use `addDeletion` to specify a range of source code text to delete.
The `DartFileEditBuilder` class has many utilities for adding various edits.
Writing a quick assist can be non-trivial, even for changes which are
conceptually simple. It may be helpful to see examples that are similar to a
desired assist. See the [assists that are offered by Dart Analysis
Server][existing-assists] for hundreds of examples.
Instances of the correction producer class are short-lived, and they can contain
state related to the source-code-under-analysis. Indeed, the
`CorrectionProducerContext`, which is passed into the constructor, and available
as a field in the super-class, contains information specific to the
code-under-analysis.
## Registering a quick assist
In order for a quick assist to be used in an analyzer plugin, it must be
registered. Register the quick assist's constructor inside a plugin's
`register` method:
```dart
import 'package:analysis_server_plugin/plugin.dart';
import 'package:analysis_server_plugin/registry.dart';
final plugin = SimplePlugin();
class SimplePlugin extends Plugin {
@override
String get name => 'Simple plugin';
@override
void register(PluginRegistry registry) {
registry.registerAssist(RemoveAwait.new);
}
}
```
Instances of correction producers contain state related to the specific
source-code-under-analysis, which is why the constructor is given here,
instead of a long-lived instance.
See [writing a plugin][] for information about the `Plugin` class.
[writing rules]: https://github.com/dart-lang/sdk/blob/main/pkg/analysis_server_plugin/doc/writing_rules.md
[existing-assists]: https://github.com/dart-lang/sdk/tree/main/pkg/analysis_server/lib/src/services/correction/dart
[writing a plugin]: https://github.com/dart-lang/sdk/blob/main/pkg/analysis_server_plugin/doc/writing_rules.md