| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 * This generates the reference documentation for the core libraries that come | 6 * This generates the reference documentation for the core libraries that come |
| 7 * with dart. It is built on top of dartdoc, which is a general-purpose library | 7 * with dart. It is built on top of dartdoc, which is a general-purpose library |
| 8 * for generating docs from any Dart code. This library extends that to include | 8 * for generating docs from any Dart code. This library extends that to include |
| 9 * additional information and styling specific to our standard library. | 9 * additional information and styling specific to our standard library. |
| 10 * | 10 * |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 }); | 147 }); |
| 148 }; | 148 }; |
| 149 } | 149 } |
| 150 | 150 |
| 151 /** | 151 /** |
| 152 * This class is purely here to scrape handwritten HTML documentation. | 152 * This class is purely here to scrape handwritten HTML documentation. |
| 153 * This scraped documentation will later be merged with the generated | 153 * This scraped documentation will later be merged with the generated |
| 154 * HTML library. | 154 * HTML library. |
| 155 */ | 155 */ |
| 156 class Htmldoc extends doc.Dartdoc { | 156 class Htmldoc extends doc.Dartdoc { |
| 157 String libraryComment; | 157 doc.DocComment libraryComment; |
| 158 | 158 |
| 159 /** | 159 /** |
| 160 * Map from qualified type names to comments. | 160 * Map from qualified type names to comments. |
| 161 */ | 161 */ |
| 162 Map<String, String> typeComments; | 162 Map<String, doc.DocComment> typeComments; |
| 163 | 163 |
| 164 /** | 164 /** |
| 165 * Map from qualified member names to comments. | 165 * Map from qualified member names to comments. |
| 166 */ | 166 */ |
| 167 Map<String, String> memberComments; | 167 Map<String, doc.DocComment> memberComments; |
| 168 | 168 |
| 169 Htmldoc() { | 169 Htmldoc() { |
| 170 typeComments = new Map<String, String>(); | 170 typeComments = new Map<String, doc.DocComment>(); |
| 171 memberComments = new Map<String, String>(); | 171 memberComments = new Map<String, doc.DocComment>(); |
| 172 } | 172 } |
| 173 | 173 |
| 174 // Suppress any actual writing to file. This is only for analysis. | 174 // Suppress any actual writing to file. This is only for analysis. |
| 175 void endFile() { | 175 void endFile() { |
| 176 } | 176 } |
| 177 | 177 |
| 178 void write(String s) { | 178 void write(String s) { |
| 179 } | 179 } |
| 180 | 180 |
| 181 String getRecordedLibraryComment(LibraryMirror library) { | 181 doc.DocComment getRecordedLibraryComment(LibraryMirror library) { |
| 182 if (library.simpleName == HTML_LIBRARY_NAME) { | 182 if (library.simpleName == HTML_LIBRARY_NAME) { |
| 183 return libraryComment; | 183 return libraryComment; |
| 184 } | 184 } |
| 185 return null; | 185 return null; |
| 186 } | 186 } |
| 187 | 187 |
| 188 String getRecordedTypeComment(TypeMirror type) { | 188 doc.DocComment getRecordedTypeComment(TypeMirror type) { |
| 189 if (typeComments.containsKey(type.qualifiedName)) { | 189 if (typeComments.containsKey(type.qualifiedName)) { |
| 190 return typeComments[type.qualifiedName]; | 190 return typeComments[type.qualifiedName]; |
| 191 } | 191 } |
| 192 return null; | 192 return null; |
| 193 } | 193 } |
| 194 | 194 |
| 195 String getRecordedMemberComment(MemberMirror member) { | 195 doc.DocComment getRecordedMemberComment(MemberMirror member) { |
| 196 if (memberComments.containsKey(member.qualifiedName)) { | 196 if (memberComments.containsKey(member.qualifiedName)) { |
| 197 return memberComments[member.qualifiedName]; | 197 return memberComments[member.qualifiedName]; |
| 198 } | 198 } |
| 199 return null; | 199 return null; |
| 200 } | 200 } |
| 201 | 201 |
| 202 // These methods are subclassed and used for internal processing. | 202 // These methods are subclassed and used for internal processing. |
| 203 // Do not invoke outside of this class. | 203 // Do not invoke outside of this class. |
| 204 String getLibraryComment(LibraryMirror library) { | 204 doc.DocComment getLibraryComment(LibraryMirror library) { |
| 205 String comment = super.getLibraryComment(library); | 205 doc.DocComment comment = super.getLibraryComment(library); |
| 206 libraryComment = comment; | 206 libraryComment = comment; |
| 207 return comment; | 207 return comment; |
| 208 } | 208 } |
| 209 | 209 |
| 210 String getTypeComment(TypeMirror type) { | 210 doc.DocComment getTypeComment(TypeMirror type) { |
| 211 String comment = super.getTypeComment(type); | 211 doc.DocComment comment = super.getTypeComment(type); |
| 212 recordTypeComment(type, comment); | 212 recordTypeComment(type, comment); |
| 213 return comment; | 213 return comment; |
| 214 } | 214 } |
| 215 | 215 |
| 216 String getMethodComment(MethodMirror method) { | 216 doc.DocComment getMemberComment(MemberMirror member) { |
| 217 String comment = super.getMethodComment(method); | 217 doc.DocComment comment = super.getMemberComment(member); |
| 218 recordMemberComment(method, comment); | 218 recordMemberComment(member, comment); |
| 219 return comment; | 219 return comment; |
| 220 } | 220 } |
| 221 | 221 |
| 222 String getFieldComment(FieldMirror field) { | 222 void recordTypeComment(TypeMirror type, doc.DocComment comment) { |
| 223 String comment = super.getFieldComment(field); | 223 if (comment != null && comment.text.contains('@domName')) { |
| 224 recordMemberComment(field, comment); | |
| 225 return comment; | |
| 226 } | |
| 227 | |
| 228 void recordTypeComment(TypeMirror type, String comment) { | |
| 229 if (comment != null && comment.contains('@domName')) { | |
| 230 // This is not a handwritten comment. | 224 // This is not a handwritten comment. |
| 231 return; | 225 return; |
| 232 } | 226 } |
| 233 typeComments[type.qualifiedName] = comment; | 227 typeComments[type.qualifiedName] = comment; |
| 234 } | 228 } |
| 235 | 229 |
| 236 void recordMemberComment(MemberMirror member, String comment) { | 230 void recordMemberComment(MemberMirror member, doc.DocComment comment) { |
| 237 if (comment != null && comment.contains('@domName')) { | 231 if (comment != null && comment.text.contains('@domName')) { |
| 238 // This is not a handwritten comment. | 232 // This is not a handwritten comment. |
| 239 return; | 233 return; |
| 240 } | 234 } |
| 241 memberComments[member.qualifiedName] = comment; | 235 memberComments[member.qualifiedName] = comment; |
| 242 } | 236 } |
| 243 } | 237 } |
| 244 | 238 |
| 245 class Apidoc extends doc.Dartdoc { | 239 class Apidoc extends doc.Dartdoc { |
| 246 /** Big ball of JSON containing the scraped MDN documentation. */ | 240 /** Big ball of JSON containing the scraped MDN documentation. */ |
| 247 final Map mdn; | 241 final Map mdn; |
| 248 | 242 |
| 249 final Htmldoc htmldoc; | 243 final Htmldoc htmldoc; |
| 250 | 244 |
| 251 static const disqusShortname = 'dartapidocs'; | 245 static const disqusShortname = 'dartapidocs'; |
| 252 | 246 |
| 253 // A set of type names (TypeMirror.simpleName values) to ignore while | 247 // A set of type names (TypeMirror.simpleName values) to ignore while |
| 254 // looking up information from MDN data. TODO(eub, jacobr): fix up the MDN | 248 // looking up information from MDN data. TODO(eub, jacobr): fix up the MDN |
| 255 // import scripts so they run correctly and generate data that doesn't have | 249 // import scripts so they run correctly and generate data that doesn't have |
| 256 // any entries that need to be ignored. | 250 // any entries that need to be ignored. |
| 257 static Set<String> _mdnTypeNamesToSkip = null; | 251 static Set<String> _mdnTypeNamesToSkip = null; |
| 258 | 252 |
| 259 /** | 253 /** |
| 260 * The URL to the page on MDN that content was pulled from for the current | 254 * The URL to the page on MDN that content was pulled from for the current |
| 261 * type being documented. Will be `null` if the type doesn't use any MDN | 255 * type being documented. Will be `null` if the type doesn't use any MDN |
| 262 * content. | 256 * content. |
| 263 */ | 257 */ |
| 264 String mdnUrl; | 258 String mdnUrl = null; |
| 265 | 259 |
| 266 Apidoc(this.mdn, this.htmldoc, Path outputDir, int mode, | 260 Apidoc(this.mdn, this.htmldoc, Path outputDir, int mode, |
| 267 bool generateAppCache) { | 261 bool generateAppCache) { |
| 268 this.outputDir = outputDir; | 262 this.outputDir = outputDir; |
| 269 this.mode = mode; | 263 this.mode = mode; |
| 270 this.generateAppCache = generateAppCache; | 264 this.generateAppCache = generateAppCache; |
| 271 | 265 |
| 272 // Skip bad entries in the checked-in mdn/database.json: | 266 // Skip bad entries in the checked-in mdn/database.json: |
| 273 // * UnknownElement has a top-level Gecko DOM page in German. | 267 // * UnknownElement has a top-level Gecko DOM page in German. |
| 274 if (_mdnTypeNamesToSkip == null) | 268 if (_mdnTypeNamesToSkip == null) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 } | 363 } |
| 370 | 364 |
| 371 void docLibrary(LibraryMirror library) { | 365 void docLibrary(LibraryMirror library) { |
| 372 // TODO(rnystrom): Hackish. The IO libraries reference this but we don't | 366 // TODO(rnystrom): Hackish. The IO libraries reference this but we don't |
| 373 // want it in the docs. | 367 // want it in the docs. |
| 374 if (library.simpleName == 'dart:nativewrappers') return; | 368 if (library.simpleName == 'dart:nativewrappers') return; |
| 375 super.docLibrary(library); | 369 super.docLibrary(library); |
| 376 } | 370 } |
| 377 | 371 |
| 378 /** Override definition from parent class to strip out annotation tags. */ | 372 /** Override definition from parent class to strip out annotation tags. */ |
| 379 String commentToHtml(String comment) { | 373 doc.DocComment createDocComment(String text, |
| 380 return super.commentToHtml( | 374 [InterfaceMirror inheritedFrom]) { |
| 381 comment.replaceAll(const RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"), '')); | 375 String strippedText = |
| 376 text.replaceAll(const RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"), |
| 377 '').trim(); |
| 378 if (strippedText.isEmpty()) return null; |
| 379 return super.createDocComment(strippedText, inheritedFrom); |
| 382 } | 380 } |
| 383 | 381 |
| 384 String getLibraryComment(LibraryMirror library) { | 382 doc.DocComment getLibraryComment(LibraryMirror library) { |
| 385 if (library.simpleName == HTML_LIBRARY_NAME) { | 383 if (library.simpleName == HTML_LIBRARY_NAME) { |
| 386 return htmldoc.libraryComment; | 384 return htmldoc.libraryComment; |
| 387 } | 385 } |
| 388 return super.getLibraryComment(library); | 386 return super.getLibraryComment(library); |
| 389 } | 387 } |
| 390 | 388 |
| 391 String getTypeComment(TypeMirror type) { | 389 doc.DocComment getTypeComment(TypeMirror type) { |
| 392 return _mergeDocs( | 390 return _mergeDocs( |
| 393 includeMdnTypeComment(type), super.getTypeComment(type), | 391 includeMdnTypeComment(type), super.getTypeComment(type), |
| 394 htmldoc.getRecordedTypeComment(type)); | 392 htmldoc.getRecordedTypeComment(type)); |
| 395 } | 393 } |
| 396 | 394 |
| 397 String getMethodComment(MethodMirror method) { | 395 doc.DocComment getMemberComment(MemberMirror member) { |
| 398 return _mergeDocs( | 396 return _mergeDocs( |
| 399 includeMdnMemberComment(method), super.getMethodComment(method), | 397 includeMdnMemberComment(member), super.getMemberComment(member), |
| 400 htmldoc.getRecordedMemberComment(method)); | 398 htmldoc.getRecordedMemberComment(member)); |
| 401 } | 399 } |
| 402 | 400 |
| 403 String getFieldComment(FieldMirror field) { | 401 doc.DocComment _mergeDocs(MdnComment mdnComment, |
| 404 return _mergeDocs( | 402 doc.DocComment fileComment, |
| 405 includeMdnMemberComment(field), super.getFieldComment(field), | 403 doc.DocComment handWrittenComment) { |
| 406 htmldoc.getRecordedMemberComment(field)); | 404 // Prefer the hand-written comment first. |
| 407 } | 405 if (handWrittenComment !== null) return handWrittenComment; |
| 408 | 406 |
| 409 bool isNonEmpty(String string) => (string != null) && (string.trim() != ''); | 407 // Otherwise, prefer comment from the (possibly generated) Dart file. |
| 410 | 408 if (fileComment !== null) return fileComment; |
| 411 String _mergeDocs(String mdnComment, String fileComment, | |
| 412 String handWrittenComment) { | |
| 413 // Prefer the hand-written comment first. | |
| 414 if (isNonEmpty(handWrittenComment)) return handWrittenComment; | |
| 415 | |
| 416 // Otherwise, prefer comment from the (possibly generated) Dart | |
| 417 // file. | |
| 418 if (isNonEmpty(fileComment)) return fileComment; | |
| 419 | 409 |
| 420 // Finally, fallback on MDN if available. | 410 // Finally, fallback on MDN if available. |
| 421 if (isNonEmpty(mdnComment)) { | 411 if (mdnComment !== null) { |
| 422 // Wrap it so we can highlight it and so we handle MDN scraped content | 412 mdnUrl = mdnComment.mdnUrl; |
| 423 // that lacks a top-level block tag. | 413 return mdnComment; |
| 424 return ''' | |
| 425 <div class="mdn"> | |
| 426 $mdnComment | |
| 427 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> | |
| 428 </div> | |
| 429 '''; | |
| 430 } | 414 } |
| 431 | 415 |
| 432 // We got nothing! | 416 // We got nothing! |
| 433 return ''; | 417 return null; |
| 434 } | 418 } |
| 435 | 419 |
| 436 void docType(TypeMirror type) { | 420 void docType(TypeMirror type) { |
| 437 // Track whether we've inserted MDN content into this page. | 421 // Track whether we've inserted MDN content into this page. |
| 438 mdnUrl = null; | 422 mdnUrl = null; |
| 439 | 423 |
| 440 super.docType(type); | 424 super.docType(type); |
| 441 } | 425 } |
| 442 | 426 |
| 443 void writeTypeFooter() { | 427 void writeTypeFooter() { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 462 <a href="$MDN">The Mozilla Developer Network</a>. | 446 <a href="$MDN">The Mozilla Developer Network</a>. |
| 463 </p> | 447 </p> |
| 464 '''); | 448 '''); |
| 465 } | 449 } |
| 466 } | 450 } |
| 467 | 451 |
| 468 /** | 452 /** |
| 469 * Gets the MDN-scraped docs for [type], or `null` if this type isn't | 453 * Gets the MDN-scraped docs for [type], or `null` if this type isn't |
| 470 * scraped from MDN. | 454 * scraped from MDN. |
| 471 */ | 455 */ |
| 472 includeMdnTypeComment(TypeMirror type) { | 456 MdnComment includeMdnTypeComment(TypeMirror type) { |
| 473 if (_mdnTypeNamesToSkip.contains(type.simpleName)) { | 457 if (_mdnTypeNamesToSkip.contains(type.simpleName)) { |
| 474 print('Skipping MDN type ${type.simpleName}'); | 458 print('Skipping MDN type ${type.simpleName}'); |
| 475 return null; | 459 return null; |
| 476 } | 460 } |
| 477 | 461 |
| 478 var typeString = ''; | 462 var typeString = ''; |
| 479 if (type.library.simpleName == HTML_LIBRARY_NAME) { | 463 if (type.library.simpleName == HTML_LIBRARY_NAME) { |
| 480 // If it's an HTML type, try to map it to a base DOM type so we can find | 464 // If it's an HTML type, try to map it to a base DOM type so we can find |
| 481 // the MDN docs. | 465 // the MDN docs. |
| 482 final domTypes = _diff.htmlTypesToDom[type.qualifiedName]; | 466 final domTypes = _diff.htmlTypesToDom[type.qualifiedName]; |
| 483 | 467 |
| 484 // Couldn't find a DOM type. | 468 // Couldn't find a DOM type. |
| 485 if ((domTypes == null) || (domTypes.length != 1)) return null; | 469 if ((domTypes == null) || (domTypes.length != 1)) return null; |
| 486 | 470 |
| 487 // Use the corresponding DOM type when searching MDN. | 471 // Use the corresponding DOM type when searching MDN. |
| 488 // TODO(rnystrom): Shame there isn't a simpler way to get the one item | 472 // TODO(rnystrom): Shame there isn't a simpler way to get the one item |
| 489 // out of a singleton Set. | 473 // out of a singleton Set. |
| 490 typeString = domTypes.iterator().next(); | 474 typeString = domTypes.iterator().next(); |
| 491 } else { | 475 } else { |
| 492 // Not a DOM type. | 476 // Not a DOM type. |
| 493 return null; | 477 return null; |
| 494 } | 478 } |
| 495 | 479 |
| 496 final mdnType = mdn[typeString]; | 480 final mdnType = mdn[typeString]; |
| 497 if (mdnType == null) return null; | 481 if (mdnType == null) return null; |
| 498 if (mdnType['skipped'] != null) return null; | 482 if (mdnType['skipped'] != null) return null; |
| 483 if (mdnType['summary'] == null) return null; |
| 484 if (mdnType['summary'].trim().isEmpty()) return null; |
| 499 | 485 |
| 500 // Remember which MDN page we're using so we can attribute it. | 486 // Remember which MDN page we're using so we can attribute it. |
| 501 mdnUrl = mdnType['srcUrl']; | 487 return new MdnComment(mdnType['summary'], mdnType['srcUrl']); |
| 502 return mdnType['summary']; | |
| 503 } | 488 } |
| 504 | 489 |
| 505 /** | 490 /** |
| 506 * Gets the MDN-scraped docs for [member], or `null` if this type isn't | 491 * Gets the MDN-scraped docs for [member], or `null` if this type isn't |
| 507 * scraped from MDN. | 492 * scraped from MDN. |
| 508 */ | 493 */ |
| 509 includeMdnMemberComment(MemberMirror member) { | 494 MdnComment includeMdnMemberComment(MemberMirror member) { |
| 510 var library = findLibrary(member); | 495 var library = findLibrary(member); |
| 511 var memberString = ''; | 496 var memberString = ''; |
| 512 if (library.simpleName == HTML_LIBRARY_NAME) { | 497 if (library.simpleName == HTML_LIBRARY_NAME) { |
| 513 // If it's an HTML type, try to map it to a DOM type name so we can find | 498 // If it's an HTML type, try to map it to a DOM type name so we can find |
| 514 // the MDN docs. | 499 // the MDN docs. |
| 515 final domMembers = _diff.htmlToDom[member.qualifiedName]; | 500 final domMembers = _diff.htmlToDom[member.qualifiedName]; |
| 516 | 501 |
| 517 // Couldn't find a DOM type. | 502 // Couldn't find a DOM type. |
| 518 if ((domMembers == null) || (domMembers.length != 1)) return null; | 503 if ((domMembers == null) || (domMembers.length != 1)) return null; |
| 519 | 504 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 538 var nameToFind = pieces[1]; | 523 var nameToFind = pieces[1]; |
| 539 for (final candidateMember in mdnType['members']) { | 524 for (final candidateMember in mdnType['members']) { |
| 540 if (candidateMember['name'] == nameToFind) { | 525 if (candidateMember['name'] == nameToFind) { |
| 541 mdnMember = candidateMember; | 526 mdnMember = candidateMember; |
| 542 break; | 527 break; |
| 543 } | 528 } |
| 544 } | 529 } |
| 545 } | 530 } |
| 546 | 531 |
| 547 if (mdnMember == null) return null; | 532 if (mdnMember == null) return null; |
| 533 if (mdnMember['help'] == null) return null; |
| 534 if (mdnMember['help'].trim().isEmpty()) return null; |
| 548 | 535 |
| 549 // Remember which MDN page we're using so we can attribute it. | 536 // Remember which MDN page we're using so we can attribute it. |
| 550 mdnUrl = mdnType['srcUrl']; | 537 return new MdnComment(mdnMember['help'], mdnType['srcUrl']); |
| 551 return mdnMember['help']; | |
| 552 } | 538 } |
| 553 | 539 |
| 554 /** | 540 /** |
| 555 * Returns a link to [member], relative to a type page that may be in a | 541 * Returns a link to [member], relative to a type page that may be in a |
| 556 * different library than [member]. | 542 * different library than [member]. |
| 557 */ | 543 */ |
| 558 String _linkMember(MemberMirror member) { | 544 String _linkMember(MemberMirror member) { |
| 559 final typeName = member.surroundingDeclaration.simpleName; | 545 final typeName = member.surroundingDeclaration.simpleName; |
| 560 var memberName = '$typeName.${member.simpleName}'; | 546 var memberName = '$typeName.${member.simpleName}'; |
| 561 if (member is MethodMirror && (member.isConstructor || member.isFactory)) { | 547 if (member is MethodMirror && (member.isConstructor || member.isFactory)) { |
| 562 final separator = member.constructorName == '' ? '' : '.'; | 548 final separator = member.constructorName == '' ? '' : '.'; |
| 563 memberName = 'new $typeName$separator${member.constructorName}'; | 549 memberName = 'new $typeName$separator${member.constructorName}'; |
| 564 } | 550 } |
| 565 | 551 |
| 566 return a(memberUrl(member), memberName); | 552 return a(memberUrl(member), memberName); |
| 567 } | 553 } |
| 568 } | 554 } |
| 555 |
| 556 class MdnComment implements doc.DocComment { |
| 557 final String mdnComment; |
| 558 final String mdnUrl; |
| 559 |
| 560 MdnComment(String this.mdnComment, String this.mdnUrl); |
| 561 |
| 562 String get text => mdnComment; |
| 563 |
| 564 InterfaceMirror get inheritedFrom => null; |
| 565 |
| 566 String get html { |
| 567 // Wrap the mdn comment so we can highlight it and so we handle MDN scraped |
| 568 // content that lacks a top-level block tag. |
| 569 return ''' |
| 570 <div class="mdn"> |
| 571 $mdnComment |
| 572 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> |
| 573 </div> |
| 574 '''; |
| 575 } |
| 576 |
| 577 String toString() => mdnComment; |
| 578 } |
| OLD | NEW |