| Index: pkg/analyzer/lib/src/dart/analysis/search.dart
|
| diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e28a5b6dd5ba76d02ecf69aa933232c975b15938
|
| --- /dev/null
|
| +++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
|
| @@ -0,0 +1,207 @@
|
| +// 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 'dart:async';
|
| +
|
| +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/analysis/driver.dart';
|
| +import 'package:analyzer/src/dart/ast/utilities.dart';
|
| +import 'package:analyzer/src/dart/element/element.dart';
|
| +
|
| +/**
|
| + * Search support for an [AnalysisDriver].
|
| + */
|
| +class Search {
|
| + final AnalysisDriver _driver;
|
| +
|
| + Search(this._driver);
|
| +
|
| + /**
|
| + * Returns references to the element at the given [offset] in the file with
|
| + * the given [path].
|
| + */
|
| + Future<List<SearchResult>> references(String path, int offset) async {
|
| + // Search only in added files.
|
| + if (!_driver.addedFiles.contains(path)) {
|
| + return const <SearchResult>[];
|
| + }
|
| +
|
| + AnalysisResult analysisResult = await _driver.getResult(path);
|
| + CompilationUnit unit = analysisResult.unit;
|
| +
|
| + // Prepare the node.
|
| + AstNode node = new NodeLocator(offset).searchWithin(unit);
|
| + if (node == null) {
|
| + return const <SearchResult>[];
|
| + }
|
| +
|
| + // Prepare the element.
|
| + Element element = ElementLocator.locate(node);
|
| + if (element == null) {
|
| + return const <SearchResult>[];
|
| + }
|
| +
|
| + ElementKind kind = element.kind;
|
| + if (kind == ElementKind.LABEL || kind == ElementKind.LOCAL_VARIABLE) {
|
| + Block block = node.getAncestor((n) => n is Block);
|
| + return _searchReferences_Local(element, unit.element, block);
|
| + }
|
| + // TODO(scheglov) support other kinds
|
| + return [];
|
| + }
|
| +
|
| + Future<List<SearchResult>> _searchReferences_Local(
|
| + Element element,
|
| + CompilationUnitElement enclosingUnitElement,
|
| + AstNode enclosingNode) async {
|
| + _LocalReferencesVisitor visitor =
|
| + new _LocalReferencesVisitor(element, enclosingUnitElement);
|
| + enclosingNode?.accept(visitor);
|
| + return visitor.matches;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A single search result.
|
| + */
|
| +class SearchResult {
|
| + /**
|
| + * The element that is used at this result.
|
| + */
|
| + final Element element;
|
| +
|
| + /**
|
| + * The deep most element that contains this result.
|
| + */
|
| + final Element enclosingElement;
|
| +
|
| + /**
|
| + * 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;
|
| +
|
| + /**
|
| + * Is `true` if a field or a method is using with a qualifier.
|
| + */
|
| + final bool isResolved;
|
| +
|
| + /**
|
| + * Is `true` if the result is a resolved reference to [element].
|
| + */
|
| + final bool isQualified;
|
| +
|
| + SearchResult._(this.element, this.enclosingElement, this.kind, this.offset,
|
| + this.length, this.isResolved, this.isQualified);
|
| +
|
| + @override
|
| + String toString() {
|
| + StringBuffer buffer = new StringBuffer();
|
| + buffer.write("SearchResult(kind=");
|
| + buffer.write(kind);
|
| + 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(", enclosingElement=");
|
| + buffer.write(enclosingElement);
|
| + buffer.write(")");
|
| + return buffer.toString();
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The kind of reference in a [SearchResult].
|
| + */
|
| +enum SearchResultKind { READ, READ_WRITE, WRITE, INVOCATION, REFERENCE }
|
| +
|
| +/**
|
| + * 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 [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 {
|
| + final List<SearchResult> matches = <SearchResult>[];
|
| +
|
| + final Element element;
|
| + final CompilationUnitElement enclosingUnitElement;
|
| +
|
| + _LocalReferencesVisitor(this.element, this.enclosingUnitElement);
|
| +
|
| + @override
|
| + visitSimpleIdentifier(SimpleIdentifier node) {
|
| + if (node.inDeclarationContext()) {
|
| + return;
|
| + }
|
| + if (node.staticElement == element) {
|
| + AstNode parent = node.parent;
|
| + SearchResultKind kind = SearchResultKind.REFERENCE;
|
| + if (element is FunctionElement) {
|
| + 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;
|
| + }
|
| + }
|
| + _addMatch(node, kind);
|
| + }
|
| + }
|
| +
|
| + void _addMatch(AstNode node, SearchResultKind kind) {
|
| + bool isQualified = node.parent is Label;
|
| + var finder = new _ContainingElementFinder(node.offset);
|
| + enclosingUnitElement.accept(finder);
|
| + matches.add(new SearchResult._(element, finder.containingElement, kind,
|
| + node.offset, node.length, true, isQualified));
|
| + }
|
| +}
|
|
|