| OLD | NEW | 
|---|
| 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library analysis_server.src.provisional.completion.dart.completion_target; | 5 library analysis_server.src.provisional.completion.dart.completion_target; | 
| 6 | 6 | 
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; | 
| 8 import 'package:analyzer/src/generated/element.dart'; | 8 import 'package:analyzer/src/generated/element.dart'; | 
| 9 import 'package:analyzer/src/generated/scanner.dart'; | 9 import 'package:analyzer/src/generated/scanner.dart'; | 
| 10 import 'package:analyzer/src/generated/utilities_dart.dart'; | 10 import 'package:analyzer/src/generated/utilities_dart.dart'; | 
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 96    * | 96    * | 
| 97    * Usually, the entity won't be the first child of the [containingNode] (this | 97    * Usually, the entity won't be the first child of the [containingNode] (this | 
| 98    * is a consequence of placing the completion target as high in the tree as | 98    * is a consequence of placing the completion target as high in the tree as | 
| 99    * possible).  However, there is one exception: when the cursor is inside of | 99    * possible).  However, there is one exception: when the cursor is inside of | 
| 100    * a multi-character token which is not a keyword or identifier (e.g. a | 100    * a multi-character token which is not a keyword or identifier (e.g. a | 
| 101    * comment, or a token like "+=", the entity will be always be the token. | 101    * comment, or a token like "+=", the entity will be always be the token. | 
| 102    */ | 102    */ | 
| 103   final Object entity; | 103   final Object entity; | 
| 104 | 104 | 
| 105   /** | 105   /** | 
|  | 106    * The [entity] is a comment token, which is either not a documentation | 
|  | 107    * comment or the position is not in a [CommentReference]. | 
|  | 108    */ | 
|  | 109   final bool isCommentText; | 
|  | 110 | 
|  | 111   /** | 
| 106    * If the target is an argument in an [ArgumentList], then this is the index | 112    * If the target is an argument in an [ArgumentList], then this is the index | 
| 107    * of the argument in the list, otherwise this is `null`. | 113    * of the argument in the list, otherwise this is `null`. | 
| 108    */ | 114    */ | 
| 109   final int argIndex; | 115   final int argIndex; | 
| 110 | 116 | 
| 111   /** | 117   /** | 
| 112    * Compute the appropriate [CompletionTarget] for the given [offset] within | 118    * Compute the appropriate [CompletionTarget] for the given [offset] within | 
| 113    * the [compilationUnit]. | 119    * the [compilationUnit]. | 
| 114    */ | 120    */ | 
| 115   factory CompletionTarget.forOffset( | 121   factory CompletionTarget.forOffset( | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
| 138           if (commentReference.offset <= offset && | 144           if (commentReference.offset <= offset && | 
| 139               offset <= commentReference.end) { | 145               offset <= commentReference.end) { | 
| 140             containingNode = commentReference; | 146             containingNode = commentReference; | 
| 141             continue outerLoop; | 147             continue outerLoop; | 
| 142           } | 148           } | 
| 143         } | 149         } | 
| 144       } | 150       } | 
| 145       for (var entity in containingNode.childEntities) { | 151       for (var entity in containingNode.childEntities) { | 
| 146         if (entity is Token) { | 152         if (entity is Token) { | 
| 147           if (_isCandidateToken(entity, offset)) { | 153           if (_isCandidateToken(entity, offset)) { | 
|  | 154             // Try to replace with a comment token. | 
|  | 155             Token commentToken = _getContainingCommentToken(entity, offset); | 
|  | 156             if (commentToken != null) { | 
|  | 157               return new CompletionTarget._(containingNode, commentToken, true); | 
|  | 158             } | 
| 148             // Target found. | 159             // Target found. | 
| 149             return new CompletionTarget._(containingNode, entity); | 160             return new CompletionTarget._(containingNode, entity, false); | 
| 150           } else { | 161           } else { | 
| 151             // Since entity is a token, we don't need to look inside it; just | 162             // Since entity is a token, we don't need to look inside it; just | 
| 152             // proceed to the next entity. | 163             // proceed to the next entity. | 
| 153             continue; | 164             continue; | 
| 154           } | 165           } | 
| 155         } else if (entity is AstNode) { | 166         } else if (entity is AstNode) { | 
| 156           // If the last token in the node isn't a candidate target, then | 167           // If the last token in the node isn't a candidate target, then | 
| 157           // neither the node nor any of its descendants can possibly be the | 168           // neither the node nor any of its descendants can possibly be the | 
| 158           // completion target, so we can skip the node entirely. | 169           // completion target, so we can skip the node entirely. | 
| 159           if (!_isCandidateToken(entity.endToken, offset)) { | 170           if (!_isCandidateToken(entity.endToken, offset)) { | 
| 160             continue; | 171             continue; | 
| 161           } | 172           } | 
| 162 | 173 | 
| 163           // If the node is a candidate target, then we are done. | 174           // If the node is a candidate target, then we are done. | 
| 164           if (_isCandidateNode(entity, offset)) { | 175           if (_isCandidateNode(entity, offset)) { | 
| 165             // Check to see if the offset is in a preceeding comment | 176             // Check to see if the offset is in a preceding comment | 
| 166             Token commentToken = _getContainingCommentToken(entity, offset); | 177             Token commentToken = | 
|  | 178                 _getContainingCommentToken(entity.beginToken, offset); | 
| 167             if (commentToken != null) { | 179             if (commentToken != null) { | 
| 168               entity = commentToken; | 180               entity = commentToken; | 
| 169               // If the preceeding comment is dartdoc token then update | 181               // If the preceding comment is dartdoc token, then update | 
| 170               // the containing node to be the dartdoc comment | 182               // the containing node to be the dartdoc comment. | 
|  | 183               // Otherwise completion is not required. | 
| 171               Comment docComment = | 184               Comment docComment = | 
| 172                   _getContainingDocComment(containingNode, commentToken); | 185                   _getContainingDocComment(containingNode, commentToken); | 
| 173               if (docComment != null) { | 186               if (docComment != null) { | 
| 174                 containingNode = docComment; | 187                 containingNode = docComment; | 
|  | 188               } else { | 
|  | 189                 return new CompletionTarget._( | 
|  | 190                     compilationUnit, commentToken, true); | 
| 175               } | 191               } | 
| 176             } | 192             } | 
| 177             return new CompletionTarget._(containingNode, entity); | 193             return new CompletionTarget._(containingNode, entity, false); | 
| 178           } | 194           } | 
| 179 | 195 | 
| 180           // Otherwise, the completion target is somewhere inside the entity, | 196           // Otherwise, the completion target is somewhere inside the entity, | 
| 181           // so we need to jump to the start of the outer loop to examine its | 197           // so we need to jump to the start of the outer loop to examine its | 
| 182           // contents. | 198           // contents. | 
| 183           containingNode = entity; | 199           containingNode = entity; | 
| 184           continue outerLoop; | 200           continue outerLoop; | 
| 185         } else { | 201         } else { | 
| 186           // Unexpected entity found (all entities in a parse tree should be | 202           // Unexpected entity found (all entities in a parse tree should be | 
| 187           // AST nodes or tokens). | 203           // AST nodes or tokens). | 
| 188           assert(false); | 204           assert(false); | 
| 189         } | 205         } | 
| 190       } | 206       } | 
| 191 | 207 | 
| 192       // No completion target found.  It should only be possible to reach here | 208       // No completion target found.  It should only be possible to reach here | 
| 193       // the first time through the outer loop (since we only jump to the start | 209       // the first time through the outer loop (since we only jump to the start | 
| 194       // of the outer loop after determining that the completion target is | 210       // of the outer loop after determining that the completion target is | 
| 195       // inside an entity).  We can check that assumption by verifying that | 211       // inside an entity).  We can check that assumption by verifying that | 
| 196       // containingNode is still the compilationUnit. | 212       // containingNode is still the compilationUnit. | 
| 197       assert(identical(containingNode, compilationUnit)); | 213       assert(identical(containingNode, compilationUnit)); | 
| 198 | 214 | 
| 199       // Since no completion target was found, we set the completion target | 215       // Since no completion target was found, we set the completion target | 
| 200       // entity to null and use the compilationUnit as the parent. | 216       // entity to null and use the compilationUnit as the parent. | 
| 201       return new CompletionTarget._(compilationUnit, null); | 217       return new CompletionTarget._(compilationUnit, null, false); | 
| 202     } | 218     } | 
| 203   } | 219   } | 
| 204 | 220 | 
| 205   /** | 221   /** | 
| 206    * Create a [CompletionTarget] holding the given [containingNode] and | 222    * Create a [CompletionTarget] holding the given [containingNode] and | 
| 207    * [entity]. | 223    * [entity]. | 
| 208    */ | 224    */ | 
| 209   CompletionTarget._(AstNode containingNode, Object entity) | 225   CompletionTarget._(AstNode containingNode, Object entity, this.isCommentText) | 
| 210       : this.containingNode = containingNode, | 226       : this.containingNode = containingNode, | 
| 211         this.entity = entity, | 227         this.entity = entity, | 
| 212         this.argIndex = _computeArgIndex(containingNode, entity); | 228         this.argIndex = _computeArgIndex(containingNode, entity); | 
| 213 | 229 | 
| 214   /** | 230   /** | 
| 215    * Return `true` if the target is a functional argument in an argument list. | 231    * Return `true` if the target is a functional argument in an argument list. | 
| 216    * The target [AstNode] hierarchy *must* be resolved for this to work. | 232    * The target [AstNode] hierarchy *must* be resolved for this to work. | 
| 217    */ | 233    */ | 
| 218   bool isFunctionalArgument() { | 234   bool isFunctionalArgument() { | 
| 219     if (argIndex == null) { | 235     if (argIndex == null) { | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 245           return _isFunctionalParameter(methodElem.parameters, argIndex); | 261           return _isFunctionalParameter(methodElem.parameters, argIndex); | 
| 246         } else if (methodElem is FunctionElement) { | 262         } else if (methodElem is FunctionElement) { | 
| 247           return _isFunctionalParameter(methodElem.parameters, argIndex); | 263           return _isFunctionalParameter(methodElem.parameters, argIndex); | 
| 248         } | 264         } | 
| 249       } | 265       } | 
| 250     } | 266     } | 
| 251     return false; | 267     return false; | 
| 252   } | 268   } | 
| 253 | 269 | 
| 254   /** | 270   /** | 
| 255    * Determine if the offset is contained in a preceeding comment token | 271    * Determine if the offset is contained in a preceding comment token | 
| 256    * and return that token, otherwise return `null`. | 272    * and return that token, otherwise return `null`. | 
| 257    */ | 273    */ | 
| 258   static Token _getContainingCommentToken(AstNode node, int offset) { | 274   static Token _getContainingCommentToken(Token token, int offset) { | 
| 259     if (offset >= node.offset) { |  | 
| 260       return null; |  | 
| 261     } |  | 
| 262     Token token = node.beginToken; |  | 
| 263     if (token == null) { | 275     if (token == null) { | 
| 264       return null; | 276       return null; | 
| 265     } | 277     } | 
|  | 278     if (offset >= token.offset) { | 
|  | 279       return null; | 
|  | 280     } | 
| 266     token = token.precedingComments; | 281     token = token.precedingComments; | 
| 267     while (token != null) { | 282     while (token != null) { | 
| 268       if (offset <= token.offset) { | 283       if (offset <= token.offset) { | 
| 269         return null; | 284         return null; | 
| 270       } | 285       } | 
| 271       if (offset <= token.end) { | 286       if (offset <= token.end) { | 
| 272         if (token.type == TokenType.SINGLE_LINE_COMMENT || offset < token.end) { | 287         if (token.type == TokenType.SINGLE_LINE_COMMENT || offset < token.end) { | 
| 273           return token; | 288           return token; | 
| 274         } | 289         } | 
| 275       } | 290       } | 
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 353       if (param.parameterKind == ParameterKind.NAMED) { | 368       if (param.parameterKind == ParameterKind.NAMED) { | 
| 354         // TODO(danrubel) handle named parameters | 369         // TODO(danrubel) handle named parameters | 
| 355         return false; | 370         return false; | 
| 356       } else { | 371       } else { | 
| 357         return paramType is FunctionType || paramType is FunctionTypeAlias; | 372         return paramType is FunctionType || paramType is FunctionTypeAlias; | 
| 358       } | 373       } | 
| 359     } | 374     } | 
| 360     return false; | 375     return false; | 
| 361   } | 376   } | 
| 362 } | 377 } | 
| OLD | NEW | 
|---|