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

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

Issue 8771054: Add a script to generate HTML and DOM docs with cross-links to one another. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: More code review changes. Created 9 years 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) 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 * To use it, from this directory, run: 6 * To use it, from this directory, run:
7 * 7 *
8 * $ dartdoc <path to .dart file> 8 * $ dartdoc <path to .dart file>
9 * 9 *
10 * This will create a "docs" directory with the docs for your libraries. To 10 * This will create a "docs" directory with the docs for your libraries. To
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 47
48 /** 48 /**
49 * The cached lookup-table to associate doc comments with spans. The outer map 49 * The cached lookup-table to associate doc comments with spans. The outer map
50 * is from filenames to doc comments in that file. The inner map maps from the 50 * is from filenames to doc comments in that file. The inner map maps from the
51 * token positions to doc comments. Each position is the starting offset of the 51 * token positions to doc comments. Each position is the starting offset of the
52 * next non-comment token *following* the doc comment. For example, the position 52 * next non-comment token *following* the doc comment. For example, the position
53 * for this comment would be the position of the "Map" token below. 53 * for this comment would be the position of the "Map" token below.
54 */ 54 */
55 Map<String, Map<int, String>> _comments; 55 Map<String, Map<int, String>> _comments;
56 56
57 /** A callback that returns additional Markdown documentation for a type. */
58 typedef String TypeDocumenter(Type type);
59
60 /** A list of callbacks registered for documenting types. */
61 List<TypeDocumenter> _typeDocumenters;
62
63 /** A callback that returns additional Markdown documentation for a method. */
64 typedef String MethodDocumenter(MethodMember method);
65
66 /** A list of callbacks registered for documenting methods. */
67 List<MethodDocumenter> _methodDocumenters;
68
69 /** A callback that returns additional Markdown documentation for a field. */
70 typedef String FieldDocumenter(FieldMember field);
71
72 /** A list of callbacks registered for documenting fields. */
73 List<FieldDocumenter> _fieldDocumenters;
74
57 int _totalLibraries = 0; 75 int _totalLibraries = 0;
58 int _totalTypes = 0; 76 int _totalTypes = 0;
59 int _totalMembers = 0; 77 int _totalMembers = 0;
60 78
61 /** 79 /**
62 * Run this from the `utils/dartdoc` directory. 80 * Run this from the `utils/dartdoc` directory.
63 */ 81 */
64 void main() { 82 void main() {
65 // The entrypoint of the library to generate docs for. 83 // The entrypoint of the library to generate docs for.
66 final entrypoint = process.argv[2]; 84 final entrypoint = process.argv[2];
67 85
68 // Parse the dartdoc options. 86 // Parse the dartdoc options.
69 for (int i = 3; i < process.argv.length; i++) { 87 for (int i = 3; i < process.argv.length; i++) {
70 final arg = process.argv[i]; 88 final arg = process.argv[i];
71 switch (arg) { 89 switch (arg) {
72 case '--no-code': 90 case '--no-code':
73 includeSource = false; 91 includeSource = false;
74 break; 92 break;
75 93
76 default: 94 default:
77 print('Unknown option: $arg'); 95 print('Unknown option: $arg');
78 } 96 }
79 } 97 }
80 98
81 files = new NodeFileSystem(); 99 files = new NodeFileSystem();
82 parseOptions('../../frog', [] /* args */, files); 100 parseOptions('../../frog', [] /* args */, files);
83 options.dietParse = true; 101 initializeWorld(files);
102
103 final elapsed = time(() {
104 initializeDartDoc();
105 document(entrypoint);
106 });
107
108 printStats();
109 }
110
111 void initializeDartDoc() {
112 _comments = <Map<int, String>>{};
113 _typeDocumenters = <TypeDocumenter>[];
114 _methodDocumenters = <MethodDocumenter>[];
115 _fieldDocumenters = <FieldDocumenter>[];
84 116
85 // Patch in support for [:...:]-style code to the markdown parser. 117 // Patch in support for [:...:]-style code to the markdown parser.
86 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? 118 // TODO(rnystrom): Markdown already has syntax for this. Phase this out?
87 md.InlineParser.syntaxes.insertRange(0, 1, 119 md.InlineParser.syntaxes.insertRange(0, 1,
88 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); 120 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]'));
89 121
90 md.setImplicitLinkResolver(resolveNameReference); 122 md.setImplicitLinkResolver(resolveNameReference);
123 }
91 124
92 final elapsed = time(() { 125 document(String entrypoint) {
93 initializeDartDoc(); 126 try {
94 127 var oldDietParse = options.dietParse;
95 initializeWorld(files); 128 options.dietParse = true;
96 129
97 // Handle the built-in entrypoints. 130 // Handle the built-in entrypoints.
98 switch (entrypoint) { 131 switch (entrypoint) {
99 case 'corelib': 132 case 'corelib':
100 world.getOrAddLibrary('dart:core'); 133 world.getOrAddLibrary('dart:core');
101 world.getOrAddLibrary('dart:coreimpl'); 134 world.getOrAddLibrary('dart:coreimpl');
102 world.process(); 135 world.process();
103 break; 136 break;
104 137
105 case 'dom': 138 case 'dom':
(...skipping 16 matching lines...) Expand all
122 world.processDartScript(entrypoint); 155 world.processDartScript(entrypoint);
123 } 156 }
124 157
125 world.resolveAll(); 158 world.resolveAll();
126 159
127 // Generate the docs. 160 // Generate the docs.
128 docIndex(); 161 docIndex();
129 for (final library in world.libraries.getValues()) { 162 for (final library in world.libraries.getValues()) {
130 docLibrary(library); 163 docLibrary(library);
131 } 164 }
132 }); 165 } finally {
133 166 options.dietParse = oldDietParse;
134 print('Documented $_totalLibraries libraries, $_totalTypes types, and ' + 167 }
135 '$_totalMembers members in ${elapsed}msec.');
136 } 168 }
137 169
138 void initializeDartDoc() { 170 printStats() {
139 _comments = <String, Map<int, String>>{}; 171 print('Documented $_totalLibraries libraries, $_totalTypes types, and ' +
172 '$_totalMembers members in ${elapsed}msec.');
140 } 173 }
141 174
142 writeHeader(String title) { 175 writeHeader(String title) {
143 writeln( 176 writeln(
144 ''' 177 '''
145 <!DOCTYPE html> 178 <!DOCTYPE html>
146 <html> 179 <html>
147 <head> 180 <head>
148 <meta charset="utf-8"> 181 <meta charset="utf-8">
149 <title>$title</title> 182 <title>$title</title>
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 write('<div class="$icon"></div><strong>${typeName(type)}</strong>'); 265 write('<div class="$icon"></div><strong>${typeName(type)}</strong>');
233 } else { 266 } else {
234 write(a(typeUrl(type), '<div class="$icon"></div>${typeName(type)}')); 267 write(a(typeUrl(type), '<div class="$icon"></div>${typeName(type)}'));
235 } 268 }
236 269
237 writeln('</li>'); 270 writeln('</li>');
238 } 271 }
239 writeln('</ul>'); 272 writeln('</ul>');
240 } 273 }
241 274
275 String _runDocumenters(var item, List<Function> documenters) =>
276 Strings.join(map(documenters, (doc) => doc(item)), '\n\n');
277
242 docLibrary(Library library) { 278 docLibrary(Library library) {
243 _totalLibraries++; 279 _totalLibraries++;
244 _currentLibrary = library; 280 _currentLibrary = library;
245 _currentType = null; 281 _currentType = null;
246 282
247 startFile(libraryUrl(library)); 283 startFile(libraryUrl(library));
248 writeHeader(library.name); 284 writeHeader(library.name);
249 writeln('<h1>Library <strong>${library.name}</strong></h1>'); 285 writeln('<h1>Library <strong>${library.name}</strong></h1>');
250 286
251 // Look for a comment for the entire library. 287 // Look for a comment for the entire library.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 writeHeader('Library ${type.library.name} / $typeTitle'); 351 writeHeader('Library ${type.library.name} / $typeTitle');
316 writeln( 352 writeln(
317 ''' 353 '''
318 <h1>${a(libraryUrl(type.library), 354 <h1>${a(libraryUrl(type.library),
319 "Library <strong>${type.library.name}</strong>")}</h1> 355 "Library <strong>${type.library.name}</strong>")}</h1>
320 <h2>${type.isClass ? "Class" : "Interface"} 356 <h2>${type.isClass ? "Class" : "Interface"}
321 <strong>${typeName(type, showBounds: true)}</strong></h2> 357 <strong>${typeName(type, showBounds: true)}</strong></h2>
322 '''); 358 ''');
323 359
324 docInheritance(type); 360 docInheritance(type);
325 docCode(type.span); 361 docCode(type.span, _runDocumenters(type, _typeDocumenters));
326 docConstructors(type); 362 docConstructors(type);
327 docMembers(type); 363 docMembers(type);
328 364
329 writeFooter(); 365 writeFooter();
330 endFile(); 366 endFile();
331 } 367 }
332 368
333 void docMembers(Type type) { 369 void docMembers(Type type) {
334 // Collect the different kinds of members. 370 // Collect the different kinds of members.
335 final methods = []; 371 final methods = [];
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 409
374 if (isSubclass || 410 if (isSubclass ||
375 (type.interfaces != null && type.interfaces.length > 0) || 411 (type.interfaces != null && type.interfaces.length > 0) ||
376 (factory != null)) { 412 (factory != null)) {
377 writeln('<p>'); 413 writeln('<p>');
378 414
379 if (isSubclass) { 415 if (isSubclass) {
380 write('Extends ${typeReference(type.parent)}. '); 416 write('Extends ${typeReference(type.parent)}. ');
381 } 417 }
382 418
383 if (type.interfaces != null) { 419 if (type.interfaces != null && type.interfaces.length > 0) {
384 switch (type.interfaces.length) { 420 var interfaceStr = joinWithCommas(map(type.interfaces, typeReference));
385 case 0: 421 write('Implements ${interfaceStr}. ');
386 // Do nothing.
387 break;
388
389 case 1:
390 write('Implements ${typeReference(type.interfaces[0])}. ');
391 break;
392
393 case 2:
394 write('''Implements ${typeReference(type.interfaces[0])} and
395 ${typeReference(type.interfaces[1])}. ''');
396 break;
397
398 default:
399 write('Implements ');
400 for (final i = 0; i < type.interfaces.length; i++) {
401 write('${typeReference(type.interfaces[i])}');
402 if (i < type.interfaces.length - 2) {
403 write(', ');
404 } else if (i < type.interfaces.length - 1) {
405 write(', and ');
406 }
407 }
408 write('. ');
409 break;
410 }
411 } 422 }
412 423
413 if (factory != null) { 424 if (factory != null) {
414 write('Has factory class ${typeReference(factory)}.'); 425 write('Has factory class ${typeReference(factory)}.');
415 } 426 }
416 } 427 }
417 } 428 }
418 429
419 /** Document the constructors for [Type], if any. */ 430 /** Document the constructors for [Type], if any. */
420 docConstructors(Type type) { 431 docConstructors(Type type) {
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
482 write('.'); 493 write('.');
483 write(constructorName); 494 write(constructorName);
484 } 495 }
485 496
486 docParamList(type, method); 497 docParamList(type, method);
487 498
488 write(''' <a class="anchor-link" href="#${memberAnchor(method)}" 499 write(''' <a class="anchor-link" href="#${memberAnchor(method)}"
489 title="Permalink to ${typeName(type)}.$name">#</a>'''); 500 title="Permalink to ${typeName(type)}.$name">#</a>''');
490 writeln('</h4>'); 501 writeln('</h4>');
491 502
492 docCode(method.span, showCode: true); 503 docCode(method.span, _runDocumenters(method, _methodDocumenters),
504 showCode: true);
493 505
494 writeln('</div>'); 506 writeln('</div>');
495 } 507 }
496 508
497 docParamList(Type enclosingType, MethodMember member) { 509 docParamList(Type enclosingType, MethodMember member) {
498 write('('); 510 write('(');
499 bool first = true; 511 bool first = true;
500 bool inOptionals = false; 512 bool inOptionals = false;
501 for (final parameter in member.parameters) { 513 for (final parameter in member.parameters) {
502 if (!first) write(', '); 514 if (!first) write(', ');
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 562
551 annotateType(type, field.type); 563 annotateType(type, field.type);
552 write( 564 write(
553 ''' 565 '''
554 <strong>${field.name}</strong> <a class="anchor-link" 566 <strong>${field.name}</strong> <a class="anchor-link"
555 href="#${memberAnchor(field)}" 567 href="#${memberAnchor(field)}"
556 title="Permalink to ${typeName(type)}.${field.name}">#</a> 568 title="Permalink to ${typeName(type)}.${field.name}">#</a>
557 </h4> 569 </h4>
558 '''); 570 ''');
559 571
560 docCode(field.span, showCode: true); 572 docCode(field.span, _runDocumenters(field, _fieldDocumenters),
573 showCode: true);
561 writeln('</div>'); 574 writeln('</div>');
562 } 575 }
563 576
564 /** 577 /**
565 * Creates a hyperlink. Handles turning the [href] into an appropriate relative 578 * Creates a hyperlink. Handles turning the [href] into an appropriate relative
566 * path from the current file. 579 * path from the current file.
567 */ 580 */
568 String a(String href, String contents, [String class]) { 581 String a(String href, String contents, [String class]) {
569 final css = class == null ? '' : ' class="$class"'; 582 final css = class == null ? '' : ' class="$class"';
570 return '<a href="${relativePath(href)}"$css>$contents</a>'; 583 return '<a href="${relativePath(href)}"$css>$contents</a>';
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
738 // * Type parameters of the enclosing type. 751 // * Type parameters of the enclosing type.
739 752
740 return new md.Element.text('code', name); 753 return new md.Element.text('code', name);
741 } 754 }
742 755
743 /** 756 /**
744 * Documents the code contained within [span]. Will include the previous 757 * Documents the code contained within [span]. Will include the previous
745 * Dartdoc associated with that span if found, and will include the syntax 758 * Dartdoc associated with that span if found, and will include the syntax
746 * highlighted code itself if desired. 759 * highlighted code itself if desired.
747 */ 760 */
748 docCode(SourceSpan span, [bool showCode = false]) { 761 docCode(SourceSpan span, String extraMarkdown, [bool showCode = false]) {
749 if (span == null) return; 762 if (span == null) return;
750 763
751 writeln('<div class="doc">'); 764 writeln('<div class="doc">');
752 final comment = findComment(span); 765 final comment = findComment(span);
753 if (comment != null) { 766 if (comment != null) {
754 writeln(md.markdownToHtml(comment)); 767 writeln(md.markdownToHtml('${comment}\n\n${extraMarkdown}'));
768 } else {
769 writeln(md.markdownToHtml(extraMarkdown));
755 } 770 }
756 771
757 if (includeSource && showCode) { 772 if (includeSource && showCode) {
758 writeln('<pre class="source">'); 773 writeln('<pre class="source">');
759 write(formatCode(span)); 774 write(formatCode(span));
760 writeln('</pre>'); 775 writeln('</pre>');
761 } 776 }
762 777
763 writeln('</div>'); 778 writeln('</div>');
764 } 779 }
765 780
766 /** Finds the doc comment preceding the given source span, if there is one. */ 781 /** Finds the doc comment preceding the given source span, if there is one. */
767 findComment(SourceSpan span) => findCommentInFile(span.file, span.start); 782 findComment(SourceSpan span) => findCommentInFile(span.file, span.start);
768 783
769 /** Finds the doc comment preceding the given source span, if there is one. */ 784 /** Finds the doc comment preceding the given source span, if there is one. */
770 findCommentInFile(SourceFile file, int position) { 785 findCommentInFile(SourceFile file, int position) {
771 // Get the doc comments for this file. 786 // Get the doc comments for this file.
772 final fileComments = _comments.putIfAbsent(file.filename, 787 final fileComments = _comments.putIfAbsent(file.filename,
773 () => parseDocComments(file)); 788 () => parseDocComments(file));
774 789
775 return fileComments[position]; 790 return fileComments[position];
776 } 791 }
777 792
778 parseDocComments(SourceFile file) { 793 parseDocComments(SourceFile file) {
779 final comments = <int, String>{}; 794 final comments = new Map<int, String>();
780 795
781 final tokenizer = new Tokenizer(file, false); 796 final tokenizer = new Tokenizer(file, false);
782 var lastComment = null; 797 var lastComment = null;
783 798
784 while (true) { 799 while (true) {
785 final token = tokenizer.next(); 800 final token = tokenizer.next();
786 if (token.kind == TokenKind.END_OF_FILE) break; 801 if (token.kind == TokenKind.END_OF_FILE) break;
787 802
788 if (token.kind == TokenKind.COMMENT) { 803 if (token.kind == TokenKind.COMMENT) {
789 final text = token.text; 804 final text = token.text;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 line = line.substring(2, line.length); 877 line = line.substring(2, line.length);
863 } else if (line.startsWith('*')) { 878 } else if (line.startsWith('*')) {
864 line = line.substring(1, line.length); 879 line = line.substring(1, line.length);
865 } 880 }
866 881
867 buf.add(line); 882 buf.add(line);
868 buf.add('\n'); 883 buf.add('\n');
869 } 884 }
870 885
871 return buf.toString(); 886 return buf.toString();
872 } 887 }
888
889 /** Register a callback to add additional documentation to a type. */
890 addTypeDocumenter(TypeDocumenter fn) => _typeDocumenters.add(fn);
891
892 /** Register a callback to add additional documentation to a method. */
893 addMethodDocumenter(MethodDocumenter fn) => _methodDocumenters.add(fn);
894
895 /** Register a callback to add additional documentation to a field. */
896 addFieldDocumenter(FieldDocumenter fn) => _fieldDocumenters.add(fn);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698