OLD | NEW |
---|---|
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 * Generates the complete set of corelib reference documentation. | 6 * Generates the complete set of corelib reference documentation. |
7 */ | 7 */ |
8 #library('apidoc'); | 8 #library('apidoc'); |
9 | 9 |
10 #import('dart:json'); | |
10 #import('html_diff.dart'); | 11 #import('html_diff.dart'); |
11 #import('../../frog/lang.dart'); | 12 #import('../../frog/lang.dart'); |
12 #import('../../frog/file_system_node.dart'); | 13 #import('../../frog/file_system_node.dart'); |
13 #import('../../frog/file_system.dart'); | 14 #import('../../frog/file_system.dart'); |
14 #import('../dartdoc/dartdoc.dart', prefix: 'doc'); | 15 #import('../dartdoc/dartdoc.dart', prefix: 'doc'); |
15 | 16 |
16 HtmlDiff _diff; | 17 HtmlDiff _diff; |
17 | 18 |
18 void main() { | 19 void main() { |
19 var files = new NodeFileSystem(); | 20 var files = new NodeFileSystem(); |
20 parseOptions('../../frog', [] /* args */, files); | 21 parseOptions('../../frog', [] /* args */, files); |
21 initializeWorld(files); | 22 initializeWorld(files); |
22 final apidoc = new Apidoc(); | |
23 | 23 |
24 print('Parsing MDN data...'); | |
Jacob
2012/01/30 21:36:21
Seems like a low value message to print. did you m
Bob Nystrom
2012/01/31 20:56:29
The stages can take a little while, so I thought i
| |
25 final mdn = JSON.parse(files.readAll('mdn/database.json')); | |
26 | |
27 print('Cross-referencing dart:dom and dart:html...'); | |
24 HtmlDiff.initialize(); | 28 HtmlDiff.initialize(); |
25 | |
26 _diff = new HtmlDiff(); | 29 _diff = new HtmlDiff(); |
27 _diff.run(); | 30 _diff.run(); |
28 world.reset(); | 31 world.reset(); |
29 | 32 |
33 print('Generating docs...'); | |
34 final apidoc = new Apidoc(mdn); | |
30 apidoc.document('html'); | 35 apidoc.document('html'); |
31 } | 36 } |
32 | 37 |
33 class Apidoc extends doc.Dartdoc { | 38 class Apidoc extends doc.Dartdoc { |
34 Apidoc() { | 39 /** Big ball of JSON containing the scraped MDN documentation. */ |
40 final mdn; | |
Jacob
2012/01/30 21:36:21
final Map mdn;
Bob Nystrom
2012/01/31 20:56:29
Why bother?
Jacob
2012/01/31 21:08:19
When your documentation is describing the type wit
Bob Nystrom
2012/01/31 21:26:38
Done.
| |
41 | |
42 /** | |
43 * The URL to the page on MDN that content was pulled from for the current | |
44 * type being documented. Will be `null` if the type doesn't use any MDN | |
45 * content. | |
46 */ | |
47 String mdnUrl; | |
48 | |
49 Apidoc(this.mdn) { | |
35 mainTitle = 'Dart API Reference'; | 50 mainTitle = 'Dart API Reference'; |
36 mainUrl = 'http://dartlang.org'; | 51 mainUrl = 'http://dartlang.org'; |
37 | 52 |
38 final note = 'http://code.google.com/policies.html#restrictions'; | 53 final note = 'http://code.google.com/policies.html#restrictions'; |
39 final cca = 'http://creativecommons.org/licenses/by/3.0/'; | 54 final cca = 'http://creativecommons.org/licenses/by/3.0/'; |
40 final bsd = 'http://code.google.com/google_bsd_license.html'; | 55 final bsd = 'http://code.google.com/google_bsd_license.html'; |
41 final tos = 'http://www.dartlang.org/tos.html'; | 56 final tos = 'http://www.dartlang.org/tos.html'; |
42 final privacy = 'http://www.google.com/intl/en/privacy/privacy-policy.html'; | 57 final privacy = 'http://www.google.com/intl/en/privacy/privacy-policy.html'; |
43 | 58 |
44 footerText = | 59 footerText = |
45 ''' | 60 ''' |
46 <p>Except as otherwise <a href="$note">noted</a>, the content of this | 61 <p>Except as otherwise <a href="$note">noted</a>, the content of this |
47 page is licensed under the <a href="$cca">Creative Commons Attribution | 62 page is licensed under the <a href="$cca">Creative Commons Attribution |
48 3.0 License</a>, and code samples are licensed under the | 63 3.0 License</a>, and code samples are licensed under the |
49 <a href="$bsd">BSD License</a>.</p> | 64 <a href="$bsd">BSD License</a>.</p> |
50 <p><a href="$tos">Terms of Service</a> | | 65 <p><a href="$tos">Terms of Service</a> | |
51 <a href="$privacy">Privacy Policy</a></p> | 66 <a href="$privacy">Privacy Policy</a></p> |
52 '''; | 67 '''; |
53 } | 68 } |
54 | 69 |
55 void writeHeadContents(String title) { | 70 void writeHeadContents(String title) { |
56 super.writeHeadContents(title); | 71 super.writeHeadContents(title); |
57 | 72 |
73 // Include the apidoc-specific CSS. | |
74 // TODO(rnystrom): Use our CSS pre-processor to combine these. | |
75 writeln( | |
76 ''' | |
77 <link rel="stylesheet" type="text/css" | |
78 href="${relativePath('apidoc-styles.css')}" /> | |
79 '''); | |
80 | |
58 // Add the analytics code. | 81 // Add the analytics code. |
59 doc.writeln( | 82 writeln( |
60 ''' | 83 ''' |
61 <script type="text/javascript"> | 84 <script type="text/javascript"> |
62 var _gaq = _gaq || []; | 85 var _gaq = _gaq || []; |
63 _gaq.push(['_setAccount', 'UA-26406144-4']); | 86 _gaq.push(["_setAccount", "UA-26406144-4"]); |
64 _gaq.push(['_setDomainName', 'dartlang.org']); | 87 _gaq.push(["_setDomainName", "dartlang.org"]); |
65 _gaq.push(['_trackPageview']); | 88 _gaq.push(["_trackPageview"]); |
66 | 89 |
67 (function() { | 90 (function() { |
68 var ga = document.createElement('script'); ga.type = 'text/javascrip t'; ga.async = true; | 91 var ga = document.createElement("script"); |
69 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : ' http://www') + '.google-analytics.com/ga.js'; | 92 ga.type = "text/javascript"; ga.async = true; |
70 var s = document.getElementsByTagName('script')[0]; s.parentNode.ins ertBefore(ga, s); | 93 ga.src = ("https:" == document.location.protocol ? |
94 "https://ssl" : "http://www") + ".google-analytics.com/ga.js"; | |
95 var s = document.getElementsByTagName("script")[0]; | |
96 s.parentNode.insertBefore(ga, s); | |
71 })(); | 97 })(); |
72 </script> | 98 </script> |
73 '''); | 99 '''); |
74 } | 100 } |
75 | 101 |
76 getTypeComment(Type type) { | 102 String getTypeComment(Type type) { |
77 return _mergeComments(super.getTypeComment(type), getTypeDoc(type)); | 103 return _mergeDocs( |
78 } | 104 findMdnTypeComment(type), |
79 | 105 super.getTypeComment(type), |
80 getMethodComment(MethodMember method) { | 106 getTypeDoc(type)); |
81 return _mergeComments(super.getMethodComment(method), getMemberDoc(method)); | 107 } |
82 } | 108 |
83 | 109 String getMethodComment(MethodMember method) { |
84 getFieldComment(FieldMember field) { | 110 // TODO(rnystrom): Disabling cross-linking between individual members. |
85 return _mergeComments(super.getFieldComment(field), getMemberDoc(field)); | 111 // Now that we include MDN content for most members, it doesn't add much |
86 } | 112 // value and adds a lot of clutter. Once we're sure we want to do this, |
87 | 113 // we can delete this code entirely. |
nweiz
2012/02/01 00:10:39
I'm not sure about this for the use case of a user
| |
88 String _mergeComments(String comment, String extra) { | 114 return _mergeDocs( |
89 if (comment == null) return extra; | 115 findMdnMemberComment(method), |
90 return '$comment\n\n$extra'; | 116 super.getMethodComment(method), |
117 /* getMemberDoc(method) */ null); | |
118 } | |
119 | |
120 String getFieldComment(FieldMember field) { | |
121 // TODO(rnystrom): Disabling cross-linking between individual members. | |
122 // Now that we include MDN content for most members, it doesn't add much | |
123 // value and adds a lot of clutter. Once we're sure we want to do this, | |
124 // we can delete this code entirely. | |
125 return _mergeDocs( | |
126 findMdnMemberComment(field), | |
127 super.getFieldComment(field), | |
128 /* getMemberDoc(field) */ null); | |
129 } | |
130 | |
131 bool isNonEmpty(String string) => (string != null) && (string.trim() != ''); | |
132 | |
133 String _mergeDocs(String mdnComment, String dartComment, String diffComment) { | |
134 // Prefer hand-written Dart comments over stuff from MDN. | |
135 if (isNonEmpty(dartComment)) { | |
136 // Also include the diff comment if provided. | |
137 if (isNonEmpty(diffComment)) return dartComment + diffComment; | |
138 return dartComment; | |
139 } else if (isNonEmpty(mdnComment)) { | |
140 // Wrap it so we can highlight it and so we handle MDN scraped content | |
141 // that lacks a top-level block tag. | |
142 mdnComment = | |
143 ''' | |
144 <div class="mdn"> | |
145 $mdnComment | |
146 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> | |
147 </div> | |
148 '''; | |
149 | |
150 // Also include the diff comment if provided. | |
151 if (isNonEmpty(diffComment)) return mdnComment + diffComment; | |
152 return mdnComment; | |
153 } else if (isNonEmpty(diffComment)) { | |
154 // All we have is the diff comment. | |
155 return diffComment; | |
156 } else { | |
157 // We got nothing! | |
Jacob
2012/01/30 21:36:21
why not return ""
typically that will simplify ups
Bob Nystrom
2012/01/31 20:56:29
Done.
| |
158 return null; | |
159 } | |
160 } | |
161 | |
162 void docType(Type type) { | |
163 // Track whether we've inserted MDN content into this page. | |
164 mdnUrl = null; | |
165 | |
166 super.docType(type); | |
167 } | |
168 | |
169 void writeTypeFooter() { | |
170 if (mdnUrl != null) { | |
171 final moz = 'http://www.mozilla.org/'; | |
172 final mdn = 'https://developer.mozilla.org'; | |
Jacob
2012/01/30 21:36:21
moz --> MOZILLA_URL, mdn--> MDN_URL etc
Bob Nystrom
2012/01/31 20:56:29
The only purpose for having these variables is to
Jacob
2012/01/31 21:08:19
At least make them all caps as they are constants.
Bob Nystrom
2012/01/31 21:26:38
Done.
| |
173 final cca = 'http://creativecommons.org/licenses/by-sa/2.5/'; | |
174 final contrib = 'https://developer.mozilla.org/Project:en/How_to_Help'; | |
175 writeln( | |
176 ''' | |
177 <p class="mdn-attribution"> | |
178 <a href="$mdn"> | |
179 <img src="${relativePath('mdn-logo-tiny.png')}" class="mdn-logo" /> | |
180 </a> | |
181 This page includes <a href="$mdnUrl">content</a> from the | |
182 <a href="$moz">Mozilla Foundation</a> that is graciously | |
183 <a href="$mdn/Project:Copyrights">licensed</a> under a | |
184 <a href="$cca">Creative Commons: Attribution-Sharealike license</a>. | |
185 Mozilla has no other association with Dart or dartlang.org. We | |
186 encourage you to improve the web by | |
187 <a href="$contrib">contributing</a> to | |
188 <a href="$mdn">The Mozilla Developer Network</a>. | |
189 </p> | |
190 '''); | |
191 } | |
192 } | |
193 | |
194 /** | |
195 * Gets the MDN-scraped docs for [type], or `null` if this type isn't | |
196 * scraped from MDN. | |
197 */ | |
198 findMdnTypeComment(Type type) { | |
199 if (type.library.name == 'html') { | |
200 // If it's an HTML type, try to map it to a base DOM type so we can find | |
201 // the MDN docs. | |
202 final domTypes = _diff.htmlTypesToDom[type]; | |
203 | |
204 // Couldn't find a DOM type. | |
205 if ((domTypes == null) || (domTypes.length != 1)) return null; | |
206 | |
207 // Use the corresponding DOM type when searching MDN. | |
208 // TODO(rnystrom): Shame there isn't a simpler way to get the one item | |
209 // out of a singleton Set. | |
210 type = domTypes.iterator().next(); | |
211 } else if (type.library.name != 'dom') { | |
212 // Not a DOM type. | |
213 return null; | |
214 } | |
215 | |
216 final mdnType = mdn[type.name]; | |
217 if (mdnType == null) return null; | |
218 if (mdnType['skipped'] != null) return null; | |
219 | |
220 // Remember which MDN page we're using so we can attribute it. | |
221 mdnUrl = mdnType['srcUrl']; | |
Jacob
2012/01/30 21:36:21
code smell: fn has an unexpected side effect of se
Bob Nystrom
2012/01/31 20:56:29
Renamed to "include...".
| |
222 return mdnType['summary']; | |
223 } | |
224 | |
225 /** | |
226 * Gets the MDN-scraped docs for [member], or `null` if this type isn't | |
227 * scraped from MDN. | |
228 */ | |
229 findMdnMemberComment(Member member) { | |
230 if (member.library.name == 'html') { | |
231 // If it's an HTML type, try to map it to a base DOM type so we can find | |
232 // the MDN docs. | |
233 final domMembers = _diff.htmlToDom[member]; | |
234 | |
235 // Couldn't find a DOM type. | |
236 if ((domMembers == null) || (domMembers.length != 1)) return null; | |
237 | |
238 // Use the corresponding DOM member when searching MDN. | |
239 // TODO(rnystrom): Shame there isn't a simpler way to get the one item | |
240 // out of a singleton Set. | |
241 member = domMembers.iterator().next(); | |
242 } else if (member.library.name != 'dom') { | |
243 // Not a DOM type. | |
244 return null; | |
245 } | |
246 | |
247 // Ignore top-level functions. | |
248 if (member.declaringType.isTop) return null; | |
249 | |
250 final mdnType = mdn[member.declaringType.name]; | |
251 if (mdnType == null) return null; | |
252 | |
253 var mdnMember = null; | |
254 for (final thisMember in mdnType['members']) { | |
255 if (thisMember['name'] == member.name) { | |
256 mdnMember = thisMember; | |
257 break; | |
258 } | |
259 } | |
nweiz
2012/02/01 00:10:39
It breaks my heart that it takes seven lines in Da
| |
260 | |
261 if (mdnMember == null) return null; | |
262 | |
263 // Remember which MDN page we're using so we can attribute it. | |
264 mdnUrl = mdnType['srcUrl']; | |
265 return mdnMember['help']; | |
266 } | |
267 | |
268 /** | |
269 * Returns a link to [member], relative to a type page that may be in a | |
270 * different library than [member]. | |
271 */ | |
272 String _linkMember(Member member) { | |
273 final typeName = member.declaringType.name; | |
274 var memberName = '$typeName.${member.name}'; | |
275 if (member.isConstructor || member.isFactory) { | |
276 final separator = member.constructorName == '' ? '' : '.'; | |
277 memberName = 'new $typeName$separator${member.constructorName}'; | |
278 } else if (member.name.startsWith('get:')) { | |
Jacob
2012/01/30 21:36:21
instead of hard coding 4, define 'get:' as a cons
Bob Nystrom
2012/01/31 20:56:29
Done.
| |
279 memberName = '$typeName.${member.name.substring(4)}'; | |
280 } | |
281 | |
282 return a(memberUrl(member), memberName); | |
283 } | |
284 | |
285 /** | |
286 * Unify getters and setters of the same property. We only want to print | |
287 * explicit setters if no getter exists. | |
288 * | |
289 * If [members] contains no setters, returns it unmodified. | |
290 */ | |
291 Set<Member> _unifyProperties(Set<Member> members) { | |
292 // Only print setters if the getter doesn't exist. | |
293 return members.filter((m) { | |
294 if (!m.name.startsWith('set:')) return true; | |
295 var getName = m.name.replaceFirst('set:', 'get:'); | |
296 return !members.some((maybeGet) => maybeGet.name == getName); | |
297 }); | |
298 } | |
299 | |
300 /** | |
301 * Returns additional documentation for [member], linking it to the | |
302 * corresponding `dart:html` or `dart:dom` [Member](s). If [member] is not in | |
303 * `dart:html` or `dart:dom`, returns no additional documentation. | |
304 */ | |
305 String getMemberDoc(Member member) { | |
306 if (_diff.domToHtml.containsKey(member)) { | |
307 final htmlMemberSet = _unifyProperties(_diff.domToHtml[member]); | |
308 final allSameName = htmlMemberSet.every((m) => _diff.sameName(member, m)); | |
309 final phrase = allSameName ? 'available as' : 'renamed to'; | |
310 final htmlMembers = doc.joinWithCommas(map(htmlMemberSet, _linkMember)); | |
311 return | |
312 '''<p class="correspond">This is $phrase $htmlMembers in the | |
313 ${a("html.html", "dart:html")} library.</p> | |
314 '''; | |
315 } else if (_diff.htmlToDom.containsKey(member)) { | |
316 final domMemberSet = _unifyProperties(_diff.htmlToDom[member]); | |
317 final allSameName = domMemberSet.every((m) => _diff.sameName(m, member)); | |
318 final phrase = allSameName ? 'is the same as' : 'renames'; | |
319 final domMembers = doc.joinWithCommas(map(domMemberSet, _linkMember)); | |
320 return | |
321 ''' | |
322 <p class="correspond">This $phrase $domMembers in the | |
323 ${a("dom.html", "dart:dom")} library.</p> | |
324 '''; | |
325 } else { | |
326 return ''; | |
327 } | |
328 } | |
329 | |
330 /** | |
331 * Returns additional Markdown-formatted documentation for [type], linking it | |
332 * to the corresponding `dart:html` or `dart:dom` [Type](s). If [type] is not | |
333 * in `dart:html` or `dart:dom`, returns no additional documentation. | |
334 */ | |
335 String getTypeDoc(Type type) { | |
336 final htmlTypes = _diff.domTypesToHtml[type]; | |
337 if ((htmlTypes != null) && (htmlTypes.length > 0)) { | |
338 var htmlTypesText = doc.joinWithCommas(map(htmlTypes, typeReference)); | |
339 return | |
340 ''' | |
341 <p class="correspond">This corresponds to $htmlTypesText in the | |
342 ${a("html.html", "dart:html")} library.</p> | |
343 '''; | |
344 } | |
345 | |
346 final domTypes = _diff.htmlTypesToDom[type]; | |
347 if ((domTypes != null) && (domTypes.length > 0)) { | |
348 var domTypesText = doc.joinWithCommas(map(domTypes, typeReference)); | |
349 return | |
350 ''' | |
351 <p class="correspond">This corresponds to $domTypesText in the | |
352 ${a("dom.html", "dart:dom")} library.</p> | |
353 '''; | |
354 } | |
355 | |
356 return ''; | |
91 } | 357 } |
92 } | 358 } |
93 | |
94 /** | |
95 * Returns a Markdown-formatted link to [member], relative to a type page that | |
96 * may be in a different library than [member]. | |
97 */ | |
98 String _linkMember(Member member) { | |
99 final typeName = member.declaringType.name; | |
100 var memberName = "$typeName.${member.name}"; | |
101 if (member.isConstructor || member.isFactory) { | |
102 final separator = member.constructorName == '' ? '' : '.'; | |
103 memberName = 'new $typeName$separator${member.constructorName}'; | |
104 } else if (member.name.startsWith('get:')) { | |
105 memberName = "$typeName.${member.name.substring(4)}"; | |
106 } | |
107 | |
108 return "[$memberName](../${doc.memberUrl(member)})"; | |
109 } | |
110 | |
111 /** | |
112 * Returns a Markdown-formatted link to [type], relative to a type page that | |
113 * may be in a different library than [type]. | |
114 */ | |
115 String _linkType(Type type) => "[${type.name}](../${doc.typeUrl(type)})"; | |
116 | |
117 /** | |
118 * Unify getters and setters of the same property. We only want to print | |
119 * explicit setters if no getter exists. | |
120 * | |
121 * If [members] contains no setters, returns it unmodified. | |
122 */ | |
123 Set<Member> _unifyProperties(Set<Member> members) { | |
124 // Only print setters if the getter doesn't exist. | |
125 return members.filter((m) { | |
126 if (!m.name.startsWith('set:')) return true; | |
127 var getName = m.name.replaceFirst('set:', 'get:'); | |
128 return !members.some((maybeGet) => maybeGet.name == getName); | |
129 }); | |
130 } | |
131 | |
132 /** | |
133 * Returns additional Markdown-formatted documentation for [member], linking it | |
134 * to the corresponding `dart:html` or `dart:dom` [Member](s). If [member] is | |
135 * not in `dart:html` or `dart:dom`, returns no additional documentation. | |
136 */ | |
137 String getMemberDoc(Member member) { | |
138 if (_diff.domToHtml.containsKey(member)) { | |
139 final htmlMemberSet = _unifyProperties(_diff.domToHtml[member]); | |
140 final allSameName = htmlMemberSet.every((m) => _diff.sameName(member, m)); | |
141 final phrase = allSameName ? "available as" : "renamed to"; | |
142 final htmlMembers = doc.joinWithCommas(map(htmlMemberSet, _linkMember)); | |
143 return "_This is $phrase $htmlMembers in the " + | |
144 "[dart:html](../html.html) library._"; | |
145 } else if (_diff.htmlToDom.containsKey(member)) { | |
146 final domMemberSet = _unifyProperties(_diff.htmlToDom[member]); | |
147 final allSameName = domMemberSet.every((m) => _diff.sameName(m, member)); | |
148 final phrase = allSameName ? "is the same as" : "renames"; | |
149 final domMembers = doc.joinWithCommas(map(domMemberSet, _linkMember)); | |
150 return "_This $phrase $domMembers in the [dart:dom](../dom.html) " + | |
151 "library._"; | |
152 } else { | |
153 return ""; | |
154 } | |
155 } | |
156 | |
157 /** | |
158 * Returns additional Markdown-formatted documentation for [type], linking it to | |
159 * the corresponding `dart:html` or `dart:dom` [Type](s). If [type] is not in | |
160 * `dart:html` or `dart:dom`, returns no additional documentation. | |
161 */ | |
162 String getTypeDoc(Type type) { | |
163 if (_diff.domTypesToHtml.containsKey(type)) { | |
164 var htmlTypes = doc.joinWithCommas( | |
165 map(_diff.domTypesToHtml[type], _linkType)); | |
166 return "_This corresponds to $htmlTypes in the [dart:html](../html.html) " + | |
167 "library._"; | |
168 } else if (_diff.htmlTypesToDom.containsKey(type)) { | |
169 var domTypes = doc.joinWithCommas( | |
170 map(_diff.htmlTypesToDom[type], _linkType)); | |
171 return "_This corresponds to $domTypes in the [dart:dom](../dom.html) " + | |
172 "library._"; | |
173 } else { | |
174 return ""; | |
175 } | |
176 } | |
OLD | NEW |