Start of rename support for Cider
Change-Id: I50a47dafc9e41518a0d15f5c279a57db966afbb6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201620
Commit-Queue: Keerti Parthasarathy <keertip@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
new file mode 100644
index 0000000..72e884b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dart/micro/resolve_file.dart';
+import 'package:analyzer/src/dart/micro/utils.dart';
+
+class CiderRenameComputer {
+ final FileResolver _fileResolver;
+
+ CiderRenameComputer(this._fileResolver);
+
+ /// Check if the identifier at the [line], [column] for the file at the
+ /// [filePath] can be renamed.
+ RenameRefactoringElement? canRename(String filePath, int line, int column) {
+ var resolvedUnit = _fileResolver.resolve(path: filePath);
+ var lineInfo = resolvedUnit.lineInfo;
+ var offset = lineInfo.getOffsetOfLine(line) + column;
+
+ var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
+ var element = getElementOfNode(node);
+
+ if (node == null || element == null) {
+ return null;
+ }
+ if (element.source != null && element.source!.isInSystemLibrary) {
+ return null;
+ }
+ if (element is MethodElement && element.isOperator) {
+ return null;
+ }
+ if (!_canRenameElement(element)) {
+ return null;
+ }
+ return RenameRefactoring.getElementToRename(node, element);
+ }
+
+ bool _canRenameElement(Element element) {
+ if (element is PropertyAccessorElement) {
+ element = element.variable;
+ }
+ var enclosingElement = element.enclosingElement;
+ if (element is LabelElement || element is LocalElement) {
+ return true;
+ }
+ if (enclosingElement is ClassElement ||
+ enclosingElement is ExtensionElement) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/pkg/analysis_server/test/src/cider/rename_test.dart b/pkg/analysis_server/test/src/cider/rename_test.dart
new file mode 100644
index 0000000..290379f
--- /dev/null
+++ b/pkg/analysis_server/test/src/cider/rename_test.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/cider/rename.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'cider_service.dart';
+
+void main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(CiderRenameComputerTest);
+ });
+}
+
+@reflectiveTest
+class CiderRenameComputerTest extends CiderServiceTest {
+ late _CorrectionContext _correctionContext;
+
+ void test_canRename_field() {
+ var refactor = _compute(r'''
+class A {
+ int ^bar;
+ void foo() {
+ bar = 5;
+ }
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'bar');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ void test_canRename_function() {
+ var refactor = _compute(r'''
+void ^foo() {
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'foo');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ void test_canRename_label() {
+ var refactor = _compute(r'''
+main() {
+ myLabel:
+ while (true) {
+ continue ^myLabel;
+ break myLabel;
+ }
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'myLabel');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ void test_canRename_local() {
+ var refactor = _compute(r'''
+void foo() {
+ var ^a = 0; var b = a + 1;
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'a');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ void test_canRename_method() {
+ var refactor = _compute(r'''
+extension E on int {
+ void ^foo() {}
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'foo');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ void test_canRename_operator() {
+ var refactor = _compute(r'''
+class A{
+ A operator ^+(A other) => this;
+}
+''');
+
+ expect(refactor, isNull);
+ }
+
+ void test_canRename_parameter() {
+ var refactor = _compute(r'''
+void foo(int ^bar) {
+ var a = bar + 1;
+}
+''');
+
+ expect(refactor, isNotNull);
+ expect(refactor!.element.name, 'bar');
+ expect(refactor.offset, _correctionContext.offset);
+ }
+
+ RenameRefactoringElement? _compute(String content) {
+ _updateFile(content);
+
+ return CiderRenameComputer(
+ fileResolver,
+ ).canRename(
+ convertPath(testPath),
+ _correctionContext.line,
+ _correctionContext.character,
+ );
+ }
+
+ void _updateFile(String content) {
+ var offset = content.indexOf('^');
+ expect(offset, isPositive, reason: 'Expected to find ^');
+ expect(content.indexOf('^', offset + 1), -1, reason: 'Expected only one ^');
+
+ var lineInfo = LineInfo.fromContent(content);
+ var location = lineInfo.getLocation(offset);
+
+ content = content.substring(0, offset) + content.substring(offset + 1);
+ newFile(testPath, content: content);
+
+ _correctionContext = _CorrectionContext(
+ content,
+ offset,
+ location.lineNumber - 1,
+ location.columnNumber - 1,
+ );
+ }
+}
+
+class _CorrectionContext {
+ final String content;
+ final int offset;
+ final int line;
+ final int character;
+
+ _CorrectionContext(this.content, this.offset, this.line, this.character);
+}
diff --git a/pkg/analysis_server/test/src/cider/test_all.dart b/pkg/analysis_server/test/src/cider/test_all.dart
index 524383b..95c39ca 100644
--- a/pkg/analysis_server/test/src/cider/test_all.dart
+++ b/pkg/analysis_server/test/src/cider/test_all.dart
@@ -7,11 +7,13 @@
import 'assists_test.dart' as assists;
import 'completion_test.dart' as completion;
import 'fixes_test.dart' as fixes;
+import 'rename_test.dart' as rename;
void main() {
defineReflectiveSuite(() {
assists.main();
completion.main();
fixes.main();
+ rename.main();
});
}