|  | // Copyright (c) 2016, 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/analysis/results.dart'; | 
|  | import 'package:analyzer/dart/ast/syntactic_entity.dart'; | 
|  | import 'package:analyzer/dart/ast/visitor.dart'; | 
|  | import 'package:analyzer/dart/element/element2.dart'; | 
|  | import 'package:analyzer/source/line_info.dart'; | 
|  | import 'package:analyzer/source/source_range.dart'; | 
|  | import 'package:analyzer/src/dart/analysis/driver.dart'; | 
|  | import 'package:analyzer/src/dart/analysis/file_state.dart'; | 
|  | import 'package:analyzer/src/dart/analysis/index.dart'; | 
|  | import 'package:analyzer/src/dart/analysis/results.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/element/element.dart'; | 
|  | import 'package:analyzer/src/summary/idl.dart'; | 
|  | import 'package:analyzer/src/util/performance/operation_performance.dart'; | 
|  | import 'package:analyzer/src/utilities/cancellation.dart'; | 
|  | import 'package:analyzer/src/utilities/fuzzy_matcher.dart'; | 
|  | import 'package:collection/collection.dart'; | 
|  |  | 
|  | Fragment _getEnclosingFragment( | 
|  | CompilationUnitElementImpl libraryFragment, | 
|  | int offset, | 
|  | ) { | 
|  | Fragment? visitFragment(Fragment fragment) { | 
|  | var fragmentImpl = fragment as ElementImpl; | 
|  | var codeOffset = fragmentImpl.codeOffset; | 
|  | var codeLength = fragmentImpl.codeLength; | 
|  | if (codeOffset == null || codeLength == null) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | var codeEnd = codeOffset + codeLength; | 
|  | if (codeOffset <= offset && offset <= codeEnd) { | 
|  | for (var child in fragment.children3) { | 
|  | var result = visitFragment(child); | 
|  | if (result != null) { | 
|  | return result; | 
|  | } | 
|  | } | 
|  | return fragment; | 
|  | } | 
|  |  | 
|  | return null; | 
|  | } | 
|  |  | 
|  | var result = visitFragment(libraryFragment); | 
|  | return result ?? libraryFragment; | 
|  | } | 
|  |  | 
|  | DeclarationKind? _getSearchElementKind(Element2 element) { | 
|  | if (element is EnumElement2) { | 
|  | return DeclarationKind.ENUM; | 
|  | } | 
|  |  | 
|  | if (element is ExtensionTypeElement2) { | 
|  | return DeclarationKind.EXTENSION_TYPE; | 
|  | } | 
|  |  | 
|  | if (element is MixinElement2) { | 
|  | return DeclarationKind.MIXIN; | 
|  | } | 
|  |  | 
|  | if (element is ClassElement2) { | 
|  | if (element.isMixinApplication) { | 
|  | return DeclarationKind.CLASS_TYPE_ALIAS; | 
|  | } | 
|  | return DeclarationKind.CLASS; | 
|  | } | 
|  |  | 
|  | if (element is ConstructorElement2) { | 
|  | return DeclarationKind.CONSTRUCTOR; | 
|  | } | 
|  |  | 
|  | if (element is ExtensionElement2) { | 
|  | return DeclarationKind.EXTENSION; | 
|  | } | 
|  |  | 
|  | if (element is FieldElement2) { | 
|  | if (element.isEnumConstant) return DeclarationKind.ENUM_CONSTANT; | 
|  | return DeclarationKind.FIELD; | 
|  | } | 
|  |  | 
|  | if (element is LocalFunctionElement || element is TopLevelFunctionElement) { | 
|  | return DeclarationKind.FUNCTION; | 
|  | } | 
|  |  | 
|  | if (element is MethodElement2) { | 
|  | return DeclarationKind.METHOD; | 
|  | } | 
|  |  | 
|  | if (element is GetterElement) { | 
|  | return DeclarationKind.GETTER; | 
|  | } | 
|  |  | 
|  | if (element is SetterElement) { | 
|  | return DeclarationKind.SETTER; | 
|  | } | 
|  |  | 
|  | if (element is TypeAliasElement2) { | 
|  | return DeclarationKind.TYPE_ALIAS; | 
|  | } | 
|  |  | 
|  | if (element is VariableElement2) { | 
|  | return DeclarationKind.VARIABLE; | 
|  | } | 
|  |  | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /// An element declaration. | 
|  | class Declaration { | 
|  | final int fileIndex; | 
|  | final LineInfo lineInfo; | 
|  | final String name; | 
|  | final DeclarationKind kind; | 
|  | final int offset; | 
|  | final int line; | 
|  | final int column; | 
|  | final int codeOffset; | 
|  | final int codeLength; | 
|  | final String? className; | 
|  | final String? mixinName; | 
|  | final String? parameters; | 
|  |  | 
|  | Declaration( | 
|  | this.fileIndex, | 
|  | this.lineInfo, | 
|  | this.name, | 
|  | this.kind, | 
|  | this.offset, | 
|  | this.line, | 
|  | this.column, | 
|  | this.codeOffset, | 
|  | this.codeLength, | 
|  | this.className, | 
|  | this.mixinName, | 
|  | this.parameters, | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// The kind of a [Declaration]. | 
|  | enum DeclarationKind { | 
|  | CLASS, | 
|  | CLASS_TYPE_ALIAS, | 
|  | CONSTRUCTOR, | 
|  | ENUM, | 
|  | ENUM_CONSTANT, | 
|  | EXTENSION, | 
|  | EXTENSION_TYPE, | 
|  | FIELD, | 
|  | FUNCTION, | 
|  | FUNCTION_TYPE_ALIAS, | 
|  | GETTER, | 
|  | METHOD, | 
|  | MIXIN, | 
|  | SETTER, | 
|  | TYPE_ALIAS, | 
|  | VARIABLE | 
|  | } | 
|  |  | 
|  | /// Searches through files known to [drivers] for declarations. | 
|  | /// | 
|  | /// If files are known to multiple drivers, they will be searched only within | 
|  | /// the context of the first. | 
|  | class FindDeclarations { | 
|  | final List<AnalysisDriver> drivers; | 
|  | final WorkspaceSymbols result; | 
|  | final int? maxResults; | 
|  | final String pattern; | 
|  | final FuzzyMatcher matcher; | 
|  | final String? onlyForFile; | 
|  | final bool onlyAnalyzed; | 
|  | final OwnedFiles ownedFiles; | 
|  | final OperationPerformanceImpl performance; | 
|  |  | 
|  | FindDeclarations( | 
|  | this.drivers, | 
|  | this.result, | 
|  | this.pattern, | 
|  | this.maxResults, { | 
|  | this.onlyForFile, | 
|  | this.onlyAnalyzed = false, | 
|  | required this.ownedFiles, | 
|  | required this.performance, | 
|  | }) : matcher = FuzzyMatcher(pattern); | 
|  |  | 
|  | Future<void> compute([CancellationToken? cancellationToken]) async { | 
|  | if (!onlyAnalyzed) { | 
|  | await performance.runAsync('discoverAvailableFiles', (performance) async { | 
|  | await Future.wait( | 
|  | drivers.map((driver) => driver.discoverAvailableFiles()), | 
|  | ); | 
|  | }); | 
|  | } | 
|  |  | 
|  | var entries = [ | 
|  | ...ownedFiles.addedFiles.entries, | 
|  | if (!onlyAnalyzed) ...ownedFiles.knownFiles.entries, | 
|  | ]; | 
|  |  | 
|  | await performance.runAsync('findDeclarations', (performance) async { | 
|  | await _FindDeclarations( | 
|  | entries, | 
|  | result, | 
|  | pattern, | 
|  | matcher, | 
|  | maxResults, | 
|  | onlyForFile: onlyForFile, | 
|  | performance: performance, | 
|  | ).compute(cancellationToken); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Visitor that adds [SearchResult]s for references to the [import]. | 
|  | class ImportElementReferencesVisitor extends RecursiveAstVisitor<void> { | 
|  | final List<SearchResult> results = <SearchResult>[]; | 
|  |  | 
|  | final LibraryImport import; | 
|  | final CompilationUnitElementImpl enclosingLibraryFragment; | 
|  |  | 
|  | late final Set<Element2> importedElements; | 
|  |  | 
|  | ImportElementReferencesVisitor( | 
|  | LibraryImport element, this.enclosingLibraryFragment) | 
|  | : import = element { | 
|  | importedElements = element.namespace.definedNames2.values.toSet(); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitExportDirective(ExportDirective node) {} | 
|  |  | 
|  | @override | 
|  | void visitImportDirective(ImportDirective node) {} | 
|  |  | 
|  | @override | 
|  | void visitNamedType(NamedType node) { | 
|  | if (importedElements.contains(node.element2)) { | 
|  | var prefixFragment = import.prefix2; | 
|  | var importPrefix = node.importPrefix; | 
|  | if (prefixFragment == null) { | 
|  | if (importPrefix == null) { | 
|  | _addResult(node.offset, 0); | 
|  | } | 
|  | } else { | 
|  | if (importPrefix != null && | 
|  | importPrefix.element2 == prefixFragment.element) { | 
|  | var offset = importPrefix.offset; | 
|  | var end = importPrefix.period.end; | 
|  | _addResult(offset, end - offset); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | node.importPrefix?.accept(this); | 
|  | node.typeArguments?.accept(this); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitSimpleIdentifier(SimpleIdentifier node) { | 
|  | if (node.inDeclarationContext()) { | 
|  | return; | 
|  | } | 
|  | if (import.prefix2 != null) { | 
|  | if (node.element == import.prefix2?.element) { | 
|  | var parent = node.parent; | 
|  | if (parent is PrefixedIdentifier && parent.prefix == node) { | 
|  | var element = parent.writeOrReadElement2?.baseElement; | 
|  | if (importedElements.contains(element)) { | 
|  | _addResultForPrefix(node, parent.identifier); | 
|  | } | 
|  | } | 
|  | if (parent is MethodInvocation && parent.target == node) { | 
|  | var element = parent.methodName.element?.baseElement; | 
|  | if (importedElements.contains(element)) { | 
|  | _addResultForPrefix(node, parent.methodName); | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | var element = node.writeOrReadElement2?.baseElement; | 
|  | if (importedElements.contains(element)) { | 
|  | _addResult(node.offset, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addResult(int offset, int length) { | 
|  | var enclosingFragment = | 
|  | _getEnclosingFragment(enclosingLibraryFragment, offset); | 
|  | results.add(SearchResult._(enclosingFragment, SearchResultKind.REFERENCE, | 
|  | offset, length, true, false)); | 
|  | } | 
|  |  | 
|  | void _addResultForPrefix(SimpleIdentifier prefixNode, AstNode nextNode) { | 
|  | int prefixOffset = prefixNode.offset; | 
|  | _addResult(prefixOffset, nextNode.offset - prefixOffset); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// A single reference to a [LibraryFragment], e.g. import of a library, which | 
|  | /// is a reference to the first library fragment. | 
|  | class LibraryFragmentSearchMatch { | 
|  | /// The library fragment that contains this reference. | 
|  | final LibraryFragment libraryFragment; | 
|  |  | 
|  | /// The source range of the URI. | 
|  | final SourceRange range; | 
|  |  | 
|  | LibraryFragmentSearchMatch({ | 
|  | required this.libraryFragment, | 
|  | required this.range, | 
|  | }); | 
|  | } | 
|  |  | 
|  | /// Search support for an [AnalysisDriver]. | 
|  | class Search { | 
|  | final AnalysisDriver _driver; | 
|  |  | 
|  | Search(this._driver); | 
|  |  | 
|  | /// Returns class or mixin members with the given [name]. | 
|  | Future<List<Element2>> classMembers( | 
|  | String name, SearchedFiles searchedFiles) async { | 
|  | var elements = <Element2>[]; | 
|  |  | 
|  | void addElement(Element2 element) { | 
|  | if (!element.isSynthetic && element.displayName == name) { | 
|  | elements.add(element); | 
|  | } | 
|  | } | 
|  |  | 
|  | void addElements(InterfaceElement2 element) { | 
|  | element.getters2.forEach(addElement); | 
|  | element.setters2.forEach(addElement); | 
|  | element.fields2.forEach(addElement); | 
|  | element.methods2.forEach(addElement); | 
|  | } | 
|  |  | 
|  | var files = await _driver.getFilesDefiningClassMemberName(name); | 
|  | for (var file in files) { | 
|  | if (searchedFiles.add(file.path, this)) { | 
|  | var libraryResult = await _driver.getLibraryByUri(file.uriStr); | 
|  | if (libraryResult is LibraryElementResultImpl) { | 
|  | var element = libraryResult.element; | 
|  | element.classes.forEach(addElements); | 
|  | element.enums.forEach(addElements); | 
|  | element.extensionTypes.forEach(addElements); | 
|  | element.mixins.forEach(addElements); | 
|  | } | 
|  | } | 
|  | } | 
|  | return elements; | 
|  | } | 
|  |  | 
|  | /// Return the prefixes used to reference the [element] in any of the | 
|  | /// compilation units in the [library]. The returned set will include an empty | 
|  | /// string if the element is referenced without a prefix. | 
|  | Future<Set<String>> prefixesUsedInLibrary( | 
|  | LibraryElementImpl library, Element2 element) async { | 
|  | var prefixes = <String>{}; | 
|  | for (var unit in library.units) { | 
|  | var index = await _driver.getIndex(unit.source.fullName); | 
|  | if (index != null) { | 
|  | _IndexRequest request = _IndexRequest(index); | 
|  | int elementId = request.findElementId(element); | 
|  | if (elementId != -1) { | 
|  | var prefixList = index.elementImportPrefixes[elementId].split(','); | 
|  | prefixes.addAll(prefixList); | 
|  | } | 
|  | } | 
|  | } | 
|  | return prefixes; | 
|  | } | 
|  |  | 
|  | /// Returns references to the [element]. | 
|  | Future<List<SearchResult>> references( | 
|  | Element2? element, SearchedFiles searchedFiles) async { | 
|  | if (element == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | ElementKind kind = element.kind; | 
|  | if (element is ExtensionElement2 || | 
|  | element is InterfaceElement2 || | 
|  | element is SetterElement || | 
|  | element is TypeAliasElement2) { | 
|  | return _searchReferences(element, searchedFiles); | 
|  | } else if (element is ConstructorElement2) { | 
|  | return await _searchReferences_Constructor(element, searchedFiles); | 
|  | } else if (element is GetterElement) { | 
|  | return _searchReferences_Getter(element, searchedFiles); | 
|  | } else if (element is PropertyInducingElement2) { | 
|  | return _searchReferences_Field(element, searchedFiles); | 
|  | } else if (element is LocalFunctionElement) { | 
|  | return _searchReferences_Local(element, (n) => n is Block, searchedFiles); | 
|  | } else if (element is ExecutableElement2) { | 
|  | return _searchReferences_Function(element, searchedFiles); | 
|  | } else if (element is PatternVariableElementImpl2) { | 
|  | return _searchReferences_PatternVariable(element, searchedFiles); | 
|  | } else if (kind == ElementKind.LABEL || | 
|  | kind == ElementKind.LOCAL_VARIABLE) { | 
|  | return _searchReferences_Local( | 
|  | element, | 
|  | (n) => | 
|  | n is Block || | 
|  | n is ForElement || | 
|  | n is FunctionBody || | 
|  | n is TopLevelVariableDeclaration || | 
|  | n is SwitchExpression || | 
|  | n.parent is CompilationUnit, | 
|  | searchedFiles); | 
|  | } else if (element is LibraryElementImpl) { | 
|  | return _searchReferences_Library(element, searchedFiles); | 
|  | } else if (element is FormalParameterElement) { | 
|  | return _searchReferences_Parameter(element, searchedFiles); | 
|  | } else if (element is PrefixElementImpl2) { | 
|  | return _searchReferences_Prefix(element, searchedFiles); | 
|  | } else if (element is TypeParameterElement2) { | 
|  | return _searchReferences_Local( | 
|  | element, (n) => n.parent is CompilationUnit, searchedFiles); | 
|  | } | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | Future<List<LibraryFragmentSearchMatch>> referencesLibraryFragment( | 
|  | LibraryFragment libraryFragment, | 
|  | ) async { | 
|  | var legacyElement = libraryFragment as CompilationUnitElementImpl; | 
|  | var legacyResults = await _searchReferences_CompilationUnit(legacyElement); | 
|  |  | 
|  | return legacyResults.map((match) { | 
|  | return LibraryFragmentSearchMatch( | 
|  | libraryFragment: match.enclosingFragment as LibraryFragment, | 
|  | range: SourceRange(match.offset, match.length), | 
|  | ); | 
|  | }).toList(); | 
|  | } | 
|  |  | 
|  | Future<List<LibraryFragmentSearchMatch>> referencesLibraryImport( | 
|  | LibraryImport import, | 
|  | SearchedFiles searchedFiles, | 
|  | ) async { | 
|  | var legacyElement = import as LibraryImportElementImpl; | 
|  | var legacyResults = await _searchReferences_Import( | 
|  | legacyElement, | 
|  | searchedFiles, | 
|  | ); | 
|  |  | 
|  | return legacyResults.map((match) { | 
|  | return LibraryFragmentSearchMatch( | 
|  | libraryFragment: match.enclosingFragment.libraryFragment!, | 
|  | range: SourceRange(match.offset, match.length), | 
|  | ); | 
|  | }).toList(); | 
|  | } | 
|  |  | 
|  | /// Returns subtypes of the given [type]. | 
|  | /// | 
|  | /// The [searchedFiles] are consulted to see if a file is "owned" by this | 
|  | /// [Search] object, so should be only searched by it to avoid duplicate | 
|  | /// results; and updated to take ownership if the file is not owned yet. | 
|  | Future<List<SearchResult>> subTypes( | 
|  | InterfaceElement2? type, SearchedFiles searchedFiles, | 
|  | {List<FileState>? filesToCheck}) async { | 
|  | if (type == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | await _addResults( | 
|  | results, | 
|  | type, | 
|  | searchedFiles, | 
|  | const { | 
|  | IndexRelationKind.IS_EXTENDED_BY: | 
|  | SearchResultKind.REFERENCE_IN_EXTENDS_CLAUSE, | 
|  | IndexRelationKind.IS_MIXED_IN_BY: | 
|  | SearchResultKind.REFERENCE_IN_WITH_CLAUSE, | 
|  | IndexRelationKind.IS_IMPLEMENTED_BY: | 
|  | SearchResultKind.REFERENCE_IN_IMPLEMENTS_CLAUSE, | 
|  | IndexRelationKind.CONSTRAINS: SearchResultKind.REFERENCE_IN_ON_CLAUSE, | 
|  | }, | 
|  | filesToCheck: filesToCheck, | 
|  | ); | 
|  | return results; | 
|  | } | 
|  |  | 
|  | /// Return direct [SubtypeResult]s for either the [type] or [subtype]. | 
|  | Future<List<SubtypeResult>> subtypes(SearchedFiles searchedFiles, | 
|  | {InterfaceElement2? type, SubtypeResult? subtype}) async { | 
|  | var type1 = type; | 
|  | String name; | 
|  | String id; | 
|  | if (type1 != null) { | 
|  | name = type1.name3!; | 
|  | var librarySource = type1.library2.firstFragment.source; | 
|  | var source = type1.firstFragment.libraryFragment.source; | 
|  | id = '${librarySource.uri};${source.uri};$name'; | 
|  | } else { | 
|  | name = subtype!.name; | 
|  | id = subtype.id; | 
|  | } | 
|  |  | 
|  | await _driver.discoverAvailableFiles(); | 
|  |  | 
|  | List<SubtypeResult> results = []; | 
|  |  | 
|  | // Note, this is a defensive copy. | 
|  | var files = _driver.fsState.getFilesSubtypingName(name)?.toList(); | 
|  |  | 
|  | if (files != null) { | 
|  | for (FileState file in files) { | 
|  | if (searchedFiles.add(file.path, this)) { | 
|  | var index = await _driver.getIndex(file.path); | 
|  | if (index != null) { | 
|  | var request = _IndexRequest(index); | 
|  | request.addSubtypes(id, results, file); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return results; | 
|  | } | 
|  |  | 
|  | /// Returns top-level elements with names matching the given [regExp]. | 
|  | Future<List<Element2>> topLevelElements(RegExp regExp) async { | 
|  | var elements = <Element2>[]; | 
|  |  | 
|  | void addElement(Element2 element) { | 
|  | if (!element.isSynthetic && regExp.hasMatch(element.displayName)) { | 
|  | elements.add(element); | 
|  | } | 
|  | } | 
|  |  | 
|  | List<FileState> knownFiles = _driver.fsState.knownFiles.toList(); | 
|  | for (FileState file in knownFiles) { | 
|  | var libraryResult = await _driver.getLibraryByUri(file.uriStr); | 
|  | if (libraryResult is LibraryElementResult) { | 
|  | var element = libraryResult.element2; | 
|  | element.getters.forEach(addElement); | 
|  | element.classes.forEach(addElement); | 
|  | element.enums.forEach(addElement); | 
|  | element.extensions.forEach(addElement); | 
|  | element.extensionTypes.forEach(addElement); | 
|  | element.topLevelFunctions.forEach(addElement); | 
|  | element.mixins.forEach(addElement); | 
|  | element.setters.forEach(addElement); | 
|  | element.topLevelVariables.forEach(addElement); | 
|  | element.typeAliases.forEach(addElement); | 
|  | } | 
|  | } | 
|  | return elements; | 
|  | } | 
|  |  | 
|  | /// Returns unresolved references to the given [name]. | 
|  | Future<List<SearchResult>> unresolvedMemberReferences( | 
|  | String? name, SearchedFiles searchedFiles) async { | 
|  | if (name == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Prepare the list of files that reference the name. | 
|  | var files = await _driver.getFilesReferencingName(name); | 
|  |  | 
|  | // Check the index of every file that references the element name. | 
|  | List<SearchResult> results = []; | 
|  | for (var file in files) { | 
|  | if (searchedFiles.add(file.path, this)) { | 
|  | var index = await _driver.getIndex(file.path); | 
|  | if (index != null) { | 
|  | _IndexRequest request = _IndexRequest(index); | 
|  | var fileResults = await request.getUnresolvedMemberReferences( | 
|  | name, | 
|  | const { | 
|  | IndexRelationKind.IS_READ_BY: SearchResultKind.READ, | 
|  | IndexRelationKind.IS_WRITTEN_BY: SearchResultKind.WRITE, | 
|  | IndexRelationKind.IS_READ_WRITTEN_BY: SearchResultKind.READ_WRITE, | 
|  | IndexRelationKind.IS_INVOKED_BY: SearchResultKind.INVOCATION | 
|  | }, | 
|  | () => _getUnitElement(file.path), | 
|  | ); | 
|  | results.addAll(fileResults); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<void> _addResults( | 
|  | List<SearchResult> results, | 
|  | Element2 element, | 
|  | SearchedFiles searchedFiles, | 
|  | Map<IndexRelationKind, SearchResultKind> relationToResultKind, | 
|  | {List<FileState>? filesToCheck}) async { | 
|  | // Prepare the element name. | 
|  | String name = element.displayName; | 
|  | var externalElement = element; | 
|  | if (externalElement case FormalParameterElement(:var enclosingElement2?)) { | 
|  | externalElement = enclosingElement2; | 
|  | } | 
|  | if (externalElement is ConstructorElement2) { | 
|  | name = externalElement.enclosingElement2.displayName; | 
|  | } | 
|  |  | 
|  | var elementPath = element.firstFragment.libraryFragment!.source.fullName; | 
|  | var elementFile = _driver.fsState.getExistingFromPath(elementPath); | 
|  | if (elementFile == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Prepare the list of files that reference the element name. | 
|  | var files = <FileState>[]; | 
|  | if (name.startsWith('_')) { | 
|  | String libraryPath = element.library2!.firstFragment.source.fullName; | 
|  | if (searchedFiles.add(libraryPath, this)) { | 
|  | var libraryFile = _driver.fsState.getFileForPath(libraryPath); | 
|  | var libraryKind = libraryFile.kind; | 
|  | if (libraryKind is LibraryFileKind) { | 
|  | for (var file in libraryKind.files) { | 
|  | if (file == elementFile || file.referencedNames.contains(name)) { | 
|  | files.add(file); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | if (filesToCheck != null) { | 
|  | for (FileState file in filesToCheck) { | 
|  | if (file.referencedNames.contains(name)) { | 
|  | files.add(file); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | files = await _driver.getFilesReferencingName(name); | 
|  | } | 
|  | // Add all files of the library. | 
|  | if (elementFile.kind.library case var library?) { | 
|  | for (var file in library.files) { | 
|  | if (searchedFiles.add(file.path, this)) { | 
|  | if (!files.contains(file)) { | 
|  | files.add(file); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check the index of every file that references the element name. | 
|  | for (var file in files) { | 
|  | if (searchedFiles.add(file.path, this)) { | 
|  | await _addResultsInFile( | 
|  | results, element, relationToResultKind, file.path); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Add results for [element] usage in the given [file]. | 
|  | Future<void> _addResultsInFile( | 
|  | List<SearchResult> results, | 
|  | Element2 element, | 
|  | Map<IndexRelationKind, SearchResultKind> relationToResultKind, | 
|  | String file) async { | 
|  | var index = await _driver.getIndex(file); | 
|  | if (index != null) { | 
|  | _IndexRequest request = _IndexRequest(index); | 
|  | int elementId = request.findElementId(element); | 
|  | if (elementId != -1) { | 
|  | List<SearchResult> fileResults = await request.getRelations( | 
|  | elementId, relationToResultKind, () => _getUnitElement(file)); | 
|  | results.addAll(fileResults); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Future<CompilationUnitElementImpl?> _getUnitElement(String file) async { | 
|  | var result = await _driver.getUnitElement(file); | 
|  | return result is UnitElementResultImpl ? result.fragment : null; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences( | 
|  | Element2 element, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | await _addResults(results, element, searchedFiles, | 
|  | const {IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE}); | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_CompilationUnit( | 
|  | CompilationUnitElementImpl element) async { | 
|  | String path = element.source.fullName; | 
|  |  | 
|  | var file = _driver.resourceProvider.getFile(path); | 
|  | var fileState = _driver.fsState.getExisting(file); | 
|  |  | 
|  | // If the file is not known, then it is not referenced. | 
|  | if (fileState == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Check files that reference the given file. | 
|  | var results = <SearchResult>[]; | 
|  | for (var reference in fileState.referencingFiles) { | 
|  | var index = await _driver.getIndex(reference.path); | 
|  | if (index != null) { | 
|  | var targetId = index.getLibraryFragmentId(element); | 
|  | for (var i = 0; i < index.libFragmentRefTargets.length; i++) { | 
|  | if (index.libFragmentRefTargets[i] == targetId) { | 
|  | var refUnit = await _getUnitElement(reference.path); | 
|  | results.add( | 
|  | SearchResult._( | 
|  | refUnit!, | 
|  | SearchResultKind.REFERENCE, | 
|  | index.libFragmentRefUriOffsets[i], | 
|  | index.libFragmentRefUriLengths[i], | 
|  | true, | 
|  | true, | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Constructor( | 
|  | ConstructorElement2 element, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | await _addResults(results, element, searchedFiles, const { | 
|  | IndexRelationKind.IS_INVOKED_BY: SearchResultKind.INVOCATION, | 
|  | IndexRelationKind.IS_INVOKED_BY_ENUM_CONSTANT_WITHOUT_ARGUMENTS: | 
|  | SearchResultKind.INVOCATION_BY_ENUM_CONSTANT_WITHOUT_ARGUMENTS, | 
|  | IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE, | 
|  | IndexRelationKind.IS_REFERENCED_BY_CONSTRUCTOR_TEAR_OFF: | 
|  | SearchResultKind.REFERENCE_BY_CONSTRUCTOR_TEAR_OFF, | 
|  | }); | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Field( | 
|  | PropertyInducingElement2 field, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | var getter = field.getter2; | 
|  | var setter = field.setter2; | 
|  | if (!field.isSynthetic) { | 
|  | await _addResults(results, field, searchedFiles, const { | 
|  | IndexRelationKind.IS_WRITTEN_BY: SearchResultKind.WRITE, | 
|  | IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE | 
|  | }); | 
|  | } | 
|  | if (getter != null) { | 
|  | await _addResults(results, getter, searchedFiles, const { | 
|  | IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.READ, | 
|  | IndexRelationKind.IS_INVOKED_BY: SearchResultKind.INVOCATION | 
|  | }); | 
|  | } | 
|  | if (setter != null) { | 
|  | await _addResults(results, setter, searchedFiles, | 
|  | const {IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.WRITE}); | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Function( | 
|  | Element2 element, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | await _addResults(results, element.baseElement, searchedFiles, const { | 
|  | IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE, | 
|  | IndexRelationKind.IS_INVOKED_BY: SearchResultKind.INVOCATION | 
|  | }); | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Getter( | 
|  | GetterElement getter, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | await _addResults(results, getter, searchedFiles, const { | 
|  | IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE, | 
|  | IndexRelationKind.IS_INVOKED_BY: SearchResultKind.INVOCATION | 
|  | }); | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Import( | 
|  | LibraryImportElementImpl element, SearchedFiles searchedFiles) async { | 
|  | String path = element.source.fullName; | 
|  | if (!searchedFiles.add(path, this)) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | LibraryElementImpl libraryElement = element.library; | 
|  | for (var unitElement in libraryElement.units) { | 
|  | String unitPath = unitElement.source.fullName; | 
|  | var unitResult = await _driver.getResolvedUnit(unitPath); | 
|  | if (unitResult is ResolvedUnitResult) { | 
|  | var visitor = ImportElementReferencesVisitor(element, unitElement); | 
|  | unitResult.unit.accept(visitor); | 
|  | results.addAll(visitor.results); | 
|  | } | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Library( | 
|  | LibraryElementImpl element, SearchedFiles searchedFiles) async { | 
|  | String path = element.source.fullName; | 
|  | if (!searchedFiles.add(path, this)) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | for (var unitElement in element.units) { | 
|  | String unitPath = unitElement.source.fullName; | 
|  | var unitResult = await _driver.getResolvedUnit(unitPath); | 
|  | if (unitResult is ResolvedUnitResultImpl) { | 
|  | var unit = unitResult.unit; | 
|  | for (var directive in unit.directives) { | 
|  | if (directive is PartOfDirectiveImpl && | 
|  | directive.element == element) { | 
|  | var targetEntity = directive.libraryName ?? directive.uri; | 
|  | results.add( | 
|  | SearchResult._( | 
|  | unit.declaredFragment!, | 
|  | SearchResultKind.REFERENCE, | 
|  | targetEntity!.offset, | 
|  | targetEntity.length, | 
|  | true, | 
|  | false, | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Local(Element2 element, | 
|  | bool Function(AstNode n) isRootNode, SearchedFiles searchedFiles) async { | 
|  | String path = element.firstFragment.libraryFragment!.source.fullName; | 
|  | if (!searchedFiles.add(path, this)) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Prepare the unit. | 
|  | var unitResult = await _driver.getResolvedUnit(path); | 
|  | if (unitResult is! ResolvedUnitResultImpl) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  | var unit = unitResult.unit; | 
|  |  | 
|  | // Prepare the node. | 
|  | var node = | 
|  | NodeLocator(element.firstFragment.nameOffset2!).searchWithin(unit); | 
|  | if (node == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Prepare the enclosing node. | 
|  | var enclosingNode = node.thisOrAncestorMatching((node) => | 
|  | isRootNode(node) || node is ClassMember || node is CompilationUnit); | 
|  | assert( | 
|  | enclosingNode != null && enclosingNode is! CompilationUnit, | 
|  | 'Did not find enclosing node for local "${element.name3}". ' | 
|  | 'Perhaps the isRootNode function is missing a condition to locate the ' | 
|  | 'outermost node where this element is in scope?', | 
|  | ); | 
|  | if (enclosingNode == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Find the matches. | 
|  | var visitor = _LocalReferencesVisitor({element}, unit.declaredFragment!); | 
|  | enclosingNode.accept(visitor); | 
|  | return visitor.results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Parameter( | 
|  | FormalParameterElement parameter, SearchedFiles searchedFiles) async { | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | results.addAll(await _searchReferences_Local( | 
|  | parameter, | 
|  | (AstNode node) { | 
|  | var parent = node.parent; | 
|  | return parent is ClassDeclaration || parent is CompilationUnit; | 
|  | }, | 
|  | searchedFiles, | 
|  | )); | 
|  | if (parameter.isNamed || | 
|  | parameter.isOptionalPositional || | 
|  | parameter.enclosingElement2 is ConstructorElement2) { | 
|  | results.addAll(await _searchReferences(parameter, searchedFiles)); | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_PatternVariable( | 
|  | PatternVariableElementImpl2 element, | 
|  | SearchedFiles searchedFiles, | 
|  | ) async { | 
|  | String path = element.firstFragment.libraryFragment.source.fullName; | 
|  | if (!searchedFiles.add(path, this)) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | var rootVariable = element.rootVariable; | 
|  | var transitiveVariables = rootVariable is JoinPatternVariableElementImpl2 | 
|  | ? rootVariable.transitiveVariables | 
|  | : [rootVariable]; | 
|  |  | 
|  | // Prepare a binding element for the variable. | 
|  | var bindElement = transitiveVariables | 
|  | .whereType<BindPatternVariableElementImpl2>() | 
|  | .firstOrNull; | 
|  | if (bindElement == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Prepare the root node for search. | 
|  | var rootNode = bindElement.node.thisOrAncestorMatching( | 
|  | (node) => node is SwitchExpression || node is Block, | 
|  | ); | 
|  | if (rootNode == null) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Find the matches. | 
|  | var visitor = _LocalReferencesVisitor( | 
|  | transitiveVariables.toSet(), | 
|  | bindElement.firstFragment.libraryFragment, | 
|  | ); | 
|  | rootNode.accept(visitor); | 
|  | return visitor.results; | 
|  | } | 
|  |  | 
|  | Future<List<SearchResult>> _searchReferences_Prefix( | 
|  | PrefixElementImpl2 element, SearchedFiles searchedFiles) async { | 
|  | String path = element.firstFragment.libraryFragment.source.fullName; | 
|  | if (!searchedFiles.add(path, this)) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | var libraryElement = element.library2; | 
|  | for (var unitElement in libraryElement.units) { | 
|  | String unitPath = unitElement.source.fullName; | 
|  | var unitResult = await _driver.getResolvedUnit(unitPath); | 
|  | if (unitResult is ResolvedUnitResult) { | 
|  | var visitor = _LocalReferencesVisitor({element}, unitElement); | 
|  | unitResult.unit.accept(visitor); | 
|  | results.addAll(visitor.results); | 
|  | } | 
|  | } | 
|  | return results; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Container that keeps track of file owners. | 
|  | class SearchedFiles { | 
|  | final Map<String, Search> pathOwners = {}; | 
|  | final Map<Uri, Search> uriOwners = {}; | 
|  |  | 
|  | bool add(String path, Search search) { | 
|  | var fsState = search._driver.fsState; | 
|  | var fileState = fsState.getExistingFromPath(path); | 
|  | if (fileState == null) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | var pathOwner = pathOwners[path]; | 
|  | var uriOwner = uriOwners[fileState.uri]; | 
|  | if (pathOwner == null && uriOwner == null) { | 
|  | pathOwners[path] = search; | 
|  | uriOwners[fileState.uri] = search; | 
|  | return true; | 
|  | } | 
|  | return identical(pathOwner, search) && identical(uriOwner, search); | 
|  | } | 
|  |  | 
|  | void ownAnalyzed(Search search) { | 
|  | for (var path in search._driver.addedFiles) { | 
|  | if (path.endsWith('.dart')) { | 
|  | add(path, search); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ownKnown(Search search) { | 
|  | for (var file in search._driver.knownFiles) { | 
|  | var path = file.path; | 
|  | if (path.endsWith('.dart')) { | 
|  | add(path, search); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// A single search result. | 
|  | class SearchResult { | 
|  | /// The deep most fragment that contains this result. | 
|  | final Fragment enclosingFragment; | 
|  |  | 
|  | /// The kind of the element usage. | 
|  | final SearchResultKind kind; | 
|  |  | 
|  | /// The offset relative to the beginning of the containing file. | 
|  | final int offset; | 
|  |  | 
|  | /// The length of the usage in the containing file context. | 
|  | final int length; | 
|  |  | 
|  | /// Whether a field or a method is using with a qualifier. | 
|  | final bool isResolved; | 
|  |  | 
|  | /// Whether the result is a resolved reference to the element. | 
|  | final bool isQualified; | 
|  |  | 
|  | SearchResult._(this.enclosingFragment, this.kind, this.offset, this.length, | 
|  | this.isResolved, this.isQualified); | 
|  |  | 
|  | @override | 
|  | String toString() { | 
|  | StringBuffer buffer = StringBuffer(); | 
|  | buffer.write("SearchResult(kind="); | 
|  | buffer.write(kind); | 
|  | buffer.write(", enclosingFragment="); | 
|  | buffer.write(enclosingFragment); | 
|  | buffer.write(", offset="); | 
|  | buffer.write(offset); | 
|  | buffer.write(", length="); | 
|  | buffer.write(length); | 
|  | buffer.write(", isResolved="); | 
|  | buffer.write(isResolved); | 
|  | buffer.write(", isQualified="); | 
|  | buffer.write(isQualified); | 
|  | buffer.write(")"); | 
|  | return buffer.toString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// The kind of reference in a [SearchResult]. | 
|  | enum SearchResultKind { | 
|  | READ, | 
|  | READ_WRITE, | 
|  | WRITE, | 
|  | INVOCATION, | 
|  | INVOCATION_BY_ENUM_CONSTANT_WITHOUT_ARGUMENTS, | 
|  | REFERENCE, | 
|  | REFERENCE_BY_CONSTRUCTOR_TEAR_OFF, | 
|  | REFERENCE_IN_EXTENDS_CLAUSE, | 
|  | REFERENCE_IN_WITH_CLAUSE, | 
|  | REFERENCE_IN_ON_CLAUSE, | 
|  | REFERENCE_IN_IMPLEMENTS_CLAUSE, | 
|  | } | 
|  |  | 
|  | /// A single subtype of a type. | 
|  | class SubtypeResult { | 
|  | /// The URI of the library. | 
|  | final String libraryUri; | 
|  |  | 
|  | /// The identifier of the subtype. | 
|  | final String id; | 
|  |  | 
|  | /// The name of the subtype. | 
|  | final String name; | 
|  |  | 
|  | /// The names of instance members declared in the class. | 
|  | final List<String> members; | 
|  |  | 
|  | SubtypeResult(this.libraryUri, this.id, this.name, this.members); | 
|  |  | 
|  | @override | 
|  | String toString() => id; | 
|  | } | 
|  |  | 
|  | class WorkspaceSymbols { | 
|  | final List<Declaration> declarations = []; | 
|  | final List<String> files = []; | 
|  | final Map<String, int> _pathToIndex = {}; | 
|  |  | 
|  | /// Whether this search was marked cancelled before it completed. | 
|  | bool cancelled = false; | 
|  |  | 
|  | bool hasMoreDeclarationsThan(int? maxResults) { | 
|  | return maxResults != null && declarations.length >= maxResults; | 
|  | } | 
|  |  | 
|  | int _getPathIndex(String path) { | 
|  | var index = _pathToIndex[path]; | 
|  | if (index == null) { | 
|  | index = files.length; | 
|  | files.add(path); | 
|  | _pathToIndex[path] = index; | 
|  | } | 
|  | return index; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Searches through [fileEntries] for declarations. | 
|  | class _FindDeclarations { | 
|  | final List<MapEntry<Uri, AnalysisDriver>> fileEntries; | 
|  | final WorkspaceSymbols result; | 
|  | final int? maxResults; | 
|  | final String pattern; | 
|  | final FuzzyMatcher matcher; | 
|  | final String? onlyForFile; | 
|  | final OperationPerformanceImpl performance; | 
|  |  | 
|  | _FindDeclarations( | 
|  | this.fileEntries, | 
|  | this.result, | 
|  | this.pattern, | 
|  | this.matcher, | 
|  | this.maxResults, { | 
|  | this.onlyForFile, | 
|  | required this.performance, | 
|  | }); | 
|  |  | 
|  | /// Add matching declarations to the [result]. | 
|  | Future<void> compute(CancellationToken? cancellationToken) async { | 
|  | if (result.hasMoreDeclarationsThan(maxResults)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (cancellationToken != null && | 
|  | cancellationToken.isCancellationRequested) { | 
|  | result.cancelled = true; | 
|  | return; | 
|  | } | 
|  |  | 
|  | var filesProcessed = 0; | 
|  | try { | 
|  | for (var entry in fileEntries) { | 
|  | var uri = entry.key; | 
|  | var analysisDriver = entry.value; | 
|  |  | 
|  | var libraryElement = await performance.runAsync( | 
|  | 'getLibraryByUri', | 
|  | (performance) async { | 
|  | var result = await analysisDriver.getLibraryByUri('$uri'); | 
|  | if (result is LibraryElementResultImpl) { | 
|  | return result.element; | 
|  | } | 
|  | return null; | 
|  | }, | 
|  | ); | 
|  |  | 
|  | if (libraryElement != null) { | 
|  | // Check if there is any name that could match the pattern. | 
|  | var match = libraryElement.nameUnion.contains(pattern); | 
|  | if (!match) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | var filePath = libraryElement.firstFragment.source.fullName; | 
|  | if (onlyForFile != null && filePath != onlyForFile) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | performance.run('libraryDeclarations', (performance) { | 
|  | var finder = _FindLibraryDeclarations( | 
|  | libraryElement.firstFragment, | 
|  | result, | 
|  | maxResults, | 
|  | matcher, | 
|  | result.declarations.add, | 
|  | ); | 
|  | finder.compute(cancellationToken); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Periodically yield and check cancellation token. | 
|  | if (cancellationToken != null && (filesProcessed++) % 20 == 0) { | 
|  | await null; // allow cancellation requests to be processed. | 
|  | if (cancellationToken.isCancellationRequested) { | 
|  | result.cancelled = true; | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | } on _MaxNumberOfDeclarationsError { | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class _FindLibraryDeclarations { | 
|  | final LibraryElementImpl library; | 
|  | final WorkspaceSymbols result; | 
|  | final int? maxResults; | 
|  | final FuzzyMatcher matcher; | 
|  | final void Function(Declaration) collect; | 
|  |  | 
|  | _FindLibraryDeclarations( | 
|  | CompilationUnitElementImpl unit, | 
|  | this.result, | 
|  | this.maxResults, | 
|  | this.matcher, | 
|  | this.collect, | 
|  | ) : library = unit.element; | 
|  |  | 
|  | void compute(CancellationToken? cancellationToken) { | 
|  | if (result.hasMoreDeclarationsThan(maxResults)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | _addClasses(library.classes); | 
|  | _addGetters(library.getters); | 
|  | _addClasses(library.enums); | 
|  | _addClasses(library.mixins); | 
|  | _addExtensions(library.extensions); | 
|  | _addClasses(library.extensionTypes); | 
|  | _addSetters(library.setters); | 
|  | _addFunctions(library.topLevelFunctions); | 
|  | _addVariables(library.topLevelVariables); | 
|  | _addTypeAliases(library.typeAliases); | 
|  | } | 
|  |  | 
|  | void _addClasses(List<InterfaceElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | _addDeclaration(element, element.name3!); | 
|  | _addGetters(element.getters2); | 
|  | _addConstructors(element.constructors2); | 
|  | _addFields(element.fields2); | 
|  | _addMethods(element.methods2); | 
|  | _addSetters(element.setters2); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addConstructors(List<ConstructorElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | if (!element.isSynthetic) { | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addDeclaration(Element2 element, String name) { | 
|  | if (result.hasMoreDeclarationsThan(maxResults)) { | 
|  | throw const _MaxNumberOfDeclarationsError(); | 
|  | } | 
|  |  | 
|  | if (matcher.score(name) < 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var enclosing = element.enclosingElement2; | 
|  |  | 
|  | String? className; | 
|  | String? mixinName; | 
|  | if (enclosing is EnumElement2) { | 
|  | // skip | 
|  | } else if (enclosing is MixinElement2) { | 
|  | mixinName = enclosing.name3; | 
|  | } else if (enclosing is InterfaceElement2) { | 
|  | className = enclosing.name3; | 
|  | } | 
|  |  | 
|  | var kind = _getSearchElementKind(element); | 
|  | if (kind == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | String? parameters; | 
|  | if (element is ExecutableElement2) { | 
|  | var displayString = element.displayString2(); | 
|  | var parameterIndex = displayString.indexOf('('); | 
|  | if (parameterIndex > 0) { | 
|  | parameters = displayString.substring(parameterIndex); | 
|  | } | 
|  | } | 
|  |  | 
|  | var firstFragment = element.firstFragment; | 
|  | var libraryFragment = firstFragment.libraryFragment; | 
|  | if (libraryFragment == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var filePath = libraryFragment.source.fullName; | 
|  |  | 
|  | var locationOffset = firstFragment.nameOffset2; | 
|  | if (locationOffset == null) { | 
|  | if (firstFragment is ConstructorFragment) { | 
|  | locationOffset = firstFragment.typeNameOffset; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (locationOffset == null) { | 
|  | return; | 
|  | } | 
|  | var lineInfo = libraryFragment.lineInfo; | 
|  | var locationStart = lineInfo.getLocation(locationOffset); | 
|  |  | 
|  | var fragmentImpl = | 
|  | firstFragment as ElementImpl; // to access codeOffset/codeLength | 
|  |  | 
|  | collect( | 
|  | Declaration( | 
|  | result._getPathIndex(filePath), | 
|  | lineInfo, | 
|  | name, | 
|  | kind, | 
|  | locationOffset, | 
|  | locationStart.lineNumber, | 
|  | locationStart.columnNumber, | 
|  | fragmentImpl.codeOffset ?? 0, | 
|  | fragmentImpl.codeLength ?? 0, | 
|  | className, | 
|  | mixinName, | 
|  | parameters, | 
|  | ), | 
|  | ); | 
|  | } | 
|  |  | 
|  | void _addExtensions(List<ExtensionElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | var name = element.name3; | 
|  | if (name != null) { | 
|  | _addDeclaration(element, name); | 
|  | } | 
|  | _addFields(element.fields2); | 
|  | _addGetters(element.getters2); | 
|  | _addMethods(element.methods2); | 
|  | _addSetters(element.setters2); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addFields(List<FieldElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | if (!element.isSynthetic) { | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addFunctions(List<TopLevelFunctionElement> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addGetters(List<GetterElement> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | if (!element.isSynthetic) { | 
|  | _addDeclaration(element, element.displayName); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addMethods(List<MethodElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addSetters(List<SetterElement> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | if (!element.isSynthetic) { | 
|  | _addDeclaration(element, element.displayName); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addTypeAliases(List<TypeAliasElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addVariables(List<TopLevelVariableElement2> elements) { | 
|  | for (var i = 0; i < elements.length; i++) { | 
|  | var element = elements[i]; | 
|  | if (!element.isSynthetic) { | 
|  | _addDeclaration(element, element.name3!); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class _IndexRequest { | 
|  | final AnalysisDriverUnitIndex index; | 
|  |  | 
|  | _IndexRequest(this.index); | 
|  |  | 
|  | void addSubtypes( | 
|  | String superIdString, List<SubtypeResult> results, FileState file) { | 
|  | var superId = index.getStringId(superIdString); | 
|  | if (superId == -1) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var superIndex = _findFirstOccurrence(index.supertypes, superId); | 
|  | if (superIndex == -1) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | var library = file.kind.library; | 
|  | if (library == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (; | 
|  | superIndex < index.supertypes.length && | 
|  | index.supertypes[superIndex] == superId; | 
|  | superIndex++) { | 
|  | var subtype = index.subtypes[superIndex]; | 
|  | var name = index.strings[subtype.name]; | 
|  | var subId = '${library.file.uriStr};${file.uriStr};$name'; | 
|  | results.add(SubtypeResult( | 
|  | library.file.uriStr, | 
|  | subId, | 
|  | name, | 
|  | subtype.members.map((m) => index.strings[m]).toList(), | 
|  | )); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Return the [element]'s identifier in the [index] or `-1` if the | 
|  | /// [element] is not referenced in the [index]. | 
|  | int findElementId(Element2 element) { | 
|  | IndexElementInfo info = IndexElementInfo(element); | 
|  | element = info.element; | 
|  | // Find the id of the element's unit. | 
|  | int unitId = getUnitId(element); | 
|  | if (unitId == -1) { | 
|  | return -1; | 
|  | } | 
|  | // Prepare information about the element. | 
|  | var components = ElementNameComponents(element); | 
|  | int unitMemberId = index.getStringId(components.unitMemberName); | 
|  | if (unitMemberId == -1) { | 
|  | return -1; | 
|  | } | 
|  | int classMemberId = index.getStringId(components.classMemberName); | 
|  | if (classMemberId == -1) { | 
|  | return -1; | 
|  | } | 
|  | int parameterId = index.getStringId(components.parameterName); | 
|  | if (parameterId == -1) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Try to find the element id using classMemberId, parameterId, and kind. | 
|  | int elementId = | 
|  | _findFirstOccurrence(index.elementNameUnitMemberIds, unitMemberId); | 
|  | if (elementId == -1) { | 
|  | return -1; | 
|  | } | 
|  | for (; | 
|  | elementId < index.elementNameUnitMemberIds.length && | 
|  | index.elementNameUnitMemberIds[elementId] == unitMemberId; | 
|  | elementId++) { | 
|  | if (index.elementUnits[elementId] == unitId && | 
|  | index.elementNameClassMemberIds[elementId] == classMemberId && | 
|  | index.elementNameParameterIds[elementId] == parameterId && | 
|  | index.elementKinds[elementId] == info.kind) { | 
|  | return elementId; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /// Return a list of results where an element with the given [elementId] has | 
|  | /// a relation with the kind from [relationToResultKind]. | 
|  | /// | 
|  | /// The function [getEnclosingUnitElement] is used to lazily compute the | 
|  | /// enclosing [CompilationUnitElementImpl] if there is a relation of an | 
|  | /// interesting kind. | 
|  | Future<List<SearchResult>> getRelations( | 
|  | int elementId, | 
|  | Map<IndexRelationKind, SearchResultKind> relationToResultKind, | 
|  | Future<CompilationUnitElementImpl?> Function() | 
|  | getEnclosingUnitElement) async { | 
|  | // Find the first usage of the element. | 
|  | int i = _findFirstOccurrence(index.usedElements, elementId); | 
|  | if (i == -1) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  | // Create locations for every usage of the element. | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | CompilationUnitElementImpl? enclosingUnitElement; | 
|  | for (; | 
|  | i < index.usedElements.length && index.usedElements[i] == elementId; | 
|  | i++) { | 
|  | IndexRelationKind relationKind = index.usedElementKinds[i]; | 
|  | SearchResultKind? resultKind = relationToResultKind[relationKind]; | 
|  | if (resultKind != null) { | 
|  | int offset = index.usedElementOffsets[i]; | 
|  | enclosingUnitElement ??= await getEnclosingUnitElement(); | 
|  | if (enclosingUnitElement != null) { | 
|  | var enclosingFragment = | 
|  | _getEnclosingFragment(enclosingUnitElement, offset); | 
|  | results.add(SearchResult._( | 
|  | enclosingFragment, | 
|  | resultKind, | 
|  | offset, | 
|  | index.usedElementLengths[i], | 
|  | true, | 
|  | index.usedElementIsQualifiedFlags[i], | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  | return results; | 
|  | } | 
|  |  | 
|  | /// Return the identifier of the [CompilationUnitElementIml] containing the | 
|  | /// [element] in the [index] or `-1` if not found. | 
|  | int getUnitId(Element2 element) { | 
|  | var unitElement = getUnitElement(element); | 
|  | return index.getLibraryFragmentId(unitElement); | 
|  | } | 
|  |  | 
|  | /// Return a list of results where a class members with the given [name] is | 
|  | /// referenced with a qualifier, but is not resolved. | 
|  | Future<List<SearchResult>> getUnresolvedMemberReferences( | 
|  | String name, | 
|  | Map<IndexRelationKind, SearchResultKind> relationToResultKind, | 
|  | Future<CompilationUnitElementImpl?> Function() | 
|  | getEnclosingUnitElement) async { | 
|  | // Find the name identifier. | 
|  | int nameId = index.getStringId(name); | 
|  | if (nameId == -1) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Find the first usage of the name. | 
|  | int i = _findFirstOccurrence(index.usedNames, nameId); | 
|  | if (i == -1) { | 
|  | return const <SearchResult>[]; | 
|  | } | 
|  |  | 
|  | // Create results for every usage of the name. | 
|  | List<SearchResult> results = <SearchResult>[]; | 
|  | CompilationUnitElementImpl? enclosingUnitElement; | 
|  | for (; i < index.usedNames.length && index.usedNames[i] == nameId; i++) { | 
|  | IndexRelationKind relationKind = index.usedNameKinds[i]; | 
|  | SearchResultKind? resultKind = relationToResultKind[relationKind]; | 
|  | if (resultKind != null) { | 
|  | int offset = index.usedNameOffsets[i]; | 
|  | enclosingUnitElement ??= await getEnclosingUnitElement(); | 
|  | if (enclosingUnitElement != null) { | 
|  | var enclosingFragment = | 
|  | _getEnclosingFragment(enclosingUnitElement, offset); | 
|  | results.add(SearchResult._(enclosingFragment, resultKind, offset, | 
|  | name.length, false, index.usedNameIsQualifiedFlags[i])); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return results; | 
|  | } | 
|  |  | 
|  | /// Return the index of the first occurrence of the [value] in the | 
|  | /// [sortedList], or `-1` if the [value] is not in the list. | 
|  | int _findFirstOccurrence(List<int> sortedList, int value) { | 
|  | // Find an occurrence. | 
|  | int i = binarySearch(sortedList, value); | 
|  | if (i == -1) { | 
|  | return -1; | 
|  | } | 
|  | // Find the first occurrence. | 
|  | while (i > 0 && sortedList[i - 1] == value) { | 
|  | i--; | 
|  | } | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Visitor that adds [SearchResult]s for local elements of a block, method, | 
|  | /// class or a library - labels, local functions, local variables and | 
|  | /// parameters, type parameters, import prefixes. | 
|  | class _LocalReferencesVisitor extends RecursiveAstVisitor<void> { | 
|  | final List<SearchResult> results = <SearchResult>[]; | 
|  |  | 
|  | final Set<Element2> elements; | 
|  | final CompilationUnitElementImpl enclosingLibraryFragment; | 
|  |  | 
|  | _LocalReferencesVisitor(this.elements, this.enclosingLibraryFragment); | 
|  |  | 
|  | @override | 
|  | void visitAssignedVariablePattern(AssignedVariablePattern node) { | 
|  | if (elements.contains(node.element2)) { | 
|  | _addResult(node, SearchResultKind.WRITE); | 
|  | } | 
|  |  | 
|  | super.visitAssignedVariablePattern(node); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitExtensionOverride(ExtensionOverride node) { | 
|  | node.importPrefix?.accept(this); | 
|  | node.typeArguments?.accept(this); | 
|  | node.argumentList.accept(this); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitImportPrefixReference(ImportPrefixReference node) { | 
|  | var element = node.element2; | 
|  | if (elements.contains(element)) { | 
|  | _addResult(node.name, SearchResultKind.REFERENCE); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitNamedType(NamedType node) { | 
|  | var element = node.element2; | 
|  | if (elements.contains(element)) { | 
|  | _addResult(node.name2, SearchResultKind.REFERENCE); | 
|  | } | 
|  |  | 
|  | node.importPrefix?.accept(this); | 
|  | node.typeArguments?.accept(this); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void visitSimpleIdentifier(SimpleIdentifier node) { | 
|  | if (node.inDeclarationContext()) { | 
|  | return; | 
|  | } | 
|  | var element = node.element; | 
|  | if (elements.contains(element)) { | 
|  | var parent = node.parent; | 
|  | SearchResultKind kind = SearchResultKind.REFERENCE; | 
|  | if (element is LocalFunctionElement) { | 
|  | if (parent is MethodInvocation && parent.methodName == node) { | 
|  | kind = SearchResultKind.INVOCATION; | 
|  | } | 
|  | } else if (element is VariableElement2) { | 
|  | bool isGet = node.inGetterContext(); | 
|  | bool isSet = node.inSetterContext(); | 
|  | if (isGet && isSet) { | 
|  | kind = SearchResultKind.READ_WRITE; | 
|  | } else if (isGet) { | 
|  | if (parent is MethodInvocation && parent.methodName == node) { | 
|  | kind = SearchResultKind.INVOCATION; | 
|  | } else { | 
|  | kind = SearchResultKind.READ; | 
|  | } | 
|  | } else if (isSet) { | 
|  | kind = SearchResultKind.WRITE; | 
|  | } | 
|  | } | 
|  | _addResult(node, kind); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _addResult(SyntacticEntity entity, SearchResultKind kind) { | 
|  | bool isQualified = entity is AstNode ? entity.parent is Label : false; | 
|  | var enclosingFragment = | 
|  | _getEnclosingFragment(enclosingLibraryFragment, entity.offset); | 
|  | results.add(SearchResult._(enclosingFragment, kind, entity.offset, | 
|  | entity.length, true, isQualified)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// The marker class that is thrown to stop adding declarations. | 
|  | class _MaxNumberOfDeclarationsError { | 
|  | const _MaxNumberOfDeclarationsError(); | 
|  | } |