44
55import 'package:analysis_server/lsp_protocol/protocol_generated.dart' ;
66import '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' ;
97import 'package:analysis_server/src/lsp/constants.dart' ;
108import 'package:analysis_server/src/lsp/handlers/handlers.dart' ;
119import 'package:analysis_server/src/lsp/lsp_analysis_server.dart' ;
1210import 'package:analysis_server/src/lsp/mapping.dart' ;
11+ import 'package:analysis_server/src/plugin/plugin_manager.dart' ;
1312import 'package:analysis_server/src/protocol_server.dart' hide Position;
1413import 'package:analysis_server/src/services/correction/assist.dart' ;
1514import 'package:analysis_server/src/services/correction/assist_internal.dart' ;
@@ -22,13 +21,36 @@ import 'package:analyzer/dart/analysis/results.dart';
2221import 'package:analyzer/dart/analysis/session.dart'
2322 show InconsistentAnalysisException;
2423import 'package:analyzer/dart/element/element.dart' ;
25- import 'package:analyzer/error/error.dart' ;
2624import 'package:analyzer/src/dart/ast/utilities.dart' ;
2725import '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;
2828import 'package:collection/collection.dart' show groupBy;
2929
3030class CodeActionHandler extends MessageHandler <CodeActionParams ,
3131 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+
3254 CodeActionHandler (LspAnalysisServer server) : super (server);
3355
3456 @override
@@ -147,25 +169,25 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
147169 /// version of each document being modified so it's important to call this
148170 /// immediately after computing edits to ensure the document is not modified
149171 /// before the version number is read.
150- CodeAction _createAssistAction (Assist assist ) {
172+ CodeAction _createAssistAction (SourceChange change ) {
151173 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 ),
154176 diagnostics: const [],
155- edit: createWorkspaceEdit (server, assist. change),
177+ edit: createWorkspaceEdit (server, change),
156178 );
157179 }
158180
159181 /// Creates a CodeAction to apply this fix. Note: This code will fetch the
160182 /// version of each document being modified so it's important to call this
161183 /// immediately after computing edits to ensure the document is not modified
162184 /// before the version number is read.
163- CodeAction _createFixAction (Fix fix , Diagnostic diagnostic) {
185+ CodeAction _createFixAction (SourceChange change , Diagnostic diagnostic) {
164186 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 ),
167189 diagnostics: [diagnostic],
168- edit: createWorkspaceEdit (server, fix. change),
190+ edit: createWorkspaceEdit (server, change),
169191 );
170192 }
171193
@@ -222,6 +244,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
222244 Future <List <Either2 <Command , CodeAction >>> _getAssistActions (
223245 bool Function (CodeActionKind ? ) shouldIncludeKind,
224246 bool supportsLiteralCodeActions,
247+ String path,
225248 Range range,
226249 int offset,
227250 int length,
@@ -236,13 +259,28 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
236259 length,
237260 );
238261 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
246284 .where ((action) => shouldIncludeKind (action.kind))
247285 .map ((action) => Either2 <Command , CodeAction >.t2 (action))
248286 .toList ();
@@ -268,11 +306,11 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
268306 final results = await Future .wait ([
269307 _getSourceActions (shouldIncludeKind, supportsLiterals,
270308 supportsWorkspaceApplyEdit, path),
271- _getAssistActions (
272- shouldIncludeKind, supportsLiterals, range, offset, length, unit),
309+ _getAssistActions (shouldIncludeKind, supportsLiterals, path, range,
310+ offset, length, unit),
273311 _getRefactorActions (
274312 shouldIncludeKind, supportsLiterals, path, offset, length, unit),
275- _getFixActions (shouldIncludeKind, supportsLiterals,
313+ _getFixActions (shouldIncludeKind, supportsLiterals, path, offset,
276314 supportedDiagnosticTags, range, unit),
277315 ]);
278316 final flatResults = results.expand ((x) => x).toList ();
@@ -283,22 +321,23 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
283321 Future <List <Either2 <Command , CodeAction >>> _getFixActions (
284322 bool Function (CodeActionKind ? ) shouldIncludeKind,
285323 bool supportsLiteralCodeActions,
324+ String path,
325+ int offset,
286326 Set <DiagnosticTag > supportedDiagnosticTags,
287327 Range range,
288328 ResolvedUnitResult unit,
289329 ) 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).
290334 final lineInfo = unit.lineInfo;
291335 final codeActions = < CodeAction > [];
292336 final fixContributor = DartFixContributor ();
293337
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);
301339
340+ try {
302341 for (final error in unit.errors) {
303342 // Server lineNumber is one-based so subtract one.
304343 var errorLine = lineInfo.getLocation (error.offset).lineNumber - 1 ;
@@ -317,22 +356,44 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
317356 }, extensionCache: server.getExtensionCacheFor (unit));
318357 final fixes = await fixContributor.computeFixes (context);
319358 if (fixes.isNotEmpty) {
320- fixes.sort (Fix .SORT_BY_RELEVANCE );
321-
322359 final diagnostic = toDiagnostic (
323360 unit,
324361 error,
325362 supportedTags: supportedDiagnosticTags,
326- clientSupportsCodeDescription:
327- server.clientCapabilities? .diagnosticCodeDescription ?? false ,
363+ clientSupportsCodeDescription: clientSupportsCodeDescription,
328364 );
329365 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+ }),
331371 );
332372 }
333373 }
334374
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+
335395 final dedupedActions = _dedupeActions (codeActions, range.start);
396+ dedupedActions.sort (_codeActionComparator);
336397
337398 return dedupedActions
338399 .where ((action) => shouldIncludeKind (action.kind))
@@ -346,6 +407,61 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
346407 }
347408 }
348409
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+
349465 Future <List <Either2 <Command , CodeAction >>> _getRefactorActions (
350466 bool Function (CodeActionKind ) shouldIncludeKind,
351467 bool supportsLiteralCodeActions,
0 commit comments