Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Unified Diff: pkg/analysis_server/lib/src/services/completion/completion_target.dart

Issue 1347283005: refactor ContributionSorter to use new API (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: merge Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/lib/src/services/completion/completion_target.dart
diff --git a/pkg/analysis_server/lib/src/services/completion/completion_target.dart b/pkg/analysis_server/lib/src/services/completion/completion_target.dart
deleted file mode 100644
index a1f315bbab97740897ac0b2176ad04ae8b499786..0000000000000000000000000000000000000000
--- a/pkg/analysis_server/lib/src/services/completion/completion_target.dart
+++ /dev/null
@@ -1,356 +0,0 @@
-import 'package:analyzer/src/generated/ast.dart';
-import 'package:analyzer/src/generated/element.dart';
-import 'package:analyzer/src/generated/scanner.dart';
-import 'package:analyzer/src/generated/utilities_dart.dart';
-
-int _computeArgIndex(AstNode containingNode, Object entity) {
- var argList = containingNode;
- if (argList is ArgumentList) {
- NodeList<Expression> args = argList.arguments;
- for (int index = 0; index < args.length; ++index) {
- if (entity == args[index]) {
- return index;
- }
- }
- if (args.isEmpty) {
- return 0;
- }
- }
- return null;
-}
-
-/**
- * A CompletionTarget represents an edge in the parse tree which connects an
- * AST node (the [containingNode] of the completion) to one of its children
- * (the [entity], which represents the place in the parse tree where the newly
- * completed text will be inserted).
- *
- * To illustrate, consider the following snippet of code, and its associated
- * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial
- * AST nodes are not shown).
- *
- * ___N (function declaration)
- * / \
- * / __N_______ (function body)
- * / / |a \
- * / / N______ \ (statement)
- * / / / \ \
- * / / N____ \ \ (assignment expression)
- * | / /| \ \ \
- * . | / | _N___ \ | ("as" expression)
- * . | | | / | \c | |
- * . | N | N |b N | | (simple identifiers)
- * | | | | | | | |
- * T T T T T T T T
- * m() { foo = bar as Baz; }
- *
- * The Completion target is usually placed as high in the tree as possible so
- * that we can produce the most meaningful completions with minimal effort.
- * For instance, if the cursor is inside the identifier "foo", the completion
- * target will be the edge marked "a", so that we will produce all completions
- * that could possibly start a statement, even those which would conflict with
- * the current parse (such as the keyword "for", which begins a "for"
- * statement). As a consequence of this, the [entity] will usually not be the
- * first child of the [containingNode] node.
- *
- * Note that the [containingNode] is always an AST node, but the [entity] may
- * not be. For instance, if the cursor is inside the keyword "as", the
- * completion target will be the edge marked "b", so the [entity] is the token
- * "as".
- *
- * If the cursor is between tokens, the completion target is usually associated
- * with the token that follows the cursor (since that's the token that will be
- * displaced when the new text is inserted). For example, if the cursor is
- * after the "{" character, then the completion target will be the edge marked
- * "a", just as it is if the cursor is inside the identifier "foo". However
- * there is one exception: if the cursor is at the rightmost edge of a keyword
- * or identifier, then the completion target is associated with that token,
- * since any further letters typed will change the meaning of the identifier or
- * keyword, rather than creating a new token. So for instance, if the cursor
- * is just after the "s" of "as", the completion target will be the edge marked
- * "b", but if the cursor target is after the first space following "as", then
- * the completion target will be the edge marked "c".
- *
- * If the file is empty, or the cursor is after all the text in the file, then
- * there may be no edge in the parse tree which is appropriate to act as the
- * completion target; in this case, [entity] is set to null and
- * [containingNode] is set to the CompilationUnit.
- */
-class CompletionTarget {
- /**
- * The context in which the completion is occurring. This is the AST node
- * which is a direct parent of [entity].
- */
- final AstNode containingNode;
-
- /**
- * The entity which the completed text will replace (or which will be
- * displaced once the completed text is inserted). This may be an AstNode or
- * a Token, or it may be null if the cursor is after all tokens in the file.
- *
- * Usually, the entity won't be the first child of the [containingNode] (this
- * is a consequence of placing the completion target as high in the tree as
- * possible). However, there is one exception: when the cursor is inside of
- * a multi-character token which is not a keyword or identifier (e.g. a
- * comment, or a token like "+=", the entity will be always be the token.
- */
- final Object entity;
-
- /**
- * If the target is an argument in an [ArgumentList], then this is the index
- * of the argument in the list, otherwise this is `null`.
- */
- final int argIndex;
-
- /**
- * Compute the appropriate [CompletionTarget] for the given [offset] within
- * the [compilationUnit].
- */
- factory CompletionTarget.forOffset(
- CompilationUnit compilationUnit, int offset) {
- // The precise algorithm is as follows. We perform a depth-first search of
- // all edges in the parse tree (both those that point to AST nodes and
- // those that point to tokens), visiting parents before children. The
- // first edge which points to an entity satisfying either _isCandidateToken
- // or _isCandidateNode is the completion target. If no edge is found that
- // satisfies these two predicates, then we set the completion target entity
- // to null and the containingNode to the compilationUnit.
- //
- // Note that if a token is not a candidate target, then none of the tokens
- // that precede it are candidate targets either. Therefore any entity
- // whose last token is not a candidate target can be skipped. This lets us
- // prune the search to the point where no recursion is necessary; at each
- // step in the process we know exactly which child node we need to proceed
- // to.
- AstNode containingNode = compilationUnit;
- outerLoop: while (true) {
- if (containingNode is Comment) {
- // Comments are handled specially: we descend into any CommentReference
- // child node that contains the cursor offset.
- Comment comment = containingNode;
- for (CommentReference commentReference in comment.references) {
- if (commentReference.offset <= offset &&
- offset <= commentReference.end) {
- containingNode = commentReference;
- continue outerLoop;
- }
- }
- }
- for (var entity in containingNode.childEntities) {
- if (entity is Token) {
- if (_isCandidateToken(entity, offset)) {
- // Target found.
- return new CompletionTarget._(containingNode, entity);
- } else {
- // Since entity is a token, we don't need to look inside it; just
- // proceed to the next entity.
- continue;
- }
- } else if (entity is AstNode) {
- // If the last token in the node isn't a candidate target, then
- // neither the node nor any of its descendants can possibly be the
- // completion target, so we can skip the node entirely.
- if (!_isCandidateToken(entity.endToken, offset)) {
- continue;
- }
-
- // If the node is a candidate target, then we are done.
- if (_isCandidateNode(entity, offset)) {
- // Check to see if the offset is in a preceeding comment
- Token commentToken = _getContainingCommentToken(entity, offset);
- if (commentToken != null) {
- entity = commentToken;
- // If the preceeding comment is dartdoc token then update
- // the containing node to be the dartdoc comment
- Comment docComment =
- _getContainingDocComment(containingNode, commentToken);
- if (docComment != null) {
- containingNode = docComment;
- }
- }
- return new CompletionTarget._(containingNode, entity);
- }
-
- // Otherwise, the completion target is somewhere inside the entity,
- // so we need to jump to the start of the outer loop to examine its
- // contents.
- containingNode = entity;
- continue outerLoop;
- } else {
- // Unexpected entity found (all entities in a parse tree should be
- // AST nodes or tokens).
- assert(false);
- }
- }
-
- // No completion target found. It should only be possible to reach here
- // the first time through the outer loop (since we only jump to the start
- // of the outer loop after determining that the completion target is
- // inside an entity). We can check that assumption by verifying that
- // containingNode is still the compilationUnit.
- assert(identical(containingNode, compilationUnit));
-
- // Since no completion target was found, we set the completion target
- // entity to null and use the compilationUnit as the parent.
- return new CompletionTarget._(compilationUnit, null);
- }
- }
-
- /**
- * Create a [CompletionTarget] holding the given [containingNode] and
- * [entity].
- */
- CompletionTarget._(AstNode containingNode, Object entity)
- : this.containingNode = containingNode,
- this.entity = entity,
- this.argIndex = _computeArgIndex(containingNode, entity);
-
- /**
- * Return `true` if the target is a functional argument in an argument list.
- * The target [AstNode] hierarchy *must* be resolved for this to work.
- */
- bool isFunctionalArgument() {
- if (argIndex == null) {
- return false;
- }
- AstNode argList = containingNode;
- if (argList is! ArgumentList) {
- return false;
- }
- AstNode parent = argList.parent;
- if (parent is InstanceCreationExpression) {
- DartType instType = parent.bestType;
- if (instType != null) {
- Element intTypeElem = instType.element;
- if (intTypeElem is ClassElement) {
- SimpleIdentifier constructorName = parent.constructorName.name;
- ConstructorElement constructor = constructorName != null
- ? intTypeElem.getNamedConstructor(constructorName.name)
- : intTypeElem.unnamedConstructor;
- return constructor != null &&
- _isFunctionalParameter(constructor.parameters, argIndex);
- }
- }
- } else if (parent is MethodInvocation) {
- SimpleIdentifier methodName = parent.methodName;
- if (methodName != null) {
- Element methodElem = methodName.bestElement;
- if (methodElem is MethodElement) {
- return _isFunctionalParameter(methodElem.parameters, argIndex);
- } else if (methodElem is FunctionElement) {
- return _isFunctionalParameter(methodElem.parameters, argIndex);
- }
- }
- }
- return false;
- }
-
- /**
- * Determine if the offset is contained in a preceeding comment token
- * and return that token, otherwise return `null`.
- */
- static Token _getContainingCommentToken(AstNode node, int offset) {
- if (offset >= node.offset) {
- return null;
- }
- Token token = node.beginToken;
- if (token == null) {
- return null;
- }
- token = token.precedingComments;
- while (token != null) {
- if (offset <= token.offset) {
- return null;
- }
- if (offset <= token.end) {
- if (token.type == TokenType.SINGLE_LINE_COMMENT || offset < token.end) {
- return token;
- }
- }
- token = token.next;
- }
- return null;
- }
-
- /**
- * Determine if the given token is part of the given node's dart doc.
- */
- static Comment _getContainingDocComment(AstNode node, Token token) {
- if (node is AnnotatedNode) {
- Comment docComment = node.documentationComment;
- if (docComment != null && docComment.tokens.contains(token)) {
- return docComment;
- }
- }
- return null;
- }
-
- /**
- * Determine whether [node] could possibly be the [entity] for a
- * [CompletionTarget] associated with the given [offset].
- */
- static bool _isCandidateNode(AstNode node, int offset) {
- // If the node's first token is a keyword or identifier, then the node is a
- // candidate entity if its first token is.
- Token beginToken = node.beginToken;
- if (beginToken.type == TokenType.KEYWORD ||
- beginToken.type == TokenType.IDENTIFIER) {
- return _isCandidateToken(beginToken, offset);
- }
-
- // Otherwise, the node is a candidate entity only if the offset is before
- // the beginning of the node. This ensures that completions within a token
- // (e.g. inside a literal string or inside a comment) are evaluated within
- // the context of the token itself.
- return offset <= node.offset;
- }
-
- /**
- * Determine whether [token] could possibly be the [entity] for a
- * [CompletionTarget] associated with the given [offset].
- */
- static bool _isCandidateToken(Token token, int offset) {
- // A token is considered a candidate entity if the cursor offset is (a)
- // before the start of the token, (b) within the token, (c) at the end of
- // the token and the token is a keyword or identifier, or (d) at the
- // location of the token and the token is zero length.
- if (offset < token.end) {
- return true;
- } else if (offset == token.end) {
- return token.type == TokenType.KEYWORD ||
- token.type == TokenType.IDENTIFIER ||
- token.length == 0;
- } else if (!token.isSynthetic) {
- return false;
- }
- // If the current token is synthetic, then check the previous token
- // because it may have been dropped from the parse tree
- Token previous = token.previous;
- if (offset < previous.end) {
- return true;
- } else if (offset == previous.end) {
- return token.type == TokenType.KEYWORD ||
- previous.type == TokenType.IDENTIFIER;
- } else {
- return false;
- }
- }
-
- /**
- * Return `true` if the parameter is a functional parameter.
- */
- static bool _isFunctionalParameter(
- List<ParameterElement> parameters, int paramIndex) {
- if (paramIndex < parameters.length) {
- ParameterElement param = parameters[paramIndex];
- DartType paramType = param.type;
- if (param.parameterKind == ParameterKind.NAMED) {
- // TODO(danrubel) handle named parameters
- return false;
- } else {
- return paramType is FunctionType || paramType is FunctionTypeAlias;
- }
- }
- return false;
- }
-}

Powered by Google App Engine
This is Rietveld 408576698