33
33
import 'dart:async' ;
34
34
35
35
import 'package:analyzer/dart/analysis/results.dart' ;
36
- import 'package:analyzer/error/error .dart' ;
36
+ import 'package:analyzer_plugin/channel/channel .dart' ;
37
37
import 'package:analyzer_plugin/plugin/plugin.dart' ;
38
+ import 'package:analyzer_plugin/protocol/protocol.dart' ;
38
39
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
40
+ import 'package:analyzer_plugin/protocol/protocol_common.dart' ;
39
41
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
40
42
import 'package:analyzer_plugin/protocol/protocol_generated.dart' ;
41
43
// ignore: implementation_imports
42
44
import 'package:analyzer_plugin/src/utilities/fixes/fixes.dart' ;
43
45
import 'package:analyzer_plugin/utilities/fixes/fixes.dart' ;
44
- import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart' ;
46
+ import 'package:meta/meta.dart' ;
47
+ import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart' ;
48
+ import 'package:over_react_analyzer_plugin/src/error_filtering.dart' ;
45
49
46
50
mixin DiagnosticMixin on ServerPlugin {
47
51
List <DiagnosticContributor > getDiagnosticContributors (String path);
@@ -56,7 +60,7 @@ mixin DiagnosticMixin on ServerPlugin {
56
60
// If there is something to analyze, do so and notify the analyzer.
57
61
// Note that notifying with an empty set of errors is important as
58
62
// this clears errors if they were fixed.
59
- final generator = DiagnosticGenerator (getDiagnosticContributors (analysisResult.path));
63
+ final generator = _DiagnosticGenerator (getDiagnosticContributors (analysisResult.path));
60
64
final result = await generator.generateErrors (analysisResult);
61
65
channel.sendNotification (plugin.AnalysisErrorsParams (analysisResult.path, result.result).toNotification ());
62
66
result.sendNotifications (channel);
@@ -72,7 +76,7 @@ mixin DiagnosticMixin on ServerPlugin {
72
76
// We want request errors to propagate if they throw
73
77
final request = await _getFixesRequest (parameters);
74
78
try {
75
- final generator = DiagnosticGenerator (getDiagnosticContributors (parameters.file));
79
+ final generator = _DiagnosticGenerator (getDiagnosticContributors (parameters.file));
76
80
final result = await generator.generateFixesResponse (request);
77
81
result.sendNotifications (channel);
78
82
return result.result;
@@ -103,3 +107,99 @@ mixin DiagnosticMixin on ServerPlugin {
103
107
// return result.errors;
104
108
// }
105
109
}
110
+
111
+ /// A class that generates errors and fixes for a set of [contributors] for
112
+ /// a given result unit or fixes request.
113
+ @sealed
114
+ class _DiagnosticGenerator {
115
+ /// Initialize a newly created errors generator to use the given
116
+ /// [contributors] .
117
+ _DiagnosticGenerator (this .contributors);
118
+
119
+ /// The contributors to be used to generate the errors.
120
+ final List <DiagnosticContributor > contributors;
121
+
122
+ /// Creates a 'analysis.errors' response for the the file specified
123
+ /// by the given [unitResult] . If any of the contributors throws an exception,
124
+ /// also create a non-fatal 'plugin.error' notification.
125
+ Future <_GeneratorResult <List <AnalysisError >>> generateErrors (ResolvedUnitResult unitResult) async {
126
+ return _generateErrors (unitResult, DiagnosticCollectorImpl (shouldComputeFixes: false ));
127
+ }
128
+
129
+ /// Creates an 'edit.getFixes' response for the location in the file specified
130
+ /// by the given [request] . If any of the contributors throws an exception,
131
+ /// also create a non-fatal 'plugin.error' notification.
132
+ Future <_GeneratorResult <EditGetFixesResult >> generateFixesResponse (DartFixesRequest request) async {
133
+ // Recompute the errors and then emit the matching fixes
134
+
135
+ final collector = DiagnosticCollectorImpl (shouldComputeFixes: true );
136
+ final errorsResult = await _generateErrors (request.result, collector);
137
+ final notifications = [...errorsResult.notifications];
138
+
139
+ // Return any fixes that contain the given offset.
140
+ // TODO use request.errorsToFix instead?
141
+ final fixes = < AnalysisErrorFixes > [];
142
+ for (var i = 0 ; i < collector.errors.length; i++ ) {
143
+ final error = collector.errors[i];
144
+ final errorStart = error.location.offset;
145
+ final errorEnd = errorStart + error.location.length;
146
+
147
+ // `<=` because we do want the end to be inclusive (you should get
148
+ // the fix when your cursor is on the tail end of the error).
149
+ if (request.offset >= errorStart && request.offset <= errorEnd) {
150
+ fixes.add (AnalysisErrorFixes (
151
+ error,
152
+ fixes: [collector.fixes[i]],
153
+ ));
154
+ }
155
+ }
156
+
157
+ return _GeneratorResult (EditGetFixesResult (fixes), notifications);
158
+ }
159
+
160
+ Future <_GeneratorResult <List <AnalysisError >>> _generateErrors (
161
+ ResolvedUnitResult unitResult, DiagnosticCollectorImpl collector) async {
162
+ final notifications = < Notification > [];
163
+ for (final contributor in contributors) {
164
+ try {
165
+ await contributor.computeErrors (unitResult, collector);
166
+ } catch (exception, stackTrace) {
167
+ notifications.add (PluginErrorParams (false , exception.toString (), stackTrace.toString ()).toNotification ());
168
+ }
169
+ }
170
+
171
+ // The analyzer normally filters out errors with "ignore" comments,
172
+ // but it doesn't do it for plugin errors, so we need to do that here.
173
+ final lineInfo = unitResult.unit.lineInfo;
174
+ final filteredErrors =
175
+ filterIgnores (collector.errors, lineInfo, () => IgnoreInfo .calculateIgnores (unitResult.content, lineInfo));
176
+
177
+ return _GeneratorResult (filteredErrors, notifications);
178
+ }
179
+ }
180
+
181
+ /// The result produced by a generator.
182
+ ///
183
+ /// Adapted from `GeneratorResult` in analyzer_plugin, but with less restrictive typing.
184
+ @sealed
185
+ class _GeneratorResult <T > {
186
+ /// The result to be sent to the server, or `null` if there is no response, as
187
+ /// when the generator is generating a notification.
188
+ final T result;
189
+
190
+ /// The notifications that should be sent to the server. The list will be empty
191
+ /// if there are no notifications.
192
+ final List <Notification > notifications;
193
+
194
+ /// Initialize a newly created generator result with the given [result] and
195
+ /// [notifications] .
196
+ _GeneratorResult (this .result, this .notifications);
197
+
198
+ /// Use the given communications [channel] to send the notifications to the
199
+ /// server.
200
+ void sendNotifications (PluginCommunicationChannel channel) {
201
+ for (final notification in notifications) {
202
+ channel.sendNotification (notification);
203
+ }
204
+ }
205
+ }
0 commit comments