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();
   });
 }