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 |