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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 * | 84 * |
85 * Clients may not extend, implement or mix-in this class. | 85 * Clients may not extend, implement or mix-in this class. |
86 */ | 86 */ |
87 class CompletionTarget { | 87 class CompletionTarget { |
88 /** | 88 /** |
89 * The compilation unit in which the completion is occurring. | 89 * The compilation unit in which the completion is occurring. |
90 */ | 90 */ |
91 final CompilationUnit unit; | 91 final CompilationUnit unit; |
92 | 92 |
93 /** | 93 /** |
| 94 * The offset within the source at which the completion is being requested. |
| 95 */ |
| 96 final int offset; |
| 97 |
| 98 /** |
94 * The context in which the completion is occurring. This is the AST node | 99 * The context in which the completion is occurring. This is the AST node |
95 * which is a direct parent of [entity]. | 100 * which is a direct parent of [entity]. |
96 */ | 101 */ |
97 final AstNode containingNode; | 102 final AstNode containingNode; |
98 | 103 |
99 /** | 104 /** |
100 * The entity which the completed text will replace (or which will be | 105 * The entity which the completed text will replace (or which will be |
101 * displaced once the completed text is inserted). This may be an AstNode or | 106 * displaced once the completed text is inserted). This may be an AstNode or |
102 * a Token, or it may be null if the cursor is after all tokens in the file. | 107 * a Token, or it may be null if the cursor is after all tokens in the file. |
103 * | 108 * |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 } | 160 } |
156 } | 161 } |
157 } | 162 } |
158 for (var entity in containingNode.childEntities) { | 163 for (var entity in containingNode.childEntities) { |
159 if (entity is Token) { | 164 if (entity is Token) { |
160 if (_isCandidateToken(entity, offset)) { | 165 if (_isCandidateToken(entity, offset)) { |
161 // Try to replace with a comment token. | 166 // Try to replace with a comment token. |
162 Token commentToken = _getContainingCommentToken(entity, offset); | 167 Token commentToken = _getContainingCommentToken(entity, offset); |
163 if (commentToken != null) { | 168 if (commentToken != null) { |
164 return new CompletionTarget._( | 169 return new CompletionTarget._( |
165 compilationUnit, containingNode, commentToken, true); | 170 compilationUnit, offset, containingNode, commentToken, true); |
166 } | 171 } |
167 // Target found. | 172 // Target found. |
168 return new CompletionTarget._( | 173 return new CompletionTarget._( |
169 compilationUnit, containingNode, entity, false); | 174 compilationUnit, offset, containingNode, entity, false); |
170 } else { | 175 } else { |
171 // Since entity is a token, we don't need to look inside it; just | 176 // Since entity is a token, we don't need to look inside it; just |
172 // proceed to the next entity. | 177 // proceed to the next entity. |
173 continue; | 178 continue; |
174 } | 179 } |
175 } else if (entity is AstNode) { | 180 } else if (entity is AstNode) { |
176 // If the last token in the node isn't a candidate target, then | 181 // If the last token in the node isn't a candidate target, then |
177 // neither the node nor any of its descendants can possibly be the | 182 // neither the node nor any of its descendants can possibly be the |
178 // completion target, so we can skip the node entirely. | 183 // completion target, so we can skip the node entirely. |
179 if (!_isCandidateToken(entity.endToken, offset)) { | 184 if (!_isCandidateToken(entity.endToken, offset)) { |
180 continue; | 185 continue; |
181 } | 186 } |
182 | 187 |
183 // If the node is a candidate target, then we are done. | 188 // If the node is a candidate target, then we are done. |
184 if (_isCandidateNode(entity, offset)) { | 189 if (_isCandidateNode(entity, offset)) { |
185 // Check to see if the offset is in a preceding comment | 190 // Check to see if the offset is in a preceding comment |
186 Token commentToken = | 191 Token commentToken = |
187 _getContainingCommentToken(entity.beginToken, offset); | 192 _getContainingCommentToken(entity.beginToken, offset); |
188 if (commentToken != null) { | 193 if (commentToken != null) { |
189 entity = commentToken; | 194 entity = commentToken; |
190 // If the preceding comment is dartdoc token, then update | 195 // If the preceding comment is dartdoc token, then update |
191 // the containing node to be the dartdoc comment. | 196 // the containing node to be the dartdoc comment. |
192 // Otherwise completion is not required. | 197 // Otherwise completion is not required. |
193 Comment docComment = | 198 Comment docComment = |
194 _getContainingDocComment(containingNode, commentToken); | 199 _getContainingDocComment(containingNode, commentToken); |
195 if (docComment != null) { | 200 if (docComment != null) { |
196 containingNode = docComment; | 201 containingNode = docComment; |
197 } else { | 202 } else { |
198 return new CompletionTarget._( | 203 return new CompletionTarget._(compilationUnit, offset, |
199 compilationUnit, compilationUnit, commentToken, true); | 204 compilationUnit, commentToken, true); |
200 } | 205 } |
201 } | 206 } |
202 return new CompletionTarget._( | 207 return new CompletionTarget._( |
203 compilationUnit, containingNode, entity, false); | 208 compilationUnit, offset, containingNode, entity, false); |
204 } | 209 } |
205 | 210 |
206 // Otherwise, the completion target is somewhere inside the entity, | 211 // Otherwise, the completion target is somewhere inside the entity, |
207 // so we need to jump to the start of the outer loop to examine its | 212 // so we need to jump to the start of the outer loop to examine its |
208 // contents. | 213 // contents. |
209 containingNode = entity; | 214 containingNode = entity; |
210 continue outerLoop; | 215 continue outerLoop; |
211 } else { | 216 } else { |
212 // Unexpected entity found (all entities in a parse tree should be | 217 // Unexpected entity found (all entities in a parse tree should be |
213 // AST nodes or tokens). | 218 // AST nodes or tokens). |
214 assert(false); | 219 assert(false); |
215 } | 220 } |
216 } | 221 } |
217 | 222 |
218 // No completion target found. It should only be possible to reach here | 223 // No completion target found. It should only be possible to reach here |
219 // the first time through the outer loop (since we only jump to the start | 224 // the first time through the outer loop (since we only jump to the start |
220 // of the outer loop after determining that the completion target is | 225 // of the outer loop after determining that the completion target is |
221 // inside an entity). We can check that assumption by verifying that | 226 // inside an entity). We can check that assumption by verifying that |
222 // containingNode is still the compilationUnit. | 227 // containingNode is still the compilationUnit. |
223 assert(identical(containingNode, compilationUnit)); | 228 assert(identical(containingNode, compilationUnit)); |
224 | 229 |
225 // Since no completion target was found, we set the completion target | 230 // Since no completion target was found, we set the completion target |
226 // entity to null and use the compilationUnit as the parent. | 231 // entity to null and use the compilationUnit as the parent. |
227 return new CompletionTarget._( | 232 return new CompletionTarget._( |
228 compilationUnit, compilationUnit, null, false); | 233 compilationUnit, offset, compilationUnit, null, false); |
229 } | 234 } |
230 } | 235 } |
231 | 236 |
232 /** | 237 /** |
233 * Create a [CompletionTarget] holding the given [containingNode] and | 238 * Create a [CompletionTarget] holding the given [containingNode] and |
234 * [entity]. | 239 * [entity]. |
235 */ | 240 */ |
236 CompletionTarget._( | 241 CompletionTarget._(this.unit, this.offset, AstNode containingNode, |
237 this.unit, AstNode containingNode, Object entity, this.isCommentText) | 242 Object entity, this.isCommentText) |
238 : this.containingNode = containingNode, | 243 : this.containingNode = containingNode, |
239 this.entity = entity, | 244 this.entity = entity, |
240 this.argIndex = _computeArgIndex(containingNode, entity); | 245 this.argIndex = _computeArgIndex(containingNode, entity); |
241 | 246 |
242 /** | 247 /** |
| 248 * Return `true` if the [containingNode] is a cascade |
| 249 * and the completion insertion is not between the two dots. |
| 250 * For example, `..d^` and `..^d` are considered a cascade |
| 251 * from a completion standpoint, but `.^.d` is not. |
| 252 */ |
| 253 bool get isCascade { |
| 254 AstNode node = containingNode; |
| 255 if (node is PropertyAccess) { |
| 256 return node.isCascaded && offset > node.operator.offset + 1; |
| 257 } |
| 258 if (node is MethodInvocation) { |
| 259 return node.isCascaded && offset > node.operator.offset + 1; |
| 260 } |
| 261 return false; |
| 262 } |
| 263 |
| 264 /** |
243 * Return `true` if the target is a functional argument in an argument list. | 265 * Return `true` if the target is a functional argument in an argument list. |
244 * The target [AstNode] hierarchy *must* be resolved for this to work. | 266 * The target [AstNode] hierarchy *must* be resolved for this to work. |
245 */ | 267 */ |
246 bool isFunctionalArgument() { | 268 bool isFunctionalArgument() { |
247 if (argIndex == null) { | 269 if (argIndex == null) { |
248 return false; | 270 return false; |
249 } | 271 } |
250 AstNode argList = containingNode; | 272 AstNode argList = containingNode; |
251 if (argList is! ArgumentList) { | 273 if (argList is! ArgumentList) { |
252 return false; | 274 return false; |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 if (param.parameterKind == ParameterKind.NAMED) { | 402 if (param.parameterKind == ParameterKind.NAMED) { |
381 // TODO(danrubel) handle named parameters | 403 // TODO(danrubel) handle named parameters |
382 return false; | 404 return false; |
383 } else { | 405 } else { |
384 return paramType is FunctionType || paramType is FunctionTypeAlias; | 406 return paramType is FunctionType || paramType is FunctionTypeAlias; |
385 } | 407 } |
386 } | 408 } |
387 return false; | 409 return false; |
388 } | 410 } |
389 } | 411 } |
OLD | NEW |