Index: pkg/analysis_server/lib/src/services/search/search_engine_internal.dart |
diff --git a/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart b/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart |
index 5b2b6e3f6c680a1b9c0268e688015428b1254476..cdd6a91234d6d210c77ff182ba38cb25e511af6b 100644 |
--- a/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart |
+++ b/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart |
@@ -12,10 +12,13 @@ import 'package:analysis_server/src/services/search/search_engine.dart'; |
import 'package:analyzer/dart/ast/ast.dart'; |
import 'package:analyzer/dart/ast/visitor.dart'; |
import 'package:analyzer/dart/element/element.dart'; |
+import 'package:analyzer/dart/element/visitor.dart'; |
+import 'package:analyzer/src/dart/element/element.dart'; |
import 'package:analyzer/src/dart/element/member.dart'; |
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
import 'package:analyzer/src/generated/resolver.dart' show NamespaceBuilder; |
import 'package:analyzer/src/generated/source.dart' show Source, SourceRange; |
+import 'package:analyzer/src/generated/utilities_general.dart'; |
import 'package:analyzer/src/summary/idl.dart'; |
/** |
@@ -127,7 +130,7 @@ class SearchEngineImpl implements SearchEngine { |
throw new ArgumentError('Unsupported relation kind $relationKind'); |
} |
} |
- return new SearchMatch( |
+ return new SearchMatchImpl( |
location.context, |
location.libraryUri, |
location.unitUri, |
@@ -234,7 +237,7 @@ class SearchEngineImpl implements SearchEngine { |
for (Directive directive in unit.directives) { |
if (directive is PartOfDirective && |
directive.element == libraryElement) { |
- matches.add(new SearchMatch( |
+ matches.add(new SearchMatchImpl( |
context, |
librarySource.uri.toString(), |
unitSource.uri.toString(), |
@@ -288,6 +291,174 @@ class SearchEngineImpl implements SearchEngine { |
} |
/** |
+ * Implementation of [SearchMatch]. |
+ */ |
+class SearchMatchImpl implements SearchMatch { |
+ /** |
+ * The [AnalysisContext] containing the match. |
+ */ |
+ final AnalysisContext _context; |
+ |
+ /** |
+ * The URI of the source of the library containing the match. |
+ */ |
+ final String libraryUri; |
+ |
+ /** |
+ * The URI of the source of the unit containing the match. |
+ */ |
+ final String unitUri; |
+ |
+ /** |
+ * The kind of the match. |
+ */ |
+ final MatchKind kind; |
+ |
+ /** |
+ * The source range that was matched. |
+ */ |
+ final SourceRange sourceRange; |
+ |
+ /** |
+ * Is `true` if the match is a resolved reference to some [Element]. |
+ */ |
+ final bool isResolved; |
+ |
+ /** |
+ * Is `true` if field or method access is done using qualifier. |
+ */ |
+ final bool isQualified; |
+ |
+ Source _librarySource; |
+ Source _unitSource; |
+ LibraryElement _libraryElement; |
+ Element _element; |
+ |
+ SearchMatchImpl(this._context, this.libraryUri, this.unitUri, this.kind, |
+ this.sourceRange, this.isResolved, this.isQualified); |
+ |
+ /** |
+ * Return the [Element] containing the match. Can return `null` if the unit |
+ * does not exist, or its element was invalidated, or the element cannot be |
+ * found, etc. |
+ */ |
+ Element get element { |
+ if (_element == null) { |
+ CompilationUnitElement unitElement = |
+ _context.getCompilationUnitElement(unitSource, librarySource); |
+ if (unitElement != null) { |
+ _ContainingElementFinder finder = |
+ new _ContainingElementFinder(sourceRange.offset); |
+ unitElement.accept(finder); |
+ _element = finder.containingElement; |
+ } |
+ } |
+ return _element; |
+ } |
+ |
+ /** |
+ * The absolute path of the file containing the match. |
+ */ |
+ String get file => unitSource.fullName; |
+ |
+ @override |
+ int get hashCode { |
+ return JenkinsSmiHash.hash4(libraryUri.hashCode, unitUri.hashCode, |
+ kind.hashCode, sourceRange.hashCode); |
+ } |
+ |
+ /** |
+ * Return the [LibraryElement] for the [libraryUri] in the [context]. |
+ */ |
+ LibraryElement get libraryElement { |
+ _libraryElement ??= _context.getLibraryElement(librarySource); |
+ return _libraryElement; |
+ } |
+ |
+ /** |
+ * The library [Source] of the reference. |
+ */ |
+ Source get librarySource { |
+ _librarySource ??= _context.sourceFactory.forUri(libraryUri); |
+ return _librarySource; |
+ } |
+ |
+ /** |
+ * The unit [Source] of the reference. |
+ */ |
+ Source get unitSource { |
+ _unitSource ??= _context.sourceFactory.forUri(unitUri); |
+ return _unitSource; |
+ } |
+ |
+ @override |
+ bool operator ==(Object object) { |
+ if (identical(object, this)) { |
+ return true; |
+ } |
+ if (object is SearchMatchImpl) { |
+ return kind == object.kind && |
+ libraryUri == object.libraryUri && |
+ unitUri == object.unitUri && |
+ isResolved == object.isResolved && |
+ isQualified == object.isQualified && |
+ sourceRange == object.sourceRange; |
+ } |
+ return false; |
+ } |
+ |
+ @override |
+ String toString() { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write("SearchMatch(kind="); |
+ buffer.write(kind); |
+ buffer.write(", libraryUri="); |
+ buffer.write(libraryUri); |
+ buffer.write(", unitUri="); |
+ buffer.write(unitUri); |
+ buffer.write(", range="); |
+ buffer.write(sourceRange); |
+ buffer.write(", isResolved="); |
+ buffer.write(isResolved); |
+ buffer.write(", isQualified="); |
+ buffer.write(isQualified); |
+ buffer.write(")"); |
+ return buffer.toString(); |
+ } |
+ |
+ /** |
+ * Return elements of [matches] which has not-null elements. |
+ * |
+ * When [SearchMatch.element] is not `null` we cache its value, so it cannot |
+ * become `null` later. |
+ */ |
+ static List<SearchMatch> withNotNullElement(List<SearchMatch> matches) { |
+ return matches.where((match) => match.element != null).toList(); |
+ } |
+} |
+ |
+/** |
+ * A visitor that finds the deep-most [Element] that contains the [offset]. |
+ */ |
+class _ContainingElementFinder extends GeneralizingElementVisitor { |
+ final int offset; |
+ Element containingElement; |
+ |
+ _ContainingElementFinder(this.offset); |
+ |
+ visitElement(Element element) { |
+ if (element is ElementImpl) { |
+ if (element.codeOffset != null && |
+ element.codeOffset <= offset && |
+ offset <= element.codeOffset + element.codeLength) { |
+ containingElement = element; |
+ super.visitElement(element); |
+ } |
+ } |
+ } |
+} |
+ |
+/** |
* Visitor that adds [SearchMatch]es for [importElement], both with an explicit |
* prefix or an implicit one. |
*/ |
@@ -350,7 +521,7 @@ class _ImportElementReferencesVisitor extends RecursiveAstVisitor { |
} |
void _addMatchForRange(SourceRange range) { |
- matches.add(new SearchMatch( |
+ matches.add(new SearchMatchImpl( |
context, libraryUri, unitUri, MatchKind.REFERENCE, range, true, false)); |
} |
} |
@@ -407,7 +578,7 @@ class _LocalReferencesVisitor extends RecursiveAstVisitor { |
void _addMatch(AstNode node, MatchKind kind) { |
bool isQualified = node.parent is Label; |
- matches.add(new SearchMatch(context, libraryUri, unitUri, kind, |
+ matches.add(new SearchMatchImpl(context, libraryUri, unitUri, kind, |
rangeNode(node), true, isQualified)); |
} |
} |