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

Side by Side Diff: pkg/analysis_server/tool/spec/api.dart

Issue 725143004: Format and sort analyzer and analysis_server packages. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 /** 5 /**
6 * Data structures representing an API definition, and visitor base classes 6 * Data structures representing an API definition, and visitor base classes
7 * for visiting those data structures. 7 * for visiting those data structures.
8 */ 8 */
9 library api; 9 library api;
10 10
11 import 'dart:collection'; 11 import 'dart:collection';
12 12
13 import 'package:html5lib/dom.dart' as dom; 13 import 'package:html5lib/dom.dart' as dom;
14 14
15 /** 15 /**
16 * Toplevel container for the API.
17 */
18 class Api extends ApiNode {
19 final String version;
20 final List<Domain> domains;
21 final Types types;
22 final Refactorings refactorings;
23
24 Api(this.version, this.domains, this.types, this.refactorings,
25 dom.Element html)
26 : super(html);
27 }
28
29 /**
30 * Base class for objects in the API model.
31 */
32 class ApiNode {
33 /**
34 * Html element representing this part of the API.
35 */
36 final dom.Element html;
37
38 ApiNode(this.html);
39 }
40
41 /**
16 * Base class for visiting the API definition. 42 * Base class for visiting the API definition.
17 */ 43 */
18 abstract class ApiVisitor<T> { 44 abstract class ApiVisitor<T> {
19 T visitTypeReference(TypeReference typeReference);
20 T visitTypeUnion(TypeUnion typeUnion);
21 T visitTypeObject(TypeObject typeObject);
22 T visitTypeList(TypeList typeList);
23 T visitTypeMap(TypeMap typeMap);
24 T visitTypeEnum(TypeEnum typeEnum);
25
26 /** 45 /**
27 * Dispatch the given [type] to the visitor. 46 * Dispatch the given [type] to the visitor.
28 */ 47 */
29 T visitTypeDecl(TypeDecl type) => type.accept(this); 48 T visitTypeDecl(TypeDecl type) => type.accept(this);
49 T visitTypeEnum(TypeEnum typeEnum);
50 T visitTypeList(TypeList typeList);
51 T visitTypeMap(TypeMap typeMap);
52 T visitTypeObject(TypeObject typeObject);
53 T visitTypeReference(TypeReference typeReference);
54
55 T visitTypeUnion(TypeUnion typeUnion);
30 } 56 }
31 57
32 /** 58 /**
59 * Definition of a single domain.
60 */
61 class Domain extends ApiNode {
62 final String name;
63 final List<Request> requests;
64 final List<Notification> notifications;
65
66 Domain(this.name, this.requests, this.notifications, dom.Element html)
67 : super(html);
68 }
69
70 /**
33 * API visitor that visits the entire API hierarchically by default. 71 * API visitor that visits the entire API hierarchically by default.
34 */ 72 */
35 class HierarchicalApiVisitor extends ApiVisitor { 73 class HierarchicalApiVisitor extends ApiVisitor {
36 /** 74 /**
37 * The API to visit. 75 * The API to visit.
38 */ 76 */
39 final Api api; 77 final Api api;
40 78
41 HierarchicalApiVisitor(this.api); 79 HierarchicalApiVisitor(this.api);
42 80
81 /**
82 * If [type] is a [TypeReference] that is defined in the API, follow the
83 * chain until a non-[TypeReference] is found, if possible.
84 *
85 * If it is not possible (because the chain ends with a [TypeReference] that
86 * is not defined in the API), then that final [TypeReference] is returned.
87 */
88 TypeDecl resolveTypeReferenceChain(TypeDecl type) {
89 while (type is TypeReference && api.types.containsKey(type.typeName)) {
90 type = api.types[(type as TypeReference).typeName].type;
91 }
92 return type;
93 }
94
43 void visitApi() { 95 void visitApi() {
44 api.domains.forEach(visitDomain); 96 api.domains.forEach(visitDomain);
45 visitTypes(api.types); 97 visitTypes(api.types);
46 visitRefactorings(api.refactorings); 98 visitRefactorings(api.refactorings);
47 } 99 }
48 100
49 void visitRefactorings(Refactorings refactorings) { 101 void visitDomain(Domain domain) {
50 refactorings.forEach(visitRefactoring); 102 domain.requests.forEach(visitRequest);
103 domain.notifications.forEach(visitNotification);
104 }
105
106 void visitNotification(Notification notification) {
107 if (notification.params != null) {
108 visitTypeDecl(notification.params);
109 }
51 } 110 }
52 111
53 void visitRefactoring(Refactoring refactoring) { 112 void visitRefactoring(Refactoring refactoring) {
54 if (refactoring.feedback != null) { 113 if (refactoring.feedback != null) {
55 visitTypeDecl(refactoring.feedback); 114 visitTypeDecl(refactoring.feedback);
56 } 115 }
57 if (refactoring.options != null) { 116 if (refactoring.options != null) {
58 visitTypeDecl(refactoring.options); 117 visitTypeDecl(refactoring.options);
59 } 118 }
60 } 119 }
61 120
62 void visitTypes(Types types) { 121 void visitRefactorings(Refactorings refactorings) {
63 types.forEach(visitTypeDefinition); 122 refactorings.forEach(visitRefactoring);
64 }
65
66 void visitDomain(Domain domain) {
67 domain.requests.forEach(visitRequest);
68 domain.notifications.forEach(visitNotification);
69 }
70
71 void visitNotification(Notification notification) {
72 if (notification.params != null) {
73 visitTypeDecl(notification.params);
74 }
75 } 123 }
76 124
77 void visitRequest(Request request) { 125 void visitRequest(Request request) {
78 if (request.params != null) { 126 if (request.params != null) {
79 visitTypeDecl(request.params); 127 visitTypeDecl(request.params);
80 } 128 }
81 if (request.result != null) { 129 if (request.result != null) {
82 visitTypeDecl(request.result); 130 visitTypeDecl(request.result);
83 } 131 }
84 } 132 }
(...skipping 27 matching lines...) Expand all
112 } 160 }
113 161
114 void visitTypeObjectField(TypeObjectField typeObjectField) { 162 void visitTypeObjectField(TypeObjectField typeObjectField) {
115 visitTypeDecl(typeObjectField.type); 163 visitTypeDecl(typeObjectField.type);
116 } 164 }
117 165
118 @override 166 @override
119 void visitTypeReference(TypeReference typeReference) { 167 void visitTypeReference(TypeReference typeReference) {
120 } 168 }
121 169
170 void visitTypes(Types types) {
171 types.forEach(visitTypeDefinition);
172 }
173
122 @override 174 @override
123 void visitTypeUnion(TypeUnion typeUnion) { 175 void visitTypeUnion(TypeUnion typeUnion) {
124 typeUnion.choices.forEach(visitTypeDecl); 176 typeUnion.choices.forEach(visitTypeDecl);
125 } 177 }
178 }
179
180 /**
181 * Description of a request method.
182 */
183 class Notification extends ApiNode {
184 /**
185 * Name of the domain enclosing this request.
186 */
187 final String domainName;
126 188
127 /** 189 /**
128 * If [type] is a [TypeReference] that is defined in the API, follow the 190 * Name of the notification, without the domain prefix.
129 * chain until a non-[TypeReference] is found, if possible.
130 *
131 * If it is not possible (because the chain ends with a [TypeReference] that
132 * is not defined in the API), then that final [TypeReference] is returned.
133 */ 191 */
134 TypeDecl resolveTypeReferenceChain(TypeDecl type) { 192 final String event;
135 while (type is TypeReference && api.types.containsKey(type.typeName)) { 193
136 type = api.types[(type as TypeReference).typeName].type; 194 /**
195 * Type of the object associated with the "params" key in the notification
196 * object, or null if the notification has no parameters.
197 */
198 final TypeObject params;
199
200 Notification(this.domainName, this.event, this.params, dom.Element html)
201 : super(html);
202
203 /**
204 * Get the name of the notification, including the domain prefix.
205 */
206 String get longEvent => '$domainName.$event';
207
208 /**
209 * Get the full type of the notification object, including the common "id"
210 * and "error" fields.
211 */
212 TypeDecl get notificationType {
213 List<TypeObjectField> fields = [
214 new TypeObjectField(
215 'event',
216 new TypeReference('String', null),
217 null,
218 value: '$domainName.$event')];
219 if (params != null) {
220 fields.add(new TypeObjectField('params', params, null));
137 } 221 }
138 return type; 222 return new TypeObject(fields, null);
139 } 223 }
140 } 224 }
141 225
142 /** 226 /**
143 * Base class for objects in the API model.
144 */
145 class ApiNode {
146 /**
147 * Html element representing this part of the API.
148 */
149 final dom.Element html;
150
151 ApiNode(this.html);
152 }
153
154 /**
155 * Toplevel container for the API.
156 */
157 class Api extends ApiNode {
158 final String version;
159 final List<Domain> domains;
160 final Types types;
161 final Refactorings refactorings;
162
163 Api(this.version, this.domains, this.types, this.refactorings, dom.Element
164 html) : super(html);
165 }
166
167 /**
168 * A collection of refactoring definitions.
169 */
170 class Refactorings extends ApiNode with IterableMixin<Refactoring> {
171 final List<Refactoring> refactorings;
172
173 Refactorings(this.refactorings, dom.Element html) : super(html);
174
175 @override
176 Iterator<Refactoring> get iterator => refactorings.iterator;
177 }
178
179 /**
180 * Description of a single refactoring. 227 * Description of a single refactoring.
181 */ 228 */
182 class Refactoring extends ApiNode { 229 class Refactoring extends ApiNode {
183 /** 230 /**
184 * Name of the refactoring. This should match one of the values allowed for 231 * Name of the refactoring. This should match one of the values allowed for
185 * RefactoringKind. 232 * RefactoringKind.
186 */ 233 */
187 final String kind; 234 final String kind;
188 235
189 /** 236 /**
190 * Type of the refactoring feedback, or null if the refactoring has no 237 * Type of the refactoring feedback, or null if the refactoring has no
191 * feedback. 238 * feedback.
192 */ 239 */
193 final TypeObject feedback; 240 final TypeObject feedback;
194 241
195 /** 242 /**
196 * Type of the refactoring options, or null if the refactoring has no options. 243 * Type of the refactoring options, or null if the refactoring has no options.
197 */ 244 */
198 final TypeObject options; 245 final TypeObject options;
199 246
200 Refactoring(this.kind, this.feedback, this.options, dom.Element html) : super( 247 Refactoring(this.kind, this.feedback, this.options, dom.Element html)
201 html); 248 : super(html);
202 } 249 }
203 250
204 /** 251 /**
205 * A collection of type definitions. 252 * A collection of refactoring definitions.
206 */ 253 */
207 class Types extends ApiNode with IterableMixin<TypeDefinition> { 254 class Refactorings extends ApiNode with IterableMixin<Refactoring> {
208 final Map<String, TypeDefinition> types; 255 final List<Refactoring> refactorings;
209 256
210 Types(this.types, dom.Element html) : super(html); 257 Refactorings(this.refactorings, dom.Element html) : super(html);
211
212 bool containsKey(String typeName) => types.containsKey(typeName);
213 258
214 @override 259 @override
215 Iterator<TypeDefinition> get iterator => types.values.iterator; 260 Iterator<Refactoring> get iterator => refactorings.iterator;
216
217 TypeDefinition operator [](String typeName) => types[typeName];
218
219 Iterable<String> get keys => types.keys;
220 } 261 }
221 262
222 /** 263 /**
223 * Definition of a single domain.
224 */
225 class Domain extends ApiNode {
226 final String name;
227 final List<Request> requests;
228 final List<Notification> notifications;
229
230 Domain(this.name, this.requests, this.notifications, dom.Element html) :
231 super(html);
232 }
233
234 /**
235 * Description of a request method. 264 * Description of a request method.
236 */ 265 */
237 class Request extends ApiNode { 266 class Request extends ApiNode {
238 /** 267 /**
239 * Name of the domain enclosing this request. 268 * Name of the domain enclosing this request.
240 */ 269 */
241 final String domainName; 270 final String domainName;
242 271
243 /** 272 /**
244 * Name of the request, without the domain prefix. 273 * Name of the request, without the domain prefix.
245 */ 274 */
246 final String method; 275 final String method;
247 276
248 /** 277 /**
249 * Type of the object associated with the "params" key in the request object, 278 * Type of the object associated with the "params" key in the request object,
250 * or null if the request has no parameters. 279 * or null if the request has no parameters.
251 */ 280 */
252 final TypeObject params; 281 final TypeObject params;
253 282
254 /** 283 /**
255 * Type of the object associated with the "result" key in the response object, 284 * Type of the object associated with the "result" key in the response object,
256 * or null if the response has no results. 285 * or null if the response has no results.
257 */ 286 */
258 final TypeObject result; 287 final TypeObject result;
259 288
260 Request(this.domainName, this.method, this.params, this.result, dom.Element 289 Request(this.domainName, this.method, this.params, this.result,
261 html) : super(html); 290 dom.Element html)
291 : super(html);
262 292
263 /** 293 /**
264 * Get the name of the request, including the domain prefix. 294 * Get the name of the request, including the domain prefix.
265 */ 295 */
266 String get longMethod => '$domainName.$method'; 296 String get longMethod => '$domainName.$method';
267 297
268 /** 298 /**
269 * Get the full type of the request object, including the common "id" and 299 * Get the full type of the request object, including the common "id" and
270 * "method" fields. 300 * "method" fields.
271 */ 301 */
272 TypeDecl get requestType { 302 TypeDecl get requestType {
273 List<TypeObjectField> fields = [new TypeObjectField('id', new TypeReference( 303 List<TypeObjectField> fields = [
274 'String', null), null), new TypeObjectField('method', new TypeReference( 304 new TypeObjectField('id', new TypeReference('String', null), null),
275 'String', null), null, value: '$domainName.$method')]; 305 new TypeObjectField(
306 'method',
307 new TypeReference('String', null),
308 null,
309 value: '$domainName.$method')];
276 if (params != null) { 310 if (params != null) {
277 fields.add(new TypeObjectField('params', params, null)); 311 fields.add(new TypeObjectField('params', params, null));
278 } 312 }
279 return new TypeObject(fields, null); 313 return new TypeObject(fields, null);
280 } 314 }
281 315
282 /** 316 /**
283 * Get the full type of the response object, including the common "id" and 317 * Get the full type of the response object, including the common "id" and
284 * "error" fields. 318 * "error" fields.
285 */ 319 */
286 TypeDecl get responseType { 320 TypeDecl get responseType {
287 List<TypeObjectField> fields = [ 321 List<TypeObjectField> fields = [
288 new TypeObjectField('id', new TypeReference('String', null), null), 322 new TypeObjectField('id', new TypeReference('String', null), null),
289 new TypeObjectField( 323 new TypeObjectField(
290 'error', 324 'error',
291 new TypeReference('RequestError', null), 325 new TypeReference('RequestError', null),
292 null, 326 null,
293 optional: true)]; 327 optional: true)];
294 if (result != null) { 328 if (result != null) {
295 fields.add(new TypeObjectField('result', result, null)); 329 fields.add(new TypeObjectField('result', result, null));
296 } 330 }
297 return new TypeObject(fields, null); 331 return new TypeObject(fields, null);
298 } 332 }
299 } 333 }
300 334
301 /** 335 /**
302 * Description of a request method. 336 * Base class for all possible types.
303 */ 337 */
304 class Notification extends ApiNode { 338 abstract class TypeDecl extends ApiNode {
305 /** 339 TypeDecl(dom.Element html) : super(html);
306 * Name of the domain enclosing this request.
307 */
308 final String domainName;
309 340
310 /** 341 accept(ApiVisitor visitor);
311 * Name of the notification, without the domain prefix.
312 */
313 final String event;
314
315 /**
316 * Type of the object associated with the "params" key in the notification
317 * object, or null if the notification has no parameters.
318 */
319 final TypeObject params;
320
321 Notification(this.domainName, this.event, this.params, dom.Element html) :
322 super(html);
323
324 /**
325 * Get the name of the notification, including the domain prefix.
326 */
327 String get longEvent => '$domainName.$event';
328
329 /**
330 * Get the full type of the notification object, including the common "id"
331 * and "error" fields.
332 */
333 TypeDecl get notificationType {
334 List<TypeObjectField> fields = [new TypeObjectField('event',
335 new TypeReference('String', null), null, value: '$domainName.$event')];
336 if (params != null) {
337 fields.add(new TypeObjectField('params', params, null));
338 }
339 return new TypeObject(fields, null);
340 }
341 } 342 }
342 343
343 /** 344 /**
344 * Description of a named type definition. 345 * Description of a named type definition.
345 */ 346 */
346 class TypeDefinition extends ApiNode { 347 class TypeDefinition extends ApiNode {
347 final String name; 348 final String name;
348 final TypeDecl type; 349 final TypeDecl type;
349 350
350 TypeDefinition(this.name, this.type, dom.Element html) : super(html); 351 TypeDefinition(this.name, this.type, dom.Element html) : super(html);
351 } 352 }
352 353
353 /** 354 /**
354 * Base class for all possible types. 355 * Type of an enum. We represent enums in JSON as strings, so this type
356 * declaration simply lists the allowed values.
355 */ 357 */
356 abstract class TypeDecl extends ApiNode { 358 class TypeEnum extends TypeDecl {
357 TypeDecl(dom.Element html) : super(html); 359 final List<TypeEnumValue> values;
358 360
359 accept(ApiVisitor visitor); 361 TypeEnum(this.values, dom.Element html) : super(html);
362
363 accept(ApiVisitor visitor) => visitor.visitTypeEnum(this);
360 } 364 }
361 365
362 /** 366 /**
363 * A reference to a type which is either defined elsewhere in the API or which 367 * Description of a single allowed value for an enum.
364 * is built-in ([String], [bool], or [int]).
365 */ 368 */
366 class TypeReference extends TypeDecl { 369 class TypeEnumValue extends ApiNode {
367 final String typeName; 370 final String value;
368 371
369 TypeReference(this.typeName, dom.Element html) : super(html) { 372 TypeEnumValue(this.value, dom.Element html) : super(html);
370 if (typeName.isEmpty) {
371 throw new Exception('Empty type name');
372 }
373 }
374
375 accept(ApiVisitor visitor) => visitor.visitTypeReference(this);
376 } 373 }
377 374
378 /** 375 /**
379 * Type which represents a union among multiple choices. 376 * Type of a JSON list.
380 */ 377 */
381 class TypeUnion extends TypeDecl { 378 class TypeList extends TypeDecl {
382 final List<TypeDecl> choices; 379 final TypeDecl itemType;
383 380
384 /** 381 TypeList(this.itemType, dom.Element html) : super(html);
385 * The field that is used to disambiguate this union
386 */
387 final String field;
388 382
389 TypeUnion(this.choices, this.field, dom.Element html) : super(html); 383 accept(ApiVisitor visitor) => visitor.visitTypeList(this);
390
391 accept(ApiVisitor visitor) => visitor.visitTypeUnion(this);
392 } 384 }
393 385
394 /** 386 /**
387 * Type of a JSON map.
388 */
389 class TypeMap extends TypeDecl {
390 /**
391 * Type of map keys. Note that since JSON map keys must always be strings,
392 * this must either be a [TypeReference] for [String], or a [TypeReference]
393 * to a type which is defined in the API as an enum or a synonym for [String].
394 */
395 final TypeReference keyType;
396
397 /**
398 * Type of map values.
399 */
400 final TypeDecl valueType;
401
402 TypeMap(this.keyType, this.valueType, dom.Element html) : super(html);
403
404 accept(ApiVisitor visitor) => visitor.visitTypeMap(this);
405 }
406
407 /**
395 * Type of a JSON object with specified fields, some of which may be optional. 408 * Type of a JSON object with specified fields, some of which may be optional.
396 */ 409 */
397 class TypeObject extends TypeDecl { 410 class TypeObject extends TypeDecl {
398 final List<TypeObjectField> fields; 411 final List<TypeObjectField> fields;
399 412
400 TypeObject(this.fields, dom.Element html) : super(html); 413 TypeObject(this.fields, dom.Element html) : super(html);
401 414
402 accept(ApiVisitor visitor) => visitor.visitTypeObject(this); 415 accept(ApiVisitor visitor) => visitor.visitTypeObject(this);
403 416
404 /** 417 /**
(...skipping 15 matching lines...) Expand all
420 class TypeObjectField extends ApiNode { 433 class TypeObjectField extends ApiNode {
421 final String name; 434 final String name;
422 final TypeDecl type; 435 final TypeDecl type;
423 final bool optional; 436 final bool optional;
424 437
425 /** 438 /**
426 * Value which the field is required to contain, or null if it may vary. 439 * Value which the field is required to contain, or null if it may vary.
427 */ 440 */
428 final Object value; 441 final Object value;
429 442
430 TypeObjectField(this.name, this.type, dom.Element html, {this.optional: 443 TypeObjectField(this.name, this.type, dom.Element html, {this.optional: false,
431 false, this.value}) : super(html); 444 this.value})
445 : super(html);
432 } 446 }
433 447
434 /** 448 /**
435 * Type of a JSON list. 449 * A reference to a type which is either defined elsewhere in the API or which
450 * is built-in ([String], [bool], or [int]).
436 */ 451 */
437 class TypeList extends TypeDecl { 452 class TypeReference extends TypeDecl {
438 final TypeDecl itemType; 453 final String typeName;
439 454
440 TypeList(this.itemType, dom.Element html) : super(html); 455 TypeReference(this.typeName, dom.Element html) : super(html) {
456 if (typeName.isEmpty) {
457 throw new Exception('Empty type name');
458 }
459 }
441 460
442 accept(ApiVisitor visitor) => visitor.visitTypeList(this); 461 accept(ApiVisitor visitor) => visitor.visitTypeReference(this);
443 } 462 }
444 463
445 /** 464 /**
446 * Type of a JSON map. 465 * A collection of type definitions.
447 */ 466 */
448 class TypeMap extends TypeDecl { 467 class Types extends ApiNode with IterableMixin<TypeDefinition> {
449 /** 468 final Map<String, TypeDefinition> types;
450 * Type of map keys. Note that since JSON map keys must always be strings,
451 * this must either be a [TypeReference] for [String], or a [TypeReference]
452 * to a type which is defined in the API as an enum or a synonym for [String].
453 */
454 final TypeReference keyType;
455 469
456 /** 470 Types(this.types, dom.Element html) : super(html);
457 * Type of map values.
458 */
459 final TypeDecl valueType;
460 471
461 TypeMap(this.keyType, this.valueType, dom.Element html) : super(html); 472 @override
473 Iterator<TypeDefinition> get iterator => types.values.iterator;
462 474
463 accept(ApiVisitor visitor) => visitor.visitTypeMap(this); 475 Iterable<String> get keys => types.keys;
476
477 TypeDefinition operator [](String typeName) => types[typeName];
478
479 bool containsKey(String typeName) => types.containsKey(typeName);
464 } 480 }
465 481
466 /** 482 /**
467 * Type of an enum. We represent enums in JSON as strings, so this type 483 * Type which represents a union among multiple choices.
468 * declaration simply lists the allowed values.
469 */ 484 */
470 class TypeEnum extends TypeDecl { 485 class TypeUnion extends TypeDecl {
471 final List<TypeEnumValue> values; 486 final List<TypeDecl> choices;
472 487
473 TypeEnum(this.values, dom.Element html) : super(html); 488 /**
489 * The field that is used to disambiguate this union
490 */
491 final String field;
474 492
475 accept(ApiVisitor visitor) => visitor.visitTypeEnum(this); 493 TypeUnion(this.choices, this.field, dom.Element html) : super(html);
494
495 accept(ApiVisitor visitor) => visitor.visitTypeUnion(this);
476 } 496 }
477
478 /**
479 * Description of a single allowed value for an enum.
480 */
481 class TypeEnumValue extends ApiNode {
482 final String value;
483
484 TypeEnumValue(this.value, dom.Element html) : super(html);
485 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/test/timing/timing_framework.dart ('k') | pkg/analysis_server/tool/spec/codegen_analysis_server.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698