| // Copyright (c) 2026, 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_plugin/src/utilities/selection.dart'; | |
| import 'package:analyzer/dart/ast/ast.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| extension SelectionExtension on Selection { | |
| /// The end of the selection. | |
| int get end => offset + length; | |
| /// Returns the element of the constructor at the given [selection]. | |
| /// | |
| /// Returns `null` if | |
| /// - the selection doesn't identify a constructor, or | |
| /// - [mustHaveName] is `true` and the referenced constructor doesn't have a | |
| /// name, or | |
| /// - [mustNotHaveName] is `true` and the referenced constructor has a name. | |
| ConstructorElement? constructor({ | |
| bool mustHaveName = false, | |
| bool mustNotHaveName = false, | |
| }) { | |
| var node = coveringNode; | |
| bool meetsRequirements(Object? name) { | |
| if (name == null) { | |
| if (mustHaveName) return false; | |
| } else { | |
| if (mustNotHaveName) return false; | |
| } | |
| return true; | |
| } | |
| if (node is ConstructorDeclaration) { | |
| if (!meetsRequirements(node.name)) return null; | |
| var left = | |
| node.typeName?.offset ?? | |
| node.factoryKeyword?.offset ?? | |
| node.newKeyword?.offset ?? | |
| node.firstTokenAfterCommentAndMetadata.offset; | |
| var right = node.separator?.offset ?? node.parameters.offset; | |
| if (left <= offset && end <= right) { | |
| return node.declaredFragment?.element; | |
| } | |
| } else if (node is SimpleIdentifier || node is FormalParameterList) { | |
| var parent = node.parent; | |
| if (parent is ConstructorDeclaration) { | |
| if (!meetsRequirements(parent.name)) return null; | |
| return parent.declaredFragment?.element; | |
| } else if (parent is ConstructorName) { | |
| if (!meetsRequirements(parent.name)) return null; | |
| return parent.element; | |
| } | |
| } else if (node is PrimaryConstructorDeclaration) { | |
| if (!meetsRequirements(node.constructorName)) return null; | |
| return node.declaredFragment?.element; | |
| } else if (node is PrimaryConstructorName) { | |
| if (!meetsRequirements(node.name)) return null; | |
| var parent = node.parent; | |
| if (parent is PrimaryConstructorDeclaration) { | |
| return parent.declaredFragment?.element; | |
| } | |
| } else if (node is NamedType) { | |
| var parent = node.parent; | |
| if (parent is ConstructorName) { | |
| if (!meetsRequirements(parent.name)) return null; | |
| return parent.element; | |
| } | |
| } else if (node is ConstructorName) { | |
| if (!meetsRequirements(node.name)) return null; | |
| return node.element; | |
| } | |
| return null; | |
| } | |
| /// Returns the [LibraryImport] at the given [selection]. | |
| /// | |
| /// Returns `null` if | |
| /// - the selection doesn't identify an import directive, or | |
| /// - [mustHavePrefix] is `true` and the directive doesn't have a | |
| /// prefix, or | |
| /// - [mustNotHavePrefix] is `true` and the directive has a prefix, or | |
| /// - the AST has not been resolved so the node has no `libraryImport`. | |
| /// - [mustNotBeDeferred] is `true` and the directive is deferred, or | |
| /// - the AST has not been resolved so the node has no `libraryImport`. | |
| LibraryImport? importDirective({ | |
| bool mustHavePrefix = false, | |
| bool mustNotHavePrefix = false, | |
| bool mustNotBeDeferred = false, | |
| }) { | |
| var node = coveringNode.thisOrAncestorOfType<ImportDirective>(); | |
| if (node == null) { | |
| return null; | |
| } | |
| var hasPrefix = node.prefix != null; | |
| var isDeferred = node.deferredKeyword != null; | |
| if (mustHavePrefix && !hasPrefix) { | |
| return null; | |
| } else if (mustNotHavePrefix && hasPrefix) { | |
| return null; | |
| } else if (mustNotBeDeferred && isDeferred) { | |
| return null; | |
| } else { | |
| return node.libraryImport; | |
| } | |
| } | |
| } |