Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: pkg/dartdoc/dartdoc.dart

Issue 10829361: 'Find-as-you-type'-search in dartdoc/apidoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: CSS bug fixed Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 * To generate docs for a library, run this script with the path to an 6 * To generate docs for a library, run this script with the path to an
7 * entrypoint .dart file, like: 7 * entrypoint .dart file, like:
8 * 8 *
9 * $ dart dartdoc.dart foo.dart 9 * $ dart dartdoc.dart foo.dart
10 * 10 *
(...skipping 11 matching lines...) Expand all
22 #import('mirrors/mirrors.dart'); 22 #import('mirrors/mirrors.dart');
23 #import('mirrors/mirrors_util.dart'); 23 #import('mirrors/mirrors_util.dart');
24 #import('mirrors/dart2js_mirror.dart', prefix: 'dart2js'); 24 #import('mirrors/dart2js_mirror.dart', prefix: 'dart2js');
25 #import('classify.dart'); 25 #import('classify.dart');
26 #import('markdown.dart', prefix: 'md'); 26 #import('markdown.dart', prefix: 'md');
27 #import('../../lib/compiler/implementation/scanner/scannerlib.dart', 27 #import('../../lib/compiler/implementation/scanner/scannerlib.dart',
28 prefix: 'dart2js'); 28 prefix: 'dart2js');
29 #import('../../lib/compiler/implementation/library_map.dart'); 29 #import('../../lib/compiler/implementation/library_map.dart');
30 30
31 #source('comment_map.dart'); 31 #source('comment_map.dart');
32 #source('nav.dart');
32 #source('utils.dart'); 33 #source('utils.dart');
33 34
34 // TODO(johnniwinther): Note that [IN_SDK] gets initialized to true when this 35 // TODO(johnniwinther): Note that [IN_SDK] gets initialized to true when this
35 // file is modified by the SDK deployment script. If you change, be sure to test 36 // file is modified by the SDK deployment script. If you change, be sure to test
36 // that dartdoc still works when run from the built SDK directory. 37 // that dartdoc still works when run from the built SDK directory.
37 final bool IN_SDK = false; 38 final bool IN_SDK = false;
38 39
39 /** 40 /**
40 * Generates completely static HTML containing everything you need to browse 41 * Generates completely static HTML containing everything you need to browse
41 * the docs. The only client side behavior is trivial stuff like syntax 42 * the docs. The only client side behavior is trivial stuff like syntax
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 // Compile the client-side code to JS. 153 // Compile the client-side code to JS.
153 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; 154 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav';
154 Future compiled = compileScript( 155 Future compiled = compileScript(
155 scriptDir.append('client-$clientScript.dart'), 156 scriptDir.append('client-$clientScript.dart'),
156 dartdoc.outputDir.append('client-$clientScript.js')); 157 dartdoc.outputDir.append('client-$clientScript.js'));
157 158
158 Future filesCopied = copyDirectory(scriptDir.append('static'), 159 Future filesCopied = copyDirectory(scriptDir.append('static'),
159 dartdoc.outputDir); 160 dartdoc.outputDir);
160 161
161 Futures.wait([compiled, filesCopied]).then((_) { 162 Futures.wait([compiled, filesCopied]).then((_) {
163 dartdoc.cleanup();
162 print('Documented ${dartdoc._totalLibraries} libraries, ' 164 print('Documented ${dartdoc._totalLibraries} libraries, '
163 '${dartdoc._totalTypes} types, and ' 165 '${dartdoc._totalTypes} types, and '
164 '${dartdoc._totalMembers} members.'); 166 '${dartdoc._totalMembers} members.');
165 }); 167 });
166 } 168 }
167 169
168 void printUsage() { 170 void printUsage() {
169 print(''' 171 print('''
170 Usage dartdoc [options] <entrypoint(s)> 172 Usage dartdoc [options] <entrypoint(s)>
171 [options] include 173 [options] include
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 * want the client-side behavior to be. The value for this should be one of 307 * want the client-side behavior to be. The value for this should be one of
306 * the `MODE_` constants. 308 * the `MODE_` constants.
307 */ 309 */
308 int mode = MODE_LIVE_NAV; 310 int mode = MODE_LIVE_NAV;
309 311
310 /** 312 /**
311 * Generates the App Cache manifest file, enabling offline doc viewing. 313 * Generates the App Cache manifest file, enabling offline doc viewing.
312 */ 314 */
313 bool generateAppCache = false; 315 bool generateAppCache = false;
314 316
317 /** Path to the dartdoc directory. */
318 Path dartdocPath;
319
315 /** Path to generate HTML files into. */ 320 /** Path to generate HTML files into. */
316 Path outputDir = const Path('docs'); 321 Path outputDir = const Path('docs');
317 322
318 /** 323 /**
319 * The title used for the overall generated output. Set this to change it. 324 * The title used for the overall generated output. Set this to change it.
320 */ 325 */
321 String mainTitle = 'Dart Documentation'; 326 String mainTitle = 'Dart Documentation';
322 327
323 /** 328 /**
324 * The URL that the Dart logo links to. Defaults "index.html", the main 329 * The URL that the Dart logo links to. Defaults "index.html", the main
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 Path _filePath; 384 Path _filePath;
380 385
381 /** The file currently being written to. */ 386 /** The file currently being written to. */
382 StringBuffer _file; 387 StringBuffer _file;
383 388
384 int _totalLibraries = 0; 389 int _totalLibraries = 0;
385 int _totalTypes = 0; 390 int _totalTypes = 0;
386 int _totalMembers = 0; 391 int _totalMembers = 0;
387 392
388 Dartdoc() 393 Dartdoc()
389 : _comments = new CommentMap() { 394 : _comments = new CommentMap(),
395 dartdocPath = scriptDir {
396
390 // Patch in support for [:...:]-style code to the markdown parser. 397 // Patch in support for [:...:]-style code to the markdown parser.
391 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? 398 // TODO(rnystrom): Markdown already has syntax for this. Phase this out?
392 md.InlineParser.syntaxes.insertRange(0, 1, 399 md.InlineParser.syntaxes.insertRange(0, 1,
393 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); 400 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]'));
394 401
395 md.setImplicitLinkResolver((name) => resolveNameReference(name, 402 md.setImplicitLinkResolver((name) => resolveNameReference(name,
396 currentLibrary: _currentLibrary, currentType: _currentType, 403 currentLibrary: _currentLibrary, currentType: _currentType,
397 currentMember: _currentMember)); 404 currentMember: _currentMember));
398 } 405 }
399 406
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 // Sort the libraries by name (not key). 482 // Sort the libraries by name (not key).
476 _sortedLibraries = new List<LibraryMirror>.from( 483 _sortedLibraries = new List<LibraryMirror>.from(
477 compilation.mirrors.libraries.getValues().filter( 484 compilation.mirrors.libraries.getValues().filter(
478 shouldIncludeLibrary)); 485 shouldIncludeLibrary));
479 _sortedLibraries.sort((x, y) { 486 _sortedLibraries.sort((x, y) {
480 return x.simpleName.toUpperCase().compareTo( 487 return x.simpleName.toUpperCase().compareTo(
481 y.simpleName.toUpperCase()); 488 y.simpleName.toUpperCase());
482 }); 489 });
483 490
484 // Generate the docs. 491 // Generate the docs.
485 if (mode == MODE_LIVE_NAV) docNavigationJson(); 492 if (mode == MODE_LIVE_NAV) {
493 docNavigationJson();
494 } else {
495 docNavigationDart();
496 }
486 497
487 docIndex(); 498 docIndex();
488 for (final library in _sortedLibraries) { 499 for (final library in _sortedLibraries) {
489 docLibrary(library); 500 docLibrary(library);
490 } 501 }
491 502
492 if (generateAppCache) { 503 if (generateAppCache) {
493 generateAppCacheManifest(); 504 generateAppCacheManifest();
494 } 505 }
495 } 506 }
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
581 } 592 }
582 593
583 if (searchEngineId != null) { 594 if (searchEngineId != null) {
584 writeln( 595 writeln(
585 ''' 596 '''
586 <form action="$searchResultsUrl" id="search-box"> 597 <form action="$searchResultsUrl" id="search-box">
587 <input type="hidden" name="cx" value="$searchEngineId"> 598 <input type="hidden" name="cx" value="$searchEngineId">
588 <input type="hidden" name="ie" value="UTF-8"> 599 <input type="hidden" name="ie" value="UTF-8">
589 <input type="hidden" name="hl" value="en"> 600 <input type="hidden" name="hl" value="en">
590 <input type="search" name="q" id="q" autocomplete="off" 601 <input type="search" name="q" id="q" autocomplete="off"
591 placeholder="Search"> 602 class="search-input" placeholder="Search">
592 </form> 603 </form>
593 '''); 604 ''');
605 } else {
606 writeln(
607 '''
608 <div id="search-box">
609 <input type="search" name="q" id="q" autocomplete="off"
610 class="search-input" placeholder="Search">
611 </div>
612 ''');
594 } 613 }
595 614
596 writeln('</div>'); 615 writeln(
616 '''
617 </div>
618 <div class="drop-down" id="drop-down"></div>
Lasse Reichstein Nielsen 2012/08/20 13:07:58 Why both id and class? Are there any other element
Johnni Winther 2012/08/23 10:19:50 I just dislike referring to 'id' in CSS and 'class
619 ''');
597 620
598 docNavigation(); 621 docNavigation();
599 writeln('<div class="content">'); 622 writeln('<div class="content">');
600 } 623 }
601 624
602 String get clientScript() { 625 String get clientScript() {
603 switch (mode) { 626 switch (mode) {
604 case MODE_STATIC: return 'client-static'; 627 case MODE_STATIC: return 'client-static';
605 case MODE_LIVE_NAV: return 'client-live-nav'; 628 case MODE_LIVE_NAV: return 'client-live-nav';
606 default: throw 'Unknown mode $mode.'; 629 default: throw 'Unknown mode $mode.';
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
653 void docIndexLibrary(LibraryMirror library) { 676 void docIndexLibrary(LibraryMirror library) {
654 writeln('<h4>${a(libraryUrl(library), library.simpleName)}</h4>'); 677 writeln('<h4>${a(libraryUrl(library), library.simpleName)}</h4>');
655 } 678 }
656 679
657 /** 680 /**
658 * Walks the libraries and creates a JSON object containing the data needed 681 * Walks the libraries and creates a JSON object containing the data needed
659 * to generate navigation for them. 682 * to generate navigation for them.
660 */ 683 */
661 void docNavigationJson() { 684 void docNavigationJson() {
662 startFile('nav.json'); 685 startFile('nav.json');
663 686 writeln(JSON.stringify(createNavigationInfo()));
664 final libraryMap = {};
665
666 for (final library in _sortedLibraries) {
667 docLibraryNavigationJson(library, libraryMap);
668 }
669
670 writeln(JSON.stringify(libraryMap));
671 endFile(); 687 endFile();
672 } 688 }
673 689
674 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) { 690 void docNavigationDart() {
691 final dir = new Directory.fromPath(tmpPath);
692 if (!dir.existsSync()) {
693 // TODO(3914): Hack to avoid 'file already exists' exception
694 // thrown due to invalid result from dir.existsSync() (probably due to
695 // race conditions).
696 try {
697 dir.createSync();
698 } catch (DirectoryIOException e) {
699 // Ignore.
Lasse Reichstein Nielsen 2012/08/20 13:07:58 I still don't like this. It's really a bad idea to
Johnni Winther 2012/08/23 10:19:50 There seems to be a solution to 3914 underway.
700 }
701 }
702 String jsonString = JSON.stringify(createNavigationInfo());
703 String dartString = jsonString.replaceAll(@"$", @"\$");
704 final filePath = tmpPath.append('nav.dart');
705 writeString(new File.fromPath(filePath),
706 'get json() => $dartString;');
707 }
708
709 Path get tmpPath() => dartdocPath.append('tmp');
710
711 void cleanup() {
712 final dir = new Directory.fromPath(tmpPath);
713 if (dir.existsSync()) {
714 dir.deleteRecursivelySync();
715 }
716 }
717
718 List createNavigationInfo() {
719 final libraryList = [];
720 for (final library in _sortedLibraries) {
721 docLibraryNavigationJson(library, libraryList);
722 }
723 return libraryList;
724 }
725
726 void docLibraryNavigationJson(LibraryMirror library, List libraryList) {
727 var libraryInfo = {};
728 libraryInfo[NAME] = library.simpleName;
729 final List members = docMembersJson(library.declaredMembers);
730 if (!members.isEmpty()) {
731 libraryInfo[MEMBERS] = members;
732 }
733
675 final types = []; 734 final types = [];
676
677 for (InterfaceMirror type in orderByName(library.types.getValues())) { 735 for (InterfaceMirror type in orderByName(library.types.getValues())) {
678 if (type.isPrivate) continue; 736 if (type.isPrivate) continue;
679 737
680 final kind = type.isClass ? 'class' : 'interface'; 738 var typeInfo = {};
681 final url = typeUrl(type); 739 typeInfo[NAME] = type.simpleName;
682 types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); 740 if (type.isClass) {
741 typeInfo[KIND] = CLASS;
742 } else if (type.isInterface) {
743 typeInfo[KIND] = INTERFACE;
744 } else {
745 assert(type.isTypedef);
746 typeInfo[KIND] = TYPEDEF;
747 }
748 final List typeMembers = docMembersJson(type.declaredMembers);
749 if (!typeMembers.isEmpty()) {
750 typeInfo[MEMBERS] = typeMembers;
751 }
752
753 if (!type.declaration.typeVariables.isEmpty()) {
754 final typeVariables = [];
755 for (final typeVariable in type.declaration.typeVariables) {
756 typeVariables.add(typeVariable.simpleName);
757 }
758 typeInfo[ARGS] = Strings.join(typeVariables, ', ');
759 }
760 types.add(typeInfo);
761 }
762 if (!types.isEmpty()) {
763 libraryInfo[TYPES] = types;
683 } 764 }
684 765
685 libraryMap[library.simpleName] = types; 766 libraryList.add(libraryInfo);
767 }
768
769 List docMembersJson(Map<Object,MemberMirror> memberMap) {
770 final members = [];
771 for (MemberMirror member in orderByName(memberMap.getValues())) {
772 if (member.isPrivate) continue;
773
774 var memberInfo = {};
775 if (member.isField) {
776 memberInfo[NAME] = member.simpleName;
777 memberInfo[KIND] = FIELD;
778 } else {
779 MethodMirror method = member;
780 if (method.isConstructor) {
781 if (method.constructorName != '') {
782 memberInfo[NAME] = '${method.simpleName}.${method.constructorName}';
783 memberInfo[KIND] = CONSTRUCTOR;
784 } else {
785 memberInfo[NAME] = member.simpleName;
786 memberInfo[KIND] = CONSTRUCTOR;
787 }
788 } else if (method.isOperator) {
789 memberInfo[NAME] = '${method.simpleName} ${method.operatorName}';
790 memberInfo[KIND] = METHOD;
791 } else if (method.isSetter) {
792 memberInfo[NAME] = member.simpleName;
793 memberInfo[KIND] = SETTER;
794 } else if (method.isGetter) {
795 memberInfo[NAME] = member.simpleName;
796 memberInfo[KIND] = GETTER;
797 } else {
798 memberInfo[NAME] = member.simpleName;
799 memberInfo[KIND] = METHOD;
800 }
801 }
802 var anchor = memberAnchor(member);
803 if (anchor != memberInfo[NAME]) {
804 memberInfo[LINK_NAME] = anchor;
805 }
806 members.add(memberInfo);
807 }
808 return members;
686 } 809 }
687 810
688 void docNavigation() { 811 void docNavigation() {
689 writeln( 812 writeln(
690 ''' 813 '''
691 <div class="nav"> 814 <div class="nav">
692 '''); 815 ''');
693 816
694 if (mode == MODE_STATIC) { 817 if (mode == MODE_STATIC) {
695 for (final library in _sortedLibraries) { 818 for (final library in _sortedLibraries) {
(...skipping 585 matching lines...) Expand 10 before | Expand all | Expand 10 after
1281 // Always get the generic type to strip off any type parameters or 1404 // Always get the generic type to strip off any type parameters or
1282 // arguments. If the type isn't generic, genericType returns `this`, so it 1405 // arguments. If the type isn't generic, genericType returns `this`, so it
1283 // works for non-generic types too. 1406 // works for non-generic types too.
1284 return '${sanitize(type.library.simpleName)}/' 1407 return '${sanitize(type.library.simpleName)}/'
1285 '${type.declaration.simpleName}.html'; 1408 '${type.declaration.simpleName}.html';
1286 } 1409 }
1287 1410
1288 /** Gets the URL for the documentation for [member]. */ 1411 /** Gets the URL for the documentation for [member]. */
1289 String memberUrl(MemberMirror member) { 1412 String memberUrl(MemberMirror member) {
1290 String url = typeUrl(member.surroundingDeclaration); 1413 String url = typeUrl(member.surroundingDeclaration);
1291 if (!member.isConstructor) { 1414 return '$url#${memberAnchor(member)}';
1292 return '$url#${member.simpleName}';
1293 }
1294 assert (member is MethodMirror);
1295 if (member.constructorName == '') {
1296 return '$url#new:${member.simpleName}';
1297 }
1298 return '$url#new:${member.simpleName}.${member.constructorName}';
1299 } 1415 }
1300 1416
1301 /** Gets the anchor id for the document for [member]. */ 1417 /** Gets the anchor id for the document for [member]. */
1302 String memberAnchor(MemberMirror member) { 1418 String memberAnchor(MemberMirror member) {
1303 return '${member.simpleName}'; 1419 if (member.isField) {
1420 return member.simpleName;
1421 }
1422 MethodMirror method = member;
1423 if (method.isConstructor) {
1424 if (method.constructorName == '') {
1425 return method.simpleName;
1426 } else {
1427 return '${method.simpleName}.${method.constructorName}';
1428 }
1429 } else if (method.isOperator) {
1430 return '${method.simpleName} ${method.operatorName}';
1431 } else if (method.isSetter) {
1432 return '${method.simpleName}=';
1433 } else {
1434 return method.simpleName;
1435 }
1304 } 1436 }
1305 1437
1306 /** 1438 /**
1307 * Creates a hyperlink. Handles turning the [href] into an appropriate 1439 * Creates a hyperlink. Handles turning the [href] into an appropriate
1308 * relative path from the current file. 1440 * relative path from the current file.
1309 */ 1441 */
1310 String a(String href, String contents, [String css]) { 1442 String a(String href, String contents, [String css]) {
1311 // Mark outgoing external links, mainly so we can style them. 1443 // Mark outgoing external links, mainly so we can style them.
1312 final rel = isAbsolute(href) ? ' ref="external"' : ''; 1444 final rel = isAbsolute(href) ? ' ref="external"' : '';
1313 final cssClass = css == null ? '' : ' class="$css"'; 1445 final cssClass = css == null ? '' : ' class="$css"';
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
1400 } 1532 }
1401 1533
1402 /** Generates a human-friendly string representation for a type. */ 1534 /** Generates a human-friendly string representation for a type. */
1403 typeName(TypeMirror type, [bool showBounds = false]) { 1535 typeName(TypeMirror type, [bool showBounds = false]) {
1404 if (type.isVoid) { 1536 if (type.isVoid) {
1405 return 'void'; 1537 return 'void';
1406 } 1538 }
1407 if (type is TypeVariableMirror) { 1539 if (type is TypeVariableMirror) {
1408 return type.simpleName; 1540 return type.simpleName;
1409 } 1541 }
1410 assert (type is InterfaceMirror); 1542 assert(type is InterfaceMirror);
1411 1543
1412 // See if it's a generic type. 1544 // See if it's a generic type.
1413 if (type.isDeclaration) { 1545 if (type.isDeclaration) {
1414 final typeParams = []; 1546 final typeParams = [];
1415 for (final typeParam in type.declaration.typeVariables) { 1547 for (final typeParam in type.declaration.typeVariables) {
1416 if (showBounds && 1548 if (showBounds &&
1417 (typeParam.bound != null) && 1549 (typeParam.bound != null) &&
1418 !typeParam.bound.isObject) { 1550 !typeParam.bound.isObject) {
1419 final bound = typeName(typeParam.bound, showBounds: true); 1551 final bound = typeName(typeParam.bound, showBounds: true);
1420 typeParams.add('${typeParam.simpleName} extends $bound'); 1552 typeParams.add('${typeParam.simpleName} extends $bound');
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
1584 } 1716 }
1585 1717
1586 /** 1718 /**
1587 * Returns [:true:] if [type] should be regarded as an exception. 1719 * Returns [:true:] if [type] should be regarded as an exception.
1588 */ 1720 */
1589 bool isException(TypeMirror type) { 1721 bool isException(TypeMirror type) {
1590 return type.simpleName.endsWith('Exception'); 1722 return type.simpleName.endsWith('Exception');
1591 } 1723 }
1592 } 1724 }
1593 1725
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698