| Index: pkg/analysis_server/tool/spec/to_html.dart
|
| diff --git a/pkg/analysis_server/tool/spec/to_html.dart b/pkg/analysis_server/tool/spec/to_html.dart
|
| index 8e8a8920e6186f8bbc802ba49dfa107d3a11e900..ed3827c3b5a3138666fbc9e0c2c11a35f5f6232a 100644
|
| --- a/pkg/analysis_server/tool/spec/to_html.dart
|
| +++ b/pkg/analysis_server/tool/spec/to_html.dart
|
| @@ -63,35 +63,61 @@ dt.typeDefinition {
|
| }
|
| '''.trim();
|
|
|
| +final GeneratedFile target = new GeneratedFile('../../doc/api.html', () {
|
| + ToHtmlVisitor visitor = new ToHtmlVisitor(readApi());
|
| + dom.Document document = new dom.Document();
|
| + for (dom.Node node in visitor.collectHtml(visitor.visitApi)) {
|
| + document.append(node);
|
| + }
|
| + return document.outerHtml;
|
| +});
|
| +
|
| +/**
|
| + * Translate spec_input.html into api.html.
|
| + */
|
| +main() {
|
| + target.generate();
|
| +}
|
| +
|
| +/**
|
| + * Visitor that records the mapping from HTML elements to various kinds of API
|
| + * nodes.
|
| + */
|
| +class ApiMappings extends HierarchicalApiVisitor {
|
| + Map<dom.Element, Domain> domains = <dom.Element, Domain>{};
|
| +
|
| + ApiMappings(Api api) : super(api);
|
| +
|
| + @override
|
| + void visitDomain(Domain domain) {
|
| + domains[domain.html] = domain;
|
| + }
|
| +}
|
| +
|
| /**
|
| * Helper methods for creating HTML elements.
|
| */
|
| abstract class HtmlMixin {
|
| - void element(String name, Map<dynamic, String> attributes, [void callback()]);
|
| -
|
| void anchor(String id, void callback()) {
|
| element('a', {
|
| 'name': id
|
| }, callback);
|
| }
|
| - void link(String id, void callback()) {
|
| - element('a', {
|
| - 'href': '#$id'
|
| - }, callback);
|
| - }
|
| +
|
| void b(void callback()) => element('b', {}, callback);
|
| + void body(void callback()) => element('body', {}, callback);
|
| void box(void callback()) {
|
| element('div', {
|
| 'class': 'box'
|
| }, callback);
|
| }
|
| void br() => element('br', {});
|
| - void body(void callback()) => element('body', {}, callback);
|
| void dd(void callback()) => element('dd', {}, callback);
|
| void dl(void callback()) => element('dl', {}, callback);
|
| void dt(String cls, void callback()) => element('dt', {
|
| 'class': cls
|
| }, callback);
|
| + void element(String name, Map<dynamic, String> attributes, [void callback()]);
|
| void gray(void callback()) => element('span', {
|
| 'style': 'color:#999999'
|
| }, callback);
|
| @@ -112,6 +138,11 @@ abstract class HtmlMixin {
|
| void head(void callback()) => element('head', {}, callback);
|
| void html(void callback()) => element('html', {}, callback);
|
| void i(void callback()) => element('i', {}, callback);
|
| + void link(String id, void callback()) {
|
| + element('a', {
|
| + 'href': '#$id'
|
| + }, callback);
|
| + }
|
| void p(void callback()) => element('p', {}, callback);
|
| void pre(void callback()) => element('pre', {}, callback);
|
| void title(void callback()) => element('title', {}, callback);
|
| @@ -119,141 +150,10 @@ abstract class HtmlMixin {
|
| }
|
|
|
| /**
|
| - * Visitor that generates a compact representation of a type, such as:
|
| - *
|
| - * {
|
| - * "id": String
|
| - * "error": optional Error
|
| - * "result": {
|
| - * "version": String
|
| - * }
|
| - * }
|
| - */
|
| -class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlCodeGenerator {
|
| - /**
|
| - * Set of fields which should be shown in boldface, or null if no field
|
| - * should be shown in boldface.
|
| - */
|
| - final Set<String> fieldsToBold;
|
| -
|
| - /**
|
| - * True if a short description should be generated. In a short description,
|
| - * objects are shown as simply "object", and enums are shown as "String".
|
| - */
|
| - final bool short;
|
| -
|
| - TypeVisitor(Api api, {this.fieldsToBold, this.short: false}) : super(api);
|
| -
|
| - @override
|
| - void visitTypeEnum(TypeEnum typeEnum) {
|
| - if (short) {
|
| - write('String');
|
| - return;
|
| - }
|
| - writeln('enum {');
|
| - indent(() {
|
| - for (TypeEnumValue value in typeEnum.values) {
|
| - writeln(value.value);
|
| - }
|
| - });
|
| - write('}');
|
| - }
|
| -
|
| - @override
|
| - void visitTypeList(TypeList typeList) {
|
| - write('List<');
|
| - visitTypeDecl(typeList.itemType);
|
| - write('>');
|
| - }
|
| -
|
| - @override
|
| - void visitTypeMap(TypeMap typeMap) {
|
| - write('Map<');
|
| - visitTypeDecl(typeMap.keyType);
|
| - write(', ');
|
| - visitTypeDecl(typeMap.valueType);
|
| - write('>');
|
| - }
|
| -
|
| - @override
|
| - void visitTypeObject(TypeObject typeObject) {
|
| - if (short) {
|
| - write('object');
|
| - return;
|
| - }
|
| - writeln('{');
|
| - indent(() {
|
| - for (TypeObjectField field in typeObject.fields) {
|
| - write('"');
|
| - if (fieldsToBold != null && fieldsToBold.contains(field.name)) {
|
| - b(() {
|
| - write(field.name);
|
| - });
|
| - } else {
|
| - write(field.name);
|
| - }
|
| - write('": ');
|
| - if (field.value != null) {
|
| - write(JSON.encode(field.value));
|
| - } else {
|
| - if (field.optional) {
|
| - gray(() {
|
| - write('optional');
|
| - });
|
| - write(' ');
|
| - }
|
| - visitTypeDecl(field.type);
|
| - }
|
| - writeln();
|
| - }
|
| - });
|
| - write('}');
|
| - }
|
| -
|
| - @override
|
| - void visitTypeReference(TypeReference typeReference) {
|
| - String displayName = typeReference.typeName;
|
| - if (api.types.containsKey(typeReference.typeName)) {
|
| - link('type_${typeReference.typeName}', () {
|
| - write(displayName);
|
| - });
|
| - } else {
|
| - write(displayName);
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void visitTypeUnion(TypeUnion typeUnion) {
|
| - bool verticalBarNeeded = false;
|
| - for (TypeDecl choice in typeUnion.choices) {
|
| - if (verticalBarNeeded) {
|
| - write(' | ');
|
| - }
|
| - visitTypeDecl(choice);
|
| - verticalBarNeeded = true;
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Visitor that records the mapping from HTML elements to various kinds of API
|
| - * nodes.
|
| - */
|
| -class ApiMappings extends HierarchicalApiVisitor {
|
| - ApiMappings(Api api) : super(api);
|
| -
|
| - Map<dom.Element, Domain> domains = <dom.Element, Domain>{};
|
| -
|
| - @override
|
| - void visitDomain(Domain domain) {
|
| - domains[domain.html] = domain;
|
| - }
|
| -}
|
| -
|
| -/**
|
| * Visitor that generates HTML documentation of the API.
|
| */
|
| -class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator {
|
| +class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
|
| + {
|
| /**
|
| * Set of types defined in the API.
|
| */
|
| @@ -270,79 +170,64 @@ class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
|
| apiMappings.visitApi();
|
| }
|
|
|
| - @override
|
| - void visitApi() {
|
| - definedTypes = api.types.keys.toSet();
|
| -
|
| - html(() {
|
| - translateHtml(api.html);
|
| - });
|
| - }
|
| -
|
| - @override
|
| - void visitRefactorings(Refactorings refactorings) {
|
| - translateHtml(refactorings.html);
|
| - dl(() {
|
| - super.visitRefactorings(refactorings);
|
| - });
|
| - }
|
| -
|
| - @override visitRefactoring(Refactoring refactoring) {
|
| - dt('refactoring', () {
|
| - write(refactoring.kind);
|
| - });
|
| - dd(() {
|
| - translateHtml(refactoring.html);
|
| - describePayload(refactoring.feedback, 'Feedback', force: true);
|
| - describePayload(refactoring.options, 'Options', force: true);
|
| - });
|
| - }
|
| -
|
| - @override
|
| - void visitTypes(Types types) {
|
| - translateHtml(types.html);
|
| - dl(() {
|
| - super.visitTypes(types);
|
| - });
|
| - }
|
| -
|
| - @override
|
| - void visitDomain(Domain domain) {
|
| - h2('domain', () {
|
| - anchor('domain_${domain.name}', () {
|
| - write('Domain: ${domain.name}');
|
| - });
|
| - });
|
| - translateHtml(domain.html);
|
| - if (domain.requests.isNotEmpty) {
|
| - h3(() {
|
| - write('Requests');
|
| - });
|
| - dl(() {
|
| - domain.requests.forEach(visitRequest);
|
| + /**
|
| + * Describe the payload of request, response, notification, refactoring
|
| + * feedback, or refactoring options.
|
| + *
|
| + * If [force] is true, then a section is inserted even if the payload is
|
| + * null.
|
| + */
|
| + void describePayload(TypeObject subType, String name, {bool force: false}) {
|
| + if (force || subType != null) {
|
| + h4(() {
|
| + write(name);
|
| });
|
| + if (subType == null) {
|
| + p(() {
|
| + write('none');
|
| + });
|
| + } else {
|
| + visitTypeDecl(subType);
|
| + }
|
| }
|
| - if (domain.notifications.isNotEmpty) {
|
| - h3(() {
|
| - write('Notifications');
|
| - });
|
| - dl(() {
|
| - domain.notifications.forEach(visitNotification);
|
| - });
|
| + }
|
| +
|
| + void javadocParams(TypeObject typeObject) {
|
| + if (typeObject != null) {
|
| + for (TypeObjectField field in typeObject.fields) {
|
| + hangingIndent(() {
|
| + write('@param ${field.name} ');
|
| + translateHtml(field.html, squashParagraphs: true);
|
| + });
|
| + }
|
| }
|
| }
|
|
|
| - @override
|
| - void visitNotification(Notification notification) {
|
| - dt('notification', () {
|
| - write(notification.longEvent);
|
| - });
|
| - dd(() {
|
| - box(() {
|
| - showType('notification', notification.notificationType, notification.params);
|
| - });
|
| - translateHtml(notification.html);
|
| - describePayload(notification.params, 'Parameters');
|
| + /**
|
| + * Generate a description of [type] using [TypeVisitor].
|
| + *
|
| + * If [shortDesc] is non-null, the output is prefixed with this string
|
| + * and a colon.
|
| + *
|
| + * If [typeForBolding] is supplied, then fields in this type are shown in
|
| + * boldface.
|
| + */
|
| + void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) {
|
| + Set<String> fieldsToBold = new Set<String>();
|
| + if (typeForBolding != null) {
|
| + for (TypeObjectField field in typeForBolding.fields) {
|
| + fieldsToBold.add(field.name);
|
| + }
|
| + }
|
| + pre(() {
|
| + if (shortDesc != null) {
|
| + write('$shortDesc: ');
|
| + }
|
| + TypeVisitor typeVisitor =
|
| + new TypeVisitor(api, fieldsToBold: fieldsToBold);
|
| + addAll(typeVisitor.collectHtml(() {
|
| + typeVisitor.visitTypeDecl(type);
|
| + }));
|
| });
|
| }
|
|
|
| @@ -395,64 +280,75 @@ class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
|
| }
|
| }
|
|
|
| - /**
|
| - * Generate a description of [type] using [TypeVisitor].
|
| - *
|
| - * If [shortDesc] is non-null, the output is prefixed with this string
|
| - * and a colon.
|
| - *
|
| - * If [typeForBolding] is supplied, then fields in this type are shown in
|
| - * boldface.
|
| - */
|
| - void showType(String shortDesc, TypeDecl type, [TypeObject typeForBolding]) {
|
| - Set<String> fieldsToBold = new Set<String>();
|
| - if (typeForBolding != null) {
|
| - for (TypeObjectField field in typeForBolding.fields) {
|
| - fieldsToBold.add(field.name);
|
| - }
|
| - }
|
| - pre(() {
|
| - if (shortDesc != null) {
|
| - write('$shortDesc: ');
|
| - }
|
| - TypeVisitor typeVisitor = new TypeVisitor(api, fieldsToBold: fieldsToBold);
|
| - addAll(typeVisitor.collectHtml(() {
|
| - typeVisitor.visitTypeDecl(type);
|
| - }));
|
| + @override
|
| + void visitApi() {
|
| + definedTypes = api.types.keys.toSet();
|
| +
|
| + html(() {
|
| + translateHtml(api.html);
|
| });
|
| }
|
|
|
| - /**
|
| - * Describe the payload of request, response, notification, refactoring
|
| - * feedback, or refactoring options.
|
| - *
|
| - * If [force] is true, then a section is inserted even if the payload is
|
| - * null.
|
| - */
|
| - void describePayload(TypeObject subType, String name, {bool force: false}) {
|
| - if (force || subType != null) {
|
| - h4(() {
|
| - write(name);
|
| + @override
|
| + void visitDomain(Domain domain) {
|
| + h2('domain', () {
|
| + anchor('domain_${domain.name}', () {
|
| + write('Domain: ${domain.name}');
|
| + });
|
| + });
|
| + translateHtml(domain.html);
|
| + if (domain.requests.isNotEmpty) {
|
| + h3(() {
|
| + write('Requests');
|
| + });
|
| + dl(() {
|
| + domain.requests.forEach(visitRequest);
|
| + });
|
| + }
|
| + if (domain.notifications.isNotEmpty) {
|
| + h3(() {
|
| + write('Notifications');
|
| + });
|
| + dl(() {
|
| + domain.notifications.forEach(visitNotification);
|
| });
|
| - if (subType == null) {
|
| - p(() {
|
| - write('none');
|
| - });
|
| - } else {
|
| - visitTypeDecl(subType);
|
| - }
|
| }
|
| }
|
|
|
| - void javadocParams(TypeObject typeObject) {
|
| - if (typeObject != null) {
|
| - for (TypeObjectField field in typeObject.fields) {
|
| - hangingIndent(() {
|
| - write('@param ${field.name} ');
|
| - translateHtml(field.html, squashParagraphs: true);
|
| - });
|
| - }
|
| - }
|
| + @override
|
| + void visitNotification(Notification notification) {
|
| + dt('notification', () {
|
| + write(notification.longEvent);
|
| + });
|
| + dd(() {
|
| + box(() {
|
| + showType(
|
| + 'notification',
|
| + notification.notificationType,
|
| + notification.params);
|
| + });
|
| + translateHtml(notification.html);
|
| + describePayload(notification.params, 'Parameters');
|
| + });
|
| + }
|
| +
|
| + @override visitRefactoring(Refactoring refactoring) {
|
| + dt('refactoring', () {
|
| + write(refactoring.kind);
|
| + });
|
| + dd(() {
|
| + translateHtml(refactoring.html);
|
| + describePayload(refactoring.feedback, 'Feedback', force: true);
|
| + describePayload(refactoring.options, 'Options', force: true);
|
| + });
|
| + }
|
| +
|
| + @override
|
| + void visitRefactorings(Refactorings refactorings) {
|
| + translateHtml(refactorings.html);
|
| + dl(() {
|
| + super.visitRefactorings(refactorings);
|
| + });
|
| }
|
|
|
| @override
|
| @@ -500,7 +396,8 @@ class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
|
| void visitTypeEnumValue(TypeEnumValue typeEnumValue) {
|
| bool isDocumented = false;
|
| for (dom.Node node in typeEnumValue.html.nodes) {
|
| - if ((node is dom.Element && node.localName != 'code') || (node is dom.Text && node.text.trim().isNotEmpty)) {
|
| + if ((node is dom.Element && node.localName != 'code') ||
|
| + (node is dom.Text && node.text.trim().isNotEmpty)) {
|
| isDocumented = true;
|
| break;
|
| }
|
| @@ -565,20 +462,130 @@ class ToHtmlVisitor extends HierarchicalApiVisitor with HtmlMixin, HtmlGenerator
|
| @override
|
| void visitTypeReference(TypeReference typeReference) {
|
| }
|
| -}
|
|
|
| -final GeneratedFile target = new GeneratedFile('../../doc/api.html', () {
|
| - ToHtmlVisitor visitor = new ToHtmlVisitor(readApi());
|
| - dom.Document document = new dom.Document();
|
| - for (dom.Node node in visitor.collectHtml(visitor.visitApi)) {
|
| - document.append(node);
|
| + @override
|
| + void visitTypes(Types types) {
|
| + translateHtml(types.html);
|
| + dl(() {
|
| + super.visitTypes(types);
|
| + });
|
| }
|
| - return document.outerHtml;
|
| -});
|
| +}
|
|
|
| /**
|
| - * Translate spec_input.html into api.html.
|
| + * Visitor that generates a compact representation of a type, such as:
|
| + *
|
| + * {
|
| + * "id": String
|
| + * "error": optional Error
|
| + * "result": {
|
| + * "version": String
|
| + * }
|
| + * }
|
| */
|
| -main() {
|
| - target.generate();
|
| +class TypeVisitor extends HierarchicalApiVisitor with HtmlMixin,
|
| + HtmlCodeGenerator {
|
| + /**
|
| + * Set of fields which should be shown in boldface, or null if no field
|
| + * should be shown in boldface.
|
| + */
|
| + final Set<String> fieldsToBold;
|
| +
|
| + /**
|
| + * True if a short description should be generated. In a short description,
|
| + * objects are shown as simply "object", and enums are shown as "String".
|
| + */
|
| + final bool short;
|
| +
|
| + TypeVisitor(Api api, {this.fieldsToBold, this.short: false}) : super(api);
|
| +
|
| + @override
|
| + void visitTypeEnum(TypeEnum typeEnum) {
|
| + if (short) {
|
| + write('String');
|
| + return;
|
| + }
|
| + writeln('enum {');
|
| + indent(() {
|
| + for (TypeEnumValue value in typeEnum.values) {
|
| + writeln(value.value);
|
| + }
|
| + });
|
| + write('}');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeList(TypeList typeList) {
|
| + write('List<');
|
| + visitTypeDecl(typeList.itemType);
|
| + write('>');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeMap(TypeMap typeMap) {
|
| + write('Map<');
|
| + visitTypeDecl(typeMap.keyType);
|
| + write(', ');
|
| + visitTypeDecl(typeMap.valueType);
|
| + write('>');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeObject(TypeObject typeObject) {
|
| + if (short) {
|
| + write('object');
|
| + return;
|
| + }
|
| + writeln('{');
|
| + indent(() {
|
| + for (TypeObjectField field in typeObject.fields) {
|
| + write('"');
|
| + if (fieldsToBold != null && fieldsToBold.contains(field.name)) {
|
| + b(() {
|
| + write(field.name);
|
| + });
|
| + } else {
|
| + write(field.name);
|
| + }
|
| + write('": ');
|
| + if (field.value != null) {
|
| + write(JSON.encode(field.value));
|
| + } else {
|
| + if (field.optional) {
|
| + gray(() {
|
| + write('optional');
|
| + });
|
| + write(' ');
|
| + }
|
| + visitTypeDecl(field.type);
|
| + }
|
| + writeln();
|
| + }
|
| + });
|
| + write('}');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeReference(TypeReference typeReference) {
|
| + String displayName = typeReference.typeName;
|
| + if (api.types.containsKey(typeReference.typeName)) {
|
| + link('type_${typeReference.typeName}', () {
|
| + write(displayName);
|
| + });
|
| + } else {
|
| + write(displayName);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void visitTypeUnion(TypeUnion typeUnion) {
|
| + bool verticalBarNeeded = false;
|
| + for (TypeDecl choice in typeUnion.choices) {
|
| + if (verticalBarNeeded) {
|
| + write(' | ');
|
| + }
|
| + visitTypeDecl(choice);
|
| + verticalBarNeeded = true;
|
| + }
|
| + }
|
| }
|
|
|