Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: pkg/analysis_server/lib/src/services/completion/completion_target.dart

Issue 977223003: When target requires a function, propose a function reference, not an invocation (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: merge and update editor to process IDENTIFIER suggestions Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 import 'package:analyzer/src/generated/ast.dart'; 1 import 'package:analyzer/src/generated/ast.dart';
2 import 'package:analyzer/src/generated/element.dart';
2 import 'package:analyzer/src/generated/scanner.dart'; 3 import 'package:analyzer/src/generated/scanner.dart';
4 import 'package:analyzer/src/generated/utilities_dart.dart';
5
6 int _computeArgIndex(AstNode containingNode, Object entity) {
7 var argList = containingNode;
8 if (argList is ArgumentList) {
9 NodeList<Expression> args = argList.arguments;
10 for (int index = 0; index < args.length; ++index) {
11 if (entity == args[index]) {
12 return index;
13 }
14 }
15 if (args.isEmpty) {
16 return 0;
17 }
18 }
19 return null;
20 }
3 21
4 /** 22 /**
5 * A CompletionTarget represents an edge in the parse tree which connects an 23 * A CompletionTarget represents an edge in the parse tree which connects an
6 * AST node (the [containingNode] of the completion) to one of its children 24 * AST node (the [containingNode] of the completion) to one of its children
7 * (the [entity], which represents the place in the parse tree where the newly 25 * (the [entity], which represents the place in the parse tree where the newly
8 * completed text will be inserted). 26 * completed text will be inserted).
9 * 27 *
10 * To illustrate, consider the following snippet of code, and its associated 28 * To illustrate, consider the following snippet of code, and its associated
11 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial 29 * parse tree. (T's represent tokens, N's represent AST nodes. Some trivial
12 * AST nodes are not shown). 30 * AST nodes are not shown).
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 * 90 *
73 * Usually, the entity won't be the first child of the [containingNode] (this 91 * Usually, the entity won't be the first child of the [containingNode] (this
74 * is a consequence of placing the completion target as high in the tree as 92 * is a consequence of placing the completion target as high in the tree as
75 * possible). However, there is one exception: when the cursor is inside of 93 * possible). However, there is one exception: when the cursor is inside of
76 * a multi-character token which is not a keyword or identifier (e.g. a 94 * a multi-character token which is not a keyword or identifier (e.g. a
77 * comment, or a token like "+=", the entity will be always be the token. 95 * comment, or a token like "+=", the entity will be always be the token.
78 */ 96 */
79 final Object entity; 97 final Object entity;
80 98
81 /** 99 /**
100 * If the target is an argument in an [ArgumentList], then this is the index
101 * of the argument in the list, otherwise this is `null`.
102 */
103 final int argIndex;
104
105 /**
82 * Compute the appropriate [CompletionTarget] for the given [offset] within 106 * Compute the appropriate [CompletionTarget] for the given [offset] within
83 * the [compilationUnit]. 107 * the [compilationUnit].
84 */ 108 */
85 factory CompletionTarget.forOffset( 109 factory CompletionTarget.forOffset(
86 CompilationUnit compilationUnit, int offset) { 110 CompilationUnit compilationUnit, int offset) {
87 // The precise algorithm is as follows. We perform a depth-first search of 111 // The precise algorithm is as follows. We perform a depth-first search of
88 // all edges in the parse tree (both those that point to AST nodes and 112 // all edges in the parse tree (both those that point to AST nodes and
89 // those that point to tokens), visiting parents before children. The 113 // those that point to tokens), visiting parents before children. The
90 // first edge which points to an entity satisfying either _isCandidateToken 114 // first edge which points to an entity satisfying either _isCandidateToken
91 // or _isCandidateNode is the completion target. If no edge is found that 115 // or _isCandidateNode is the completion target. If no edge is found that
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 // Since no completion target was found, we set the completion target 181 // Since no completion target was found, we set the completion target
158 // entity to null and use the compilationUnit as the parent. 182 // entity to null and use the compilationUnit as the parent.
159 return new CompletionTarget._(compilationUnit, null); 183 return new CompletionTarget._(compilationUnit, null);
160 } 184 }
161 } 185 }
162 186
163 /** 187 /**
164 * Create a [CompletionTarget] holding the given [containingNode] and 188 * Create a [CompletionTarget] holding the given [containingNode] and
165 * [entity]. 189 * [entity].
166 */ 190 */
167 CompletionTarget._(this.containingNode, this.entity); 191 CompletionTarget._(AstNode containingNode, Object entity)
192 : this.containingNode = containingNode,
193 this.entity = entity,
194 this.argIndex = _computeArgIndex(containingNode, entity);
195
196 /**
197 * Return `true` if the target is a functional argument in an argument list.
198 * The target [AstNode] hierarchy *must* be resolved for this to work.
199 */
200 bool isFunctionalArgument() {
201 if (argIndex == null) {
202 return false;
203 }
204 AstNode argList = containingNode;
205 if (argList is! ArgumentList) {
206 return false;
207 }
208 AstNode parent = argList.parent;
209 if (parent is InstanceCreationExpression) {
210 parent.constructorName;
scheglov 2015/03/05 20:51:09 Remove this?
danrubel 2015/03/05 21:23:40 Good eyes! Done.
211 DartType instType = parent.bestType;
212 if (instType != null) {
213 Element intTypeElem = instType.element;
214 if (intTypeElem is ClassElement) {
215 SimpleIdentifier constructorName = parent.constructorName.name;
216 ConstructorElement constructor = constructorName != null
217 ? intTypeElem.getNamedConstructor(constructorName.name)
218 : intTypeElem.unnamedConstructor;
219 return constructor != null &&
220 _isFunctionalParameter(constructor.parameters, argIndex);
221 }
222 }
223 } else if (parent is MethodInvocation) {
224 SimpleIdentifier methodName = parent.methodName;
225 if (methodName != null) {
226 Element methodElem = methodName.bestElement;
227 if (methodElem is MethodElement) {
228 return _isFunctionalParameter(methodElem.parameters, argIndex);
229 } else if (methodElem is FunctionElement) {
230 return _isFunctionalParameter(methodElem.parameters, argIndex);
231 }
232 }
233 }
234 return false;
235 }
168 236
169 /** 237 /**
170 * Determine whether [node] could possibly be the [entity] for a 238 * Determine whether [node] could possibly be the [entity] for a
171 * [CompletionTarget] associated with the given [offset]. 239 * [CompletionTarget] associated with the given [offset].
172 */ 240 */
173 static bool _isCandidateNode(AstNode node, int offset) { 241 static bool _isCandidateNode(AstNode node, int offset) {
174 // If the node's first token is a keyword or identifier, then the node is a 242 // If the node's first token is a keyword or identifier, then the node is a
175 // candidate entity if its first token is. 243 // candidate entity if its first token is.
176 Token beginToken = node.beginToken; 244 Token beginToken = node.beginToken;
177 if (beginToken.type == TokenType.KEYWORD || 245 if (beginToken.type == TokenType.KEYWORD ||
(...skipping 20 matching lines...) Expand all
198 if (offset < token.end) { 266 if (offset < token.end) {
199 return true; 267 return true;
200 } else if (offset == token.end) { 268 } else if (offset == token.end) {
201 return token.type == TokenType.KEYWORD || 269 return token.type == TokenType.KEYWORD ||
202 token.type == TokenType.IDENTIFIER || 270 token.type == TokenType.IDENTIFIER ||
203 token.length == 0; 271 token.length == 0;
204 } else { 272 } else {
205 return false; 273 return false;
206 } 274 }
207 } 275 }
276
277 /**
278 * Return `true` if the parameter is a functional parameter.
279 */
280 static bool _isFunctionalParameter(
281 List<ParameterElement> parameters, int paramIndex) {
282 if (paramIndex < parameters.length) {
283 ParameterElement param = parameters[paramIndex];
284 DartType paramType = param.type;
285 if (param.parameterKind == ParameterKind.NAMED) {
286 // TODO(danrubel) handle named parameters
287 return false;
288 } else {
289 return paramType is FunctionType || paramType is FunctionTypeAlias;
290 }
291 }
292 return false;
293 }
208 } 294 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698