Chromium Code Reviews| 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 * 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 * |
| 11 * This will create a "docs" directory with the docs for your libraries. To | 11 * This will create a "docs" directory with the docs for your libraries. To |
| 12 * create these beautiful docs, dartdoc parses your library and every library | 12 * create these beautiful docs, dartdoc parses your library and every library |
| 13 * it imports (recursively). From each library, it parses all classes and | 13 * it imports (recursively). From each library, it parses all classes and |
| 14 * members, finds the associated doc comments and builds crosslinked docs from | 14 * members, finds the associated doc comments and builds crosslinked docs from |
| 15 * them. | 15 * them. |
| 16 */ | 16 */ |
| 17 #library('dartdoc'); | 17 #library('dartdoc'); |
| 18 | 18 |
| 19 #import('dart:io'); | 19 #import('dart:io'); |
| 20 #import('dart:uri'); | 20 #import('dart:uri'); |
| 21 #import('dart:json'); | 21 #import('dart:json'); |
| 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('../compiler/implementation/dart2js.dart', prefix: 'dart2js'); | |
| 28 #import('../compiler/implementation/scanner/scannerlib.dart', | 27 #import('../compiler/implementation/scanner/scannerlib.dart', |
| 29 prefix: 'dart2js'); | 28 prefix: 'dart2js'); |
| 30 #import('file_util.dart'); | |
| 31 | 29 |
| 32 #source('comment_map.dart'); | 30 #source('comment_map.dart'); |
| 33 #source('utils.dart'); | 31 #source('utils.dart'); |
| 34 | 32 |
| 35 /** | 33 /** |
| 36 * Generates completely static HTML containing everything you need to browse | 34 * Generates completely static HTML containing everything you need to browse |
| 37 * the docs. The only client side behavior is trivial stuff like syntax | 35 * the docs. The only client side behavior is trivial stuff like syntax |
| 38 * highlighting code. | 36 * highlighting code. |
| 39 */ | 37 */ |
| 40 final MODE_STATIC = 0; | 38 final MODE_STATIC = 0; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 96 | 94 |
| 97 case '--omit-generation-time': | 95 case '--omit-generation-time': |
| 98 omitGenerationTime = true; | 96 omitGenerationTime = true; |
| 99 break; | 97 break; |
| 100 case '--verbose': | 98 case '--verbose': |
| 101 verbose = true; | 99 verbose = true; |
| 102 break; | 100 break; |
| 103 | 101 |
| 104 default: | 102 default: |
| 105 if (arg.startsWith('--out=')) { | 103 if (arg.startsWith('--out=')) { |
| 106 outputDir = arg.substring('--out='.length); | 104 outputDir = arg.substring('--out='.length); |
|
Bill Hesse
2012/07/17 13:42:13
Path?
Johnni Winther
2012/07/19 08:37:07
The type of outputDir is changed to Path.
| |
| 107 } else { | 105 } else { |
| 108 print('Unknown option: $arg'); | 106 print('Unknown option: $arg'); |
| 109 printUsage(); | 107 printUsage(); |
| 110 return; | 108 return; |
| 111 } | 109 } |
| 112 break; | 110 break; |
| 113 } | 111 } |
| 114 } | 112 } |
| 115 | 113 |
| 116 if (args.length == 0) { | 114 if (args.length == 0) { |
| 117 print('Provide at least one dart file to process.'); | 115 print('Provide at least one dart file to process.'); |
|
Bill Hesse
2012/07/17 13:42:13
"exactly one file"?
Johnni Winther
2012/07/19 08:37:07
Yes, currently. This is going to change soon. And
| |
| 118 return; | 116 return; |
| 119 } | 117 } |
| 120 | 118 |
| 121 // TODO(rnystrom): Note that the following lines get munged by create-sdk to | 119 final entrypoint = new Path.fromNative(args[args.length - 1]); |
| 122 // work with the SDK's different file layout. If you change, be sure to test | |
| 123 // that dartdoc still works when run from the built SDK directory. | |
| 124 final String libPath = joinPaths(scriptDir, '../'); | |
| 125 | |
| 126 // The entrypoint of the library to generate docs for. | |
| 127 // TODO(johnniwinther): Handle absolute/relative paths | |
| 128 final entrypoint = canonicalizePath(args[args.length - 1]); | |
| 129 | 120 |
| 130 final dartdoc = new Dartdoc(); | 121 final dartdoc = new Dartdoc(); |
| 131 | 122 |
| 132 if (includeSource != null) dartdoc.includeSource = includeSource; | 123 if (includeSource != null) dartdoc.includeSource = includeSource; |
| 133 if (mode != null) dartdoc.mode = mode; | 124 if (mode != null) dartdoc.mode = mode; |
| 134 if (outputDir != null) dartdoc.outputDir = outputDir; | 125 if (outputDir != null) dartdoc.outputDir = new Path.fromNative(outputDir); |
| 135 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache; | 126 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache; |
| 136 if (omitGenerationTime != null) { | 127 if (omitGenerationTime != null) { |
| 137 dartdoc.omitGenerationTime = omitGenerationTime; | 128 dartdoc.omitGenerationTime = omitGenerationTime; |
| 138 } | 129 } |
| 139 if (verbose != null) dartdoc.verbose = verbose; | 130 if (verbose != null) dartdoc.verbose = verbose; |
| 140 | 131 |
| 141 cleanOutputDirectory(dartdoc.outputDir); | 132 cleanOutputDirectory(dartdoc.outputDir); |
| 142 | 133 |
| 143 dartdoc.documentEntryPoint(entrypoint, libPath); | 134 dartdoc.documentEntryPoint(entrypoint, libPath); |
| 144 | 135 |
| 145 // Compile the client-side code to JS. | 136 // Compile the client-side code to JS. |
| 146 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; | 137 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; |
| 147 compileScript( | 138 Future compiled = compileScript( |
| 148 '$scriptDir/client-$clientScript.dart', | 139 scriptDir.append('client-$clientScript.dart'), |
| 149 '${dartdoc.outputDir}/client-$clientScript.js'); | 140 dartdoc.outputDir.append('client-$clientScript.js')); |
| 150 | 141 |
| 151 final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir); | 142 Future filesCopied = copyFiles(scriptDir.append('static'), dartdoc.outputDir); |
| 152 | 143 |
| 153 Futures.wait([filesCopied]).then((_) { | 144 Futures.wait([compiled, filesCopied]).then((_) { |
|
Bill Hesse
2012/07/17 13:42:13
I should start using _ as my "ignore" argument. S
Johnni Winther
2012/07/19 08:37:07
I think so.
| |
| 154 print('Documented ${dartdoc._totalLibraries} libraries, ' | 145 print('Documented ${dartdoc._totalLibraries} libraries, ' |
| 155 '${dartdoc._totalTypes} types, and ' | 146 '${dartdoc._totalTypes} types, and ' |
| 156 '${dartdoc._totalMembers} members.'); | 147 '${dartdoc._totalMembers} members.'); |
| 157 }); | 148 }); |
| 158 } | 149 } |
| 159 | 150 |
| 160 void printUsage() { | 151 void printUsage() { |
| 161 print(''' | 152 print(''' |
| 162 Usage dartdoc [options] <entrypoint> | 153 Usage dartdoc [options] <entrypoint> |
| 163 [options] include | 154 [options] include |
| 164 --no-code Do not include source code in the documentation. | 155 --no-code Do not include source code in the documentation. |
| 165 | 156 |
| 166 --mode=static Generates completely static HTML containing | 157 --mode=static Generates completely static HTML containing |
| 167 everything you need to browse the docs. The only | 158 everything you need to browse the docs. The only |
| 168 client side behavior is trivial stuff like syntax | 159 client side behavior is trivial stuff like syntax |
| 169 highlighting code. | 160 highlighting code. |
| 170 | 161 |
| 171 --mode=live-nav (default) Generated docs do not include baked HTML | 162 --mode=live-nav (default) Generated docs do not include baked HTML |
| 172 navigation. Instead, a single `nav.json` file is | 163 navigation. Instead, a single `nav.json` file is |
| 173 created and the appropriate navigation is generated | 164 created and the appropriate navigation is generated |
| 174 client-side by parsing that and building HTML. | 165 client-side by parsing that and building HTML. |
| 175 This dramatically reduces the generated size of | 166 This dramatically reduces the generated size of |
| 176 the HTML since a large fraction of each static page | 167 the HTML since a large fraction of each static page |
| 177 is just redundant navigation links. | 168 is just redundant navigation links. |
| 178 In this mode, the browser will do a XHR for | 169 In this mode, the browser will do a XHR for |
| 179 nav.json which means that to preview docs locally, | 170 nav.json which means that to preview docs locally, |
| 180 you will need to enable requesting file:// links in | 171 you will need to enable requesting file:// links in |
| 181 your browser or run a little local server like | 172 your browser or run a little local server like |
| 182 `python -m SimpleHTTPServer`. | 173 `python -m SimpleHTTPServer`. |
| 183 | 174 |
| 184 --generate-app-cache Generates the App Cache manifest file, enabling | 175 --generate-app-cache Generates the App Cache manifest file, enabling |
| 185 offline doc viewing. | 176 offline doc viewing. |
| 186 | 177 |
| 187 --out=<dir> Generates files into directory <dir>. If omitted | 178 --out=<dir> Generates files into directory <dir>. If omitted |
| 188 the files are generated into ./docs/ | 179 the files are generated into ./docs/ |
| 189 | 180 |
| 190 --verbose Print verbose information during generation. | 181 --verbose Print verbose information during generation. |
| 191 '''); | 182 '''); |
| 192 } | 183 } |
| 193 | 184 |
| 194 /** | 185 /** |
| 195 * Gets the full path to the directory containing the entrypoint of the current | 186 * Gets the full path to the directory containing the entrypoint of the current |
| 196 * script. In other words, if you invoked dartdoc, directly, it will be the | 187 * script. In other words, if you invoked dartdoc, directly, it will be the |
| 197 * path to the directory containing `dartdoc.dart`. If you're running a script | 188 * path to the directory containing `dartdoc.dart`. If you're running a script |
| 198 * that imports dartdoc, it will be the path to that script. | 189 * that imports dartdoc, it will be the path to that script. |
| 199 */ | 190 */ |
| 200 String get scriptDir() { | 191 Path get scriptDir() { |
|
Bill Hesse
2012/07/17 13:42:13
Lazy intialized statics work perfectly here, I thi
Johnni Winther
2012/07/19 08:37:07
This is not currently supported since current impl
Bill Hesse
2012/07/19 13:36:19
Can we write a TODO to fix this once they are supp
| |
| 201 return dirname(new File(new Options().script).fullPathSync()); | 192 return new Path.fromNative(new Options().script).directoryPath; |
| 193 } | |
| 194 | |
| 195 Path get libPath() { | |
| 196 // TODO(rnystrom): Note that the following lines get munged by create-sdk to | |
| 197 // work with the SDK's different file layout. If you change, be sure to test | |
| 198 // that dartdoc still works when run from the built SDK directory. | |
| 199 return scriptDir.append('../..'); | |
|
Bill Hesse
2012/07/17 13:42:13
Can't we have a const flag or enum, munged by the
Johnni Winther
2012/07/19 08:37:07
Changed to use a bool IN_SDK flag.
| |
| 202 } | 200 } |
| 203 | 201 |
| 204 /** | 202 /** |
| 205 * Deletes and recreates the output directory at [path] if it exists. | 203 * Deletes and recreates the output directory at [path] if it exists. |
| 206 */ | 204 */ |
| 207 void cleanOutputDirectory(String path) { | 205 void cleanOutputDirectory(Path path) { |
| 208 final outputDir = new Directory(path); | 206 final outputDir = new Directory.fromPath(path); |
| 209 if (outputDir.existsSync()) { | 207 if (outputDir.existsSync()) { |
| 210 outputDir.deleteRecursivelySync(); | 208 outputDir.deleteRecursivelySync(); |
| 211 } | 209 } |
| 212 | 210 |
| 213 try { | 211 try { |
| 214 // TODO(johnniwinther): Hack to avoid 'file already exists' exception thrown | 212 // TODO(johnniwinther): Hack to avoid 'file already exists' exception thrown |
| 215 // due to invalid result from dir.existsSync() (probably due to race | 213 // due to invalid result from dir.existsSync() (probably due to race |
| 216 // conditions). | 214 // conditions). |
|
Bill Hesse
2012/07/17 13:42:13
There is a bug for this - http://code.google.com/p
Johnni Winther
2012/07/19 08:37:07
Done.
| |
| 217 outputDir.createSync(); | 215 outputDir.createSync(); |
| 218 } catch (DirectoryIOException e) { | 216 } catch (DirectoryIOException e) { |
| 219 // Ignore. | 217 // Ignore. |
| 220 } | 218 } |
| 221 } | 219 } |
| 222 | 220 |
| 223 /** | 221 /** |
| 224 * Copies all of the files in the directory [from] to [to]. Does *not* | 222 * Copies all of the files in the directory [from] to [to]. Does *not* |
| 225 * recursively copy subdirectories. | 223 * recursively copy subdirectories. |
| 226 * | 224 * |
| 227 * Note: runs asynchronously, so you won't see any files copied until after the | 225 * Note: runs asynchronously, so you won't see any files copied until after the |
| 228 * event loop has had a chance to pump (i.e. after `main()` has returned). | 226 * event loop has had a chance to pump (i.e. after `main()` has returned). |
| 229 */ | 227 */ |
| 230 Future copyFiles(String from, String to) { | 228 Future copyFiles(Path from, Path to) { |
|
Bill Hesse
2012/07/17 13:42:13
copyDirectory?
Johnni Winther
2012/07/19 08:37:07
Done.
| |
| 231 final completer = new Completer(); | 229 final completer = new Completer(); |
| 232 final fromDir = new Directory(from); | 230 final fromDir = new Directory.fromPath(from); |
| 233 final lister = fromDir.list(recursive: false); | 231 final lister = fromDir.list(recursive: false); |
| 234 | 232 |
| 235 lister.onFile = (path) { | 233 lister.onFile = (String path) { |
| 236 final name = basename(path); | 234 final name = new Path.fromNative(path).filename; |
| 237 // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store. | 235 // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store. |
| 238 if (name.startsWith('.')) return; | 236 if (name.startsWith('.')) return; |
| 239 | 237 |
| 240 new File(path).readAsBytes().then((bytes) { | 238 new File(path).readAsBytes().then((bytes) { |
|
Bill Hesse
2012/07/17 13:42:13
Copying should work better with
File from;
File to
Johnni Winther
2012/07/19 08:37:07
Done.
| |
| 241 final outFile = new File('$to/$name'); | 239 final outFile = new File.fromPath(to.append(name)); |
| 242 final stream = outFile.openOutputStream(FileMode.WRITE); | 240 final stream = outFile.openOutputStream(FileMode.WRITE); |
| 243 stream.write(bytes, copyBuffer: false); | 241 stream.write(bytes, copyBuffer: false); |
| 244 stream.close(); | 242 stream.close(); |
| 245 }); | 243 }); |
| 246 }; | 244 }; |
| 247 lister.onDone = (done) => completer.complete(true); | 245 lister.onDone = (done) => completer.complete(true); |
| 248 return completer.future; | 246 return completer.future; |
| 249 } | 247 } |
| 250 | 248 |
| 251 /** | 249 /** |
| 252 * Compiles the given Dart script to a JavaScript file at [jsPath] using the | 250 * Compiles the given Dart script to a JavaScript file at [jsPath] using the |
| 253 * Dart2js compiler. | 251 * Dart2js compiler. |
| 254 */ | 252 */ |
| 255 void compileScript(String dartPath, String jsPath) { | 253 Future<bool> compileScript(Path dartPath, Path jsPath) { |
| 256 dart2js.compile([ | 254 var completer = new Completer<bool>(); |
| 257 '--no-colors', | 255 var compilation = new Compilation(dartPath, libPath); |
| 258 // TODO(johnniwinther): The following lines get munged by create-sdk to | 256 Future<String> result = compilation.compileToJavaScript(); |
| 259 // work with the SDK's different file layout. If you change, be sure to | 257 result.then((jsCode) { |
| 260 // test that dartdoc still works when run from the built SDK directory. | 258 writeString(new File.fromPath(jsPath), jsCode); |
| 261 '--library-root=${joinPaths(scriptDir, '../../')}', | 259 completer.complete(true); |
| 262 '--out=$jsPath', | 260 }); |
| 263 '--throw-on-error', | 261 result.handleException((e) => completer.completeException(e)); |
| 264 '--suppress-warnings', | 262 return completer.future; |
| 265 dartPath]); | |
| 266 } | 263 } |
| 267 | 264 |
| 268 class Dartdoc { | 265 class Dartdoc { |
| 269 | 266 |
| 270 /** Set to `false` to not include the source code in the generated docs. */ | 267 /** Set to `false` to not include the source code in the generated docs. */ |
| 271 bool includeSource = true; | 268 bool includeSource = true; |
| 272 | 269 |
| 273 /** | 270 /** |
| 274 * Dartdoc can generate docs in a few different ways based on how dynamic you | 271 * Dartdoc can generate docs in a few different ways based on how dynamic you |
| 275 * want the client-side behavior to be. The value for this should be one of | 272 * want the client-side behavior to be. The value for this should be one of |
| 276 * the `MODE_` constants. | 273 * the `MODE_` constants. |
| 277 */ | 274 */ |
| 278 int mode = MODE_LIVE_NAV; | 275 int mode = MODE_LIVE_NAV; |
| 279 | 276 |
| 280 /** | 277 /** |
| 281 * Generates the App Cache manifest file, enabling offline doc viewing. | 278 * Generates the App Cache manifest file, enabling offline doc viewing. |
| 282 */ | 279 */ |
| 283 bool generateAppCache = false; | 280 bool generateAppCache = false; |
| 284 | 281 |
| 285 /** Path to generate HTML files into. */ | 282 /** Path to generate HTML files into. */ |
| 286 String outputDir = 'docs'; | 283 Path outputDir = const Path('docs'); |
| 287 | 284 |
| 288 /** | 285 /** |
| 289 * The title used for the overall generated output. Set this to change it. | 286 * The title used for the overall generated output. Set this to change it. |
| 290 */ | 287 */ |
| 291 String mainTitle = 'Dart Documentation'; | 288 String mainTitle = 'Dart Documentation'; |
| 292 | 289 |
| 293 /** | 290 /** |
| 294 * The URL that the Dart logo links to. Defaults "index.html", the main | 291 * The URL that the Dart logo links to. Defaults "index.html", the main |
| 295 * page for the generated docs, but can be anything. | 292 * page for the generated docs, but can be anything. |
| 296 */ | 293 */ |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 330 /** The library that we're currently generating docs for. */ | 327 /** The library that we're currently generating docs for. */ |
| 331 LibraryMirror _currentLibrary; | 328 LibraryMirror _currentLibrary; |
| 332 | 329 |
| 333 /** The type that we're currently generating docs for. */ | 330 /** The type that we're currently generating docs for. */ |
| 334 InterfaceMirror _currentType; | 331 InterfaceMirror _currentType; |
| 335 | 332 |
| 336 /** The member that we're currently generating docs for. */ | 333 /** The member that we're currently generating docs for. */ |
| 337 MemberMirror _currentMember; | 334 MemberMirror _currentMember; |
| 338 | 335 |
| 339 /** The path to the file currently being written to, relative to [outdir]. */ | 336 /** The path to the file currently being written to, relative to [outdir]. */ |
| 340 String _filePath; | 337 String _filePath; |
|
Bill Hesse
2012/07/17 13:42:13
Make this a Path object?
Johnni Winther
2012/07/19 08:37:07
Done.
| |
| 341 | 338 |
| 342 /** The file currently being written to. */ | 339 /** The file currently being written to. */ |
| 343 StringBuffer _file; | 340 StringBuffer _file; |
| 344 | 341 |
| 345 int _totalLibraries = 0; | 342 int _totalLibraries = 0; |
| 346 int _totalTypes = 0; | 343 int _totalTypes = 0; |
| 347 int _totalMembers = 0; | 344 int _totalMembers = 0; |
| 348 | 345 |
| 349 Dartdoc() | 346 Dartdoc() |
| 350 : _comments = new CommentMap() { | 347 : _comments = new CommentMap() { |
| 351 // Patch in support for [:...:]-style code to the markdown parser. | 348 // Patch in support for [:...:]-style code to the markdown parser. |
| 352 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? | 349 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? |
| 353 md.InlineParser.syntaxes.insertRange(0, 1, | 350 md.InlineParser.syntaxes.insertRange(0, 1, |
| 354 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); | 351 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); |
| 355 | 352 |
| 356 md.setImplicitLinkResolver((name) => resolveNameReference(name, | 353 md.setImplicitLinkResolver((name) => resolveNameReference(name, |
| 357 library: _currentLibrary, type: _currentType, | 354 currentLibrary: _currentLibrary, currentType: _currentType, |
| 358 member: _currentMember)); | 355 currentMember: _currentMember)); |
| 359 } | 356 } |
| 360 | 357 |
| 361 bool includeLibrary(LibraryMirror library) { | 358 bool includeLibrary(LibraryMirror library) { |
| 362 if (libraries != null) { | 359 if (libraries != null) { |
| 363 return libraries.indexOf(library.simpleName()) != -1; | 360 return libraries.indexOf(library.simpleName()) != -1; |
| 364 } | 361 } |
| 365 return true; | 362 return true; |
| 366 } | 363 } |
| 367 | 364 |
| 368 String get footerContent(){ | 365 String get footerContent(){ |
| 369 var footerItems = []; | 366 var footerItems = []; |
| 370 if(!omitGenerationTime) { | 367 if(!omitGenerationTime) { |
| 371 footerItems.add("This page was generated at ${new Date.now()}"); | 368 footerItems.add("This page was generated at ${new Date.now()}"); |
| 372 } | 369 } |
| 373 if(footerText != null) { | 370 if(footerText != null) { |
| 374 footerItems.add(footerText); | 371 footerItems.add(footerText); |
| 375 } | 372 } |
| 376 var content = ''; | 373 var content = ''; |
| 377 for (int i = 0; i < footerItems.length; i++) { | 374 for (int i = 0; i < footerItems.length; i++) { |
| 378 if(i > 0){ | 375 if(i > 0){ |
| 379 content = content.concat('\n'); | 376 content = content.concat('\n'); |
| 380 } | 377 } |
| 381 content = content.concat('<div>${footerItems[i]}</div>'); | 378 content = content.concat('<div>${footerItems[i]}</div>'); |
| 382 } | 379 } |
| 383 return content; | 380 return content; |
| 384 } | 381 } |
| 385 | 382 |
| 386 void documentEntryPoint(String entrypoint, String libPath) { | 383 void documentEntryPoint(Path entrypoint, Path libPath) { |
| 387 final compilation = new Compilation(entrypoint, libPath); | 384 final compilation = new Compilation(entrypoint, libPath); |
| 388 _document(compilation); | 385 _document(compilation); |
| 389 } | 386 } |
| 390 | 387 |
| 391 void documentLibraries(List<String> libraries, String libPath) { | 388 void documentLibraries(List<Path> libraryList, Path libPath) { |
| 392 final compilation = new Compilation.library(libraries, libPath); | 389 final compilation = new Compilation.library(libraryList, libPath); |
| 393 _document(compilation); | 390 _document(compilation); |
| 394 } | 391 } |
| 395 | 392 |
| 396 void _document(Compilation compilation) { | 393 void _document(Compilation compilation) { |
| 397 // Sort the libraries by name (not key). | 394 // Sort the libraries by name (not key). |
| 398 _sortedLibraries = new List<LibraryMirror>.from( | 395 _sortedLibraries = new List<LibraryMirror>.from( |
| 399 compilation.mirrors().libraries().getValues().filter(includeLibrary)); | 396 compilation.mirrors().libraries().getValues().filter(includeLibrary)); |
| 400 _sortedLibraries.sort((x, y) { | 397 _sortedLibraries.sort((x, y) { |
| 401 return x.simpleName().toUpperCase().compareTo( | 398 return x.simpleName().toUpperCase().compareTo( |
| 402 y.simpleName().toUpperCase()); | 399 y.simpleName().toUpperCase()); |
| 403 }); | 400 }); |
| 404 | 401 |
| 405 // Generate the docs. | 402 // Generate the docs. |
| 406 if (mode == MODE_LIVE_NAV) docNavigationJson(); | 403 if (mode == MODE_LIVE_NAV) docNavigationJson(); |
| 407 | 404 |
| 408 docIndex(); | 405 docIndex(); |
| 409 for (final library in _sortedLibraries) { | 406 for (final library in _sortedLibraries) { |
| 410 docLibrary(library); | 407 docLibrary(library); |
| 411 } | 408 } |
| 412 | 409 |
| 413 if (generateAppCache) { | 410 if (generateAppCache) { |
| 414 generateAppCacheManifest(); | 411 generateAppCacheManifest(); |
| 415 } | 412 } |
| 416 } | 413 } |
| 417 | 414 |
| 418 void startFile(String path) { | 415 void startFile(String path) { |
| 419 _filePath = path; | 416 _filePath = path; |
|
Bill Hesse
2012/07/17 13:42:13
I see that it is convenient to have startFile take
Johnni Winther
2012/07/19 08:37:07
Done.
| |
| 420 _file = new StringBuffer(); | 417 _file = new StringBuffer(); |
| 421 } | 418 } |
| 422 | 419 |
| 423 void endFile() { | 420 void endFile() { |
| 424 final outPath = '$outputDir/$_filePath'; | 421 final outPath = outputDir.join(new Path.fromNative(_filePath)); |
| 425 final dir = new Directory(dirname(outPath)); | 422 final dir = new Directory.fromPath(outPath.directoryPath); |
| 426 if (!dir.existsSync()) { | 423 if (!dir.existsSync()) { |
| 427 // TODO(johnniwinther): Hack to avoid 'file already exists' exception | 424 // TODO(johnniwinther): Hack to avoid 'file already exists' exception |
|
Bill Hesse
2012/07/17 13:42:13
TODO(3914): There is a bug for this issue: http://
| |
| 428 // thrown due to invalid result from dir.existsSync() (probably due to | 425 // thrown due to invalid result from dir.existsSync() (probably due to |
| 429 // race conditions). | 426 // race conditions). |
| 430 try { | 427 try { |
| 431 dir.createSync(); | 428 dir.createSync(); |
| 432 } catch (DirectoryIOException e) { | 429 } catch (DirectoryIOException e) { |
| 433 // Ignore. | 430 // Ignore. |
| 434 } | 431 } |
| 435 } | 432 } |
| 436 | 433 |
| 437 writeString(new File(outPath), _file.toString()); | 434 writeString(new File.fromPath(outPath), _file.toString()); |
| 438 _filePath = null; | 435 _filePath = null; |
| 439 _file = null; | 436 _file = null; |
| 440 } | 437 } |
| 441 | 438 |
| 442 void write(String s) { | 439 void write(String s) { |
| 443 _file.add(s); | 440 _file.add(s); |
| 444 } | 441 } |
| 445 | 442 |
| 446 void writeln(String s) { | 443 void writeln(String s) { |
| 447 write(s); | 444 write(s); |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 588 docLibraryNavigationJson(library, libraryMap); | 585 docLibraryNavigationJson(library, libraryMap); |
| 589 } | 586 } |
| 590 | 587 |
| 591 writeln(JSON.stringify(libraryMap)); | 588 writeln(JSON.stringify(libraryMap)); |
| 592 endFile(); | 589 endFile(); |
| 593 } | 590 } |
| 594 | 591 |
| 595 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) { | 592 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) { |
| 596 final types = []; | 593 final types = []; |
| 597 | 594 |
| 598 for (final type in orderByName(library.types().getValues())) { | 595 for (InterfaceMirror type in orderByName(library.types().getValues())) { |
| 599 if (type.isPrivate) continue; | 596 if (type.isPrivate) continue; |
| 600 | 597 |
| 601 final kind = type.isClass ? 'class' : 'interface'; | 598 final kind = type.isClass ? 'class' : 'interface'; |
| 602 final url = typeUrl(type); | 599 final url = typeUrl(type); |
| 603 types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); | 600 types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); |
| 604 } | 601 } |
| 605 | 602 |
| 606 libraryMap[library.simpleName()] = types; | 603 libraryMap[library.simpleName()] = types; |
| 607 } | 604 } |
| 608 | 605 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 630 | 627 |
| 631 writeln('</div>'); | 628 writeln('</div>'); |
| 632 } | 629 } |
| 633 | 630 |
| 634 /** Writes the navigation for the types contained by the given library. */ | 631 /** Writes the navigation for the types contained by the given library. */ |
| 635 void docLibraryNavigation(LibraryMirror library) { | 632 void docLibraryNavigation(LibraryMirror library) { |
| 636 // Show the exception types separately. | 633 // Show the exception types separately. |
| 637 final types = <InterfaceMirror>[]; | 634 final types = <InterfaceMirror>[]; |
| 638 final exceptions = <InterfaceMirror>[]; | 635 final exceptions = <InterfaceMirror>[]; |
| 639 | 636 |
| 640 for (final type in orderByName(library.types().getValues())) { | 637 for (InterfaceMirror type in orderByName(library.types().getValues())) { |
| 641 if (type.isPrivate) continue; | 638 if (type.isPrivate) continue; |
| 642 | 639 |
| 643 if (isException(type)) { | 640 if (isException(type)) { |
| 644 exceptions.add(type); | 641 exceptions.add(type); |
| 645 } else { | 642 } else { |
| 646 types.add(type); | 643 types.add(type); |
| 647 } | 644 } |
| 648 } | 645 } |
| 649 | 646 |
| 650 if ((types.length == 0) && (exceptions.length == 0)) return; | 647 if ((types.length == 0) && (exceptions.length == 0)) return; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 695 } | 692 } |
| 696 | 693 |
| 697 // Document the top-level members. | 694 // Document the top-level members. |
| 698 docMembers(library); | 695 docMembers(library); |
| 699 | 696 |
| 700 // Document the types. | 697 // Document the types. |
| 701 final classes = <InterfaceMirror>[]; | 698 final classes = <InterfaceMirror>[]; |
| 702 final interfaces = <InterfaceMirror>[]; | 699 final interfaces = <InterfaceMirror>[]; |
| 703 final exceptions = <InterfaceMirror>[]; | 700 final exceptions = <InterfaceMirror>[]; |
| 704 | 701 |
| 705 for (final type in orderByName(library.types().getValues())) { | 702 for (InterfaceMirror type in orderByName(library.types().getValues())) { |
| 706 if (type.isPrivate) continue; | 703 if (type.isPrivate) continue; |
| 707 | 704 |
| 708 if (isException(type)) { | 705 if (isException(type)) { |
| 709 exceptions.add(type); | 706 exceptions.add(type); |
| 710 } else if (type.isClass) { | 707 } else if (type.isClass) { |
| 711 classes.add(type); | 708 classes.add(type); |
| 712 } else { | 709 } else { |
| 713 interfaces.add(type); | 710 interfaces.add(type); |
| 714 } | 711 } |
| 715 } | 712 } |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 945 } | 942 } |
| 946 } | 943 } |
| 947 | 944 |
| 948 void docMembers(ObjectMirror host) { | 945 void docMembers(ObjectMirror host) { |
| 949 // Collect the different kinds of members. | 946 // Collect the different kinds of members. |
| 950 final staticMethods = []; | 947 final staticMethods = []; |
| 951 final staticFields = []; | 948 final staticFields = []; |
| 952 final instanceMethods = []; | 949 final instanceMethods = []; |
| 953 final instanceFields = []; | 950 final instanceFields = []; |
| 954 | 951 |
| 955 for (final member in orderByName(host.declaredMembers().getValues())) { | 952 for (MemberMirror member in orderByName(host.declaredMembers().getValues())) { |
| 956 if (member.isPrivate) continue; | 953 if (member.isPrivate) continue; |
| 957 | 954 |
| 958 final methods = member.isStatic ? staticMethods : instanceMethods; | 955 final methods = member.isStatic ? staticMethods : instanceMethods; |
| 959 final fields = member.isStatic ? staticFields : instanceFields; | 956 final fields = member.isStatic ? staticFields : instanceFields; |
| 960 | 957 |
| 961 if (member.isMethod) { | 958 if (member.isMethod) { |
| 962 methods.add(member); | 959 methods.add(member); |
| 963 } else if (member.isField) { | 960 } else if (member.isField) { |
| 964 fields.add(member); | 961 fields.add(member); |
| 965 } | 962 } |
| (...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1371 // Syntax highlight. | 1368 // Syntax highlight. |
| 1372 return classifySource(code); | 1369 return classifySource(code); |
| 1373 } | 1370 } |
| 1374 | 1371 |
| 1375 /** | 1372 /** |
| 1376 * This will be called whenever a doc comment hits a `[name]` in square | 1373 * This will be called whenever a doc comment hits a `[name]` in square |
| 1377 * brackets. It will try to figure out what the name refers to and link or | 1374 * brackets. It will try to figure out what the name refers to and link or |
| 1378 * style it appropriately. | 1375 * style it appropriately. |
| 1379 */ | 1376 */ |
| 1380 md.Node resolveNameReference(String name, | 1377 md.Node resolveNameReference(String name, |
| 1381 [MemberMirror member = null, | 1378 [MemberMirror currentMember = null, |
| 1382 ObjectMirror type = null, | 1379 ObjectMirror currentType = null, |
| 1383 LibraryMirror library = null]) { | 1380 LibraryMirror currentLibrary = null]) { |
| 1384 makeLink(String href) { | 1381 makeLink(String href) { |
| 1385 final anchor = new md.Element.text('a', name); | 1382 final anchor = new md.Element.text('a', name); |
| 1386 anchor.attributes['href'] = relativePath(href); | 1383 anchor.attributes['href'] = relativePath(href); |
| 1387 anchor.attributes['class'] = 'crossref'; | 1384 anchor.attributes['class'] = 'crossref'; |
| 1388 return anchor; | 1385 return anchor; |
| 1389 } | 1386 } |
| 1390 | 1387 |
| 1391 // See if it's a parameter of the current method. | 1388 // See if it's a parameter of the current method. |
| 1392 if (member is MethodMirror) { | 1389 if (currentMember is MethodMirror) { |
| 1393 for (final parameter in member.parameters()) { | 1390 for (final parameter in currentMember.parameters()) { |
| 1394 if (parameter.simpleName() == name) { | 1391 if (parameter.simpleName() == name) { |
| 1395 final element = new md.Element.text('span', name); | 1392 final element = new md.Element.text('span', name); |
| 1396 element.attributes['class'] = 'param'; | 1393 element.attributes['class'] = 'param'; |
| 1397 return element; | 1394 return element; |
| 1398 } | 1395 } |
| 1399 } | 1396 } |
| 1400 } | 1397 } |
| 1401 | 1398 |
| 1402 // See if it's another member of the current type. | 1399 // See if it's another member of the current type. |
| 1403 if (type != null) { | 1400 if (currentType != null) { |
| 1404 final member = findMirror(type.declaredMembers(), name); | 1401 final foundMember = findMirror(currentType.declaredMembers(), name); |
| 1405 if (member != null) { | 1402 if (foundMember != null) { |
| 1406 return makeLink(memberUrl(member)); | 1403 return makeLink(memberUrl(foundMember)); |
| 1407 } | 1404 } |
| 1408 } | 1405 } |
| 1409 | 1406 |
| 1410 // See if it's another type or a member of another type in the current | 1407 // See if it's another type or a member of another type in the current |
| 1411 // library. | 1408 // library. |
| 1412 if (library != null) { | 1409 if (currentLibrary != null) { |
| 1413 // See if it's a constructor | 1410 // See if it's a constructor |
| 1414 final constructorLink = (() { | 1411 final constructorLink = (() { |
| 1415 final match = | 1412 final match = |
| 1416 new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); | 1413 new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); |
| 1417 if (match == null) return; | 1414 if (match == null) return; |
| 1418 final type = findMirror(library.types(), match[1]); | 1415 InterfaceMirror foundtype = findMirror(currentLibrary.types(), match[1]) ; |
| 1419 if (type == null) return; | 1416 if (foundtype == null) return; |
| 1420 final constructor = | 1417 final constructor = |
| 1421 findMirror(type.constructors(), | 1418 findMirror(foundtype.constructors(), |
| 1422 match[2] == null ? '' : match[2]); | 1419 match[2] == null ? '' : match[2]); |
| 1423 if (constructor == null) return; | 1420 if (constructor == null) return; |
| 1424 return makeLink(memberUrl(constructor)); | 1421 return makeLink(memberUrl(constructor)); |
| 1425 })(); | 1422 })(); |
| 1426 if (constructorLink != null) return constructorLink; | 1423 if (constructorLink != null) return constructorLink; |
| 1427 | 1424 |
| 1428 // See if it's a member of another type | 1425 // See if it's a member of another type |
| 1429 final foreignMemberLink = (() { | 1426 final foreignMemberLink = (() { |
| 1430 final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name); | 1427 final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name); |
| 1431 if (match == null) return; | 1428 if (match == null) return; |
| 1432 final type = findMirror(library.types(), match[1]); | 1429 InterfaceMirror foundtype = findMirror(currentLibrary.types(), match[1]) ; |
| 1433 if (type == null) return; | 1430 if (foundtype == null) return; |
| 1434 final member = findMirror(type.declaredMembers(), match[2]); | 1431 MemberMirror foundMember = findMirror(foundtype.declaredMembers(), match [2]); |
| 1435 if (member == null) return; | 1432 if (foundMember == null) return; |
| 1436 return makeLink(memberUrl(member)); | 1433 return makeLink(memberUrl(foundMember)); |
| 1437 })(); | 1434 })(); |
| 1438 if (foreignMemberLink != null) return foreignMemberLink; | 1435 if (foreignMemberLink != null) return foreignMemberLink; |
| 1439 | 1436 |
| 1440 final type = findMirror(library.types(), name); | 1437 InterfaceMirror foundType = findMirror(currentLibrary.types(), name); |
| 1441 if (type != null) { | 1438 if (foundType != null) { |
| 1442 return makeLink(typeUrl(type)); | 1439 return makeLink(typeUrl(foundType)); |
| 1443 } | 1440 } |
| 1444 | 1441 |
| 1445 // See if it's a top-level member in the current library. | 1442 // See if it's a top-level member in the current library. |
| 1446 final member = findMirror(library.declaredMembers(), name); | 1443 MemberMirror foundMember = findMirror(currentLibrary.declaredMembers(), na me); |
| 1447 if (member != null) { | 1444 if (foundMember != null) { |
| 1448 return makeLink(memberUrl(member)); | 1445 return makeLink(memberUrl(foundMember)); |
| 1449 } | 1446 } |
| 1450 } | 1447 } |
| 1451 | 1448 |
| 1452 // TODO(rnystrom): Should also consider: | 1449 // TODO(rnystrom): Should also consider: |
| 1453 // * Names imported by libraries this library imports. | 1450 // * Names imported by libraries this library imports. |
| 1454 // * Type parameters of the enclosing type. | 1451 // * Type parameters of the enclosing type. |
| 1455 | 1452 |
| 1456 return new md.Element.text('code', name); | 1453 return new md.Element.text('code', name); |
| 1457 } | 1454 } |
| 1458 | 1455 |
| 1459 generateAppCacheManifest() { | 1456 generateAppCacheManifest() { |
| 1460 print('Generating app cache manifest from output $outputDir'); | 1457 print('Generating app cache manifest from output $outputDir'); |
| 1461 startFile('appcache.manifest'); | 1458 startFile('appcache.manifest'); |
| 1462 write("CACHE MANIFEST\n\n"); | 1459 write("CACHE MANIFEST\n\n"); |
| 1463 write("# VERSION: ${new Date.now()}\n\n"); | 1460 write("# VERSION: ${new Date.now()}\n\n"); |
| 1464 write("NETWORK:\n*\n\n"); | 1461 write("NETWORK:\n*\n\n"); |
| 1465 write("CACHE:\n"); | 1462 write("CACHE:\n"); |
| 1466 var toCache = new Directory(outputDir); | 1463 var toCache = new Directory.fromPath(outputDir); |
| 1467 var pathPrefix = new File(outputDir).fullPathSync(); | 1464 var pathPrefix = new File.fromPath(outputDir).fullPathSync(); |
| 1468 var pathPrefixLength = pathPrefix.length; | 1465 var pathPrefixLength = pathPrefix.length; |
| 1469 toCache.onFile = (filename) { | 1466 var toCacheLister = toCache.list(recursive: true); |
| 1467 toCacheLister.onFile = (filename) { | |
| 1470 if (filename.endsWith('appcache.manifest')) { | 1468 if (filename.endsWith('appcache.manifest')) { |
| 1471 return; | 1469 return; |
| 1472 } | 1470 } |
|
Bill Hesse
2012/07/17 13:42:13
var relativePath =
new Path.fromNative(filename).r
Johnni Winther
2012/07/19 08:37:07
Changed, but a lot of new issues arose. Check the
| |
| 1473 var relativePath = filename.substring(pathPrefixLength + 1); | 1471 var relativePath = filename.substring(pathPrefixLength + 1); |
| 1474 write("$relativePath\n"); | 1472 write("$relativePath\n"); |
| 1475 }; | 1473 }; |
| 1476 toCache.onDone = (done) => endFile(); | 1474 toCacheLister.onDone = (done) => endFile(); |
| 1477 toCache.list(recursive: true); | |
| 1478 } | 1475 } |
| 1479 | 1476 |
| 1480 /** | 1477 /** |
| 1481 * Returns [:true:] if [type] should be regarded as an exception. | 1478 * Returns [:true:] if [type] should be regarded as an exception. |
| 1482 */ | 1479 */ |
| 1483 bool isException(TypeMirror type) { | 1480 bool isException(TypeMirror type) { |
| 1484 return type.simpleName().endsWith('Exception'); | 1481 return type.simpleName().endsWith('Exception'); |
| 1485 } | 1482 } |
| 1486 } | 1483 } |
| 1487 | 1484 |
| OLD | NEW |