Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 import 'package:analyzer/src/generated/ast.dart'; | 1 import 'package:analyzer/src/generated/ast.dart'; |
| 2 import 'package:analyzer/src/generated/element.dart'; | |
| 2 import 'package:analyzer/src/generated/scanner.dart'; | 3 import 'package:analyzer/src/generated/scanner.dart'; |
| 4 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| 5 | |
| 6 int _computeArgIndex(AstNode containingNode, Object entity) { | |
| 7 var argList = containingNode; | |
| 8 if (argList is ArgumentList) { | |
| 9 NodeList<Expression> args = argList.arguments; | |
| 10 for (int index = 0; index < args.length; ++index) { | |
| 11 if (entity == args[index]) { | |
| 12 return index; | |
| 13 } | |
| 14 } | |
| 15 if (args.isEmpty) { | |
| 16 return 0; | |
| 17 } | |
| 18 } | |
| 19 return null; | |
| 20 } | |
| 3 | 21 |
| 4 /** | 22 /** |
| 5 * A CompletionTarget represents an edge in the parse tree which connects an | 23 * A CompletionTarget represents an edge in the parse tree which connects an |
| 6 * AST node (the [containingNode] of the completion) to one of its children | 24 * AST node (the [containingNode] of the completion) to one of its children |
| 7 * (the [entity], which represents the place in the parse tree where the newly | 25 * (the [entity], which represents the place in the parse tree where the newly |
| 8 * completed text will be inserted). | 26 * completed text will be inserted). |
| 9 * | 27 * |
| 10 * To illustrate, consider the following snippet of code, and its associated | 28 * To illustrate, consider the following snippet of code, and its associated |
| 11 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial | 29 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial |
| 12 * AST nodes are not shown). | 30 * AST nodes are not shown). |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 72 * | 90 * |
| 73 * Usually, the entity won't be the first child of the [containingNode] (this | 91 * Usually, the entity won't be the first child of the [containingNode] (this |
| 74 * is a consequence of placing the completion target as high in the tree as | 92 * is a consequence of placing the completion target as high in the tree as |
| 75 * possible). However, there is one exception: when the cursor is inside of | 93 * possible). However, there is one exception: when the cursor is inside of |
| 76 * a multi-character token which is not a keyword or identifier (e.g. a | 94 * a multi-character token which is not a keyword or identifier (e.g. a |
| 77 * comment, or a token like "+=", the entity will be always be the token. | 95 * comment, or a token like "+=", the entity will be always be the token. |
| 78 */ | 96 */ |
| 79 final Object entity; | 97 final Object entity; |
| 80 | 98 |
| 81 /** | 99 /** |
| 100 * If the target is an argument in an [ArgumentList], then this is the index | |
| 101 * of the argument in the list, otherwise this is `null`. | |
| 102 */ | |
| 103 final int argIndex; | |
| 104 | |
| 105 /** | |
| 82 * Compute the appropriate [CompletionTarget] for the given [offset] within | 106 * Compute the appropriate [CompletionTarget] for the given [offset] within |
| 83 * the [compilationUnit]. | 107 * the [compilationUnit]. |
| 84 */ | 108 */ |
| 85 factory CompletionTarget.forOffset( | 109 factory CompletionTarget.forOffset( |
| 86 CompilationUnit compilationUnit, int offset) { | 110 CompilationUnit compilationUnit, int offset) { |
| 87 // The precise algorithm is as follows. We perform a depth-first search of | 111 // The precise algorithm is as follows. We perform a depth-first search of |
| 88 // all edges in the parse tree (both those that point to AST nodes and | 112 // all edges in the parse tree (both those that point to AST nodes and |
| 89 // those that point to tokens), visiting parents before children. The | 113 // those that point to tokens), visiting parents before children. The |
| 90 // first edge which points to an entity satisfying either _isCandidateToken | 114 // first edge which points to an entity satisfying either _isCandidateToken |
| 91 // or _isCandidateNode is the completion target. If no edge is found that | 115 // or _isCandidateNode is the completion target. If no edge is found that |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 // Since no completion target was found, we set the completion target | 181 // Since no completion target was found, we set the completion target |
| 158 // entity to null and use the compilationUnit as the parent. | 182 // entity to null and use the compilationUnit as the parent. |
| 159 return new CompletionTarget._(compilationUnit, null); | 183 return new CompletionTarget._(compilationUnit, null); |
| 160 } | 184 } |
| 161 } | 185 } |
| 162 | 186 |
| 163 /** | 187 /** |
| 164 * Create a [CompletionTarget] holding the given [containingNode] and | 188 * Create a [CompletionTarget] holding the given [containingNode] and |
| 165 * [entity]. | 189 * [entity]. |
| 166 */ | 190 */ |
| 167 CompletionTarget._(this.containingNode, this.entity); | 191 CompletionTarget._(AstNode containingNode, Object entity) |
| 192 : this.containingNode = containingNode, | |
| 193 this.entity = entity, | |
| 194 this.argIndex = _computeArgIndex(containingNode, entity); | |
| 195 | |
| 196 /** | |
| 197 * Return `true` if the target is a functional argument in an argument list. | |
| 198 * The target [AstNode] hierarchy *must* be resolved for this to work. | |
| 199 */ | |
| 200 bool isFunctionalArgument() { | |
| 201 if (argIndex == null) { | |
| 202 return false; | |
| 203 } | |
| 204 AstNode argList = containingNode; | |
| 205 if (argList is! ArgumentList) { | |
| 206 return false; | |
| 207 } | |
| 208 AstNode parent = argList.parent; | |
| 209 if (parent is InstanceCreationExpression) { | |
| 210 parent.constructorName; | |
|
scheglov
2015/03/05 20:51:09
Remove this?
danrubel
2015/03/05 21:23:40
Good eyes! Done.
| |
| 211 DartType instType = parent.bestType; | |
| 212 if (instType != null) { | |
| 213 Element intTypeElem = instType.element; | |
| 214 if (intTypeElem is ClassElement) { | |
| 215 SimpleIdentifier constructorName = parent.constructorName.name; | |
| 216 ConstructorElement constructor = constructorName != null | |
| 217 ? intTypeElem.getNamedConstructor(constructorName.name) | |
| 218 : intTypeElem.unnamedConstructor; | |
| 219 return constructor != null && | |
| 220 _isFunctionalParameter(constructor.parameters, argIndex); | |
| 221 } | |
| 222 } | |
| 223 } else if (parent is MethodInvocation) { | |
| 224 SimpleIdentifier methodName = parent.methodName; | |
| 225 if (methodName != null) { | |
| 226 Element methodElem = methodName.bestElement; | |
| 227 if (methodElem is MethodElement) { | |
| 228 return _isFunctionalParameter(methodElem.parameters, argIndex); | |
| 229 } else if (methodElem is FunctionElement) { | |
| 230 return _isFunctionalParameter(methodElem.parameters, argIndex); | |
| 231 } | |
| 232 } | |
| 233 } | |
| 234 return false; | |
| 235 } | |
| 168 | 236 |
| 169 /** | 237 /** |
| 170 * Determine whether [node] could possibly be the [entity] for a | 238 * Determine whether [node] could possibly be the [entity] for a |
| 171 * [CompletionTarget] associated with the given [offset]. | 239 * [CompletionTarget] associated with the given [offset]. |
| 172 */ | 240 */ |
| 173 static bool _isCandidateNode(AstNode node, int offset) { | 241 static bool _isCandidateNode(AstNode node, int offset) { |
| 174 // If the node's first token is a keyword or identifier, then the node is a | 242 // If the node's first token is a keyword or identifier, then the node is a |
| 175 // candidate entity if its first token is. | 243 // candidate entity if its first token is. |
| 176 Token beginToken = node.beginToken; | 244 Token beginToken = node.beginToken; |
| 177 if (beginToken.type == TokenType.KEYWORD || | 245 if (beginToken.type == TokenType.KEYWORD || |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 198 if (offset < token.end) { | 266 if (offset < token.end) { |
| 199 return true; | 267 return true; |
| 200 } else if (offset == token.end) { | 268 } else if (offset == token.end) { |
| 201 return token.type == TokenType.KEYWORD || | 269 return token.type == TokenType.KEYWORD || |
| 202 token.type == TokenType.IDENTIFIER || | 270 token.type == TokenType.IDENTIFIER || |
| 203 token.length == 0; | 271 token.length == 0; |
| 204 } else { | 272 } else { |
| 205 return false; | 273 return false; |
| 206 } | 274 } |
| 207 } | 275 } |
| 276 | |
| 277 /** | |
| 278 * Return `true` if the parameter is a functional parameter. | |
| 279 */ | |
| 280 static bool _isFunctionalParameter( | |
| 281 List<ParameterElement> parameters, int paramIndex) { | |
| 282 if (paramIndex < parameters.length) { | |
| 283 ParameterElement param = parameters[paramIndex]; | |
| 284 DartType paramType = param.type; | |
| 285 if (param.parameterKind == ParameterKind.NAMED) { | |
| 286 // TODO(danrubel) handle named parameters | |
| 287 return false; | |
| 288 } else { | |
| 289 return paramType is FunctionType || paramType is FunctionTypeAlias; | |
| 290 } | |
| 291 } | |
| 292 return false; | |
| 293 } | |
| 208 } | 294 } |
| OLD | NEW |