OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library analysis_server.src.utilities.change_builder_dart; | |
6 | |
7 import 'package:analysis_server/src/protocol.dart' hide ElementKind; | |
8 import 'package:analysis_server/src/services/correction/name_suggestion.dart'; | |
9 import 'package:analysis_server/src/services/correction/util.dart'; | |
10 import 'package:analysis_server/src/utilities/change_builder_core.dart'; | |
11 import 'package:analysis_server/utilities/change_builder_core.dart'; | |
12 import 'package:analysis_server/utilities/change_builder_dart.dart'; | |
13 import 'package:analyzer/src/generated/ast.dart'; | |
14 import 'package:analyzer/src/generated/element.dart'; | |
15 import 'package:analyzer/src/generated/engine.dart'; | |
16 import 'package:analyzer/src/generated/source.dart'; | |
17 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
18 | |
19 /** | |
20 * A [ChangeBuilder] used to build changes in Dart files. | |
21 */ | |
22 class DartChangeBuilderImpl extends ChangeBuilderImpl | |
23 implements DartChangeBuilder { | |
24 /** | |
25 * The analysis context in which the files being edited were analyzed. | |
26 */ | |
27 final AnalysisContext context; | |
28 | |
29 /** | |
30 * Initialize a newly created change builder. | |
31 */ | |
32 DartChangeBuilderImpl(this.context); | |
33 | |
34 @override | |
35 DartFileEditBuilderImpl createFileEditBuilder(Source source, int fileStamp) { | |
36 return new DartFileEditBuilderImpl(this, source, fileStamp); | |
37 } | |
38 } | |
39 | |
40 /** | |
41 * An [EditBuilder] used to build edits in Dart files. | |
42 */ | |
43 class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder { | |
44 /** | |
45 * The string used when writing the 'abstract' modifier. | |
46 */ | |
47 static const String ABSTRACT_MODIFIER = 'abstract '; | |
Paul Berry
2015/05/04 16:00:29
Personally, I'm a little dubious about making stri
| |
48 | |
49 /** | |
50 * The string used when writing the 'const' modifier. | |
51 */ | |
52 static const String CONST_MODIFIER = 'const '; | |
53 | |
54 /** | |
55 * A utility class used to help build the source code. | |
56 */ | |
57 final CorrectionUtils utils; | |
58 | |
59 // /** | |
60 // * The string used when writing the 'final' modifier. | |
61 // */ | |
62 // static const String FINAL_MODIFIER = 'final '; | |
63 // | |
64 // /** | |
65 // * The string used when writing the 'static' modifier. | |
66 // */ | |
67 // static const String STATIC_MODIFIER = 'static '; | |
68 | |
69 /** | |
70 * Initialize a newly created builder to build a source edit. | |
71 */ | |
72 DartEditBuilderImpl( | |
73 DartFileEditBuilderImpl sourceFileEditBuilder, int offset, int length) | |
74 : utils = sourceFileEditBuilder.utils, | |
75 super(sourceFileEditBuilder, offset, length); | |
76 | |
77 DartFileEditBuilderImpl get dartFileEditBuilder => fileEditBuilder; | |
78 | |
79 @override | |
80 void writeClassDeclaration(String name, {Iterator<DartType> interfaces, | |
81 bool isAbstract: false, void memberWriter(), Iterator<DartType> mixins, Da rtType superclass}) { | |
82 // TODO(brianwilkerson) Add support for type parameters | |
83 // Map<String, DartType>, List<TypeParameter>? | |
84 // | |
85 // TODO(brianwilkerson) Make additional optional parameters visible in the | |
86 // public API. | |
87 if (isAbstract) { | |
88 write(ABSTRACT_MODIFIER); | |
89 } | |
90 write('class '); | |
91 addLinkedEdit(DartEditBuilder.NAME_GROUP_ID, (LinkedEditBuilder builder) { | |
92 write(name); | |
93 }); | |
94 if (superclass != null) { | |
Paul Berry
2015/05/04 16:00:29
Two things:
1. If superclass == null && mixins.is
| |
95 write(' extends '); | |
96 writeType(superclass, groupName: DartEditBuilder.SUPERCLASS_GROUP_ID); | |
97 } | |
98 writeTypes(mixins, prefix: ' with '); | |
99 writeTypes(interfaces, prefix: ' implements '); | |
100 writeln(' {'); | |
101 if (memberWriter != null) { | |
102 memberWriter(); | |
103 } | |
104 write('}'); | |
105 } | |
106 | |
107 /** | |
108 * Write the code for a comma-separated list of [types], optionally prefixed | |
109 * by a [prefix]. If the list of [types] is `null` or does not return any | |
110 * types, then nothing will be written. | |
111 */ | |
112 void writeTypes(Iterator<DartType> types, {String prefix}) { | |
113 if (types == null) { | |
114 return; | |
115 } | |
116 bool first = true; | |
117 while (types.moveNext()) { | |
118 if (first) { | |
119 if (prefix != null) { | |
120 write(prefix); | |
121 } | |
122 first = false; | |
123 } else { | |
124 write(', '); | |
125 } | |
126 writeType(types.current); | |
127 } | |
128 } | |
129 | |
130 void writeConstructorDeclaration(ClassElement classElement, | |
131 {ArgumentList argumentList, SimpleIdentifier constructorName, | |
132 bool isConst: false}) { | |
133 // TODO(brianwilkerson) Clean up the API and add it to the public API. | |
134 // | |
135 // TODO(brianwilkerson) Support passing a list of final fields rather than | |
136 // an argument list. | |
137 if (isConst) { | |
138 write(CONST_MODIFIER); | |
139 } | |
140 write(classElement.name); | |
141 write('.'); | |
142 if (constructorName != null) { | |
143 addLinkedEdit(DartEditBuilder.NAME_GROUP_ID, (LinkedEditBuilder builder) { | |
144 write(constructorName.name); | |
145 }); | |
146 CompilationUnit unit = constructorName | |
147 .getAncestor((AstNode node) => node is CompilationUnit); | |
148 if (unit != null) { | |
149 CompilationUnitElement element = unit.element; | |
150 if (element != null) { | |
151 String referenceFile = element.source.fullName; | |
152 if (referenceFile == dartFileEditBuilder.fileEdit.file) { | |
153 dartFileEditBuilder.addLinkedPosition(constructorName.offset, | |
154 constructorName.length, DartEditBuilder.NAME_GROUP_ID); | |
155 } | |
156 } | |
157 } | |
158 } | |
159 if (argumentList != null) { | |
160 writeParametersMatchingArguments(argumentList); | |
161 } else { | |
162 write('()'); | |
163 } | |
164 writeln(' {'); | |
165 write(' }'); | |
166 } | |
167 | |
168 @override | |
169 void writeOverrideOfInheritedMember(ExecutableElement member) { | |
170 // prepare environment | |
171 String prefix = utils.getIndent(1); | |
172 // may be property | |
173 String prefix2 = utils.getIndent(2); | |
174 ElementKind elementKind = member.kind; | |
175 bool isGetter = elementKind == ElementKind.GETTER; | |
176 bool isSetter = elementKind == ElementKind.SETTER; | |
177 bool isMethod = elementKind == ElementKind.METHOD; | |
178 bool isOperator = isMethod && (member as MethodElement).isOperator; | |
179 write(prefix); | |
180 if (isGetter) { | |
181 writeln('// TODO: implement ${member.displayName}'); | |
182 write(prefix); | |
183 } | |
184 // @override | |
185 writeln('@override'); | |
186 write(prefix); | |
187 // return type | |
188 // REVIEW: Added groupId | |
189 bool shouldReturn = writeType(member.type.returnType, | |
190 groupName: DartEditBuilder.RETURN_TYPE_GROUP_ID); | |
191 write(' '); | |
192 if (isGetter) { | |
193 write('get '); | |
194 } else if (isSetter) { | |
195 write('set '); | |
196 } else if (isOperator) { | |
197 write('operator '); | |
198 } | |
199 // name | |
200 write(member.displayName); | |
201 // parameters + body | |
202 if (isGetter) { | |
203 writeln(' => null;'); | |
204 } else { | |
205 List<ParameterElement> parameters = member.parameters; | |
206 writeParameters(parameters); | |
207 writeln(' {'); | |
208 // TO-DO | |
209 write(prefix2); | |
210 writeln('// TODO: implement ${member.displayName}'); | |
211 // REVIEW: Added return statement. | |
212 if (shouldReturn) { | |
213 write(prefix2); | |
214 writeln('return null;'); | |
215 } | |
216 // close method | |
217 write(prefix); | |
218 writeln('}'); | |
219 } | |
220 } | |
221 | |
222 @override | |
223 void writeParameters(Iterable<ParameterElement> parameters) { | |
224 write('('); | |
225 bool sawNamed = false; | |
226 bool sawPositional = false; | |
227 for (int i = 0; i < parameters.length; i++) { | |
228 ParameterElement parameter = parameters.elementAt(i); | |
229 if (i > 0) { | |
230 write(', '); | |
231 } | |
232 // may be optional | |
233 ParameterKind parameterKind = parameter.parameterKind; | |
234 if (parameterKind == ParameterKind.NAMED) { | |
235 if (!sawNamed) { | |
236 write('{'); | |
237 sawNamed = true; | |
238 } | |
239 } | |
240 if (parameterKind == ParameterKind.POSITIONAL) { | |
241 if (!sawPositional) { | |
242 write('['); | |
243 sawPositional = true; | |
244 } | |
245 } | |
246 // parameter | |
247 writeParameterSource(parameter.type, parameter.name); | |
248 // default value | |
249 String defaultCode = parameter.defaultValueCode; | |
250 if (defaultCode != null) { | |
251 if (sawPositional) { | |
252 write(' = '); | |
253 } else { | |
254 write(': '); | |
255 } | |
256 write(defaultCode); | |
257 } | |
258 } | |
259 // close parameters | |
260 if (sawNamed) { | |
261 write('}'); | |
262 } | |
263 if (sawPositional) { | |
264 write(']'); | |
265 } | |
266 write(')'); | |
267 } | |
268 | |
269 @override | |
270 void writeParametersMatchingArguments(ArgumentList arguments) { | |
271 Set<String> excluded = new Set(); | |
272 bool namedFound = false; | |
273 write('('); | |
274 List<Expression> argumentList = arguments.arguments; | |
275 for (int i = 0; i < argumentList.length; i++) { | |
276 Expression argument = argumentList[i]; | |
277 DartType type = argument.bestType; | |
278 List<String> suggestions = | |
279 _getParameterNameSuggestions(excluded, type, argument, i); | |
280 String favorite = suggestions[0]; | |
281 // append separator | |
282 if (i > 0) { | |
283 write(', '); | |
284 } | |
285 if (argument is NamedExpression) { | |
286 if (!namedFound) { | |
287 namedFound = true; | |
288 write('['); | |
289 } | |
290 favorite = argument.name.label.name; | |
291 } | |
292 // append type name | |
293 writeType(type, addSupertypeProposals: true, groupName: 'TYPE$i'); | |
294 write(' '); | |
295 // append parameter name | |
296 excluded.add(favorite); | |
297 addLinkedEdit('ARG$i', (LinkedEditBuilder builder) { | |
298 builder.write(favorite); | |
299 builder.addSuggestions(LinkedEditSuggestionKind.PARAMETER, suggestions); | |
300 }); | |
301 } | |
302 if (namedFound) { | |
303 write(']'); | |
304 } | |
305 write(')'); | |
306 } | |
307 | |
308 @override | |
309 void writeParameterSource(DartType type, String name) { | |
310 String parameterSource = utils.getParameterSource( | |
311 type, name, dartFileEditBuilder.librariesToImport); | |
312 write(parameterSource); | |
313 } | |
314 | |
315 @override | |
316 bool writeType(DartType type, {bool addSupertypeProposals: false, | |
317 String groupName, bool required: false}) { | |
318 if (type != null && !type.isDynamic) { | |
319 String typeSource = | |
320 utils.getTypeSource(type, dartFileEditBuilder.librariesToImport); | |
321 if (groupName != null) { | |
322 addLinkedEdit(groupName, (LinkedEditBuilder builder) { | |
323 write(typeSource); | |
324 if (addSupertypeProposals) { | |
325 _addSuperTypeProposals(builder, type, new Set<DartType>()); | |
326 } | |
327 }); | |
328 } else { | |
329 write(typeSource); | |
330 } | |
331 return true; | |
332 } else if (required) { | |
333 write('var'); | |
334 } | |
335 return false; | |
336 } | |
337 | |
338 void _addSuperTypeProposals( | |
339 LinkedEditBuilder builder, DartType type, Set<DartType> alreadyAdded) { | |
340 if (type != null && | |
341 type.element is ClassElement && | |
342 alreadyAdded.add(type)) { | |
343 ClassElement element = type.element as ClassElement; | |
344 builder.addSuggestion(LinkedEditSuggestionKind.TYPE, element.name); | |
345 _addSuperTypeProposals(builder, element.supertype, alreadyAdded); | |
346 for (InterfaceType interfaceType in element.interfaces) { | |
347 _addSuperTypeProposals(builder, interfaceType, alreadyAdded); | |
348 } | |
349 } | |
350 } | |
351 | |
352 /** | |
353 * Return a list containing the suggested names for a parmeter with the given | |
354 * [type] whose value in one location is computed by the given [expression]. | |
355 * The list will not contain any names in the set of [excluded] names. The | |
356 * [index] is the index of the argument, used to create a name if no better | |
357 * name could be created. The first name in the list will be the best name. | |
358 */ | |
359 List<String> _getParameterNameSuggestions( | |
360 Set<String> excluded, DartType type, Expression expression, int index) { | |
361 List<String> suggestions = | |
362 getVariableNameSuggestionsForExpression(type, expression, excluded); | |
363 if (suggestions.length != 0) { | |
364 return suggestions; | |
365 } | |
366 return <String>['arg$index']; | |
367 } | |
368 } | |
369 | |
370 /** | |
371 * A [FileEditBuilder] used to build edits for Dart files. | |
372 */ | |
373 class DartFileEditBuilderImpl extends FileEditBuilderImpl | |
374 implements DartFileEditBuilder { | |
375 /** | |
376 * The compilation unit to which the code will be added. | |
377 */ | |
378 CompilationUnit unit; | |
379 | |
380 /** | |
381 * A utility class used to help build the source code. | |
382 */ | |
383 CorrectionUtils utils; | |
384 | |
385 /** | |
386 * A set containing the elements of the libraries that need to be imported in | |
387 * order to make visible the names used in generated code. | |
388 */ | |
389 Set<LibraryElement> librariesToImport = new Set<LibraryElement>(); | |
390 | |
391 /** | |
392 * Initialize a newly created builder to build a source file edit within the | |
393 * change being built by the given [changeBuilder]. The file being edited has | |
394 * the given [source] and [timeStamp]. | |
395 */ | |
396 DartFileEditBuilderImpl( | |
397 DartChangeBuilderImpl changeBuilder, Source source, int timeStamp) | |
398 : super(changeBuilder, source, timeStamp) { | |
399 AnalysisContext context = changeBuilder.context; | |
400 List<Source> librariesContaining = context.getLibrariesContaining(source); | |
401 if (librariesContaining.length < 1) { | |
402 throw new StateError('Cannot build edits for ${source.fullName}'); | |
403 } | |
404 unit = context.resolveCompilationUnit2(source, librariesContaining[0]); | |
405 utils = new CorrectionUtils(unit); | |
406 } | |
407 | |
408 @override | |
409 DartEditBuilderImpl createEditBuilder(int offset, int length) { | |
410 return new DartEditBuilderImpl(this, offset, length); | |
411 } | |
412 } | |
OLD | NEW |