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

Side by Side Diff: pkg/analysis_server/spec/to_html.dart

Issue 443873006: Add analysis server API specification and related tools. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 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) 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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Code for displaying the API as HTML. This is used both for generating a
7 * full description of the API as a web page, and for generating doc comments
8 * in generated code.
9 */
10 library toHtml;
11
12 import 'dart:convert';
13 import 'dart:io';
14
15 import 'package:html5lib/dom.dart' as dom;
16
17 import 'api.dart';
18 import 'codegen_tools.dart';
19 import 'from_html.dart';
20 import 'html_tools.dart';
21
22 /**
23 * Embedded stylesheet
24 */
25 final String stylesheet =
26 '''
27 h1 {
28 text-align: center;
29 }
30 pre {
31 margin: 0px;
32 }
33 div.box {
34 border: 1px solid rgb(0, 0, 0);
35 background-color: rgb(207, 226, 243);
36 padding: 0.5em;
37 }
38 dt {
39 margin-top: 1em;
40 margin-bottom: 1em;
41 }
42 '''.trim(
43 );
44
45 /**
46 * Helper methods for creating HTML elements.
47 */
48 abstract class HtmlMixin {
49 void element(String name, Map<dynamic, String> attributes, [void callback()]);
50
51 void anchor(String id, void callback()) {
52 element('a', {
53 'name': id
54 }, callback);
55 }
56 void link(String id, void callback()) {
57 element('a', {
58 'href': '#$id'
59 }, callback);
60 }
61 void b(void callback()) => element('b', {}, callback);
62 void box(void callback()) {
63 element('div', {
64 'class': 'box'
65 }, callback);
66 }
67 void br() => element('br', {});
68 void body(void callback()) => element('body', {}, callback);
69 void dd(void callback()) => element('dd', {}, callback);
70 void dl(void callback()) => element('dl', {}, callback);
71 void dt(String cls, void callback()) => element('dt', {
72 'class': cls
73 }, callback);
74 void gray(void callback()) => element('span', {
75 'style': 'color:#999999'
76 }, callback);
77 void h1(void callback()) => element('h1', {}, callback);
78 void h2(void callback()) => element('h2', {}, callback);
79 void h3(void callback()) => element('h3', {}, callback);
80 void h4(void callback()) => element('h4', {}, callback);
81 void head(void callback()) => element('head', {}, callback);
82 void html(void callback()) => element('html', {}, callback);
83 void i(void callback()) => element('i', {}, callback);
84 void p(void callback()) => element('p', {}, callback);
85 void pre(void callback()) => element('pre', {}, callback);
86 void title(void callback()) => element('title', {}, callback);
87 void tt(void callback()) => element('tt', {}, callback);
88 }
89
90 /**
91 * Visitor that generates a compact representation of a type, such as:
92 *
93 * {
94 * "id": String
95 * "error": optional Error
96 * "result": {
97 * "version": String
98 * }
99 * }
100 */
101 class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin,
102 HtmlCodeGenerator {
103 /**
104 * Set of fields which should be shown in boldface, or null if no field
105 * should be shown in boldface.
106 */
107 final Set<String> fieldsToBold;
108
109 /**
110 * True if a short description should be generated. In a short description,
111 * objects are shown as simply "object", and enums are shown as "String".
112 */
113 final bool short;
114
115 TypeVisitor(Api api, {this.fieldsToBold, this.short: false}) : super(api);
116
117 @override
118 void visitTypeEnum(TypeEnum typeEnum) {
119 if (short) {
120 write('String');
121 return;
122 }
123 writeln('enum {');
124 indent(() {
125 for (TypeEnumValue value in typeEnum.values) {
126 writeln(value.value);
127 }
128 });
129 write('}');
130 }
131
132 @override
133 void visitTypeList(TypeList typeList) {
134 write('List<');
135 visitTypeDecl(typeList.itemType);
136 write('>');
137 }
138
139 @override
140 void visitTypeMap(TypeMap typeMap) {
141 write('Map<');
142 visitTypeDecl(typeMap.keyType);
143 write(', ');
144 visitTypeDecl(typeMap.valueType);
145 write('>');
146 }
147
148 @override
149 void visitTypeObject(TypeObject typeObject) {
150 if (short) {
151 write('object');
152 return;
153 }
154 writeln('{');
155 indent(() {
156 for (TypeObjectField field in typeObject.fields) {
157 write('"');
158 if (fieldsToBold != null && fieldsToBold.contains(field.name)) {
159 b(() {
160 write(field.name);
161 });
162 } else {
163 write(field.name);
164 }
165 write('": ');
166 if (field.value != null) {
167 write(JSON.encode(field.value));
168 } else {
169 if (field.optional) {
170 gray(() {
171 write('optional');
172 });
173 write(' ');
174 }
175 visitTypeDecl(field.type);
176 }
177 writeln();
178 }
179 });
180 write('}');
181 }
182
183 @override
184 void visitTypeReference(TypeReference typeReference) {
185 String displayName = typeReference.typeName;
186 if (api.types.containsKey(typeReference.typeName)) {
187 link('type_${typeReference.typeName}', () {
188 write(displayName);
189 });
190 } else {
191 write(displayName);
192 }
193 }
194 }
195
196 /**
197 * Visitor that records the mapping from HTML elements to various kinds of API
198 * nodes.
199 */
200 class ApiMappings extends HierarchicalApiVisitor {
201 ApiMappings(Api api) : super(api);
202
203 Map<dom.Element, Domain> domains = <dom.Element, Domain> {};
204
205 @override
206 void visitDomain(Domain domain) {
207 domains[domain.html] = domain;
208 }
209 }
210
211 /**
212 * Visitor that generates HTML documentation of the API.
213 */
214 class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
215 {
216 /**
217 * Set of types defined in the API.
218 */
219 Set<String> definedTypes = new Set<String>();
220
221 /**
222 * Mappings from HTML elements to API nodes.
223 */
224 ApiMappings apiMappings;
225
226 ToHtmlVisitor(Api api)
227 : super(api),
228 apiMappings = new ApiMappings(api) {
229 apiMappings.visitApi();
230 }
231
232 @override
233 void visitApi() {
234 definedTypes = api.types.keys.toSet();
235
236 html(() {
237 translateHtml(api.html);
238 });
239 }
240
241 @override
242 void visitRefactorings(Refactorings refactorings) {
243 translateHtml(refactorings.html);
244 dl(() {
245 super.visitRefactorings(refactorings);
246 });
247 }
248
249 @override visitRefactoring(Refactoring refactoring) {
250 dt('refactoring', () {
251 write(refactoring.kind);
252 });
253 dd(() {
254 translateHtml(refactoring.html);
255 describePayload(refactoring.feedback, 'Feedback', force: true);
256 describePayload(refactoring.options, 'Options', force: true);
257 });
258 }
259
260 @override
261 void visitTypes(Types types) {
262 translateHtml(types.html);
263 dl(() {
264 super.visitTypes(types);
265 });
266 }
267
268 @override
269 void visitDomain(Domain domain) {
270 h2(() {
271 anchor('domain_${domain.name}', () {
272 write('Domain: ${domain.name}');
273 });
274 });
275 translateHtml(domain.html);
276 if (domain.requests.isNotEmpty) {
277 h3(() {
278 write('Requests');
279 });
280 dl(() {
281 domain.requests.forEach(visitRequest);
282 });
283 }
284 if (domain.notifications.isNotEmpty) {
285 h3(() {
286 write('Notifications');
287 });
288 dl(() {
289 domain.notifications.forEach(visitNotification);
290 });
291 }
292 }
293
294 @override
295 void visitNotification(Notification notification) {
296 dt('notification', () {
297 write(notification.longEvent);
298 });
299 dd(() {
300 box(() {
301 showType('notification', notification.notificationType,
302 notification.params);
303 });
304 translateHtml(notification.html);
305 describePayload(notification.params, 'Parameters');
306 });
307 }
308
309 /**
310 * Copy the contents of the given HTML element, translating the special
311 * elements that define the API appropriately.
312 */
313 void translateHtml(dom.Element html) {
314 for (dom.Node node in html.nodes) {
315 if (node is dom.Element) {
316 switch (node.localName) {
317 case 'api':
318 translateHtml(node);
319 break;
320 case 'domain':
321 visitDomain(apiMappings.domains[node]);
322 break;
323 case 'head':
324 head(() {
325 translateHtml(node);
326 element('style', {}, () {
327 writeln(stylesheet);
328 });
329 });
330 break;
331 case 'refactorings':
332 visitRefactorings(api.refactorings);
333 break;
334 case 'types':
335 visitTypes(api.types);
336 break;
337 case 'version':
338 translateHtml(node);
339 break;
340 default:
341 if (!specialElements.contains(node.localName)) {
342 element(node.localName, node.attributes, () {
343 translateHtml(node);
344 });
345 }
346 }
347 } else if (node is dom.Text) {
348 String text = node.text;
349 write(text);
350 }
351 }
352 }
353
354 /**
355 * Generate a description of [type] using [TypeVisitor].
356 *
357 * If [shortDesc] is non-null, the output is prefixed with this string
358 * and a colon.
359 *
360 * If [typeForBolding] is supplied, then fields in this type are shown in
361 * boldface.
362 */
363 void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) {
364 Set<String> fieldsToBold = new Set<String>();
365 if (typeForBolding != null) {
366 for (TypeObjectField field in typeForBolding.fields) {
367 fieldsToBold.add(field.name);
368 }
369 }
370 pre(() {
371 if (shortDesc != null) {
372 write('$shortDesc: ');
373 }
374 TypeVisitor typeVisitor = new TypeVisitor(api, fieldsToBold: fieldsToBold
375 );
376 addAll(typeVisitor.collectHtml(() {
377 typeVisitor.visitTypeDecl(type);
378 }));
379 });
380 }
381
382 /**
383 * Describe the payload of request, response, notification, refactoring
384 * feedback, or refactoring options.
385 *
386 * If [force] is true, then a section is inserted even if the payload is
387 * null.
388 */
389 void describePayload(TypeObject subType, String name, {bool force: false}) {
390 if (force || subType != null) {
391 h4(() {
392 write(name);
393 });
394 if (subType == null) {
395 p(() {
396 write('none');
397 });
398 } else {
399 visitTypeDecl(subType);
400 }
401 }
402 }
403
404 @override
405 void visitRequest(Request request) {
406 dt('request', () {
407 write(request.longMethod);
408 });
409 dd(() {
410 box(() {
411 showType('request', request.requestType, request.params);
412 br();
413 showType('response', request.responseType, request.result);
414 });
415 translateHtml(request.html);
416 describePayload(request.params, 'Parameters');
417 describePayload(request.result, 'Returns');
418 });
419 }
420
421 @override
422 void visitTypeDefinition(TypeDefinition typeDefinition) {
423 dt('typeDefinition', () {
424 anchor('type_${typeDefinition.name}', () {
425 write('${typeDefinition.name}: ');
426 TypeVisitor typeVisitor = new TypeVisitor(api, short: true);
427 addAll(typeVisitor.collectHtml(() {
428 typeVisitor.visitTypeDecl(typeDefinition.type);
429 }));
430 });
431 });
432 dd(() {
433 translateHtml(typeDefinition.html);
434 visitTypeDecl(typeDefinition.type);
435 });
436 }
437
438 @override
439 void visitTypeEnum(TypeEnum typeEnum) {
440 dl(() {
441 super.visitTypeEnum(typeEnum);
442 });
443 }
444
445 @override
446 void visitTypeEnumValue(TypeEnumValue typeEnumValue) {
447 bool isDocumented = false;
448 for (dom.Node node in typeEnumValue.html.nodes) {
449 if ((node is dom.Element && node.localName != 'code') || (node is dom.Text
450 && node.text.trim().isNotEmpty)) {
451 isDocumented = true;
452 break;
453 }
454 }
455 dt('value', () {
456 write(typeEnumValue.value);
457 });
458 if (isDocumented) {
459 dd(() {
460 translateHtml(typeEnumValue.html);
461 });
462 }
463 }
464
465 @override
466 void visitTypeList(TypeList typeList) {
467 visitTypeDecl(typeList.itemType);
468 }
469
470 @override
471 void visitTypeMap(TypeMap typeMap) {
472 visitTypeDecl(typeMap.valueType);
473 }
474
475 @override
476 void visitTypeObject(TypeObject typeObject) {
477 dl(() {
478 super.visitTypeObject(typeObject);
479 });
480 }
481
482 @override
483 void visitTypeObjectField(TypeObjectField typeObjectField) {
484 dt('field', () {
485 b(() {
486 i(() {
487 write(typeObjectField.name);
488 if (typeObjectField.value != null) {
489 write(' = ${typeObjectField.value}');
490 } else {
491 write(' ( ');
492 if (typeObjectField.optional) {
493 gray(() {
494 write('optional');
495 });
496 write(' ');
497 }
498 TypeVisitor typeVisitor = new TypeVisitor(api, short: true);
499 addAll(typeVisitor.collectHtml(() {
500 typeVisitor.visitTypeDecl(typeObjectField.type);
501 }));
502 write(' )');
503 }
504 });
505 });
506 });
507 dd(() {
508 translateHtml(typeObjectField.html);
509 });
510 }
511
512 @override
513 void visitTypeReference(TypeReference typeReference) {
514 }
515 }
516
517 /**
518 * Translate spec_input.html into api.html.
519 */
520 main() {
521 ToHtmlVisitor visitor = new ToHtmlVisitor(readApi());
522 dom.Document document = new dom.Document();
523 for (dom.Node node in visitor.collectHtml(visitor.visitApi)) {
524 document.append(node);
525 }
526 File outputFile = new File('api.html');
527 outputFile.writeAsStringSync(document.outerHtml);
528 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698