4
4
5
5
import 'package:analysis_server/lsp_protocol/protocol_generated.dart' ;
6
6
import 'package:analysis_server/lsp_protocol/protocol_special.dart' ;
7
- import 'package:analysis_server/plugin/edit/assist/assist_core.dart' ;
8
- import 'package:analysis_server/plugin/edit/fix/fix_core.dart' ;
9
7
import 'package:analysis_server/src/lsp/constants.dart' ;
10
8
import 'package:analysis_server/src/lsp/handlers/handlers.dart' ;
11
9
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart' ;
12
10
import 'package:analysis_server/src/lsp/mapping.dart' ;
11
+ import 'package:analysis_server/src/plugin/plugin_manager.dart' ;
13
12
import 'package:analysis_server/src/protocol_server.dart' hide Position;
14
13
import 'package:analysis_server/src/services/correction/assist.dart' ;
15
14
import 'package:analysis_server/src/services/correction/assist_internal.dart' ;
@@ -22,13 +21,36 @@ import 'package:analyzer/dart/analysis/results.dart';
22
21
import 'package:analyzer/dart/analysis/session.dart'
23
22
show InconsistentAnalysisException;
24
23
import 'package:analyzer/dart/element/element.dart' ;
25
- import 'package:analyzer/error/error.dart' ;
26
24
import 'package:analyzer/src/dart/ast/utilities.dart' ;
27
25
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
26
+ import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
27
+ import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
28
28
import 'package:collection/collection.dart' show groupBy;
29
29
30
30
class CodeActionHandler extends MessageHandler <CodeActionParams ,
31
31
List <Either2 <Command , CodeAction >>> {
32
+ // Because server+plugin results are different types and we lose
33
+ // priorites when converting them to CodeActions, store the priorities
34
+ // against each action in an expando. This avoids wrapping CodeActions in
35
+ // another wrapper class (since we can't modify the LSP-spec-generated
36
+ // CodeAction class).
37
+ final codeActionPriorities = Expando <int >();
38
+
39
+ /// A comparator that can be used to sort [CodeActions] s using priorties
40
+ /// in [codeActionPriorities] . The highest number priority will be sorted
41
+ /// before lower number priorityies. Items with the same relevance are sorted
42
+ /// alphabetically by their title.
43
+ late final Comparator <CodeAction > _codeActionComparator =
44
+ (CodeAction a, CodeAction b) {
45
+ // We should never be sorting actions without priorities.
46
+ final aPriority = codeActionPriorities[a] ?? 0 ;
47
+ final bPriority = codeActionPriorities[b] ?? 0 ;
48
+ if (aPriority != bPriority) {
49
+ return bPriority - aPriority;
50
+ }
51
+ return a.title.compareTo (b.title);
52
+ };
53
+
32
54
CodeActionHandler (LspAnalysisServer server) : super (server);
33
55
34
56
@override
@@ -147,25 +169,25 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
147
169
/// version of each document being modified so it's important to call this
148
170
/// immediately after computing edits to ensure the document is not modified
149
171
/// before the version number is read.
150
- CodeAction _createAssistAction (Assist assist ) {
172
+ CodeAction _createAssistAction (SourceChange change ) {
151
173
return CodeAction (
152
- title: assist. change.message,
153
- kind: toCodeActionKind (assist. change.id, CodeActionKind .Refactor ),
174
+ title: change.message,
175
+ kind: toCodeActionKind (change.id, CodeActionKind .Refactor ),
154
176
diagnostics: const [],
155
- edit: createWorkspaceEdit (server, assist. change),
177
+ edit: createWorkspaceEdit (server, change),
156
178
);
157
179
}
158
180
159
181
/// Creates a CodeAction to apply this fix. Note: This code will fetch the
160
182
/// version of each document being modified so it's important to call this
161
183
/// immediately after computing edits to ensure the document is not modified
162
184
/// before the version number is read.
163
- CodeAction _createFixAction (Fix fix , Diagnostic diagnostic) {
185
+ CodeAction _createFixAction (SourceChange change , Diagnostic diagnostic) {
164
186
return CodeAction (
165
- title: fix. change.message,
166
- kind: toCodeActionKind (fix. change.id, CodeActionKind .QuickFix ),
187
+ title: change.message,
188
+ kind: toCodeActionKind (change.id, CodeActionKind .QuickFix ),
167
189
diagnostics: [diagnostic],
168
- edit: createWorkspaceEdit (server, fix. change),
190
+ edit: createWorkspaceEdit (server, change),
169
191
);
170
192
}
171
193
@@ -222,6 +244,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
222
244
Future <List <Either2 <Command , CodeAction >>> _getAssistActions (
223
245
bool Function (CodeActionKind ? ) shouldIncludeKind,
224
246
bool supportsLiteralCodeActions,
247
+ String path,
225
248
Range range,
226
249
int offset,
227
250
int length,
@@ -236,13 +259,28 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
236
259
length,
237
260
);
238
261
final processor = AssistProcessor (context);
239
- final assists = await processor.compute ();
240
- assists.sort (Assist .SORT_BY_RELEVANCE );
241
-
242
- final assistActions =
243
- _dedupeActions (assists.map (_createAssistAction), range.start);
244
-
245
- return assistActions
262
+ final serverFuture = processor.compute ();
263
+ final pluginFuture = _getPluginAssistChanges (path, offset, length);
264
+
265
+ final assists = await serverFuture;
266
+ final pluginChanges = await pluginFuture;
267
+
268
+ final codeActions = < CodeAction > [];
269
+ codeActions.addAll (assists.map ((assist) {
270
+ final action = _createAssistAction (assist.change);
271
+ codeActionPriorities[action] = assist.kind.priority;
272
+ return action;
273
+ }));
274
+ codeActions.addAll (pluginChanges.map ((change) {
275
+ final action = _createAssistAction (change.change);
276
+ codeActionPriorities[action] = change.priority;
277
+ return action;
278
+ }));
279
+
280
+ final dedupedCodeActions = _dedupeActions (codeActions, range.start);
281
+ dedupedCodeActions.sort (_codeActionComparator);
282
+
283
+ return dedupedCodeActions
246
284
.where ((action) => shouldIncludeKind (action.kind))
247
285
.map ((action) => Either2 <Command , CodeAction >.t2 (action))
248
286
.toList ();
@@ -268,11 +306,11 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
268
306
final results = await Future .wait ([
269
307
_getSourceActions (shouldIncludeKind, supportsLiterals,
270
308
supportsWorkspaceApplyEdit, path),
271
- _getAssistActions (
272
- shouldIncludeKind, supportsLiterals, range, offset, length, unit),
309
+ _getAssistActions (shouldIncludeKind, supportsLiterals, path, range,
310
+ offset, length, unit),
273
311
_getRefactorActions (
274
312
shouldIncludeKind, supportsLiterals, path, offset, length, unit),
275
- _getFixActions (shouldIncludeKind, supportsLiterals,
313
+ _getFixActions (shouldIncludeKind, supportsLiterals, path, offset,
276
314
supportedDiagnosticTags, range, unit),
277
315
]);
278
316
final flatResults = results.expand ((x) => x).toList ();
@@ -283,22 +321,23 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
283
321
Future <List <Either2 <Command , CodeAction >>> _getFixActions (
284
322
bool Function (CodeActionKind ? ) shouldIncludeKind,
285
323
bool supportsLiteralCodeActions,
324
+ String path,
325
+ int offset,
286
326
Set <DiagnosticTag > supportedDiagnosticTags,
287
327
Range range,
288
328
ResolvedUnitResult unit,
289
329
) async {
330
+ final clientSupportsCodeDescription =
331
+ server.clientCapabilities? .diagnosticCodeDescription ?? false ;
332
+ // TODO(dantup): We may be missing fixes for pubspec, analysis_options,
333
+ // android manifests (see _computeServerErrorFixes in EditDomainHandler).
290
334
final lineInfo = unit.lineInfo;
291
335
final codeActions = < CodeAction > [];
292
336
final fixContributor = DartFixContributor ();
293
337
294
- try {
295
- final errorCodeCounts = < ErrorCode , int > {};
296
- // Count the errors by code so we know whether to include a fix-all.
297
- for (final error in unit.errors) {
298
- errorCodeCounts[error.errorCode] =
299
- (errorCodeCounts[error.errorCode] ?? 0 ) + 1 ;
300
- }
338
+ final pluginFuture = _getPluginFixActions (unit, offset);
301
339
340
+ try {
302
341
for (final error in unit.errors) {
303
342
// Server lineNumber is one-based so subtract one.
304
343
var errorLine = lineInfo.getLocation (error.offset).lineNumber - 1 ;
@@ -317,22 +356,44 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
317
356
}, extensionCache: server.getExtensionCacheFor (unit));
318
357
final fixes = await fixContributor.computeFixes (context);
319
358
if (fixes.isNotEmpty) {
320
- fixes.sort (Fix .SORT_BY_RELEVANCE );
321
-
322
359
final diagnostic = toDiagnostic (
323
360
unit,
324
361
error,
325
362
supportedTags: supportedDiagnosticTags,
326
- clientSupportsCodeDescription:
327
- server.clientCapabilities? .diagnosticCodeDescription ?? false ,
363
+ clientSupportsCodeDescription: clientSupportsCodeDescription,
328
364
);
329
365
codeActions.addAll (
330
- fixes.map ((fix) => _createFixAction (fix, diagnostic)),
366
+ fixes.map ((fix) {
367
+ final action = _createFixAction (fix.change, diagnostic);
368
+ codeActionPriorities[action] = fix.kind.priority;
369
+ return action;
370
+ }),
331
371
);
332
372
}
333
373
}
334
374
375
+ Diagnostic pluginErrorToDiagnostic (AnalysisError error) {
376
+ return pluginToDiagnostic (
377
+ (_) => lineInfo,
378
+ error,
379
+ supportedTags: supportedDiagnosticTags,
380
+ clientSupportsCodeDescription: clientSupportsCodeDescription,
381
+ );
382
+ }
383
+
384
+ final pluginFixes = await pluginFuture;
385
+ final pluginFixActions = pluginFixes.expand (
386
+ (fix) => fix.fixes.map ((fixChange) {
387
+ final action = _createFixAction (
388
+ fixChange.change, pluginErrorToDiagnostic (fix.error));
389
+ codeActionPriorities[action] = fixChange.priority;
390
+ return action;
391
+ }),
392
+ );
393
+ codeActions.addAll (pluginFixActions);
394
+
335
395
final dedupedActions = _dedupeActions (codeActions, range.start);
396
+ dedupedActions.sort (_codeActionComparator);
336
397
337
398
return dedupedActions
338
399
.where ((action) => shouldIncludeKind (action.kind))
@@ -346,6 +407,61 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
346
407
}
347
408
}
348
409
410
+ Future <Iterable <plugin.PrioritizedSourceChange >> _getPluginAssistChanges (
411
+ String path, int offset, int length) async {
412
+ final requestParams = plugin.EditGetAssistsParams (path, offset, length);
413
+ final driver = server.getAnalysisDriver (path);
414
+
415
+ Map <PluginInfo , Future <plugin.Response >> pluginFutures;
416
+ if (driver == null ) {
417
+ pluginFutures = < PluginInfo , Future <plugin.Response >> {};
418
+ } else {
419
+ pluginFutures = server.pluginManager.broadcastRequest (
420
+ requestParams,
421
+ contextRoot: driver.analysisContext! .contextRoot,
422
+ );
423
+ }
424
+
425
+ final pluginChanges = < plugin.PrioritizedSourceChange > [];
426
+ final responses =
427
+ await waitForResponses (pluginFutures, requestParameters: requestParams);
428
+
429
+ for (final response in responses) {
430
+ final result = plugin.EditGetAssistsResult .fromResponse (response);
431
+ pluginChanges.addAll (result.assists);
432
+ }
433
+
434
+ return pluginChanges;
435
+ }
436
+
437
+ Future <Iterable <plugin.AnalysisErrorFixes >> _getPluginFixActions (
438
+ ResolvedUnitResult unit, int offset) async {
439
+ final file = unit.path;
440
+ final requestParams = plugin.EditGetFixesParams (file, offset);
441
+ final driver = server.getAnalysisDriver (file);
442
+
443
+ Map <PluginInfo , Future <plugin.Response >> pluginFutures;
444
+ if (driver == null ) {
445
+ pluginFutures = < PluginInfo , Future <plugin.Response >> {};
446
+ } else {
447
+ pluginFutures = server.pluginManager.broadcastRequest (
448
+ requestParams,
449
+ contextRoot: driver.analysisContext! .contextRoot,
450
+ );
451
+ }
452
+
453
+ final pluginFixes = < plugin.AnalysisErrorFixes > [];
454
+ final responses =
455
+ await waitForResponses (pluginFutures, requestParameters: requestParams);
456
+
457
+ for (final response in responses) {
458
+ final result = plugin.EditGetFixesResult .fromResponse (response);
459
+ pluginFixes.addAll (result.fixes);
460
+ }
461
+
462
+ return pluginFixes;
463
+ }
464
+
349
465
Future <List <Either2 <Command , CodeAction >>> _getRefactorActions (
350
466
bool Function (CodeActionKind ) shouldIncludeKind,
351
467
bool supportsLiteralCodeActions,
0 commit comments