| 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; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 font-weight: bold; | 56 font-weight: bold; |
| 57 } | 57 } |
| 58 dt.request { | 58 dt.request { |
| 59 font-weight: bold; | 59 font-weight: bold; |
| 60 } | 60 } |
| 61 dt.typeDefinition { | 61 dt.typeDefinition { |
| 62 font-weight: bold; | 62 font-weight: bold; |
| 63 } | 63 } |
| 64 '''.trim(); | 64 '''.trim(); |
| 65 | 65 |
| 66 final GeneratedFile target = new GeneratedFile('../../doc/api.html', () { |
| 67 ToHtmlVisitor visitor = new ToHtmlVisitor(readApi()); |
| 68 dom.Document document = new dom.Document(); |
| 69 for (dom.Node node in visitor.collectHtml(visitor.visitApi)) { |
| 70 document.append(node); |
| 71 } |
| 72 return document.outerHtml; |
| 73 }); |
| 74 |
| 75 /** |
| 76 * Translate spec_input.html into api.html. |
| 77 */ |
| 78 main() { |
| 79 target.generate(); |
| 80 } |
| 81 |
| 82 /** |
| 83 * Visitor that records the mapping from HTML elements to various kinds of API |
| 84 * nodes. |
| 85 */ |
| 86 class ApiMappings extends HierarchicalApiVisitor { |
| 87 Map<dom.Element, Domain> domains = <dom.Element, Domain>{}; |
| 88 |
| 89 ApiMappings(Api api) : super(api); |
| 90 |
| 91 @override |
| 92 void visitDomain(Domain domain) { |
| 93 domains[domain.html] = domain; |
| 94 } |
| 95 } |
| 96 |
| 66 /** | 97 /** |
| 67 * Helper methods for creating HTML elements. | 98 * Helper methods for creating HTML elements. |
| 68 */ | 99 */ |
| 69 abstract class HtmlMixin { | 100 abstract class HtmlMixin { |
| 70 void element(String name, Map<dynamic, String> attributes, [void callback()]); | |
| 71 | |
| 72 void anchor(String id, void callback()) { | 101 void anchor(String id, void callback()) { |
| 73 element('a', { | 102 element('a', { |
| 74 'name': id | 103 'name': id |
| 75 }, callback); | 104 }, callback); |
| 76 } | 105 } |
| 77 void link(String id, void callback()) { | 106 |
| 78 element('a', { | |
| 79 'href': '#$id' | |
| 80 }, callback); | |
| 81 } | |
| 82 void b(void callback()) => element('b', {}, callback); | 107 void b(void callback()) => element('b', {}, callback); |
| 108 void body(void callback()) => element('body', {}, callback); |
| 83 void box(void callback()) { | 109 void box(void callback()) { |
| 84 element('div', { | 110 element('div', { |
| 85 'class': 'box' | 111 'class': 'box' |
| 86 }, callback); | 112 }, callback); |
| 87 } | 113 } |
| 88 void br() => element('br', {}); | 114 void br() => element('br', {}); |
| 89 void body(void callback()) => element('body', {}, callback); | |
| 90 void dd(void callback()) => element('dd', {}, callback); | 115 void dd(void callback()) => element('dd', {}, callback); |
| 91 void dl(void callback()) => element('dl', {}, callback); | 116 void dl(void callback()) => element('dl', {}, callback); |
| 92 void dt(String cls, void callback()) => element('dt', { | 117 void dt(String cls, void callback()) => element('dt', { |
| 93 'class': cls | 118 'class': cls |
| 94 }, callback); | 119 }, callback); |
| 120 void element(String name, Map<dynamic, String> attributes, [void callback()]); |
| 95 void gray(void callback()) => element('span', { | 121 void gray(void callback()) => element('span', { |
| 96 'style': 'color:#999999' | 122 'style': 'color:#999999' |
| 97 }, callback); | 123 }, callback); |
| 98 void h1(void callback()) => element('h1', {}, callback); | 124 void h1(void callback()) => element('h1', {}, callback); |
| 99 void h2(String cls, void callback()) { | 125 void h2(String cls, void callback()) { |
| 100 if (cls == null) { | 126 if (cls == null) { |
| 101 return element('h2', {}, callback); | 127 return element('h2', {}, callback); |
| 102 } | 128 } |
| 103 return element('h2', { | 129 return element('h2', { |
| 104 'class': cls | 130 'class': cls |
| 105 }, callback); | 131 }, callback); |
| 106 } | 132 } |
| 107 void h3(void callback()) => element('h3', {}, callback); | 133 void h3(void callback()) => element('h3', {}, callback); |
| 108 void h4(void callback()) => element('h4', {}, callback); | 134 void h4(void callback()) => element('h4', {}, callback); |
| 109 void hangingIndent(void callback()) => element('div', { | 135 void hangingIndent(void callback()) => element('div', { |
| 110 'class': 'hangingIndent' | 136 'class': 'hangingIndent' |
| 111 }, callback); | 137 }, callback); |
| 112 void head(void callback()) => element('head', {}, callback); | 138 void head(void callback()) => element('head', {}, callback); |
| 113 void html(void callback()) => element('html', {}, callback); | 139 void html(void callback()) => element('html', {}, callback); |
| 114 void i(void callback()) => element('i', {}, callback); | 140 void i(void callback()) => element('i', {}, callback); |
| 141 void link(String id, void callback()) { |
| 142 element('a', { |
| 143 'href': '#$id' |
| 144 }, callback); |
| 145 } |
| 115 void p(void callback()) => element('p', {}, callback); | 146 void p(void callback()) => element('p', {}, callback); |
| 116 void pre(void callback()) => element('pre', {}, callback); | 147 void pre(void callback()) => element('pre', {}, callback); |
| 117 void title(void callback()) => element('title', {}, callback); | 148 void title(void callback()) => element('title', {}, callback); |
| 118 void tt(void callback()) => element('tt', {}, callback); | 149 void tt(void callback()) => element('tt', {}, callback); |
| 119 } | 150 } |
| 120 | 151 |
| 121 /** | 152 /** |
| 122 * Visitor that generates a compact representation of a type, such as: | |
| 123 * | |
| 124 * { | |
| 125 * "id": String | |
| 126 * "error": optional Error | |
| 127 * "result": { | |
| 128 * "version": String | |
| 129 * } | |
| 130 * } | |
| 131 */ | |
| 132 class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlCodeGenerat
or { | |
| 133 /** | |
| 134 * Set of fields which should be shown in boldface, or null if no field | |
| 135 * should be shown in boldface. | |
| 136 */ | |
| 137 final Set<String> fieldsToBold; | |
| 138 | |
| 139 /** | |
| 140 * True if a short description should be generated. In a short description, | |
| 141 * objects are shown as simply "object", and enums are shown as "String". | |
| 142 */ | |
| 143 final bool short; | |
| 144 | |
| 145 TypeVisitor(Api api, {this.fieldsToBold, this.short: false}) : super(api); | |
| 146 | |
| 147 @override | |
| 148 void visitTypeEnum(TypeEnum typeEnum) { | |
| 149 if (short) { | |
| 150 write('String'); | |
| 151 return; | |
| 152 } | |
| 153 writeln('enum {'); | |
| 154 indent(() { | |
| 155 for (TypeEnumValue value in typeEnum.values) { | |
| 156 writeln(value.value); | |
| 157 } | |
| 158 }); | |
| 159 write('}'); | |
| 160 } | |
| 161 | |
| 162 @override | |
| 163 void visitTypeList(TypeList typeList) { | |
| 164 write('List<'); | |
| 165 visitTypeDecl(typeList.itemType); | |
| 166 write('>'); | |
| 167 } | |
| 168 | |
| 169 @override | |
| 170 void visitTypeMap(TypeMap typeMap) { | |
| 171 write('Map<'); | |
| 172 visitTypeDecl(typeMap.keyType); | |
| 173 write(', '); | |
| 174 visitTypeDecl(typeMap.valueType); | |
| 175 write('>'); | |
| 176 } | |
| 177 | |
| 178 @override | |
| 179 void visitTypeObject(TypeObject typeObject) { | |
| 180 if (short) { | |
| 181 write('object'); | |
| 182 return; | |
| 183 } | |
| 184 writeln('{'); | |
| 185 indent(() { | |
| 186 for (TypeObjectField field in typeObject.fields) { | |
| 187 write('"'); | |
| 188 if (fieldsToBold != null && fieldsToBold.contains(field.name)) { | |
| 189 b(() { | |
| 190 write(field.name); | |
| 191 }); | |
| 192 } else { | |
| 193 write(field.name); | |
| 194 } | |
| 195 write('": '); | |
| 196 if (field.value != null) { | |
| 197 write(JSON.encode(field.value)); | |
| 198 } else { | |
| 199 if (field.optional) { | |
| 200 gray(() { | |
| 201 write('optional'); | |
| 202 }); | |
| 203 write(' '); | |
| 204 } | |
| 205 visitTypeDecl(field.type); | |
| 206 } | |
| 207 writeln(); | |
| 208 } | |
| 209 }); | |
| 210 write('}'); | |
| 211 } | |
| 212 | |
| 213 @override | |
| 214 void visitTypeReference(TypeReference typeReference) { | |
| 215 String displayName = typeReference.typeName; | |
| 216 if (api.types.containsKey(typeReference.typeName)) { | |
| 217 link('type_${typeReference.typeName}', () { | |
| 218 write(displayName); | |
| 219 }); | |
| 220 } else { | |
| 221 write(displayName); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 @override | |
| 226 void visitTypeUnion(TypeUnion typeUnion) { | |
| 227 bool verticalBarNeeded = false; | |
| 228 for (TypeDecl choice in typeUnion.choices) { | |
| 229 if (verticalBarNeeded) { | |
| 230 write(' | '); | |
| 231 } | |
| 232 visitTypeDecl(choice); | |
| 233 verticalBarNeeded = true; | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 * Visitor that records the mapping from HTML elements to various kinds of API | |
| 240 * nodes. | |
| 241 */ | |
| 242 class ApiMappings extends HierarchicalApiVisitor { | |
| 243 ApiMappings(Api api) : super(api); | |
| 244 | |
| 245 Map<dom.Element, Domain> domains = <dom.Element, Domain>{}; | |
| 246 | |
| 247 @override | |
| 248 void visitDomain(Domain domain) { | |
| 249 domains[domain.html] = domain; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 /** | |
| 254 * Visitor that generates HTML documentation of the API. | 153 * Visitor that generates HTML documentation of the API. |
| 255 */ | 154 */ |
| 256 class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
{ | 155 class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator |
| 156 { |
| 257 /** | 157 /** |
| 258 * Set of types defined in the API. | 158 * Set of types defined in the API. |
| 259 */ | 159 */ |
| 260 Set<String> definedTypes = new Set<String>(); | 160 Set<String> definedTypes = new Set<String>(); |
| 261 | 161 |
| 262 /** | 162 /** |
| 263 * Mappings from HTML elements to API nodes. | 163 * Mappings from HTML elements to API nodes. |
| 264 */ | 164 */ |
| 265 ApiMappings apiMappings; | 165 ApiMappings apiMappings; |
| 266 | 166 |
| 267 ToHtmlVisitor(Api api) | 167 ToHtmlVisitor(Api api) |
| 268 : super(api), | 168 : super(api), |
| 269 apiMappings = new ApiMappings(api) { | 169 apiMappings = new ApiMappings(api) { |
| 270 apiMappings.visitApi(); | 170 apiMappings.visitApi(); |
| 271 } | 171 } |
| 272 | 172 |
| 273 @override | 173 /** |
| 274 void visitApi() { | 174 * Describe the payload of request, response, notification, refactoring |
| 275 definedTypes = api.types.keys.toSet(); | 175 * feedback, or refactoring options. |
| 176 * |
| 177 * If [force] is true, then a section is inserted even if the payload is |
| 178 * null. |
| 179 */ |
| 180 void describePayload(TypeObject subType, String name, {bool force: false}) { |
| 181 if (force || subType != null) { |
| 182 h4(() { |
| 183 write(name); |
| 184 }); |
| 185 if (subType == null) { |
| 186 p(() { |
| 187 write('none'); |
| 188 }); |
| 189 } else { |
| 190 visitTypeDecl(subType); |
| 191 } |
| 192 } |
| 193 } |
| 276 | 194 |
| 277 html(() { | 195 void javadocParams(TypeObject typeObject) { |
| 278 translateHtml(api.html); | 196 if (typeObject != null) { |
| 197 for (TypeObjectField field in typeObject.fields) { |
| 198 hangingIndent(() { |
| 199 write('@param ${field.name} '); |
| 200 translateHtml(field.html, squashParagraphs: true); |
| 201 }); |
| 202 } |
| 203 } |
| 204 } |
| 205 |
| 206 /** |
| 207 * Generate a description of [type] using [TypeVisitor]. |
| 208 * |
| 209 * If [shortDesc] is non-null, the output is prefixed with this string |
| 210 * and a colon. |
| 211 * |
| 212 * If [typeForBolding] is supplied, then fields in this type are shown in |
| 213 * boldface. |
| 214 */ |
| 215 void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) { |
| 216 Set<String> fieldsToBold = new Set<String>(); |
| 217 if (typeForBolding != null) { |
| 218 for (TypeObjectField field in typeForBolding.fields) { |
| 219 fieldsToBold.add(field.name); |
| 220 } |
| 221 } |
| 222 pre(() { |
| 223 if (shortDesc != null) { |
| 224 write('$shortDesc: '); |
| 225 } |
| 226 TypeVisitor typeVisitor = |
| 227 new TypeVisitor(api, fieldsToBold: fieldsToBold); |
| 228 addAll(typeVisitor.collectHtml(() { |
| 229 typeVisitor.visitTypeDecl(type); |
| 230 })); |
| 279 }); | 231 }); |
| 280 } | 232 } |
| 281 | 233 |
| 282 @override | |
| 283 void visitRefactorings(Refactorings refactorings) { | |
| 284 translateHtml(refactorings.html); | |
| 285 dl(() { | |
| 286 super.visitRefactorings(refactorings); | |
| 287 }); | |
| 288 } | |
| 289 | |
| 290 @override visitRefactoring(Refactoring refactoring) { | |
| 291 dt('refactoring', () { | |
| 292 write(refactoring.kind); | |
| 293 }); | |
| 294 dd(() { | |
| 295 translateHtml(refactoring.html); | |
| 296 describePayload(refactoring.feedback, 'Feedback', force: true); | |
| 297 describePayload(refactoring.options, 'Options', force: true); | |
| 298 }); | |
| 299 } | |
| 300 | |
| 301 @override | |
| 302 void visitTypes(Types types) { | |
| 303 translateHtml(types.html); | |
| 304 dl(() { | |
| 305 super.visitTypes(types); | |
| 306 }); | |
| 307 } | |
| 308 | |
| 309 @override | |
| 310 void visitDomain(Domain domain) { | |
| 311 h2('domain', () { | |
| 312 anchor('domain_${domain.name}', () { | |
| 313 write('Domain: ${domain.name}'); | |
| 314 }); | |
| 315 }); | |
| 316 translateHtml(domain.html); | |
| 317 if (domain.requests.isNotEmpty) { | |
| 318 h3(() { | |
| 319 write('Requests'); | |
| 320 }); | |
| 321 dl(() { | |
| 322 domain.requests.forEach(visitRequest); | |
| 323 }); | |
| 324 } | |
| 325 if (domain.notifications.isNotEmpty) { | |
| 326 h3(() { | |
| 327 write('Notifications'); | |
| 328 }); | |
| 329 dl(() { | |
| 330 domain.notifications.forEach(visitNotification); | |
| 331 }); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 @override | |
| 336 void visitNotification(Notification notification) { | |
| 337 dt('notification', () { | |
| 338 write(notification.longEvent); | |
| 339 }); | |
| 340 dd(() { | |
| 341 box(() { | |
| 342 showType('notification', notification.notificationType, notification.par
ams); | |
| 343 }); | |
| 344 translateHtml(notification.html); | |
| 345 describePayload(notification.params, 'Parameters'); | |
| 346 }); | |
| 347 } | |
| 348 | |
| 349 /** | 234 /** |
| 350 * Copy the contents of the given HTML element, translating the special | 235 * Copy the contents of the given HTML element, translating the special |
| 351 * elements that define the API appropriately. | 236 * elements that define the API appropriately. |
| 352 */ | 237 */ |
| 353 void translateHtml(dom.Element html, {bool squashParagraphs: false}) { | 238 void translateHtml(dom.Element html, {bool squashParagraphs: false}) { |
| 354 for (dom.Node node in html.nodes) { | 239 for (dom.Node node in html.nodes) { |
| 355 if (node is dom.Element) { | 240 if (node is dom.Element) { |
| 356 if (squashParagraphs && node.localName == 'p') { | 241 if (squashParagraphs && node.localName == 'p') { |
| 357 translateHtml(node, squashParagraphs: squashParagraphs); | 242 translateHtml(node, squashParagraphs: squashParagraphs); |
| 358 continue; | 243 continue; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 388 }); | 273 }); |
| 389 } | 274 } |
| 390 } | 275 } |
| 391 } else if (node is dom.Text) { | 276 } else if (node is dom.Text) { |
| 392 String text = node.text; | 277 String text = node.text; |
| 393 write(text); | 278 write(text); |
| 394 } | 279 } |
| 395 } | 280 } |
| 396 } | 281 } |
| 397 | 282 |
| 398 /** | |
| 399 * Generate a description of [type] using [TypeVisitor]. | |
| 400 * | |
| 401 * If [shortDesc] is non-null, the output is prefixed with this string | |
| 402 * and a colon. | |
| 403 * | |
| 404 * If [typeForBolding] is supplied, then fields in this type are shown in | |
| 405 * boldface. | |
| 406 */ | |
| 407 void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) { | |
| 408 Set<String> fieldsToBold = new Set<String>(); | |
| 409 if (typeForBolding != null) { | |
| 410 for (TypeObjectField field in typeForBolding.fields) { | |
| 411 fieldsToBold.add(field.name); | |
| 412 } | |
| 413 } | |
| 414 pre(() { | |
| 415 if (shortDesc != null) { | |
| 416 write('$shortDesc: '); | |
| 417 } | |
| 418 TypeVisitor typeVisitor = new TypeVisitor(api, fieldsToBold: fieldsToBold)
; | |
| 419 addAll(typeVisitor.collectHtml(() { | |
| 420 typeVisitor.visitTypeDecl(type); | |
| 421 })); | |
| 422 }); | |
| 423 } | |
| 424 | |
| 425 /** | |
| 426 * Describe the payload of request, response, notification, refactoring | |
| 427 * feedback, or refactoring options. | |
| 428 * | |
| 429 * If [force] is true, then a section is inserted even if the payload is | |
| 430 * null. | |
| 431 */ | |
| 432 void describePayload(TypeObject subType, String name, {bool force: false}) { | |
| 433 if (force || subType != null) { | |
| 434 h4(() { | |
| 435 write(name); | |
| 436 }); | |
| 437 if (subType == null) { | |
| 438 p(() { | |
| 439 write('none'); | |
| 440 }); | |
| 441 } else { | |
| 442 visitTypeDecl(subType); | |
| 443 } | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 void javadocParams(TypeObject typeObject) { | |
| 448 if (typeObject != null) { | |
| 449 for (TypeObjectField field in typeObject.fields) { | |
| 450 hangingIndent(() { | |
| 451 write('@param ${field.name} '); | |
| 452 translateHtml(field.html, squashParagraphs: true); | |
| 453 }); | |
| 454 } | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 @override | 283 @override |
| 284 void visitApi() { |
| 285 definedTypes = api.types.keys.toSet(); |
| 286 |
| 287 html(() { |
| 288 translateHtml(api.html); |
| 289 }); |
| 290 } |
| 291 |
| 292 @override |
| 293 void visitDomain(Domain domain) { |
| 294 h2('domain', () { |
| 295 anchor('domain_${domain.name}', () { |
| 296 write('Domain: ${domain.name}'); |
| 297 }); |
| 298 }); |
| 299 translateHtml(domain.html); |
| 300 if (domain.requests.isNotEmpty) { |
| 301 h3(() { |
| 302 write('Requests'); |
| 303 }); |
| 304 dl(() { |
| 305 domain.requests.forEach(visitRequest); |
| 306 }); |
| 307 } |
| 308 if (domain.notifications.isNotEmpty) { |
| 309 h3(() { |
| 310 write('Notifications'); |
| 311 }); |
| 312 dl(() { |
| 313 domain.notifications.forEach(visitNotification); |
| 314 }); |
| 315 } |
| 316 } |
| 317 |
| 318 @override |
| 319 void visitNotification(Notification notification) { |
| 320 dt('notification', () { |
| 321 write(notification.longEvent); |
| 322 }); |
| 323 dd(() { |
| 324 box(() { |
| 325 showType( |
| 326 'notification', |
| 327 notification.notificationType, |
| 328 notification.params); |
| 329 }); |
| 330 translateHtml(notification.html); |
| 331 describePayload(notification.params, 'Parameters'); |
| 332 }); |
| 333 } |
| 334 |
| 335 @override visitRefactoring(Refactoring refactoring) { |
| 336 dt('refactoring', () { |
| 337 write(refactoring.kind); |
| 338 }); |
| 339 dd(() { |
| 340 translateHtml(refactoring.html); |
| 341 describePayload(refactoring.feedback, 'Feedback', force: true); |
| 342 describePayload(refactoring.options, 'Options', force: true); |
| 343 }); |
| 344 } |
| 345 |
| 346 @override |
| 347 void visitRefactorings(Refactorings refactorings) { |
| 348 translateHtml(refactorings.html); |
| 349 dl(() { |
| 350 super.visitRefactorings(refactorings); |
| 351 }); |
| 352 } |
| 353 |
| 354 @override |
| 459 void visitRequest(Request request) { | 355 void visitRequest(Request request) { |
| 460 dt('request', () { | 356 dt('request', () { |
| 461 write(request.longMethod); | 357 write(request.longMethod); |
| 462 }); | 358 }); |
| 463 dd(() { | 359 dd(() { |
| 464 box(() { | 360 box(() { |
| 465 showType('request', request.requestType, request.params); | 361 showType('request', request.requestType, request.params); |
| 466 br(); | 362 br(); |
| 467 showType('response', request.responseType, request.result); | 363 showType('response', request.responseType, request.result); |
| 468 }); | 364 }); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 493 void visitTypeEnum(TypeEnum typeEnum) { | 389 void visitTypeEnum(TypeEnum typeEnum) { |
| 494 dl(() { | 390 dl(() { |
| 495 super.visitTypeEnum(typeEnum); | 391 super.visitTypeEnum(typeEnum); |
| 496 }); | 392 }); |
| 497 } | 393 } |
| 498 | 394 |
| 499 @override | 395 @override |
| 500 void visitTypeEnumValue(TypeEnumValue typeEnumValue) { | 396 void visitTypeEnumValue(TypeEnumValue typeEnumValue) { |
| 501 bool isDocumented = false; | 397 bool isDocumented = false; |
| 502 for (dom.Node node in typeEnumValue.html.nodes) { | 398 for (dom.Node node in typeEnumValue.html.nodes) { |
| 503 if ((node is dom.Element && node.localName != 'code') || (node is dom.Text
&& node.text.trim().isNotEmpty)) { | 399 if ((node is dom.Element && node.localName != 'code') || |
| 400 (node is dom.Text && node.text.trim().isNotEmpty)) { |
| 504 isDocumented = true; | 401 isDocumented = true; |
| 505 break; | 402 break; |
| 506 } | 403 } |
| 507 } | 404 } |
| 508 dt('value', () { | 405 dt('value', () { |
| 509 write(typeEnumValue.value); | 406 write(typeEnumValue.value); |
| 510 }); | 407 }); |
| 511 if (isDocumented) { | 408 if (isDocumented) { |
| 512 dd(() { | 409 dd(() { |
| 513 translateHtml(typeEnumValue.html); | 410 translateHtml(typeEnumValue.html); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 }); | 455 }); |
| 559 }); | 456 }); |
| 560 dd(() { | 457 dd(() { |
| 561 translateHtml(typeObjectField.html); | 458 translateHtml(typeObjectField.html); |
| 562 }); | 459 }); |
| 563 } | 460 } |
| 564 | 461 |
| 565 @override | 462 @override |
| 566 void visitTypeReference(TypeReference typeReference) { | 463 void visitTypeReference(TypeReference typeReference) { |
| 567 } | 464 } |
| 465 |
| 466 @override |
| 467 void visitTypes(Types types) { |
| 468 translateHtml(types.html); |
| 469 dl(() { |
| 470 super.visitTypes(types); |
| 471 }); |
| 472 } |
| 568 } | 473 } |
| 569 | 474 |
| 570 final GeneratedFile target = new GeneratedFile('../../doc/api.html', () { | 475 /** |
| 571 ToHtmlVisitor visitor = new ToHtmlVisitor(readApi()); | 476 * Visitor that generates a compact representation of a type, such as: |
| 572 dom.Document document = new dom.Document(); | 477 * |
| 573 for (dom.Node node in visitor.collectHtml(visitor.visitApi)) { | 478 * { |
| 574 document.append(node); | 479 * "id": String |
| 480 * "error": optional Error |
| 481 * "result": { |
| 482 * "version": String |
| 483 * } |
| 484 * } |
| 485 */ |
| 486 class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin, |
| 487 HtmlCodeGenerator { |
| 488 /** |
| 489 * Set of fields which should be shown in boldface, or null if no field |
| 490 * should be shown in boldface. |
| 491 */ |
| 492 final Set<String> fieldsToBold; |
| 493 |
| 494 /** |
| 495 * True if a short description should be generated. In a short description, |
| 496 * objects are shown as simply "object", and enums are shown as "String". |
| 497 */ |
| 498 final bool short; |
| 499 |
| 500 TypeVisitor(Api api, {this.fieldsToBold, this.short: false}) : super(api); |
| 501 |
| 502 @override |
| 503 void visitTypeEnum(TypeEnum typeEnum) { |
| 504 if (short) { |
| 505 write('String'); |
| 506 return; |
| 507 } |
| 508 writeln('enum {'); |
| 509 indent(() { |
| 510 for (TypeEnumValue value in typeEnum.values) { |
| 511 writeln(value.value); |
| 512 } |
| 513 }); |
| 514 write('}'); |
| 575 } | 515 } |
| 576 return document.outerHtml; | |
| 577 }); | |
| 578 | 516 |
| 579 /** | 517 @override |
| 580 * Translate spec_input.html into api.html. | 518 void visitTypeList(TypeList typeList) { |
| 581 */ | 519 write('List<'); |
| 582 main() { | 520 visitTypeDecl(typeList.itemType); |
| 583 target.generate(); | 521 write('>'); |
| 522 } |
| 523 |
| 524 @override |
| 525 void visitTypeMap(TypeMap typeMap) { |
| 526 write('Map<'); |
| 527 visitTypeDecl(typeMap.keyType); |
| 528 write(', '); |
| 529 visitTypeDecl(typeMap.valueType); |
| 530 write('>'); |
| 531 } |
| 532 |
| 533 @override |
| 534 void visitTypeObject(TypeObject typeObject) { |
| 535 if (short) { |
| 536 write('object'); |
| 537 return; |
| 538 } |
| 539 writeln('{'); |
| 540 indent(() { |
| 541 for (TypeObjectField field in typeObject.fields) { |
| 542 write('"'); |
| 543 if (fieldsToBold != null && fieldsToBold.contains(field.name)) { |
| 544 b(() { |
| 545 write(field.name); |
| 546 }); |
| 547 } else { |
| 548 write(field.name); |
| 549 } |
| 550 write('": '); |
| 551 if (field.value != null) { |
| 552 write(JSON.encode(field.value)); |
| 553 } else { |
| 554 if (field.optional) { |
| 555 gray(() { |
| 556 write('optional'); |
| 557 }); |
| 558 write(' '); |
| 559 } |
| 560 visitTypeDecl(field.type); |
| 561 } |
| 562 writeln(); |
| 563 } |
| 564 }); |
| 565 write('}'); |
| 566 } |
| 567 |
| 568 @override |
| 569 void visitTypeReference(TypeReference typeReference) { |
| 570 String displayName = typeReference.typeName; |
| 571 if (api.types.containsKey(typeReference.typeName)) { |
| 572 link('type_${typeReference.typeName}', () { |
| 573 write(displayName); |
| 574 }); |
| 575 } else { |
| 576 write(displayName); |
| 577 } |
| 578 } |
| 579 |
| 580 @override |
| 581 void visitTypeUnion(TypeUnion typeUnion) { |
| 582 bool verticalBarNeeded = false; |
| 583 for (TypeDecl choice in typeUnion.choices) { |
| 584 if (verticalBarNeeded) { |
| 585 write(' | '); |
| 586 } |
| 587 visitTypeDecl(choice); |
| 588 verticalBarNeeded = true; |
| 589 } |
| 590 } |
| 584 } | 591 } |
| OLD | NEW |