OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'package:analyzer/dart/ast/ast.dart'; | 5 import 'package:analyzer/dart/ast/ast.dart'; |
| 6 import 'package:analyzer/dart/ast/standard_ast_factory.dart'; |
6 import 'package:analyzer/dart/ast/token.dart'; | 7 import 'package:analyzer/dart/ast/token.dart'; |
7 import 'package:analyzer/dart/element/element.dart'; | 8 import 'package:analyzer/dart/element/element.dart'; |
8 import 'package:analyzer/dart/element/type.dart'; | 9 import 'package:analyzer/dart/element/type.dart'; |
| 10 import 'package:analyzer/src/dart/ast/token.dart'; |
| 11 import 'package:analyzer/src/generated/source.dart'; |
9 import 'package:analyzer/src/generated/utilities_dart.dart'; | 12 import 'package:analyzer/src/generated/utilities_dart.dart'; |
10 | 13 |
11 /** | 14 /** |
12 * A CompletionTarget represents an edge in the parse tree which connects an | 15 * A CompletionTarget represents an edge in the parse tree which connects an |
13 * AST node (the [containingNode] of the completion) to one of its children | 16 * AST node (the [containingNode] of the completion) to one of its children |
14 * (the [entity], which represents the place in the parse tree where the newly | 17 * (the [entity], which represents the place in the parse tree where the newly |
15 * completed text will be inserted). | 18 * completed text will be inserted). |
16 * | 19 * |
17 * To illustrate, consider the following snippet of code, and its associated | 20 * To illustrate, consider the following snippet of code, and its associated |
18 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial | 21 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 if (node is PropertyAccess) { | 248 if (node is PropertyAccess) { |
246 return node.isCascaded && offset > node.operator.offset + 1; | 249 return node.isCascaded && offset > node.operator.offset + 1; |
247 } | 250 } |
248 if (node is MethodInvocation) { | 251 if (node is MethodInvocation) { |
249 return node.isCascaded && offset > node.operator.offset + 1; | 252 return node.isCascaded && offset > node.operator.offset + 1; |
250 } | 253 } |
251 return false; | 254 return false; |
252 } | 255 } |
253 | 256 |
254 /** | 257 /** |
| 258 * Return a source range that represents the region of text that should be |
| 259 * replaced when a suggestion based on this target is selected, given that the |
| 260 * completion was requested at the given [requestOffset]. |
| 261 */ |
| 262 SourceRange computeReplacementRange(int requestOffset) { |
| 263 bool isKeywordOrIdentifier(Token token) => |
| 264 token.type.isKeyword || token.type == TokenType.IDENTIFIER; |
| 265 |
| 266 Token token = entity is AstNode ? (entity as AstNode).beginToken : entity; |
| 267 if (token != null && requestOffset < token.offset) { |
| 268 token = token.previous; |
| 269 } |
| 270 if (token != null) { |
| 271 if (requestOffset == token.offset && !isKeywordOrIdentifier(token)) { |
| 272 // If the insertion point is at the beginning of the current token |
| 273 // and the current token is not an identifier |
| 274 // then check the previous token to see if it should be replaced |
| 275 token = token.previous; |
| 276 } |
| 277 if (token != null && isKeywordOrIdentifier(token)) { |
| 278 if (token.offset <= requestOffset && requestOffset <= token.end) { |
| 279 // Replacement range for typical identifier completion |
| 280 return new SourceRange(token.offset, token.length); |
| 281 } |
| 282 } |
| 283 if (token is StringToken) { |
| 284 SimpleStringLiteral uri = |
| 285 astFactory.simpleStringLiteral(token, token.lexeme); |
| 286 Keyword keyword = token.previous?.keyword; |
| 287 if (keyword == Keyword.IMPORT || |
| 288 keyword == Keyword.EXPORT || |
| 289 keyword == Keyword.PART) { |
| 290 int start = uri.contentsOffset; |
| 291 int end = uri.contentsEnd; |
| 292 if (start <= requestOffset && requestOffset <= end) { |
| 293 // Replacement range for import URI |
| 294 return new SourceRange(start, end - start); |
| 295 } |
| 296 } |
| 297 } |
| 298 } |
| 299 return new SourceRange(requestOffset, 0); |
| 300 } |
| 301 |
| 302 /** |
255 * Return `true` if the target is a functional argument in an argument list. | 303 * Return `true` if the target is a functional argument in an argument list. |
256 * The target [AstNode] hierarchy *must* be resolved for this to work. | 304 * The target [AstNode] hierarchy *must* be resolved for this to work. |
257 * See [maybeFunctionalArgument]. | 305 * See [maybeFunctionalArgument]. |
258 */ | 306 */ |
259 bool isFunctionalArgument() { | 307 bool isFunctionalArgument() { |
260 if (!maybeFunctionalArgument()) { | 308 if (!maybeFunctionalArgument()) { |
261 return false; | 309 return false; |
262 } | 310 } |
263 AstNode parent = containingNode.parent; | 311 AstNode parent = containingNode.parent; |
264 if (parent is ArgumentList) { | 312 if (parent is ArgumentList) { |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
446 orElse: () => null); | 494 orElse: () => null); |
447 paramType = param?.type; | 495 paramType = param?.type; |
448 } | 496 } |
449 } else { | 497 } else { |
450 paramType = param.type; | 498 paramType = param.type; |
451 } | 499 } |
452 } | 500 } |
453 return paramType is FunctionType || paramType is FunctionTypeAlias; | 501 return paramType is FunctionType || paramType is FunctionTypeAlias; |
454 } | 502 } |
455 } | 503 } |
OLD | NEW |