OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:async'; |
| 6 |
| 7 import 'package:analyzer/dart/ast/ast.dart'; |
| 8 import 'package:analyzer/dart/ast/visitor.dart'; |
| 9 import 'package:analyzer/dart/element/element.dart'; |
| 10 import 'package:analyzer/dart/element/visitor.dart'; |
| 11 import 'package:analyzer/src/dart/analysis/driver.dart'; |
| 12 import 'package:analyzer/src/dart/ast/utilities.dart'; |
| 13 import 'package:analyzer/src/dart/element/element.dart'; |
| 14 |
| 15 /** |
| 16 * Search support for an [AnalysisDriver]. |
| 17 */ |
| 18 class Search { |
| 19 final AnalysisDriver _driver; |
| 20 |
| 21 Search(this._driver); |
| 22 |
| 23 /** |
| 24 * Returns references to the element at the given [offset] in the file with |
| 25 * the given [path]. |
| 26 */ |
| 27 Future<List<SearchResult>> references(String path, int offset) async { |
| 28 // Search only in added files. |
| 29 if (!_driver.addedFiles.contains(path)) { |
| 30 return const <SearchResult>[]; |
| 31 } |
| 32 |
| 33 AnalysisResult analysisResult = await _driver.getResult(path); |
| 34 CompilationUnit unit = analysisResult.unit; |
| 35 |
| 36 // Prepare the node. |
| 37 AstNode node = new NodeLocator(offset).searchWithin(unit); |
| 38 if (node == null) { |
| 39 return const <SearchResult>[]; |
| 40 } |
| 41 |
| 42 // Prepare the element. |
| 43 Element element = ElementLocator.locate(node); |
| 44 if (element == null) { |
| 45 return const <SearchResult>[]; |
| 46 } |
| 47 |
| 48 ElementKind kind = element.kind; |
| 49 if (kind == ElementKind.LABEL || kind == ElementKind.LOCAL_VARIABLE) { |
| 50 Block block = node.getAncestor((n) => n is Block); |
| 51 return _searchReferences_Local(element, unit.element, block); |
| 52 } |
| 53 // TODO(scheglov) support other kinds |
| 54 return []; |
| 55 } |
| 56 |
| 57 Future<List<SearchResult>> _searchReferences_Local( |
| 58 Element element, |
| 59 CompilationUnitElement enclosingUnitElement, |
| 60 AstNode enclosingNode) async { |
| 61 _LocalReferencesVisitor visitor = |
| 62 new _LocalReferencesVisitor(element, enclosingUnitElement); |
| 63 enclosingNode?.accept(visitor); |
| 64 return visitor.matches; |
| 65 } |
| 66 } |
| 67 |
| 68 /** |
| 69 * A single search result. |
| 70 */ |
| 71 class SearchResult { |
| 72 /** |
| 73 * The element that is used at this result. |
| 74 */ |
| 75 final Element element; |
| 76 |
| 77 /** |
| 78 * The deep most element that contains this result. |
| 79 */ |
| 80 final Element enclosingElement; |
| 81 |
| 82 /** |
| 83 * The kind of the [element] usage. |
| 84 */ |
| 85 final SearchResultKind kind; |
| 86 |
| 87 /** |
| 88 * The offset relative to the beginning of the containing file. |
| 89 */ |
| 90 final int offset; |
| 91 |
| 92 /** |
| 93 * The length of the usage in the containing file context. |
| 94 */ |
| 95 final int length; |
| 96 |
| 97 /** |
| 98 * Is `true` if a field or a method is using with a qualifier. |
| 99 */ |
| 100 final bool isResolved; |
| 101 |
| 102 /** |
| 103 * Is `true` if the result is a resolved reference to [element]. |
| 104 */ |
| 105 final bool isQualified; |
| 106 |
| 107 SearchResult._(this.element, this.enclosingElement, this.kind, this.offset, |
| 108 this.length, this.isResolved, this.isQualified); |
| 109 |
| 110 @override |
| 111 String toString() { |
| 112 StringBuffer buffer = new StringBuffer(); |
| 113 buffer.write("SearchResult(kind="); |
| 114 buffer.write(kind); |
| 115 buffer.write(", offset="); |
| 116 buffer.write(offset); |
| 117 buffer.write(", length="); |
| 118 buffer.write(length); |
| 119 buffer.write(", isResolved="); |
| 120 buffer.write(isResolved); |
| 121 buffer.write(", isQualified="); |
| 122 buffer.write(isQualified); |
| 123 buffer.write(", enclosingElement="); |
| 124 buffer.write(enclosingElement); |
| 125 buffer.write(")"); |
| 126 return buffer.toString(); |
| 127 } |
| 128 } |
| 129 |
| 130 /** |
| 131 * The kind of reference in a [SearchResult]. |
| 132 */ |
| 133 enum SearchResultKind { READ, READ_WRITE, WRITE, INVOCATION, REFERENCE } |
| 134 |
| 135 /** |
| 136 * A visitor that finds the deep-most [Element] that contains the [offset]. |
| 137 */ |
| 138 class _ContainingElementFinder extends GeneralizingElementVisitor { |
| 139 final int offset; |
| 140 Element containingElement; |
| 141 |
| 142 _ContainingElementFinder(this.offset); |
| 143 |
| 144 visitElement(Element element) { |
| 145 if (element is ElementImpl) { |
| 146 if (element.codeOffset != null && |
| 147 element.codeOffset <= offset && |
| 148 offset <= element.codeOffset + element.codeLength) { |
| 149 containingElement = element; |
| 150 super.visitElement(element); |
| 151 } |
| 152 } |
| 153 } |
| 154 } |
| 155 |
| 156 /** |
| 157 * Visitor that adds [SearchResult]s for local elements of a block, method, |
| 158 * class or a library - labels, local functions, local variables and parameters, |
| 159 * type parameters, import prefixes. |
| 160 */ |
| 161 class _LocalReferencesVisitor extends RecursiveAstVisitor { |
| 162 final List<SearchResult> matches = <SearchResult>[]; |
| 163 |
| 164 final Element element; |
| 165 final CompilationUnitElement enclosingUnitElement; |
| 166 |
| 167 _LocalReferencesVisitor(this.element, this.enclosingUnitElement); |
| 168 |
| 169 @override |
| 170 visitSimpleIdentifier(SimpleIdentifier node) { |
| 171 if (node.inDeclarationContext()) { |
| 172 return; |
| 173 } |
| 174 if (node.staticElement == element) { |
| 175 AstNode parent = node.parent; |
| 176 SearchResultKind kind = SearchResultKind.REFERENCE; |
| 177 if (element is FunctionElement) { |
| 178 if (parent is MethodInvocation && parent.methodName == node) { |
| 179 kind = SearchResultKind.INVOCATION; |
| 180 } |
| 181 } else if (element is VariableElement) { |
| 182 bool isGet = node.inGetterContext(); |
| 183 bool isSet = node.inSetterContext(); |
| 184 if (isGet && isSet) { |
| 185 kind = SearchResultKind.READ_WRITE; |
| 186 } else if (isGet) { |
| 187 if (parent is MethodInvocation && parent.methodName == node) { |
| 188 kind = SearchResultKind.INVOCATION; |
| 189 } else { |
| 190 kind = SearchResultKind.READ; |
| 191 } |
| 192 } else if (isSet) { |
| 193 kind = SearchResultKind.WRITE; |
| 194 } |
| 195 } |
| 196 _addMatch(node, kind); |
| 197 } |
| 198 } |
| 199 |
| 200 void _addMatch(AstNode node, SearchResultKind kind) { |
| 201 bool isQualified = node.parent is Label; |
| 202 var finder = new _ContainingElementFinder(node.offset); |
| 203 enclosingUnitElement.accept(finder); |
| 204 matches.add(new SearchResult._(element, finder.containingElement, kind, |
| 205 node.offset, node.length, true, isQualified)); |
| 206 } |
| 207 } |
OLD | NEW |