Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2e22cdf

Browse files
author
Dart CI
committed
Version 2.14.0-369.0.dev
Merge commit '2a99af9bc93e8fec4f4746168803fdeaff239c7b' into 'dev'
2 parents fa72449 + 2a99af9 commit 2e22cdf

File tree

14 files changed

+385
-121
lines changed

14 files changed

+385
-121
lines changed

pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart

Lines changed: 149 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44

55
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
66
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';
97
import 'package:analysis_server/src/lsp/constants.dart';
108
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
119
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
1210
import 'package:analysis_server/src/lsp/mapping.dart';
11+
import 'package:analysis_server/src/plugin/plugin_manager.dart';
1312
import 'package:analysis_server/src/protocol_server.dart' hide Position;
1413
import 'package:analysis_server/src/services/correction/assist.dart';
1514
import 'package:analysis_server/src/services/correction/assist_internal.dart';
@@ -22,13 +21,36 @@ import 'package:analyzer/dart/analysis/results.dart';
2221
import 'package:analyzer/dart/analysis/session.dart'
2322
show InconsistentAnalysisException;
2423
import 'package:analyzer/dart/element/element.dart';
25-
import 'package:analyzer/error/error.dart';
2624
import 'package:analyzer/src/dart/ast/utilities.dart';
2725
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;
2828
import 'package:collection/collection.dart' show groupBy;
2929

3030
class 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,

pkg/analysis_server/lib/src/lsp/mapping.dart

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,19 +866,30 @@ lsp.ClosingLabel toClosingLabel(
866866
range: toRange(lineInfo, label.offset, label.length),
867867
label: label.label);
868868

869-
lsp.CodeActionKind toCodeActionKind(String? id, lsp.CodeActionKind fallback) {
869+
/// Converts [id] to a [CodeActionKind] using [fallbackOrPrefix] as a fallback
870+
/// or a prefix if the ID is not already a fix/refactor.
871+
lsp.CodeActionKind toCodeActionKind(
872+
String? id, lsp.CodeActionKind fallbackOrPrefix) {
870873
if (id == null) {
871-
return fallback;
874+
return fallbackOrPrefix;
872875
}
873876
// Dart fixes and assists start with "dart.assist." and "dart.fix." but in LSP
874877
// we want to use the predefined prefixes for CodeActions.
875-
final newId = id
878+
var newId = id
876879
.replaceAll('dart.assist', lsp.CodeActionKind.Refactor.toString())
877880
.replaceAll('dart.fix', lsp.CodeActionKind.QuickFix.toString())
878881
.replaceAll(
879882
'analysisOptions.assist', lsp.CodeActionKind.Refactor.toString())
880883
.replaceAll(
881884
'analysisOptions.fix', lsp.CodeActionKind.QuickFix.toString());
885+
886+
// If the ID does not start with either of the kinds above, prefix it as
887+
// it will be an unqualified ID from a plugin.
888+
if (!newId.startsWith(lsp.CodeActionKind.Refactor.toString()) &&
889+
!newId.startsWith(lsp.CodeActionKind.QuickFix.toString())) {
890+
newId = '$fallbackOrPrefix.$newId';
891+
}
892+
882893
return lsp.CodeActionKind(newId);
883894
}
884895

pkg/analysis_server/lib/src/utilities/mocks.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class TestPluginManager implements PluginManager {
146146
plugin.AnalysisUpdateContentParams? analysisUpdateContentParams;
147147
plugin.RequestParams? broadcastedRequest;
148148
Map<PluginInfo, Future<plugin.Response>>? broadcastResults;
149+
Map<PluginInfo, Future<plugin.Response>>? Function(plugin.RequestParams)?
150+
handleRequest;
149151

150152
@override
151153
List<PluginInfo> plugins = [];
@@ -192,7 +194,9 @@ class TestPluginManager implements PluginManager {
192194
plugin.RequestParams params,
193195
{analyzer.ContextRoot? contextRoot}) {
194196
broadcastedRequest = params;
195-
return broadcastResults ?? <PluginInfo, Future<plugin.Response>>{};
197+
return handleRequest?.call(params) ??
198+
broadcastResults ??
199+
<PluginInfo, Future<plugin.Response>>{};
196200
}
197201

198202
@override

0 commit comments

Comments
 (0)