analyzer: Move coveringNode to public API
NodeLocator and NodeLocator2 are being deprecated in favor of this
extension method. We move it to the public API here and start using it
in the analysis_server_plugin package.
Change-Id: I4783bb1f511b798e52b3383dd600b154dccc6d5b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420420
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/computer_lazy_type_hierarchy.dart b/pkg/analysis_server/lib/src/computer/computer_lazy_type_hierarchy.dart
index a8f80af..4069e9e 100644
--- a/pkg/analysis_server/lib/src/computer/computer_lazy_type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_lazy_type_hierarchy.dart
@@ -13,7 +13,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
/// A lazy computer for Type Hierarchies.
///
diff --git a/pkg/analysis_server/lib/src/computer/computer_signature.dart b/pkg/analysis_server/lib/src/computer/computer_signature.dart
index 1f63e6e..f002da6 100644
--- a/pkg/analysis_server/lib/src/computer/computer_signature.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_signature.dart
@@ -10,7 +10,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
/// A computer for the signature at the specified offset of a Dart
/// [CompilationUnit].
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
index ad855c5..fab5161 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart
@@ -9,8 +9,8 @@
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
import 'package:analyzer/src/utilities/extensions/flutter.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
/// Information about the arguments and parameters for an invocation.
typedef EditableInvocationInfo =
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmentation.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmentation.dart
index 6e22bec..8fe12fe 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmentation.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmentation.dart
@@ -8,7 +8,7 @@
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
class AugmentationHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmented.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmented.dart
index daf19dd..5511773 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmented.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_augmented.dart
@@ -8,7 +8,7 @@
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
class AugmentedHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_imports.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_imports.dart
index ac60576..643a2cf 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_imports.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_imports.dart
@@ -12,8 +12,8 @@
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
import 'package:analyzer/src/utilities/extensions/results.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
class ImportsHandler
extends SharedMessageHandler<TextDocumentPositionParams, List<Location>?> {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_super.dart b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_super.dart
index 50234df..6d21d20 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_super.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/custom/handler_super.dart
@@ -11,7 +11,7 @@
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
class SuperHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
diff --git a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
index 4ebf0d3..6dbdd0d 100644
--- a/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
+++ b/pkg/analysis_server/lib/src/services/flutter/widget_descriptions.dart
@@ -11,8 +11,8 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
import 'package:analyzer/src/utilities/extensions/flutter.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
import 'package:dart_style/dart_style.dart';
/// The result of [WidgetDescriptions.setPropertyValue] invocation.
diff --git a/pkg/analysis_server_plugin/lib/edit/correction_utils.dart b/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
index 38e6ac5..e1a7bf5 100644
--- a/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
+++ b/pkg/analysis_server_plugin/lib/edit/correction_utils.dart
@@ -12,10 +12,10 @@
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/scanner/reader.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
@@ -57,7 +57,7 @@
String get twoIndents => _twoIndents;
/// Returns the [AstNode] that encloses the given [offset].
- AstNode? findNode(int offset) => NodeLocator(offset).searchWithin(_unit);
+ AstNode? findNode(int offset) => _unit.nodeCovering(offset: offset);
/// Skips whitespace characters and single EOL on the right from [index].
///
diff --git a/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart b/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
index e79c057..890b16b 100644
--- a/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
+++ b/pkg/analysis_server_plugin/lib/edit/dart/correction_producer.dart
@@ -27,6 +27,7 @@
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -294,9 +295,8 @@
int selectionOffset = -1,
int selectionLength = 0,
}) {
- var selectionEnd = selectionOffset + selectionLength;
- var locator = NodeLocator(selectionOffset, selectionEnd);
- var node = locator.searchWithin(unitResult.unit);
+ var node = unitResult.unit
+ .nodeCovering(offset: selectionOffset, length: selectionLength);
node ??= unitResult.unit;
var token = _tokenAt(node, selectionOffset) ?? node.beginToken;
diff --git a/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
index 20593ce..b9a416b 100644
--- a/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
+++ b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart
@@ -9,8 +9,8 @@
import 'package:analysis_server_plugin/src/correction/assist_performance.dart';
import 'package:analysis_server_plugin/src/correction/fix_generators.dart';
import 'package:analyzer/error/error.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
@@ -117,10 +117,10 @@
return false;
}
- var selectionEnd =
- _assistContext.selectionOffset + _assistContext.selectionLength;
- var locator = NodeLocator(_assistContext.selectionOffset, selectionEnd);
- var node = locator.searchWithin(_assistContext.unitResult.unit);
+ var node = _assistContext.unitResult.unit.nodeCovering(
+ offset: _assistContext.selectionOffset,
+ length: _assistContext.selectionLength,
+ );
if (node == null) {
return false;
}
diff --git a/pkg/analysis_server_plugin/lib/src/utilities/selection.dart b/pkg/analysis_server_plugin/lib/src/utilities/selection.dart
index ae28126..7663b1e 100644
--- a/pkg/analysis_server_plugin/lib/src/utilities/selection.dart
+++ b/pkg/analysis_server_plugin/lib/src/utilities/selection.dart
@@ -6,7 +6,7 @@
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/source/source_range.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
/// A selection within a compilation unit.
class Selection {
diff --git a/pkg/analyzer/api.txt b/pkg/analyzer/api.txt
index f787bf4..d48cbb5 100644
--- a/pkg/analyzer/api.txt
+++ b/pkg/analyzer/api.txt
@@ -5369,6 +5369,9 @@
source (getter: Source?)
package:analyzer/src/workspace/workspace.dart:
Workspace (non-public)
+package:analyzer/utilities/extensions/ast.dart:
+ AstNodeExtension (extension on AstNode):
+ nodeCovering (method: AstNode? Function({int length, required int offset}))
package:analyzer/utilities/extensions/element.dart:
LibraryElement2Extension (extension on LibraryElement2):
exportedExtensions (getter: Iterable<ExtensionElement2>)
diff --git a/pkg/analyzer/lib/src/utilities/extensions/ast.dart b/pkg/analyzer/lib/src/utilities/extensions/ast.dart
index d11dd07..77c5809 100644
--- a/pkg/analyzer/lib/src/utilities/extensions/ast.dart
+++ b/pkg/analyzer/lib/src/utilities/extensions/ast.dart
@@ -94,69 +94,6 @@
}
return null;
}
-
- /// Return the minimal cover node for the range of characters beginning at the
- /// [offset] with the given [length], or `null` if the range is outside the
- /// range covered by the receiver.
- ///
- /// The minimal covering node is the node, rooted at the receiver, with the
- /// shortest length whose range completely includes the given range.
- AstNode? nodeCovering({required int offset, int length = 0}) {
- var end = offset + length;
-
- /// Return `true` if the [node] contains the range.
- ///
- /// When the range is an insertion point between two adjacent tokens, one of
- /// which belongs to the [node] and the other to a different node, then the
- /// [node] is considered to contain the insertion point unless the token
- /// that doesn't belonging to the [node] is an identifier.
- bool containsOffset(AstNode node) {
- if (length == 0) {
- if (offset == node.offset) {
- var previous = node.beginToken.previous;
- if (previous != null &&
- offset == previous.end &&
- previous.isIdentifier) {
- return false;
- }
- }
- if (offset == node.end) {
- var next = node.endToken.next;
- if (next != null && offset == next.offset && next.isIdentifier) {
- return false;
- }
- }
- }
- return node.offset <= offset && node.end >= end;
- }
-
- /// Return the child of the [node] that completely contains the range, or
- /// `null` if none of the children contain the range (which means that the
- /// [node] is the covering node).
- AstNode? childContainingRange(AstNode node) {
- for (var entity in node.childEntities) {
- if (entity is AstNode && containsOffset(entity)) {
- return entity;
- }
- }
- return null;
- }
-
- if (this is CompilationUnit) {
- if (offset < 0 || end > this.end) {
- return null;
- }
- } else if (!containsOffset(this)) {
- return null;
- }
- var previousNode = this;
- var currentNode = childContainingRange(previousNode);
- while (currentNode != null) {
- previousNode = currentNode;
- currentNode = childContainingRange(previousNode);
- }
- return previousNode;
- }
}
extension AstNodeNullableExtension on AstNode? {
diff --git a/pkg/analyzer/lib/utilities/extensions/ast.dart b/pkg/analyzer/lib/utilities/extensions/ast.dart
new file mode 100644
index 0000000..e3faa2d
--- /dev/null
+++ b/pkg/analyzer/lib/utilities/extensions/ast.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2025, 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:analyzer/dart/ast/ast.dart';
+
+extension AstNodeExtension on AstNode {
+ /// Returns the minimal covering node for the range of characters beginning at
+ /// the [offset] with the given [length], or `null` if the range is outside
+ /// the range covered by the receiver.
+ ///
+ /// The minimal covering node is the node, rooted at the receiver, with the
+ /// shortest length whose range completely includes the given range.
+ AstNode? nodeCovering({required int offset, int length = 0}) {
+ var end = offset + length;
+
+ /// Returns whether the [node] contains the range.
+ ///
+ /// When the range is an insertion point between two adjacent tokens, one of
+ /// which belongs to the [node] and the other to a different node, then the
+ /// [node] is considered to contain the insertion point unless the token
+ /// that doesn't belonging to the [node] is an identifier.
+ bool containsOffset(AstNode node) {
+ if (length == 0) {
+ if (offset == node.offset) {
+ var previous = node.beginToken.previous;
+ if (previous != null &&
+ offset == previous.end &&
+ previous.isIdentifier) {
+ return false;
+ }
+ }
+ if (offset == node.end) {
+ var next = node.endToken.next;
+ if (next != null && offset == next.offset && next.isIdentifier) {
+ return false;
+ }
+ }
+ }
+ return node.offset <= offset && node.end >= end;
+ }
+
+ /// Returns the child of the [node] that completely contains the range, or
+ /// `null` if none of the children contain the range (which means that the
+ /// [node] is the covering node).
+ AstNode? childContainingRange(AstNode node) {
+ for (var entity in node.childEntities) {
+ if (entity is AstNode && containsOffset(entity)) {
+ return entity;
+ }
+ }
+ return null;
+ }
+
+ if (this is CompilationUnit) {
+ if (offset < 0 || end > this.end) {
+ return null;
+ }
+ } else if (!containsOffset(this)) {
+ return null;
+ }
+ var previousNode = this;
+ var currentNode = childContainingRange(previousNode);
+ while (currentNode != null) {
+ previousNode = currentNode;
+ currentNode = childContainingRange(previousNode);
+ }
+ return previousNode;
+ }
+}
diff --git a/pkg/analyzer/test/src/utilities/extensions/ast_test.dart b/pkg/analyzer/test/src/utilities/extensions/ast_test.dart
index 32565a4..7802d2e 100644
--- a/pkg/analyzer/test/src/utilities/extensions/ast_test.dart
+++ b/pkg/analyzer/test/src/utilities/extensions/ast_test.dart
@@ -4,7 +4,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/source/source_range.dart';
-import 'package:analyzer/src/utilities/extensions/ast.dart';
+import 'package:analyzer/utilities/extensions/ast.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';