Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 * Code for displaying the API as HTML. This is used both for generating a | 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 | 7 * full description of the API as a web page, and for generating doc comments |
| 8 * in generated code. | 8 * in generated code. |
| 9 */ | 9 */ |
| 10 library to.html; | 10 library to.html; |
| 11 | 11 |
| 12 import 'dart:convert'; | 12 import 'dart:convert'; |
| 13 import 'dart:io'; | 13 import 'dart:io'; |
| 14 | 14 |
| 15 import 'package:html5lib/dom.dart' as dom; | 15 import 'package:html5lib/dom.dart' as dom; |
| 16 | 16 |
| 17 import 'api.dart'; | 17 import 'api.dart'; |
| 18 import 'codegen_tools.dart'; | 18 import 'codegen_tools.dart'; |
| 19 import 'from_html.dart'; | 19 import 'from_html.dart'; |
| 20 import 'html_tools.dart'; | 20 import 'html_tools.dart'; |
| 21 | 21 |
| 22 /** | 22 /** |
| 23 * Embedded stylesheet | 23 * Embedded stylesheet |
| 24 */ | 24 */ |
| 25 final String stylesheet = | 25 final String stylesheet = ''' |
| 26 ''' | |
| 27 h1 { | 26 h1 { |
| 28 text-align: center; | 27 text-align: center; |
| 29 } | 28 } |
| 30 pre { | 29 pre { |
| 31 margin: 0px; | 30 margin: 0px; |
| 32 } | 31 } |
| 33 div.box { | 32 div.box { |
| 34 border: 1px solid rgb(0, 0, 0); | 33 border: 1px solid rgb(0, 0, 0); |
| 35 background-color: rgb(207, 226, 243); | 34 background-color: rgb(207, 226, 243); |
| 36 padding: 0.5em; | 35 padding: 0.5em; |
| 37 } | 36 } |
| 38 dt { | 37 dt { |
| 39 margin-top: 1em; | 38 margin-top: 1em; |
| 40 margin-bottom: 1em; | 39 margin-bottom: 1em; |
| 41 } | 40 } |
| 42 '''.trim( | 41 '''.trim(); |
| 43 ); | |
| 44 | 42 |
| 45 /** | 43 /** |
| 46 * Helper methods for creating HTML elements. | 44 * Helper methods for creating HTML elements. |
| 47 */ | 45 */ |
| 48 abstract class HtmlMixin { | 46 abstract class HtmlMixin { |
| 49 void element(String name, Map<dynamic, String> attributes, [void callback()]); | 47 void element(String name, Map<dynamic, String> attributes, [void callback()]); |
| 50 | 48 |
| 51 void anchor(String id, void callback()) { | 49 void anchor(String id, void callback()) { |
| 52 element('a', { | 50 element('a', { |
| 53 'name': id | 51 'name': id |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 * Visitor that generates a compact representation of a type, such as: | 89 * Visitor that generates a compact representation of a type, such as: |
| 92 * | 90 * |
| 93 * { | 91 * { |
| 94 * "id": String | 92 * "id": String |
| 95 * "error": optional Error | 93 * "error": optional Error |
| 96 * "result": { | 94 * "result": { |
| 97 * "version": String | 95 * "version": String |
| 98 * } | 96 * } |
| 99 * } | 97 * } |
| 100 */ | 98 */ |
| 101 class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin, | 99 class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlCodeGenerat or { |
| 102 HtmlCodeGenerator { | |
| 103 /** | 100 /** |
| 104 * Set of fields which should be shown in boldface, or null if no field | 101 * Set of fields which should be shown in boldface, or null if no field |
| 105 * should be shown in boldface. | 102 * should be shown in boldface. |
| 106 */ | 103 */ |
| 107 final Set<String> fieldsToBold; | 104 final Set<String> fieldsToBold; |
| 108 | 105 |
| 109 /** | 106 /** |
| 110 * True if a short description should be generated. In a short description, | 107 * 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". | 108 * objects are shown as simply "object", and enums are shown as "String". |
| 112 */ | 109 */ |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 } | 190 } |
| 194 } | 191 } |
| 195 | 192 |
| 196 /** | 193 /** |
| 197 * Visitor that records the mapping from HTML elements to various kinds of API | 194 * Visitor that records the mapping from HTML elements to various kinds of API |
| 198 * nodes. | 195 * nodes. |
| 199 */ | 196 */ |
| 200 class ApiMappings extends HierarchicalApiVisitor { | 197 class ApiMappings extends HierarchicalApiVisitor { |
| 201 ApiMappings(Api api) : super(api); | 198 ApiMappings(Api api) : super(api); |
| 202 | 199 |
| 203 Map<dom.Element, Domain> domains = <dom.Element, Domain> {}; | 200 Map<dom.Element, Domain> domains = <dom.Element, Domain>{}; |
| 204 | 201 |
| 205 @override | 202 @override |
| 206 void visitDomain(Domain domain) { | 203 void visitDomain(Domain domain) { |
| 207 domains[domain.html] = domain; | 204 domains[domain.html] = domain; |
| 208 } | 205 } |
| 209 } | 206 } |
| 210 | 207 |
| 211 /** | 208 /** |
| 212 * Visitor that generates HTML documentation of the API. | 209 * Visitor that generates HTML documentation of the API. |
| 213 */ | 210 */ |
| 214 class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator | 211 class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator { |
| 215 { | |
| 216 /** | 212 /** |
| 217 * Set of types defined in the API. | 213 * Set of types defined in the API. |
| 218 */ | 214 */ |
| 219 Set<String> definedTypes = new Set<String>(); | 215 Set<String> definedTypes = new Set<String>(); |
| 220 | 216 |
| 221 /** | 217 /** |
| 222 * Mappings from HTML elements to API nodes. | 218 * Mappings from HTML elements to API nodes. |
| 223 */ | 219 */ |
| 224 ApiMappings apiMappings; | 220 ApiMappings apiMappings; |
| 225 | 221 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 291 } | 287 } |
| 292 } | 288 } |
| 293 | 289 |
| 294 @override | 290 @override |
| 295 void visitNotification(Notification notification) { | 291 void visitNotification(Notification notification) { |
| 296 dt('notification', () { | 292 dt('notification', () { |
| 297 write(notification.longEvent); | 293 write(notification.longEvent); |
| 298 }); | 294 }); |
| 299 dd(() { | 295 dd(() { |
| 300 box(() { | 296 box(() { |
| 301 showType('notification', notification.notificationType, | 297 showType('notification', notification.notificationType, notification.par ams); |
| 302 notification.params); | |
| 303 }); | 298 }); |
| 304 translateHtml(notification.html); | 299 translateHtml(notification.html); |
| 305 describePayload(notification.params, 'Parameters'); | 300 describePayload(notification.params, 'Parameters'); |
| 306 }); | 301 }); |
| 307 } | 302 } |
| 308 | 303 |
| 309 /** | 304 /** |
| 310 * Copy the contents of the given HTML element, translating the special | 305 * Copy the contents of the given HTML element, translating the special |
| 311 * elements that define the API appropriately. | 306 * elements that define the API appropriately. |
| 312 */ | 307 */ |
| 313 void translateHtml(dom.Element html) { | 308 void translateHtml(dom.Element html, {bool squashParagraphs: false}) { |
| 314 for (dom.Node node in html.nodes) { | 309 for (dom.Node node in html.nodes) { |
| 315 if (node is dom.Element) { | 310 if (node is dom.Element) { |
| 311 if (squashParagraphs && node.localName == 'p') { | |
| 312 translateHtml(node, squashParagraphs: squashParagraphs); | |
| 313 break; | |
|
Paul Berry
2014/08/12 18:04:02
Should be "continue;"
jwren
2014/08/12 18:23:08
Done.
| |
| 314 } | |
| 316 switch (node.localName) { | 315 switch (node.localName) { |
| 317 case 'api': | 316 case 'api': |
| 318 translateHtml(node); | 317 translateHtml(node, squashParagraphs: squashParagraphs); |
| 319 break; | 318 break; |
| 320 case 'domain': | 319 case 'domain': |
| 321 visitDomain(apiMappings.domains[node]); | 320 visitDomain(apiMappings.domains[node]); |
| 322 break; | 321 break; |
| 323 case 'head': | 322 case 'head': |
| 324 head(() { | 323 head(() { |
| 325 translateHtml(node); | 324 translateHtml(node, squashParagraphs: squashParagraphs); |
| 326 element('style', {}, () { | 325 element('style', {}, () { |
| 327 writeln(stylesheet); | 326 writeln(stylesheet); |
| 328 }); | 327 }); |
| 329 }); | 328 }); |
| 330 break; | 329 break; |
| 331 case 'refactorings': | 330 case 'refactorings': |
| 332 visitRefactorings(api.refactorings); | 331 visitRefactorings(api.refactorings); |
| 333 break; | 332 break; |
| 334 case 'types': | 333 case 'types': |
| 335 visitTypes(api.types); | 334 visitTypes(api.types); |
| 336 break; | 335 break; |
| 337 case 'version': | 336 case 'version': |
| 338 translateHtml(node); | 337 translateHtml(node, squashParagraphs: squashParagraphs); |
| 339 break; | 338 break; |
| 340 default: | 339 default: |
| 341 if (!specialElements.contains(node.localName)) { | 340 if (!specialElements.contains(node.localName)) { |
| 342 element(node.localName, node.attributes, () { | 341 element(node.localName, node.attributes, () { |
| 343 translateHtml(node); | 342 translateHtml(node, squashParagraphs: squashParagraphs); |
| 344 }); | 343 }); |
| 345 } | 344 } |
| 346 } | 345 } |
| 347 } else if (node is dom.Text) { | 346 } else if (node is dom.Text) { |
| 348 String text = node.text; | 347 String text = node.text; |
| 349 write(text); | 348 write(text); |
| 350 } | 349 } |
| 351 } | 350 } |
| 352 } | 351 } |
| 353 | 352 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 364 Set<String> fieldsToBold = new Set<String>(); | 363 Set<String> fieldsToBold = new Set<String>(); |
| 365 if (typeForBolding != null) { | 364 if (typeForBolding != null) { |
| 366 for (TypeObjectField field in typeForBolding.fields) { | 365 for (TypeObjectField field in typeForBolding.fields) { |
| 367 fieldsToBold.add(field.name); | 366 fieldsToBold.add(field.name); |
| 368 } | 367 } |
| 369 } | 368 } |
| 370 pre(() { | 369 pre(() { |
| 371 if (shortDesc != null) { | 370 if (shortDesc != null) { |
| 372 write('$shortDesc: '); | 371 write('$shortDesc: '); |
| 373 } | 372 } |
| 374 TypeVisitor typeVisitor = new TypeVisitor(api, fieldsToBold: fieldsToBold | 373 TypeVisitor typeVisitor = new TypeVisitor(api, fieldsToBold: fieldsToBold) ; |
| 375 ); | |
| 376 addAll(typeVisitor.collectHtml(() { | 374 addAll(typeVisitor.collectHtml(() { |
| 377 typeVisitor.visitTypeDecl(type); | 375 typeVisitor.visitTypeDecl(type); |
| 378 })); | 376 })); |
| 379 }); | 377 }); |
| 380 } | 378 } |
| 381 | 379 |
| 382 /** | 380 /** |
| 383 * Describe the payload of request, response, notification, refactoring | 381 * Describe the payload of request, response, notification, refactoring |
| 384 * feedback, or refactoring options. | 382 * feedback, or refactoring options. |
| 385 * | 383 * |
| 386 * If [force] is true, then a section is inserted even if the payload is | 384 * If [force] is true, then a section is inserted even if the payload is |
| 387 * null. | 385 * null. |
| 388 */ | 386 */ |
| 389 void describePayload(TypeObject subType, String name, {bool force: false}) { | 387 void describePayload(TypeObject subType, String name, {bool force: false}) { |
| 390 if (force || subType != null) { | 388 if (force || subType != null) { |
| 391 h4(() { | 389 h4(() { |
| 392 write(name); | 390 write(name); |
| 393 }); | 391 }); |
| 394 if (subType == null) { | 392 if (subType == null) { |
| 395 p(() { | 393 p(() { |
| 396 write('none'); | 394 write('none'); |
| 397 }); | 395 }); |
| 398 } else { | 396 } else { |
| 399 visitTypeDecl(subType); | 397 visitTypeDecl(subType); |
| 400 } | 398 } |
| 401 } | 399 } |
| 402 } | 400 } |
| 403 | 401 |
| 402 void javadocParams(TypeObject typeObject) { | |
| 403 if (typeObject != null) { | |
| 404 for (TypeObjectField field in typeObject.fields) { | |
| 405 write('@param ${field.name} '); | |
| 406 translateHtml(field.html, squashParagraphs: true); | |
| 407 br(); | |
| 408 } | |
| 409 } | |
| 410 } | |
| 411 | |
| 404 @override | 412 @override |
| 405 void visitRequest(Request request) { | 413 void visitRequest(Request request) { |
| 406 dt('request', () { | 414 dt('request', () { |
| 407 write(request.longMethod); | 415 write(request.longMethod); |
| 408 }); | 416 }); |
| 409 dd(() { | 417 dd(() { |
| 410 box(() { | 418 box(() { |
| 411 showType('request', request.requestType, request.params); | 419 showType('request', request.requestType, request.params); |
| 412 br(); | 420 br(); |
| 413 showType('response', request.responseType, request.result); | 421 showType('response', request.responseType, request.result); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 439 void visitTypeEnum(TypeEnum typeEnum) { | 447 void visitTypeEnum(TypeEnum typeEnum) { |
| 440 dl(() { | 448 dl(() { |
| 441 super.visitTypeEnum(typeEnum); | 449 super.visitTypeEnum(typeEnum); |
| 442 }); | 450 }); |
| 443 } | 451 } |
| 444 | 452 |
| 445 @override | 453 @override |
| 446 void visitTypeEnumValue(TypeEnumValue typeEnumValue) { | 454 void visitTypeEnumValue(TypeEnumValue typeEnumValue) { |
| 447 bool isDocumented = false; | 455 bool isDocumented = false; |
| 448 for (dom.Node node in typeEnumValue.html.nodes) { | 456 for (dom.Node node in typeEnumValue.html.nodes) { |
| 449 if ((node is dom.Element && node.localName != 'code') || (node is dom.Text | 457 if ((node is dom.Element && node.localName != 'code') || (node is dom.Text && node.text.trim().isNotEmpty)) { |
| 450 && node.text.trim().isNotEmpty)) { | |
| 451 isDocumented = true; | 458 isDocumented = true; |
| 452 break; | 459 break; |
| 453 } | 460 } |
| 454 } | 461 } |
| 455 dt('value', () { | 462 dt('value', () { |
| 456 write(typeEnumValue.value); | 463 write(typeEnumValue.value); |
| 457 }); | 464 }); |
| 458 if (isDocumented) { | 465 if (isDocumented) { |
| 459 dd(() { | 466 dd(() { |
| 460 translateHtml(typeEnumValue.html); | 467 translateHtml(typeEnumValue.html); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 519 */ | 526 */ |
| 520 main() { | 527 main() { |
| 521 ToHtmlVisitor visitor = new ToHtmlVisitor(readApi()); | 528 ToHtmlVisitor visitor = new ToHtmlVisitor(readApi()); |
| 522 dom.Document document = new dom.Document(); | 529 dom.Document document = new dom.Document(); |
| 523 for (dom.Node node in visitor.collectHtml(visitor.visitApi)) { | 530 for (dom.Node node in visitor.collectHtml(visitor.visitApi)) { |
| 524 document.append(node); | 531 document.append(node); |
| 525 } | 532 } |
| 526 File outputFile = new File('../../doc/api.html'); | 533 File outputFile = new File('../../doc/api.html'); |
| 527 outputFile.writeAsStringSync(document.outerHtml); | 534 outputFile.writeAsStringSync(document.outerHtml); |
| 528 } | 535 } |
| OLD | NEW |