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 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 * | 79 * |
80 * If the file is empty, or the cursor is after all the text in the file, then | 80 * If the file is empty, or the cursor is after all the text in the file, then |
81 * there may be no edge in the parse tree which is appropriate to act as the | 81 * there may be no edge in the parse tree which is appropriate to act as the |
82 * completion target; in this case, [entity] is set to null and | 82 * completion target; in this case, [entity] is set to null and |
83 * [containingNode] is set to the CompilationUnit. | 83 * [containingNode] is set to the CompilationUnit. |
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. |
| 90 */ |
| 91 final CompilationUnit unit; |
| 92 |
| 93 /** |
89 * The context in which the completion is occurring. This is the AST node | 94 * The context in which the completion is occurring. This is the AST node |
90 * which is a direct parent of [entity]. | 95 * which is a direct parent of [entity]. |
91 */ | 96 */ |
92 final AstNode containingNode; | 97 final AstNode containingNode; |
93 | 98 |
94 /** | 99 /** |
95 * The entity which the completed text will replace (or which will be | 100 * The entity which the completed text will replace (or which will be |
96 * displaced once the completed text is inserted). This may be an AstNode or | 101 * displaced once the completed text is inserted). This may be an AstNode or |
97 * a Token, or it may be null if the cursor is after all tokens in the file. | 102 * a Token, or it may be null if the cursor is after all tokens in the file. |
98 * | 103 * |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 continue outerLoop; | 154 continue outerLoop; |
150 } | 155 } |
151 } | 156 } |
152 } | 157 } |
153 for (var entity in containingNode.childEntities) { | 158 for (var entity in containingNode.childEntities) { |
154 if (entity is Token) { | 159 if (entity is Token) { |
155 if (_isCandidateToken(entity, offset)) { | 160 if (_isCandidateToken(entity, offset)) { |
156 // Try to replace with a comment token. | 161 // Try to replace with a comment token. |
157 Token commentToken = _getContainingCommentToken(entity, offset); | 162 Token commentToken = _getContainingCommentToken(entity, offset); |
158 if (commentToken != null) { | 163 if (commentToken != null) { |
159 return new CompletionTarget._(containingNode, commentToken, true); | 164 return new CompletionTarget._( |
| 165 compilationUnit, containingNode, commentToken, true); |
160 } | 166 } |
161 // Target found. | 167 // Target found. |
162 return new CompletionTarget._(containingNode, entity, false); | 168 return new CompletionTarget._( |
| 169 compilationUnit, containingNode, entity, false); |
163 } else { | 170 } else { |
164 // Since entity is a token, we don't need to look inside it; just | 171 // Since entity is a token, we don't need to look inside it; just |
165 // proceed to the next entity. | 172 // proceed to the next entity. |
166 continue; | 173 continue; |
167 } | 174 } |
168 } else if (entity is AstNode) { | 175 } else if (entity is AstNode) { |
169 // If the last token in the node isn't a candidate target, then | 176 // If the last token in the node isn't a candidate target, then |
170 // neither the node nor any of its descendants can possibly be the | 177 // neither the node nor any of its descendants can possibly be the |
171 // completion target, so we can skip the node entirely. | 178 // completion target, so we can skip the node entirely. |
172 if (!_isCandidateToken(entity.endToken, offset)) { | 179 if (!_isCandidateToken(entity.endToken, offset)) { |
173 continue; | 180 continue; |
174 } | 181 } |
175 | 182 |
176 // If the node is a candidate target, then we are done. | 183 // If the node is a candidate target, then we are done. |
177 if (_isCandidateNode(entity, offset)) { | 184 if (_isCandidateNode(entity, offset)) { |
178 // Check to see if the offset is in a preceding comment | 185 // Check to see if the offset is in a preceding comment |
179 Token commentToken = | 186 Token commentToken = |
180 _getContainingCommentToken(entity.beginToken, offset); | 187 _getContainingCommentToken(entity.beginToken, offset); |
181 if (commentToken != null) { | 188 if (commentToken != null) { |
182 entity = commentToken; | 189 entity = commentToken; |
183 // If the preceding comment is dartdoc token, then update | 190 // If the preceding comment is dartdoc token, then update |
184 // the containing node to be the dartdoc comment. | 191 // the containing node to be the dartdoc comment. |
185 // Otherwise completion is not required. | 192 // Otherwise completion is not required. |
186 Comment docComment = | 193 Comment docComment = |
187 _getContainingDocComment(containingNode, commentToken); | 194 _getContainingDocComment(containingNode, commentToken); |
188 if (docComment != null) { | 195 if (docComment != null) { |
189 containingNode = docComment; | 196 containingNode = docComment; |
190 } else { | 197 } else { |
191 return new CompletionTarget._( | 198 return new CompletionTarget._( |
192 compilationUnit, commentToken, true); | 199 compilationUnit, compilationUnit, commentToken, true); |
193 } | 200 } |
194 } | 201 } |
195 return new CompletionTarget._(containingNode, entity, false); | 202 return new CompletionTarget._( |
| 203 compilationUnit, containingNode, entity, false); |
196 } | 204 } |
197 | 205 |
198 // Otherwise, the completion target is somewhere inside the entity, | 206 // Otherwise, the completion target is somewhere inside the entity, |
199 // so we need to jump to the start of the outer loop to examine its | 207 // so we need to jump to the start of the outer loop to examine its |
200 // contents. | 208 // contents. |
201 containingNode = entity; | 209 containingNode = entity; |
202 continue outerLoop; | 210 continue outerLoop; |
203 } else { | 211 } else { |
204 // Unexpected entity found (all entities in a parse tree should be | 212 // Unexpected entity found (all entities in a parse tree should be |
205 // AST nodes or tokens). | 213 // AST nodes or tokens). |
206 assert(false); | 214 assert(false); |
207 } | 215 } |
208 } | 216 } |
209 | 217 |
210 // No completion target found. It should only be possible to reach here | 218 // No completion target found. It should only be possible to reach here |
211 // the first time through the outer loop (since we only jump to the start | 219 // the first time through the outer loop (since we only jump to the start |
212 // of the outer loop after determining that the completion target is | 220 // of the outer loop after determining that the completion target is |
213 // inside an entity). We can check that assumption by verifying that | 221 // inside an entity). We can check that assumption by verifying that |
214 // containingNode is still the compilationUnit. | 222 // containingNode is still the compilationUnit. |
215 assert(identical(containingNode, compilationUnit)); | 223 assert(identical(containingNode, compilationUnit)); |
216 | 224 |
217 // Since no completion target was found, we set the completion target | 225 // Since no completion target was found, we set the completion target |
218 // entity to null and use the compilationUnit as the parent. | 226 // entity to null and use the compilationUnit as the parent. |
219 return new CompletionTarget._(compilationUnit, null, false); | 227 return new CompletionTarget._( |
| 228 compilationUnit, compilationUnit, null, false); |
220 } | 229 } |
221 } | 230 } |
222 | 231 |
223 /** | 232 /** |
224 * Create a [CompletionTarget] holding the given [containingNode] and | 233 * Create a [CompletionTarget] holding the given [containingNode] and |
225 * [entity]. | 234 * [entity]. |
226 */ | 235 */ |
227 CompletionTarget._(AstNode containingNode, Object entity, this.isCommentText) | 236 CompletionTarget._( |
| 237 this.unit, AstNode containingNode, Object entity, this.isCommentText) |
228 : this.containingNode = containingNode, | 238 : this.containingNode = containingNode, |
229 this.entity = entity, | 239 this.entity = entity, |
230 this.argIndex = _computeArgIndex(containingNode, entity); | 240 this.argIndex = _computeArgIndex(containingNode, entity); |
231 | 241 |
232 /** | 242 /** |
233 * Return `true` if the target is a functional argument in an argument list. | 243 * Return `true` if the target is a functional argument in an argument list. |
234 * The target [AstNode] hierarchy *must* be resolved for this to work. | 244 * The target [AstNode] hierarchy *must* be resolved for this to work. |
235 */ | 245 */ |
236 bool isFunctionalArgument() { | 246 bool isFunctionalArgument() { |
237 if (argIndex == null) { | 247 if (argIndex == null) { |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 if (param.parameterKind == ParameterKind.NAMED) { | 380 if (param.parameterKind == ParameterKind.NAMED) { |
371 // TODO(danrubel) handle named parameters | 381 // TODO(danrubel) handle named parameters |
372 return false; | 382 return false; |
373 } else { | 383 } else { |
374 return paramType is FunctionType || paramType is FunctionTypeAlias; | 384 return paramType is FunctionType || paramType is FunctionTypeAlias; |
375 } | 385 } |
376 } | 386 } |
377 return false; | 387 return false; |
378 } | 388 } |
379 } | 389 } |
OLD | NEW |