| Index: utils/apidoc/apidoc.dart
|
| diff --git a/utils/apidoc/apidoc.dart b/utils/apidoc/apidoc.dart
|
| index 85983e0001dbb2c481c44c083727fff3c5c32cd1..1c340fc3a5c8576b847b9cf22905a3d41f4be12c 100644
|
| --- a/utils/apidoc/apidoc.dart
|
| +++ b/utils/apidoc/apidoc.dart
|
| @@ -7,6 +7,7 @@
|
| */
|
| #library('apidoc');
|
|
|
| +#import('dart:json');
|
| #import('html_diff.dart');
|
| #import('../../frog/lang.dart');
|
| #import('../../frog/file_system_node.dart');
|
| @@ -19,19 +20,33 @@ void main() {
|
| var files = new NodeFileSystem();
|
| parseOptions('../../frog', [] /* args */, files);
|
| initializeWorld(files);
|
| - final apidoc = new Apidoc();
|
|
|
| - HtmlDiff.initialize();
|
| + print('Parsing MDN data...');
|
| + final mdn = JSON.parse(files.readAll('mdn/database.json'));
|
|
|
| + print('Cross-referencing dart:dom and dart:html...');
|
| + HtmlDiff.initialize();
|
| _diff = new HtmlDiff();
|
| _diff.run();
|
| world.reset();
|
|
|
| + print('Generating docs...');
|
| + final apidoc = new Apidoc(mdn);
|
| apidoc.document('html');
|
| }
|
|
|
| class Apidoc extends doc.Dartdoc {
|
| - Apidoc() {
|
| + /** Big ball of JSON containing the scraped MDN documentation. */
|
| + final Map mdn;
|
| +
|
| + /**
|
| + * The URL to the page on MDN that content was pulled from for the current
|
| + * type being documented. Will be `null` if the type doesn't use any MDN
|
| + * content.
|
| + */
|
| + String mdnUrl;
|
| +
|
| + Apidoc(this.mdn) {
|
| mainTitle = 'Dart API Reference';
|
| mainUrl = 'http://dartlang.org';
|
|
|
| @@ -55,124 +70,291 @@ class Apidoc extends doc.Dartdoc {
|
| void writeHeadContents(String title) {
|
| super.writeHeadContents(title);
|
|
|
| + // Include the apidoc-specific CSS.
|
| + // TODO(rnystrom): Use our CSS pre-processor to combine these.
|
| + writeln(
|
| + '''
|
| + <link rel="stylesheet" type="text/css"
|
| + href="${relativePath('apidoc-styles.css')}" />
|
| + ''');
|
| +
|
| // Add the analytics code.
|
| - doc.writeln(
|
| + writeln(
|
| '''
|
| <script type="text/javascript">
|
| var _gaq = _gaq || [];
|
| - _gaq.push(['_setAccount', 'UA-26406144-4']);
|
| - _gaq.push(['_setDomainName', 'dartlang.org']);
|
| - _gaq.push(['_trackPageview']);
|
| + _gaq.push(["_setAccount", "UA-26406144-4"]);
|
| + _gaq.push(["_setDomainName", "dartlang.org"]);
|
| + _gaq.push(["_trackPageview"]);
|
|
|
| (function() {
|
| - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
| - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
| - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
| + var ga = document.createElement("script");
|
| + ga.type = "text/javascript"; ga.async = true;
|
| + ga.src = ("https:" == document.location.protocol ?
|
| + "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
|
| + var s = document.getElementsByTagName("script")[0];
|
| + s.parentNode.insertBefore(ga, s);
|
| })();
|
| </script>
|
| ''');
|
| }
|
|
|
| - getTypeComment(Type type) {
|
| - return _mergeComments(super.getTypeComment(type), getTypeDoc(type));
|
| + String getTypeComment(Type type) {
|
| + return _mergeDocs(
|
| + includeMdnTypeComment(type),
|
| + super.getTypeComment(type),
|
| + getTypeDoc(type));
|
| }
|
|
|
| - getMethodComment(MethodMember method) {
|
| - return _mergeComments(super.getMethodComment(method), getMemberDoc(method));
|
| + String getMethodComment(MethodMember method) {
|
| + // TODO(rnystrom): Disabling cross-linking between individual members.
|
| + // Now that we include MDN content for most members, it doesn't add much
|
| + // value and adds a lot of clutter. Once we're sure we want to do this,
|
| + // we can delete this code entirely.
|
| + return _mergeDocs(
|
| + includeMdnMemberComment(method),
|
| + super.getMethodComment(method),
|
| + /* getMemberDoc(method) */ null);
|
| }
|
|
|
| - getFieldComment(FieldMember field) {
|
| - return _mergeComments(super.getFieldComment(field), getMemberDoc(field));
|
| + String getFieldComment(FieldMember field) {
|
| + // TODO(rnystrom): Disabling cross-linking between individual members.
|
| + // Now that we include MDN content for most members, it doesn't add much
|
| + // value and adds a lot of clutter. Once we're sure we want to do this,
|
| + // we can delete this code entirely.
|
| + return _mergeDocs(
|
| + includeMdnMemberComment(field),
|
| + super.getFieldComment(field),
|
| + /* getMemberDoc(field) */ null);
|
| }
|
|
|
| - String _mergeComments(String comment, String extra) {
|
| - if (comment == null) return extra;
|
| - return '$comment\n\n$extra';
|
| + bool isNonEmpty(String string) => (string != null) && (string.trim() != '');
|
| +
|
| + String _mergeDocs(String mdnComment, String dartComment, String diffComment) {
|
| + // Prefer hand-written Dart comments over stuff from MDN.
|
| + if (isNonEmpty(dartComment)) {
|
| + // Also include the diff comment if provided.
|
| + if (isNonEmpty(diffComment)) return dartComment + diffComment;
|
| + return dartComment;
|
| + } else if (isNonEmpty(mdnComment)) {
|
| + // Wrap it so we can highlight it and so we handle MDN scraped content
|
| + // that lacks a top-level block tag.
|
| + mdnComment =
|
| + '''
|
| + <div class="mdn">
|
| + $mdnComment
|
| + <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div>
|
| + </div>
|
| + ''';
|
| +
|
| + // Also include the diff comment if provided.
|
| + if (isNonEmpty(diffComment)) return mdnComment + diffComment;
|
| + return mdnComment;
|
| + } else if (isNonEmpty(diffComment)) {
|
| + // All we have is the diff comment.
|
| + return diffComment;
|
| + } else {
|
| + // We got nothing!
|
| + return '';
|
| + }
|
| }
|
| -}
|
|
|
| -/**
|
| - * Returns a Markdown-formatted link to [member], relative to a type page that
|
| - * may be in a different library than [member].
|
| - */
|
| -String _linkMember(Member member) {
|
| - final typeName = member.declaringType.name;
|
| - var memberName = "$typeName.${member.name}";
|
| - if (member.isConstructor || member.isFactory) {
|
| - final separator = member.constructorName == '' ? '' : '.';
|
| - memberName = 'new $typeName$separator${member.constructorName}';
|
| - } else if (member.name.startsWith('get:')) {
|
| - memberName = "$typeName.${member.name.substring(4)}";
|
| + void docType(Type type) {
|
| + // Track whether we've inserted MDN content into this page.
|
| + mdnUrl = null;
|
| +
|
| + super.docType(type);
|
| }
|
|
|
| - return "[$memberName](../${doc.memberUrl(member)})";
|
| -}
|
| + void writeTypeFooter() {
|
| + if (mdnUrl != null) {
|
| + final MOZ = 'http://www.mozilla.org/';
|
| + final MDN = 'https://developer.mozilla.org';
|
| + final CCA = 'http://creativecommons.org/licenses/by-sa/2.5/';
|
| + final CONTRIB = 'https://developer.mozilla.org/Project:en/How_to_Help';
|
| + writeln(
|
| + '''
|
| + <p class="mdn-attribution">
|
| + <a href="$MDN">
|
| + <img src="${relativePath('mdn-logo-tiny.png')}" class="mdn-logo" />
|
| + </a>
|
| + This page includes <a href="$mdnUrl">content</a> from the
|
| + <a href="$MOZ">Mozilla Foundation</a> that is graciously
|
| + <a href="$MDN/Project:Copyrights">licensed</a> under a
|
| + <a href="$CCA">Creative Commons: Attribution-Sharealike license</a>.
|
| + Mozilla has no other association with Dart or dartlang.org. We
|
| + encourage you to improve the web by
|
| + <a href="$CONTRIB">contributing</a> to
|
| + <a href="$MDN">The Mozilla Developer Network</a>.
|
| + </p>
|
| + ''');
|
| + }
|
| + }
|
|
|
| -/**
|
| - * Returns a Markdown-formatted link to [type], relative to a type page that
|
| - * may be in a different library than [type].
|
| - */
|
| -String _linkType(Type type) => "[${type.name}](../${doc.typeUrl(type)})";
|
| + /**
|
| + * Gets the MDN-scraped docs for [type], or `null` if this type isn't
|
| + * scraped from MDN.
|
| + */
|
| + includeMdnTypeComment(Type type) {
|
| + if (type.library.name == 'html') {
|
| + // If it's an HTML type, try to map it to a base DOM type so we can find
|
| + // the MDN docs.
|
| + final domTypes = _diff.htmlTypesToDom[type];
|
|
|
| -/**
|
| - * Unify getters and setters of the same property. We only want to print
|
| - * explicit setters if no getter exists.
|
| - *
|
| - * If [members] contains no setters, returns it unmodified.
|
| - */
|
| -Set<Member> _unifyProperties(Set<Member> members) {
|
| - // Only print setters if the getter doesn't exist.
|
| - return members.filter((m) {
|
| - if (!m.name.startsWith('set:')) return true;
|
| - var getName = m.name.replaceFirst('set:', 'get:');
|
| - return !members.some((maybeGet) => maybeGet.name == getName);
|
| - });
|
| -}
|
| + // Couldn't find a DOM type.
|
| + if ((domTypes == null) || (domTypes.length != 1)) return null;
|
|
|
| -/**
|
| - * Returns additional Markdown-formatted documentation for [member], linking it
|
| - * to the corresponding `dart:html` or `dart:dom` [Member](s). If [member] is
|
| - * not in `dart:html` or `dart:dom`, returns no additional documentation.
|
| - */
|
| -String getMemberDoc(Member member) {
|
| - if (_diff.domToHtml.containsKey(member)) {
|
| - final htmlMemberSet = _unifyProperties(_diff.domToHtml[member]);
|
| - final allSameName = htmlMemberSet.every((m) => _diff.sameName(member, m));
|
| - final phrase = allSameName ? "available as" : "renamed to";
|
| - final htmlMembers = doc.joinWithCommas(map(htmlMemberSet, _linkMember));
|
| - return "_This is $phrase $htmlMembers in the " +
|
| - "[dart:html](../html.html) library._";
|
| - } else if (_diff.htmlToDom.containsKey(member)) {
|
| - final domMemberSet = _unifyProperties(_diff.htmlToDom[member]);
|
| - final allSameName = domMemberSet.every((m) => _diff.sameName(m, member));
|
| - final phrase = allSameName ? "is the same as" : "renames";
|
| - final domMembers = doc.joinWithCommas(map(domMemberSet, _linkMember));
|
| - return "_This $phrase $domMembers in the [dart:dom](../dom.html) " +
|
| - "library._";
|
| - } else {
|
| - return "";
|
| + // Use the corresponding DOM type when searching MDN.
|
| + // TODO(rnystrom): Shame there isn't a simpler way to get the one item
|
| + // out of a singleton Set.
|
| + type = domTypes.iterator().next();
|
| + } else if (type.library.name != 'dom') {
|
| + // Not a DOM type.
|
| + return null;
|
| + }
|
| +
|
| + final mdnType = mdn[type.name];
|
| + if (mdnType == null) return null;
|
| + if (mdnType['skipped'] != null) return null;
|
| +
|
| + // Remember which MDN page we're using so we can attribute it.
|
| + mdnUrl = mdnType['srcUrl'];
|
| + return mdnType['summary'];
|
| }
|
| -}
|
|
|
| -/**
|
| - * Returns additional Markdown-formatted documentation for [type], linking it to
|
| - * the corresponding `dart:html` or `dart:dom` [Type](s). If [type] is not in
|
| - * `dart:html` or `dart:dom`, returns no additional documentation.
|
| - */
|
| -String getTypeDoc(Type type) {
|
| - var types = _diff.domTypesToHtml[type];
|
| - if (types != null && types.length > 0) {
|
| - final text = doc.joinWithCommas(map(types, _linkType));
|
| - return '_This corresponds to $text in the [dart:html](../html.html) ' +
|
| - 'library._';
|
| + /**
|
| + * Gets the MDN-scraped docs for [member], or `null` if this type isn't
|
| + * scraped from MDN.
|
| + */
|
| + includeMdnMemberComment(Member member) {
|
| + if (member.library.name == 'html') {
|
| + // If it's an HTML type, try to map it to a base DOM type so we can find
|
| + // the MDN docs.
|
| + final domMembers = _diff.htmlToDom[member];
|
| +
|
| + // Couldn't find a DOM type.
|
| + if ((domMembers == null) || (domMembers.length != 1)) return null;
|
| +
|
| + // Use the corresponding DOM member when searching MDN.
|
| + // TODO(rnystrom): Shame there isn't a simpler way to get the one item
|
| + // out of a singleton Set.
|
| + member = domMembers.iterator().next();
|
| + } else if (member.library.name != 'dom') {
|
| + // Not a DOM type.
|
| + return null;
|
| + }
|
| +
|
| + // Ignore top-level functions.
|
| + if (member.declaringType.isTop) return null;
|
| +
|
| + final mdnType = mdn[member.declaringType.name];
|
| + if (mdnType == null) return null;
|
| +
|
| + var mdnMember = null;
|
| + for (final thisMember in mdnType['members']) {
|
| + if (thisMember['name'] == member.name) {
|
| + mdnMember = thisMember;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (mdnMember == null) return null;
|
| +
|
| + // Remember which MDN page we're using so we can attribute it.
|
| + mdnUrl = mdnType['srcUrl'];
|
| + return mdnMember['help'];
|
| + }
|
| +
|
| + /**
|
| + * Returns a link to [member], relative to a type page that may be in a
|
| + * different library than [member].
|
| + */
|
| + String _linkMember(Member member) {
|
| + final GET_PREFIX = 'get:';
|
| +
|
| + final typeName = member.declaringType.name;
|
| + var memberName = '$typeName.${member.name}';
|
| + if (member.isConstructor || member.isFactory) {
|
| + final separator = member.constructorName == '' ? '' : '.';
|
| + memberName = 'new $typeName$separator${member.constructorName}';
|
| + } else if (member.name.startsWith(GET_PREFIX)) {
|
| + memberName = '$typeName.${member.name.substring(GET_PREFIX.length)}';
|
| + }
|
| +
|
| + return a(memberUrl(member), memberName);
|
| }
|
|
|
| - types = _diff.htmlTypesToDom[type];
|
| - if (types != null && types.length > 0) {
|
| - final text = doc.joinWithCommas(map(types, _linkType));
|
| - return '_This corresponds to $text in the [dart:dom](../dom.html) ' +
|
| - 'library._';
|
| + /**
|
| + * Unify getters and setters of the same property. We only want to print
|
| + * explicit setters if no getter exists.
|
| + *
|
| + * If [members] contains no setters, returns it unmodified.
|
| + */
|
| + Set<Member> _unifyProperties(Set<Member> members) {
|
| + // Only print setters if the getter doesn't exist.
|
| + return members.filter((m) {
|
| + if (!m.name.startsWith('set:')) return true;
|
| + var getName = m.name.replaceFirst('set:', 'get:');
|
| + return !members.some((maybeGet) => maybeGet.name == getName);
|
| + });
|
| }
|
|
|
| - return '';
|
| + /**
|
| + * Returns additional documentation for [member], linking it to the
|
| + * corresponding `dart:html` or `dart:dom` [Member](s). If [member] is not in
|
| + * `dart:html` or `dart:dom`, returns no additional documentation.
|
| + */
|
| + String getMemberDoc(Member member) {
|
| + if (_diff.domToHtml.containsKey(member)) {
|
| + final htmlMemberSet = _unifyProperties(_diff.domToHtml[member]);
|
| + final allSameName = htmlMemberSet.every((m) => _diff.sameName(member, m));
|
| + final phrase = allSameName ? 'available as' : 'renamed to';
|
| + final htmlMembers = doc.joinWithCommas(map(htmlMemberSet, _linkMember));
|
| + return
|
| + '''<p class="correspond">This is $phrase $htmlMembers in the
|
| + ${a("html.html", "dart:html")} library.</p>
|
| + ''';
|
| + } else if (_diff.htmlToDom.containsKey(member)) {
|
| + final domMemberSet = _unifyProperties(_diff.htmlToDom[member]);
|
| + final allSameName = domMemberSet.every((m) => _diff.sameName(m, member));
|
| + final phrase = allSameName ? 'is the same as' : 'renames';
|
| + final domMembers = doc.joinWithCommas(map(domMemberSet, _linkMember));
|
| + return
|
| + '''
|
| + <p class="correspond">This $phrase $domMembers in the
|
| + ${a("dom.html", "dart:dom")} library.</p>
|
| + ''';
|
| + } else {
|
| + return '';
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Returns additional Markdown-formatted documentation for [type], linking it
|
| + * to the corresponding `dart:html` or `dart:dom` [Type](s). If [type] is not
|
| + * in `dart:html` or `dart:dom`, returns no additional documentation.
|
| + */
|
| + String getTypeDoc(Type type) {
|
| + final htmlTypes = _diff.domTypesToHtml[type];
|
| + if ((htmlTypes != null) && (htmlTypes.length > 0)) {
|
| + var htmlTypesText = doc.joinWithCommas(map(htmlTypes, typeReference));
|
| + return
|
| + '''
|
| + <p class="correspond">This corresponds to $htmlTypesText in the
|
| + ${a("html.html", "dart:html")} library.</p>
|
| + ''';
|
| + }
|
| +
|
| + final domTypes = _diff.htmlTypesToDom[type];
|
| + if ((domTypes != null) && (domTypes.length > 0)) {
|
| + var domTypesText = doc.joinWithCommas(map(domTypes, typeReference));
|
| + return
|
| + '''
|
| + <p class="correspond">This corresponds to $domTypesText in the
|
| + ${a("dom.html", "dart:dom")} library.</p>
|
| + ''';
|
| + }
|
| +
|
| + return '';
|
| + }
|
| }
|
|
|