Skip to content

Commit 8b40a7b

Browse files
DanTupCommit Bot
authored and
Commit Bot
committed
[analysis_server] Make Sort Members + Organize Imports fail silently if invoked automatically
Previously, Organise Imports always failed silently (since it was often run on-save). Sort Members had not been given the same treatment (but often now runs on-save). This uses a new LSP 3.17 field to know when the command was run automatically, so both are now consistent, showing errors if invoked manually and failing silently if run automatically. Change-Id: I48e8380fcee4e54d0f4dc3c177c9ae2362627efb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247542 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 1861eac commit 8b40a7b

File tree

5 files changed

+96
-21
lines changed

5 files changed

+96
-21
lines changed

pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class OrganizeImportsCommandHandler extends SimpleEditCommandHandler {
3030
// modified since.
3131
final path = parameters['path'] as String;
3232
final docIdentifier = server.getVersionedDocumentIdentifier(path);
33+
final autoTriggered = (parameters['autoTriggered'] as bool?) ?? false;
3334

3435
final result = await requireResolvedUnit(path);
3536

@@ -42,13 +43,15 @@ class OrganizeImportsCommandHandler extends SimpleEditCommandHandler {
4243
final unit = result.unit;
4344

4445
if (hasScanParseErrors(result.errors)) {
45-
// It's not uncommon for editors to run this command automatically on-save
46-
// so if the file in in an invalid state it's better to fail silently
47-
// than trigger errors (VS Code recently started showing popups when
48-
// LSP requests return errors).
49-
server.instrumentationService.logInfo(
50-
'Unable to $commandName because the file contains parse errors');
51-
return success(null);
46+
if (autoTriggered) {
47+
return success(null);
48+
}
49+
return ErrorOr.error(ResponseError(
50+
code: ServerErrorCodes.FileHasErrors,
51+
message:
52+
'Unable to $commandName because the file contains parse errors',
53+
data: path,
54+
));
5255
}
5356

5457
final organizer = ImportOrganizer(code, unit, result.errors);

pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class SortMembersCommandHandler extends SimpleEditCommandHandler {
3131
// modified since.
3232
final path = parameters['path'] as String;
3333
final docIdentifier = server.getVersionedDocumentIdentifier(path);
34+
final autoTriggered = (parameters['autoTriggered'] as bool?) ?? false;
3435

3536
var session = await server.getAnalysisSession(path);
3637
final result = session?.getParsedUnit(path);
@@ -40,6 +41,9 @@ class SortMembersCommandHandler extends SimpleEditCommandHandler {
4041
}
4142

4243
if (result is! ParsedUnitResult) {
44+
if (autoTriggered) {
45+
return success(null);
46+
}
4347
return ErrorOr.error(ResponseError(
4448
code: ServerErrorCodes.FileNotAnalyzed,
4549
message: '$commandName is only available for analyzed files',
@@ -50,6 +54,9 @@ class SortMembersCommandHandler extends SimpleEditCommandHandler {
5054
final unit = result.unit;
5155

5256
if (hasScanParseErrors(result.errors)) {
57+
if (autoTriggered) {
58+
return success(null);
59+
}
5360
return ErrorOr.error(ResponseError(
5461
code: ServerErrorCodes.FileHasErrors,
5562
message:

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
161161
offset,
162162
length,
163163
unit,
164+
params.context.triggerKind,
164165
),
165166
);
166167
});
@@ -344,6 +345,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
344345
int offset,
345346
int length,
346347
ResolvedUnitResult unit,
348+
CodeActionTriggerKind? triggerKind,
347349
) async {
348350
final docIdentifier = server.getVersionedDocumentIdentifier(path);
349351

@@ -352,7 +354,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
352354
performance.runAsync(
353355
'_getSourceActions',
354356
(_) => _getSourceActions(shouldIncludeKind, supportsLiterals,
355-
supportsWorkspaceApplyEdit, path),
357+
supportsWorkspaceApplyEdit, path, triggerKind),
356358
),
357359
// Assists go under the Refactor CodeActionKind so check that here.
358360
if (shouldIncludeAnyOfKind(CodeActionKind.Refactor))
@@ -644,6 +646,7 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
644646
bool supportsLiteralCodeActions,
645647
bool supportsApplyEdit,
646648
String path,
649+
CodeActionTriggerKind? triggerKind,
647650
) async {
648651
// The source actions supported are only valid for Dart files.
649652
var pathContext = server.resourceProvider.pathContext;
@@ -657,6 +660,8 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
657660
return const [];
658661
}
659662

663+
final autoTriggered = triggerKind == CodeActionTriggerKind.Automatic;
664+
660665
return [
661666
if (shouldIncludeKind(DartCodeActionKind.SortMembers))
662667
_commandOrCodeAction(
@@ -666,7 +671,10 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
666671
title: 'Sort Members',
667672
command: Commands.sortMembers,
668673
arguments: [
669-
{'path': path}
674+
{
675+
'path': path,
676+
if (autoTriggered) 'autoTriggered': true,
677+
}
670678
]),
671679
),
672680
if (shouldIncludeKind(CodeActionKind.SourceOrganizeImports))
@@ -677,7 +685,10 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
677685
title: 'Organize Imports',
678686
command: Commands.organizeImports,
679687
arguments: [
680-
{'path': path}
688+
{
689+
'path': path,
690+
if (autoTriggered) 'autoTriggered': true,
691+
}
681692
]),
682693
),
683694
if (shouldIncludeKind(DartCodeActionKind.FixAll))
@@ -688,7 +699,10 @@ class CodeActionHandler extends MessageHandler<CodeActionParams,
688699
title: 'Fix All',
689700
command: Commands.fixAll,
690701
arguments: [
691-
{'path': path}
702+
{
703+
'path': path,
704+
if (autoTriggered) 'autoTriggered': true,
705+
}
692706
],
693707
),
694708
),

