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; |
- } |
-} |