| // 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/element.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/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( | 
 |   LibraryFragmentImpl libraryFragment, | 
 |   int offset, | 
 | ) { | 
 |   Fragment? visitFragment(Fragment fragment) { | 
 |     var fragmentImpl = fragment as FragmentImpl; | 
 |     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(Element element) { | 
 |   if (element is EnumElement) { | 
 |     return DeclarationKind.ENUM; | 
 |   } | 
 |  | 
 |   if (element is ExtensionTypeElement) { | 
 |     return DeclarationKind.EXTENSION_TYPE; | 
 |   } | 
 |  | 
 |   if (element is MixinElement) { | 
 |     return DeclarationKind.MIXIN; | 
 |   } | 
 |  | 
 |   if (element is ClassElement) { | 
 |     if (element.isMixinApplication) { | 
 |       return DeclarationKind.CLASS_TYPE_ALIAS; | 
 |     } | 
 |     return DeclarationKind.CLASS; | 
 |   } | 
 |  | 
 |   if (element is ConstructorElement) { | 
 |     return DeclarationKind.CONSTRUCTOR; | 
 |   } | 
 |  | 
 |   if (element is ExtensionElement) { | 
 |     return DeclarationKind.EXTENSION; | 
 |   } | 
 |  | 
 |   if (element is FieldElement) { | 
 |     if (element.isEnumConstant) return DeclarationKind.ENUM_CONSTANT; | 
 |     return DeclarationKind.FIELD; | 
 |   } | 
 |  | 
 |   if (element is LocalFunctionElement || element is TopLevelFunctionElement) { | 
 |     return DeclarationKind.FUNCTION; | 
 |   } | 
 |  | 
 |   if (element is MethodElement) { | 
 |     return DeclarationKind.METHOD; | 
 |   } | 
 |  | 
 |   if (element is GetterElement) { | 
 |     return DeclarationKind.GETTER; | 
 |   } | 
 |  | 
 |   if (element is SetterElement) { | 
 |     return DeclarationKind.SETTER; | 
 |   } | 
 |  | 
 |   if (element is TypeAliasElement) { | 
 |     return DeclarationKind.TYPE_ALIAS; | 
 |   } | 
 |  | 
 |   if (element is VariableElement) { | 
 |     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 LibraryFragmentImpl enclosingLibraryFragment; | 
 |  | 
 |   late final Set<Element> 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<Element>> classMembers( | 
 |     String name, | 
 |     SearchedFiles searchedFiles, | 
 |   ) async { | 
 |     var elements = <Element>[]; | 
 |  | 
 |     void addElement(Element element) { | 
 |       if (!element.isSynthetic && element.displayName == name) { | 
 |         elements.add(element); | 
 |       } | 
 |     } | 
 |  | 
 |     void addElements(InterfaceElement element) { | 
 |       element.getters.forEach(addElement); | 
 |       element.setters.forEach(addElement); | 
 |       element.fields.forEach(addElement); | 
 |       element.methods.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.element2; | 
 |           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, | 
 |     Element 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( | 
 |     Element? element, | 
 |     SearchedFiles searchedFiles, | 
 |   ) async { | 
 |     if (element == null) { | 
 |       return const <SearchResult>[]; | 
 |     } | 
 |  | 
 |     ElementKind kind = element.kind; | 
 |     if (element is ExtensionElement || | 
 |         element is InterfaceElement || | 
 |         element is SetterElement || | 
 |         element is TypeAliasElement) { | 
 |       return _searchReferences(element, searchedFiles); | 
 |     } else if (element is ConstructorElement) { | 
 |       return await _searchReferences_Constructor(element, searchedFiles); | 
 |     } else if (element is GetterElement) { | 
 |       return _searchReferences_Getter(element, searchedFiles); | 
 |     } else if (element is PropertyInducingElement) { | 
 |       return _searchReferences_Field(element, searchedFiles); | 
 |     } else if (element is LocalFunctionElement) { | 
 |       return _searchReferences_Local(element, (n) => n is Block, searchedFiles); | 
 |     } else if (element is ExecutableElement) { | 
 |       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 TypeParameterElement) { | 
 |       return _searchReferences_Local( | 
 |         element, | 
 |         (n) => n.parent is CompilationUnit, | 
 |         searchedFiles, | 
 |       ); | 
 |     } | 
 |     return const <SearchResult>[]; | 
 |   } | 
 |  | 
 |   Future<List<LibraryFragmentSearchMatch>> referencesLibraryFragment( | 
 |     LibraryFragment libraryFragment, | 
 |   ) async { | 
 |     var legacyElement = libraryFragment as LibraryFragmentImpl; | 
 |     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 { | 
 |     import as LibraryImportImpl; | 
 |     var legacyResults = await _searchReferences_Import(import, 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( | 
 |     InterfaceElement? 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, { | 
 |     InterfaceElement? 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<Element>> topLevelElements(RegExp regExp) async { | 
 |     var elements = <Element>[]; | 
 |  | 
 |     void addElement(Element 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, | 
 |     Element 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 enclosingElement?)) { | 
 |       externalElement = enclosingElement; | 
 |     } | 
 |     if (externalElement is ConstructorElement) { | 
 |       name = externalElement.enclosingElement.displayName; | 
 |     } | 
 |  | 
 |     var elementPath = element.firstFragment.libraryFragment?.source.fullName; | 
 |     if (elementPath == null) { | 
 |       return; | 
 |     } | 
 |  | 
 |     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, | 
 |     Element 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<LibraryFragmentImpl?> _getUnitElement(String file) async { | 
 |     var result = await _driver.getUnitElement(file); | 
 |     return result is UnitElementResultImpl ? result.fragment : null; | 
 |   } | 
 |  | 
 |   Future<List<SearchResult>> _searchReferences( | 
 |     Element 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( | 
 |     LibraryFragmentImpl 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( | 
 |     ConstructorElement 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( | 
 |     PropertyInducingElement 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( | 
 |     Element 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( | 
 |     LibraryImportImpl element, | 
 |     SearchedFiles searchedFiles, | 
 |   ) async { | 
 |     String path = element.libraryFragment.source.fullName; | 
 |     if (!searchedFiles.add(path, this)) { | 
 |       return const <SearchResult>[]; | 
 |     } | 
 |  | 
 |     List<SearchResult> results = <SearchResult>[]; | 
 |     LibraryElementImpl libraryElement = element.libraryFragment.element; | 
 |     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) { | 
 |             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( | 
 |     Element element, | 
 |     bool Function(AstNode n) isRootNode, | 
 |     SearchedFiles searchedFiles, | 
 |   ) async { | 
 |     String? path = element.firstFragment.libraryFragment?.source.fullName; | 
 |     if (path == null || !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; | 
 |  | 
 |     var node = unit.nodeCovering(offset: element.firstFragment.nameOffset2!); | 
 |     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.enclosingElement is ConstructorElement) { | 
 |       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.element2; | 
 |           } | 
 |           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( | 
 |     LibraryFragmentImpl 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<InterfaceElement> elements) { | 
 |     for (var i = 0; i < elements.length; i++) { | 
 |       var element = elements[i]; | 
 |       _addDeclaration(element, element.name3!); | 
 |       _addGetters(element.getters); | 
 |       _addConstructors(element.constructors2); | 
 |       _addFields(element.fields); | 
 |       _addMethods(element.methods); | 
 |       _addSetters(element.setters); | 
 |     } | 
 |   } | 
 |  | 
 |   void _addConstructors(List<ConstructorElement> elements) { | 
 |     for (var i = 0; i < elements.length; i++) { | 
 |       var element = elements[i]; | 
 |       if (!element.isSynthetic) { | 
 |         _addDeclaration(element, element.name3!); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void _addDeclaration(Element element, String name) { | 
 |     if (result.hasMoreDeclarationsThan(maxResults)) { | 
 |       throw const _MaxNumberOfDeclarationsError(); | 
 |     } | 
 |  | 
 |     if (matcher.score(name) < 0) { | 
 |       return; | 
 |     } | 
 |  | 
 |     var enclosing = element.enclosingElement; | 
 |  | 
 |     String? className; | 
 |     String? mixinName; | 
 |     if (enclosing is EnumElement) { | 
 |       // skip | 
 |     } else if (enclosing is MixinElement) { | 
 |       mixinName = enclosing.name3; | 
 |     } else if (enclosing is InterfaceElement) { | 
 |       className = enclosing.name3; | 
 |     } | 
 |  | 
 |     var kind = _getSearchElementKind(element); | 
 |     if (kind == null) { | 
 |       return; | 
 |     } | 
 |  | 
 |     String? parameters; | 
 |     if (element is ExecutableElement) { | 
 |       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 FragmentImpl; // 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<ExtensionElement> 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.fields); | 
 |       _addGetters(element.getters); | 
 |       _addMethods(element.methods); | 
 |       _addSetters(element.setters); | 
 |     } | 
 |   } | 
 |  | 
 |   void _addFields(List<FieldElement> 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<MethodElement> 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<TypeAliasElement> elements) { | 
 |     for (var i = 0; i < elements.length; i++) { | 
 |       var element = elements[i]; | 
 |       _addDeclaration(element, element.name3!); | 
 |     } | 
 |   } | 
 |  | 
 |   void _addVariables(List<TopLevelVariableElement> 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(Element 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 [LibraryFragmentImpl] if there is a relation of an | 
 |   /// interesting kind. | 
 |   Future<List<SearchResult>> getRelations( | 
 |     int elementId, | 
 |     Map<IndexRelationKind, SearchResultKind> relationToResultKind, | 
 |     Future<LibraryFragmentImpl?> 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>[]; | 
 |     LibraryFragmentImpl? 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(Element 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<LibraryFragmentImpl?> 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>[]; | 
 |     LibraryFragmentImpl? 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<Element> elements; | 
 |   final LibraryFragmentImpl 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.name, 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 VariableElement) { | 
 |         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(); | 
 | } |