Chromium Code Reviews| 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 // TODO(rnystrom): This is moving from a sample to being a real project. Right | 5 /** An awesome documentation generator. */ |
| 6 // now, to try this out: | 6 #library('dartdoc'); |
| 7 // 1. Compile interact.dart to JS: | |
| 8 // $ ./frogsh --out=docs/interact.js --compile-only docs/interact.dart | |
| 9 // 2. Run the doc generator: | |
| 10 // $ ./frogsh samples/doc.dart | |
| 11 // 3. Look at the results in frog/docs/ | |
| 12 | 7 |
| 13 /** An awesome documentation generator. */ | 8 #import('../../frog/lang.dart'); |
| 14 #library('doc'); | 9 #import('../../frog/file_system_node.dart'); |
| 15 | 10 |
| 16 #import('../lang.dart'); | 11 #source('classify.dart'); |
| 17 #import('../file_system_node.dart'); | |
| 18 #import('classify.dart'); | |
| 19 | |
| 20 /** Path to starting library or application. */ | |
| 21 // TODO(rnystrom): Make this a command-line arg. | |
| 22 final libPath = 'samples/doc.dart'; | |
| 23 | 12 |
| 24 /** Path to corePath library. */ | 13 /** Path to corePath library. */ |
| 25 final corePath = 'lib'; | 14 final corePath = 'lib'; |
| 26 | 15 |
| 27 /** Path to generate html files into. */ | 16 /** Path to generate html files into. */ |
| 28 final outdir = './docs'; | 17 final outdir = 'docs'; |
| 29 | 18 |
| 30 /** Special comment position used to store the library-level doc comment. */ | 19 /** Special comment position used to store the library-level doc comment. */ |
| 31 final _libraryDoc = -1; | 20 final _libraryDoc = -1; |
| 32 | 21 |
| 33 /** The file currently being written to. */ | 22 /** The file currently being written to. */ |
| 34 StringBuffer _file; | 23 StringBuffer _file; |
| 35 | 24 |
| 36 /** | 25 /** |
| 37 * The cached lookup-table to associate doc comments with spans. The outer map | 26 * The cached lookup-table to associate doc comments with spans. The outer map |
| 38 * is from filenames to doc comments in that file. The inner map maps from the | 27 * is from filenames to doc comments in that file. The inner map maps from the |
| 39 * token positions to doc comments. Each position is the starting offset of the | 28 * token positions to doc comments. Each position is the starting offset of the |
| 40 * next non-comment token *following* the doc comment. For example, the position | 29 * next non-comment token *following* the doc comment. For example, the position |
| 41 * for this comment would be the position of the "Map" token below. | 30 * for this comment would be the position of the "Map" token below. |
| 42 */ | 31 */ |
| 43 Map<String, Map<int, String>> _comments; | 32 Map<String, Map<int, String>> _comments; |
| 44 | 33 |
| 45 // TODO(jimhug): This generates really ugly output with lots of holes. | 34 int _totalLibraries = 0; |
|
Jennifer Messerly
2011/11/11 02:29:58
haha. yes, that TODO is very obsolete :)
jimhug
2011/11/11 18:02:31
Whoo hoo!
Bob Nystrom
2011/11/11 18:21:28
:D
Still need private members, but almost everyth
| |
| 35 int _totalTypes = 0; | |
| 36 int _totalMembers = 0; | |
| 46 | 37 |
| 47 /** | 38 /** |
| 48 * Run this from the frog/samples directory. Before running, you need | 39 * Run this from the frog/samples directory. Before running, you need |
| 49 * to create a docs dir with 'mkdir docs' - since Dart currently doesn't | 40 * to create a docs dir with 'mkdir docs' - since Dart currently doesn't |
| 50 * support creating new directories. | 41 * support creating new directories. |
| 51 */ | 42 */ |
| 52 void main() { | 43 void main() { |
| 44 // The entrypoint of the library to generate docs for. | |
| 45 final libPath = process.argv[2]; | |
| 46 | |
| 53 // TODO(rnystrom): Get options and homedir like frog.dart does. | 47 // TODO(rnystrom): Get options and homedir like frog.dart does. |
| 54 final files = new NodeFileSystem(); | 48 final files = new NodeFileSystem(); |
| 55 parseOptions('.', [] /* args */, files); | 49 parseOptions('../../frog', [] /* args */, files); |
| 56 | 50 |
| 57 initializeWorld(files); | 51 final elapsed = time(() { |
| 52 initializeWorld(files); | |
| 58 | 53 |
| 59 world.withTiming('parsed', () { | |
| 60 world.processScript(libPath); | 54 world.processScript(libPath); |
| 61 }); | 55 world.resolveAll(); |
| 62 | 56 |
| 63 world.withTiming('resolved', () { | |
| 64 world.resolveAll(); | |
| 65 }); | |
| 66 | |
| 67 world.withTiming('generated docs', () { | |
| 68 _comments = <String, Map<int, String>>{}; | 57 _comments = <String, Map<int, String>>{}; |
| 69 | 58 |
| 70 for (var library in world.libraries.getValues()) { | 59 for (var library in world.libraries.getValues()) { |
| 71 docLibrary(library); | 60 docLibrary(library); |
| 72 } | 61 } |
| 73 | 62 |
| 74 docIndex(world.libraries.getValues()); | 63 docIndex(world.libraries.getValues()); |
| 75 }); | 64 }); |
| 65 | |
| 66 print('Documented $_totalLibraries libraries, $_totalTypes types, and ' + | |
| 67 '$_totalMembers members in ${elapsed}msec.'); | |
|
jimhug
2011/11/11 18:02:31
Nice reporting code.
Bob Nystrom
2011/11/11 18:21:28
Thanks! I was using withTiming() before but that d
| |
| 68 } | |
| 69 | |
| 70 num time(callback()) { | |
| 71 // Unlike world.withTiming, returns the elapsed time. | |
| 72 final watch = new StopWatch(); | |
| 73 watch.start(); | |
| 74 callback(); | |
| 75 watch.stop(); | |
| 76 return watch.elapsedInMs(); | |
| 76 } | 77 } |
| 77 | 78 |
| 78 startFile() { | 79 startFile() { |
| 79 _file = new StringBuffer(); | 80 _file = new StringBuffer(); |
| 80 } | 81 } |
| 81 | 82 |
| 82 write(String s) { | 83 write(String s) { |
| 83 _file.add(s); | 84 _file.add(s); |
| 84 } | 85 } |
| 85 | 86 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 126 ''' | 127 ''' |
| 127 </ul> | 128 </ul> |
| 128 </div> | 129 </div> |
| 129 </body></html> | 130 </body></html> |
| 130 '''); | 131 '''); |
| 131 | 132 |
| 132 endFile('$outdir/index.html'); | 133 endFile('$outdir/index.html'); |
| 133 } | 134 } |
| 134 | 135 |
| 135 docLibrary(Library library) { | 136 docLibrary(Library library) { |
| 137 _totalLibraries++; | |
| 138 | |
| 136 startFile(); | 139 startFile(); |
| 137 writeln( | 140 writeln( |
| 138 ''' | 141 ''' |
| 139 <html> | 142 <html> |
| 140 <head> | 143 <head> |
| 141 <title>${library.name}</title> | 144 <title>${library.name}</title> |
| 142 <link rel="stylesheet" type="text/css" href="styles.css" /> | 145 <link rel="stylesheet" type="text/css" href="styles.css" /> |
| 143 <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,8 00" rel="stylesheet" type="text/css"> | 146 <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,8 00" rel="stylesheet" type="text/css"> |
| 144 <script src="interact.js"></script> | 147 <script src="interact.js"></script> |
| 145 </head> | 148 </head> |
| 146 <body> | 149 <body> |
| 147 <div class="content"> | 150 <div class="content"> |
| 148 <h1>Library <strong>${library.name}</strong></h1> | 151 <h1>Library <strong>${library.name}</strong></h1> |
| 149 '''); | 152 '''); |
| 150 | 153 |
| 151 bool needsSeparator = false; | 154 bool needsSeparator = false; |
| 152 | 155 |
| 153 // Look for a comment for the entire library. | 156 // Look for a comment for the entire library. |
| 154 final comment = findCommentInFile(library.baseSource, _libraryDoc); | 157 final comment = findCommentInFile(library.baseSource, _libraryDoc); |
| 155 if (comment != null) { | 158 if (comment != null) { |
| 156 writeln('<div class="doc"><p>$comment</p></div>'); | 159 writeln('<div class="doc"><p>$comment</p></div>'); |
| 157 needsSeparator = true; | 160 needsSeparator = true; |
| 158 } | 161 } |
| 159 | 162 |
| 160 for (var type in library.types.getValues()) { | 163 for (var type in library.types.getValues()) { |
| 161 if (needsSeparator) writeln('<hr/>'); | 164 if (needsSeparator) writeln('<hr/>'); |
| 162 if (docType(type)) needsSeparator = false; | 165 if (docType(type)) needsSeparator = true; |
| 163 } | 166 } |
| 164 | 167 |
| 165 writeln( | 168 writeln( |
| 166 ''' | 169 ''' |
| 167 </div> | 170 </div> |
| 168 </body></html> | 171 </body></html> |
| 169 '''); | 172 '''); |
| 170 | 173 |
| 171 endFile('$outdir/${sanitize(library.name)}.html'); | 174 endFile('$outdir/${sanitize(library.name)}.html'); |
| 172 } | 175 } |
| 173 | 176 |
| 174 /** | 177 /** |
| 175 * Documents [Type]. Handles top-level members if given an unnamed Type. | 178 * Documents [Type]. Handles top-level members if given an unnamed Type. |
| 176 * Returns [:true:] if it wrote anything. | 179 * Returns [:true:] if it wrote anything. |
| 177 */ | 180 */ |
| 178 bool docType(Type type) { | 181 bool docType(Type type) { |
| 182 _totalTypes++; | |
| 183 | |
| 179 bool wroteSomething = false; | 184 bool wroteSomething = false; |
| 180 | 185 |
| 181 if (type.name != null) { | 186 if (type.name != null) { |
| 182 write( | 187 write( |
| 183 ''' | 188 ''' |
| 184 <h2 id="${type.name}"> | 189 <h2 id="${type.name}"> |
| 185 ${type.isClass ? "Class" : "Interface"} <strong>${type.name}</strong> | 190 ${type.isClass ? "Class" : "Interface"} <strong>${type.name}</strong> |
| 186 <a class="anchor-link" href="#${type.name}" | 191 <a class="anchor-link" href="#${type.name}" |
| 187 title="Permalink to ${type.name}">#</a> | 192 title="Permalink to ${type.name}">#</a> |
| 188 </h2> | 193 </h2> |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 279 } | 284 } |
| 280 } | 285 } |
| 281 } | 286 } |
| 282 | 287 |
| 283 /** | 288 /** |
| 284 * Documents the [method] in a type named [typeName]. Handles all kinds of | 289 * Documents the [method] in a type named [typeName]. Handles all kinds of |
| 285 * methods including getters, setters, and constructors. | 290 * methods including getters, setters, and constructors. |
| 286 */ | 291 */ |
| 287 docMethod(String typeName, MethodMember method, | 292 docMethod(String typeName, MethodMember method, |
| 288 [String namedConstructor = null]) { | 293 [String namedConstructor = null]) { |
| 294 _totalMembers++; | |
| 295 | |
| 289 writeln( | 296 writeln( |
| 290 ''' | 297 ''' |
| 291 <div class="method"><h4 id="$typeName.${method.name}"> | 298 <div class="method"><h4 id="$typeName.${method.name}"> |
| 292 <span class="show-code">Code</span> | 299 <span class="show-code">Code</span> |
| 293 '''); | 300 '''); |
| 294 | 301 |
| 295 // A null typeName means it's a top-level definition which is implicitly | 302 // A null typeName means it's a top-level definition which is implicitly |
| 296 // static so doesn't need to annotate it. | 303 // static so doesn't need to annotate it. |
| 297 if (method.isStatic && (typeName != null)) { | 304 if (method.isStatic && (typeName != null)) { |
| 298 write('static '); | 305 write('static '); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 title="Permalink to $typeName.$name">#</a>'''); | 352 title="Permalink to $typeName.$name">#</a>'''); |
| 346 writeln('</h4>'); | 353 writeln('</h4>'); |
| 347 | 354 |
| 348 docCode(method.span, showCode: true); | 355 docCode(method.span, showCode: true); |
| 349 | 356 |
| 350 writeln('</div>'); | 357 writeln('</div>'); |
| 351 } | 358 } |
| 352 | 359 |
| 353 /** Documents the field [field] in a type named [typeName]. */ | 360 /** Documents the field [field] in a type named [typeName]. */ |
| 354 docField(String typeName, FieldMember field) { | 361 docField(String typeName, FieldMember field) { |
| 362 _totalMembers++; | |
| 363 | |
| 355 writeln( | 364 writeln( |
| 356 ''' | 365 ''' |
| 357 <div class="field"><h4 id="$typeName.${field.name}"> | 366 <div class="field"><h4 id="$typeName.${field.name}"> |
| 358 <span class="show-code">Code</span> | 367 <span class="show-code">Code</span> |
| 359 '''); | 368 '''); |
| 360 | 369 |
| 361 // A null typeName means it's a top-level definition which is implicitly | 370 // A null typeName means it's a top-level definition which is implicitly |
| 362 // static so doesn't need to annotate it. | 371 // static so doesn't need to annotate it. |
| 363 if (field.isStatic && (typeName != null)) { | 372 if (field.isStatic && (typeName != null)) { |
| 364 write('static '); | 373 write('static '); |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 533 if (line.endsWith('*/')) line = line.substring(0, line.length-2); | 542 if (line.endsWith('*/')) line = line.substring(0, line.length-2); |
| 534 line = line.trim(); | 543 line = line.trim(); |
| 535 while (line.startsWith('*')) line = line.substring(1, line.length); | 544 while (line.startsWith('*')) line = line.substring(1, line.length); |
| 536 line = line.trim(); | 545 line = line.trim(); |
| 537 buf.add(line); | 546 buf.add(line); |
| 538 buf.add(' '); | 547 buf.add(' '); |
| 539 } | 548 } |
| 540 | 549 |
| 541 return buf.toString(); | 550 return buf.toString(); |
| 542 } | 551 } |
| OLD | NEW |