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.
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.
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.
For example, your contributor might look something like the following:
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:
class MyPlugin extends ServerPlugin with FixesMixin, DartFixesMixin { // ... @override List<FixContributor> getFixContributors(String path) { return <FixContributor>[MyFixContributor()]; } }