[analysis_server] [lsp] Fix navigation for field/super formal params
Fixes https://github.com/Dart-Code/Dart-Code/issues/4031.
Change-Id: Ic1a6ec5407cf3599bb4b88d5be1ff8c3efe50066
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251108
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index e75e0f1..2454202 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -52,11 +52,11 @@
? ProgressReporter.serverCreated(server)
: ProgressReporter.noop;
- // To make parsing arguments easier in commands, instead of a
+ // To make passing arguments easier in commands, instead of a
// `List<Object?>` we now use `Map<String, Object?>`.
//
// However, some handlers still support the list for compatibility so we
- // must allow the to convert a `List` to a `Map`.
+ // must allow them to convert a `List` to a `Map`.
final arguments = params.arguments ?? const [];
Map<String, Object?> commandParams;
if (arguments.length == 1 && arguments[0] is Map<String, Object?>) {
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index 1577e53..8c6c351 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -173,6 +173,17 @@
await testContents(contents);
}
+ Future<void> test_fieldFormalParam() async {
+ final contents = '''
+class A {
+ final String [[a]];
+ A(this.^a});
+}
+''';
+
+ await testContents(contents);
+ }
+
Future<void> test_fromPlugins() async {
final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
@@ -402,6 +413,19 @@
await testContents(contents);
}
+ Future<void> test_superFormalParam() async {
+ final contents = '''
+class A {
+ A({required int [[a]]});
+}
+class B extends A {
+ B({required super.^a}) : assert(a > 0);
+}
+''';
+
+ await testContents(contents);
+ }
+
Future<void> test_type() async {
final contents = '''
f() {
diff --git a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
index 418ea31..23df1dd 100644
--- a/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart
@@ -26,17 +26,39 @@
unit.accept(visitor);
} else {
var node = _getNodeForRange(unit, offset, length);
- // Take the outer-most node that shares this offset/length so that we get
- // things like ConstructorName instead of SimpleIdentifier.
- // https://github.com/dart-lang/sdk/issues/46725
if (node != null) {
- node = _getOutermostNode(node);
+ node = _getNavigationTargetNode(node);
}
node?.accept(visitor);
}
return collector;
}
+/// Gets the nearest node that should be used for navigation.
+///
+/// This is usually the outermost node with the same offset as node but in some
+/// cases may be a different ancestor where required to produce the correct
+/// result.
+AstNode _getNavigationTargetNode(AstNode node) {
+ AstNode? current = node;
+ while (current != null &&
+ current.parent != null &&
+ current.offset == current.parent!.offset) {
+ current = current.parent;
+ }
+ current ??= node;
+
+ // To navigate to formal params, we need to visit the parameter and not just
+ // the identifier but they don't start at the same offset as they have a
+ // prefix.
+ final parent = current.parent;
+ if (parent is FormalParameter) {
+ current = parent;
+ }
+
+ return current;
+}
+
AstNode? _getNodeForRange(CompilationUnit unit, int offset, int length) {
var node = NodeLocator(offset, offset + length).searchWithin(unit);
for (var n = node; n != null; n = n.parent) {
@@ -47,21 +69,6 @@
return node;
}
-/// Gets the outer-most node with the same offset as node.
-///
-/// This reduces the number of nodes the visitor needs to walk when collecting
-/// navigation for a specific location in the file.
-AstNode _getOutermostNode(AstNode node) {
- AstNode? current = node;
- while (current != null &&
- current.parent != null &&
- current != current.parent &&
- current.offset == current.parent!.offset) {
- current = current.parent;
- }
- return current ?? node;
-}
-
/// A Dart specific wrapper around [NavigationCollector].
class _DartNavigationCollector {
final NavigationCollector collector;