Version 2.18.0-161.0.dev
Merge commit '75d622a25d5928e62ee10ee12534648dd95963d0' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index de28ea1..a3a718d 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -214,7 +214,6 @@
static const FileHasErrors = ErrorCodes(-32008);
static const ClientFailedToApplyEdit = ErrorCodes(-32009);
static const RenameNotValid = ErrorCodes(-32010);
- static const RefactorFailed = ErrorCodes(-32011);
static const FeatureDisabled = ErrorCodes(-32012);
/// A file that is expected to be analyzed, but failed.
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
index 018892a..a27fa77 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -5,7 +5,6 @@
import 'dart:async';
import 'package:analysis_server/lsp_protocol/protocol.dart';
-import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/handlers/commands/abstract_refactor.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
@@ -48,7 +47,11 @@
final status = await refactoring.checkAllConditions();
if (status.hasError) {
- return error(ServerErrorCodes.RefactorFailed, status.message!);
+ // Show the error to the user but don't fail the request, as the
+ // LSP Client may show a failed request in a way that looks like a
+ // server error.
+ server.showErrorMessageToUser(status.message!);
+ return success(null);
}
if (cancellationToken.isCancellationRequested ||
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index 4fb647d..029f542 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -475,6 +475,52 @@
expect(response.message, contains('Cannot extract closure as method'));
}
+ /// Test if the client does not call refactor.validate it still gets a
+ /// sensible `showMessage` call and not a failed request.
+ Future<void> test_validLocation_failsInitialValidation_noValidation() async {
+ const content = '''
+f() {
+ var a = 0;
+ doFoo([[() => print(a)]]);
+ print(a);
+}
+
+void doFoo(void Function() a) => a();
+
+ ''';
+ newFile(mainFilePath, withoutMarkers(content));
+ await initialize(
+ // We expect an error notification so don't fail on it.
+ failTestOnAnyErrorNotification: false,
+ );
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final codeAction =
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
+
+ final command = codeAction.map(
+ (command) => command,
+ (codeAction) => codeAction.command!,
+ );
+
+ // Call the refactor without any validation and expected an error message
+ // without a request failure.
+ final errorNotification = await expectErrorNotification(() async {
+ final response = await executeCommand(
+ Command(
+ title: command.title,
+ command: command.command,
+ arguments: command.arguments),
+ );
+ expect(response, isNull);
+ });
+ expect(
+ errorNotification.message,
+ contains('Cannot extract closure as method'),
+ );
+ }
+
Future<void> test_validLocation_passesInitialValidation() async {
const content = '''
f() {
diff --git a/pkg/analysis_server/test/mocks_lsp.dart b/pkg/analysis_server/test/mocks_lsp.dart
index 07358d8..857639f 100644
--- a/pkg/analysis_server/test/mocks_lsp.dart
+++ b/pkg/analysis_server/test/mocks_lsp.dart
@@ -8,8 +8,6 @@
import 'package:analysis_server/lsp_protocol/protocol.dart' as lsp;
import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
-const _jsonEncoder = JsonEncoder.withIndent(' ');
-
/// A mock [LspServerCommunicationChannel] for testing [LspAnalysisServer].
class MockLspServerChannel implements LspServerCommunicationChannel {
final StreamController<lsp.Message> _clientToServer =
@@ -160,22 +158,14 @@
/// received response. The returned future will throw an exception if a server
/// error is reported before the response has been received.
///
- /// Unlike [sendLspRequest], this method assumes that the [request] has
+ /// Unlike [sendRequestToServer], this method assumes that the [request] has
/// already been sent to the server.
Future<lsp.ResponseMessage> waitForResponse(
- lsp.RequestMessage request, {
- bool throwOnError = true,
- }) async {
+ lsp.RequestMessage request) async {
final response = await _serverToClient.stream.firstWhere((message) =>
- (message is lsp.ResponseMessage && message.id == request.id) ||
- (throwOnError && _isShowErrorMessageNotification(message)));
+ message is lsp.ResponseMessage && message.id == request.id);
- if (response is lsp.ResponseMessage) {
- return response;
- } else {
- throw 'An error occurred while waiting for a response to ${request.method}: '
- '${_jsonEncoder.convert(response.toJson())}';
- }
+ return response as lsp.ResponseMessage;
}
/// Round trips the object to JSON and back to ensure it behaves the same as
@@ -187,19 +177,4 @@
return constructor(
jsonDecode(jsonEncode(message.toJson())) as Map<String, Object?>);
}
-
- /// Checks whether [message] is a `window/showMessage` notification with a
- /// type of [lsp.MessageType.Error].
- bool _isShowErrorMessageNotification(lsp.Message message) {
- if (message is! lsp.NotificationMessage) {
- return false;
- }
- if (message.method != lsp.Method.window_showMessage) {
- return false;
- }
- final params = lsp.ShowMessageParams.fromJson(
- message.params as Map<String, Object?>,
- );
- return params.type == lsp.MessageType.Error;
- }
}
diff --git a/tools/VERSION b/tools/VERSION
index 50b2665..ca2aee4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 160
+PRERELEASE 161
PRERELEASE_PATCH 0
\ No newline at end of file