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

Side by Side Diff: pkg/analysis_server/lib/src/utilities/change_builder_dart.dart

Issue 1110583002: Add utilities for creating SourceChanges (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add missed files Created 5 years, 7 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
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698