Implement some more LSP rename tests
Change-Id: Idd0f3734b20f716d5e92f13c3a85f716c226fc5b
Typo
Change-Id: Ica50195f94f8c57be0e16edde75e6e287b45c06b
Reviewed-on: https://dart-review.googlesource.com/c/89365
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Danny Tuppeny <dantup@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 2110d4d..464ab95 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -40,7 +40,7 @@
static const FileNotAnalyzed = const ErrorCodes(-32007);
static const FileHasErrors = const ErrorCodes(-32008);
static const ClientFailedToApplyEdit = const ErrorCodes(-32009);
- static const FatalRefactoringProblem = const ErrorCodes(-32010);
+ static const RenameNotValid = const ErrorCodes(-32010);
/// An error raised when the server detects that the server and client are out
/// of sync and cannot recover. For example if a textDocument/didChange notification
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
index 090d560..3a9075e 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
@@ -42,8 +42,8 @@
// Check the rename is valid here.
final initStatus = await refactoring.checkInitialConditions();
if (initStatus.hasFatalError) {
- return error(ServerErrorCodes.FatalRefactoringProblem,
- initStatus.problem.message, null);
+ return error(
+ ServerErrorCodes.RenameNotValid, initStatus.problem.message, null);
}
return success(new RangeAndPlaceholder(
@@ -99,26 +99,32 @@
final refactoring = new RenameRefactoring(server.refactoringWorkspace,
unit.result.session, refactorDetails.element);
+ // TODO(dantup): Consider using window/showMessageRequest to prompt
+ // the user to see if they'd like to proceed with a rename if there
+ // are non-fatal errors or warnings. For now we will reject all errors
+ // (fatal and not) as this seems like the most logical behaviour when
+ // without a prompt.
+
// Check the rename is valid here.
final initStatus = await refactoring.checkInitialConditions();
- if (initStatus.hasFatalError) {
- return error(ServerErrorCodes.FatalRefactoringProblem,
- initStatus.problem.message, null);
+ if (initStatus.hasError) {
+ return error(
+ ServerErrorCodes.RenameNotValid, initStatus.problem.message, null);
}
// Check the name is valid.
refactoring.newName = params.newName;
final optionsStatus = refactoring.checkNewName();
- if (optionsStatus.hasFatalError) {
- return error(ServerErrorCodes.FatalRefactoringProblem,
+ if (optionsStatus.hasError) {
+ return error(ServerErrorCodes.RenameNotValid,
optionsStatus.problem.message, null);
}
// Final validation.
final finalStatus = await refactoring.checkFinalConditions();
- if (finalStatus.hasFatalError) {
- return error(ServerErrorCodes.FatalRefactoringProblem,
- finalStatus.problem.message, null);
+ if (finalStatus.hasError) {
+ return error(
+ ServerErrorCodes.RenameNotValid, finalStatus.problem.message, null);
}
// Compute the actual change.
diff --git a/pkg/analysis_server/test/lsp/rename_test.dart b/pkg/analysis_server/test/lsp/rename_test.dart
index 8e984f2..6226b60 100644
--- a/pkg/analysis_server/test/lsp/rename_test.dart
+++ b/pkg/analysis_server/test/lsp/rename_test.dart
@@ -18,11 +18,8 @@
@reflectiveTest
class RenameTest extends AbstractLspAnalysisServerTest {
// TODO(dantup): send a rename without a version
- // TODO(dantup): file changes during computation?
// TODO(dantup): send an old version of the doc?
- // TODO(dantup): check the version return matches?
- // TODO(dantup): a rename that fails on options step
- // TODO(dantup): a rename that fails on final step
+ // TODO(dantup): check the version returned matches?
// TODO(dantup): renames across multiple files
test_prepare_class() {
@@ -97,7 +94,7 @@
expect(response.id, equals(request.id));
expect(response.result, isNull);
expect(response.error, isNotNull);
- expect(response.error.code, ServerErrorCodes.FatalRefactoringProblem);
+ expect(response.error.code, ServerErrorCodes.RenameNotValid);
expect(response.error.message, contains('is defined in the SDK'));
}
@@ -180,6 +177,38 @@
return _test_rename_withDocumentChanges(content, 'MyNewClass', null);
}
+ test_rename_rejectedForBadName() async {
+ const content = '''
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ final error = await _test_rename_failure(content, 'not a valid class name');
+ expect(error.code, equals(ServerErrorCodes.RenameNotValid));
+ expect(error.message, contains('name must not contain'));
+ }
+
+ test_rename_rejectedForDuplicateName() async {
+ const content = '''
+ class MyOtherClass {}
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ final error = await _test_rename_failure(content, 'MyOtherClass');
+ expect(error.code, equals(ServerErrorCodes.RenameNotValid));
+ expect(error.message, contains('already declares class with name'));
+ }
+
+ test_rename_rejectedForStaleDocument() async {
+ const content = '''
+ class MyClass {}
+ final a = n^ew MyClass();
+ ''';
+ final error =
+ await _test_rename_failure(content, 'MyNewClass', openFileVersion: 111);
+ expect(error.code, equals(ErrorCodes.ContentModified));
+ expect(error.message, contains('Document was modified'));
+ }
+
test_rename_sdkClass() async {
const content = '''
final a = new [[Ob^ject]]();
@@ -201,7 +230,7 @@
expect(response.id, equals(request.id));
expect(response.result, isNull);
expect(response.error, isNotNull);
- expect(response.error.code, ServerErrorCodes.FatalRefactoringProblem);
+ expect(response.error.code, ServerErrorCodes.RenameNotValid);
expect(response.error.message, contains('is defined in the SDK'));
}
@@ -266,6 +295,31 @@
}
}
+ Future<ResponseError> _test_rename_failure(
+ String content,
+ String newName, {
+ int openFileVersion = 222,
+ int renameRequestFileVersion = 222,
+ }) async {
+ await initialize(
+ workspaceCapabilities:
+ withDocumentChangesSupport(emptyWorkspaceClientCapabilities),
+ );
+ await openFile(mainFileUri, withoutMarkers(content),
+ version: openFileVersion);
+
+ final result = await renameRaw(
+ mainFileUri,
+ renameRequestFileVersion,
+ positionFromMarker(content),
+ newName,
+ );
+
+ expect(result.result, isNull);
+ expect(result.error, isNotNull);
+ return result.error;
+ }
+
_test_rename_withDocumentChanges(
String content, String newName, String expectedContent) async {
await initialize(
@@ -280,7 +334,6 @@
positionFromMarker(content),
newName,
);
-
if (expectedContent == null) {
expect(result, isNull);
} else {
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 1ccbbb1..fad7baf 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -446,6 +446,18 @@
return new NotificationMessage(method, params, jsonRpcVersion);
}
+ RequestMessage makeRenameRequest(
+ int version, Uri uri, Position pos, String newName) {
+ final docIdentifier = version != null
+ ? new VersionedTextDocumentIdentifier(version, uri.toString())
+ : new TextDocumentIdentifier(uri.toString());
+ final request = makeRequest(
+ Method.textDocument_rename,
+ new RenameParams(docIdentifier, pos, newName),
+ );
+ return request;
+ }
+
RequestMessage makeRequest(Method method, ToJsonable params) {
final id = Either2<num, String>.t1(_id++);
return new RequestMessage(id, method, params, jsonRpcVersion);
@@ -537,16 +549,20 @@
Position pos,
String newName,
) {
- final docIdentifier = version != null
- ? new VersionedTextDocumentIdentifier(version, uri.toString())
- : new TextDocumentIdentifier(uri.toString());
- final request = makeRequest(
- Method.textDocument_rename,
- new RenameParams(docIdentifier, pos, newName),
- );
+ final request = makeRenameRequest(version, uri, pos, newName);
return expectSuccessfulResponseTo<WorkspaceEdit>(request);
}
+ Future<ResponseMessage> renameRaw(
+ Uri uri,
+ int version,
+ Position pos,
+ String newName,
+ ) {
+ final request = makeRenameRequest(version, uri, pos, newName);
+ return channel.sendRequestToServer(request);
+ }
+
Future replaceFile(int newVersion, Uri uri, String content) {
return changeFile(
newVersion,