| # 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 |