blob: b77e517ff385b9a4b471c5384c22dc878438f20c [file] [log] [blame] [view]
# 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 mix in the class `DartFixesMixin`, then the list of errors available
through the request object will only include the errors for which fixes should
be returned and the request will be an instance of `DartFixesRequest`, which
also has analysis results.
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 =
FixKind('defineComponent', 100, "Define a component named {0}");
AnalysisSession get session => request.result.session;
@override
Future<void> computeFixesForError(AnalysisError error) async {
ErrorCode code = error.errorCode;
if (code == MyErrorCode.undefinedComponent) {
await _defineComponent(error);
await _useExistingComponent(error);
}
}
Future<void> _defineComponent(AnalysisError error) async {
// TODO Get the name from the source code.
String componentName = null;
ChangeBuilder builder = ChangeBuilder(session: session);
await changeBuilder.addDartFileEdit(path,
(DartFileEditBuilder fileEditBuilder) {
// TODO Build the edit to insert the definition of the component.
});
addFix(error, defineComponent, builder, args: [componentName]);
}
Future<void> _useExistingComponent(AnalysisError error) async {
// ...
}
}
```
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(String path) {
return <FixContributor>[MyFixContributor()];
}
}
```
[creatingEdits]: creating_edits.md