pkg/analysis_server/test/lsp/code_actions_source_test.dart

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,30 @@ int minified(int x, int y) => min(x, y);
139139
);
140140
}
141141

142-
Future<void> test_failsSilentlyIfFileHasErrors() async {
142+
Future<void> test_fileHasErrors_failsSilentlyForAutomatic() async {
143+
final content = 'invalid dart code';
144+
newFile(mainFilePath, content);
145+
await initialize(
146+
workspaceCapabilities:
147+
withApplyEditSupport(emptyWorkspaceClientCapabilities));
148+
149+
final codeActions = await getCodeActions(
150+
mainFileUri.toString(),
151+
triggerKind: CodeActionTriggerKind.Automatic,
152+
);
153+
final codeAction = findCommand(codeActions, Commands.organizeImports)!;
154+
155+
final command = codeAction.map(
156+
(command) => command,
157+
(codeAction) => codeAction.command!,
158+
);
159+
160+
// Expect a valid null result.
161+
final response = await executeCommand(command);
162+
expect(response, isNull);
163+
}
164+
165+
Future<void> test_fileHasErrors_failsWithErrorForManual() async {
143166
final content = 'invalid dart code';
144167
newFile(mainFilePath, content);
145168
await initialize(
@@ -154,10 +177,10 @@ int minified(int x, int y) => min(x, y);
154177
(codeAction) => codeAction.command!,
155178
);
156179

157-
final commandResponse = await executeCommand(command);
158-
// Invalid code returns an empty success() response to avoid triggering
159-
// errors in the editor if run automatically on every save.
160-
expect(commandResponse, isNull);
180+
// Ensure the request returned an error (error repsonses are thrown by
181+
// the test helper to make consuming success results simpler).
182+
await expectLater(executeCommand(command),
183+
throwsA(isResponseError(ServerErrorCodes.FileHasErrors)));
161184
}
162185

163186
Future<void> test_filtersCorrectly() async {
@@ -339,7 +362,30 @@ class SortMembersSourceCodeActionsTest extends AbstractCodeActionsTest {
339362
throwsA(isResponseError(ServerErrorCodes.ClientFailedToApplyEdit)));
340363
}
341364

342-
Future<void> test_failsIfFileHasErrors() async {
365+
Future<void> test_fileHasErrors_failsSilentlyForAutomatic() async {
366+
final content = 'invalid dart code';
367+
newFile(mainFilePath, content);
368+
await initialize(
369+
workspaceCapabilities:
370+
withApplyEditSupport(emptyWorkspaceClientCapabilities));
371+
372+
final codeActions = await getCodeActions(
373+
mainFileUri.toString(),
374+
triggerKind: CodeActionTriggerKind.Automatic,
375+
);
376+
final codeAction = findCommand(codeActions, Commands.sortMembers)!;
377+
378+
final command = codeAction.map(
379+
(command) => command,
380+
(codeAction) => codeAction.command!,
381+
);
382+
383+
// Expect a valid null result.
384+
final response = await executeCommand(command);
385+
expect(response, isNull);
386+
}
387+
388+
Future<void> test_fileHasErrors_failsWithErrorForManual() async {
343389
final content = 'invalid dart code';
344390
newFile(mainFilePath, content);
345391
await initialize(

pkg/analysis_server/test/lsp/server_abstract.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
10161016
Range? range,
10171017
Position? position,
10181018
List<CodeActionKind>? kinds,
1019+
CodeActionTriggerKind? triggerKind,
10191020
}) {
10201021
range ??= position != null
10211022
? Range(start: position, end: position)
@@ -1025,10 +1026,14 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
10251026
CodeActionParams(
10261027
textDocument: TextDocumentIdentifier(uri: fileUri),
10271028
range: range,
1028-
// TODO(dantup): We may need to revise the tests/implementation when
1029-
// it's clear how we're supposed to handle diagnostics:
1030-
// https://github.com/Microsoft/language-server-protocol/issues/583
1031-
context: CodeActionContext(diagnostics: [], only: kinds),
1029+
context: CodeActionContext(
1030+
// TODO(dantup): We may need to revise the tests/implementation when
1031+
// it's clear how we're supposed to handle diagnostics:
1032+
// https://github.com/Microsoft/language-server-protocol/issues/583
1033+
diagnostics: [],
1034+
only: kinds,
1035+
triggerKind: triggerKind,
1036+
),
10321037
),
10331038
);
10341039
return expectSuccessfulResponseTo(

0 commit comments

Comments
 (0)