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

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

Issue 10780030: Dartdoc and apidoc updated to use Path. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Updated cf. comments Created 8 years, 5 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
« no previous file with comments | « no previous file | lib/dartdoc/file_util.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 *
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
33 // TODO(johnniwinther): Note that the following lines get munged by create-sdk
34 // to work with the SDK's different file layout. If you change, be sure to test
Bill Hesse 2012/07/19 13:36:19 following line. Or "IN_SDK gets initialized to tr
Johnni Winther 2012/07/19 14:12:25 Done.
35 // that dartdoc still works when run from the built SDK directory.
36 final bool IN_SDK = false;
37
35 /** 38 /**
36 * Generates completely static HTML containing everything you need to browse 39 * Generates completely static HTML containing everything you need to browse
37 * the docs. The only client side behavior is trivial stuff like syntax 40 * the docs. The only client side behavior is trivial stuff like syntax
38 * highlighting code. 41 * highlighting code.
39 */ 42 */
40 final MODE_STATIC = 0; 43 final MODE_STATIC = 0;
41 44
42 /** 45 /**
43 * Generated docs do not include baked HTML navigation. Instead, a single 46 * Generated docs do not include baked HTML navigation. Instead, a single
44 * `nav.json` file is created and the appropriate navigation is generated 47 * `nav.json` file is created and the appropriate navigation is generated
(...skipping 10 matching lines...) Expand all
55 58
56 /** 59 /**
57 * Run this from the `lib/dartdoc` directory. 60 * Run this from the `lib/dartdoc` directory.
58 */ 61 */
59 void main() { 62 void main() {
60 final args = new Options().arguments; 63 final args = new Options().arguments;
61 64
62 // Parse the dartdoc options. 65 // Parse the dartdoc options.
63 bool includeSource; 66 bool includeSource;
64 int mode; 67 int mode;
65 String outputDir; 68 Path outputDir;
66 bool generateAppCache; 69 bool generateAppCache;
67 bool omitGenerationTime; 70 bool omitGenerationTime;
68 bool verbose; 71 bool verbose;
69 72
70 if (args.isEmpty()) { 73 if (args.isEmpty()) {
71 print('No arguments provided.'); 74 print('No arguments provided.');
72 printUsage(); 75 printUsage();
73 return; 76 return;
74 } 77 }
75 78
(...skipping 20 matching lines...) Expand all
96 99
97 case '--omit-generation-time': 100 case '--omit-generation-time':
98 omitGenerationTime = true; 101 omitGenerationTime = true;
99 break; 102 break;
100 case '--verbose': 103 case '--verbose':
101 verbose = true; 104 verbose = true;
102 break; 105 break;
103 106
104 default: 107 default:
105 if (arg.startsWith('--out=')) { 108 if (arg.startsWith('--out=')) {
106 outputDir = arg.substring('--out='.length); 109 outputDir = new Path.fromNative(arg.substring('--out='.length));
107 } else { 110 } else {
108 print('Unknown option: $arg'); 111 print('Unknown option: $arg');
109 printUsage(); 112 printUsage();
110 return; 113 return;
111 } 114 }
112 break; 115 break;
113 } 116 }
114 } 117 }
115 118
116 if (args.length == 0) { 119 final entrypoint = new Path.fromNative(args[args.length - 1]);
117 print('Provide at least one dart file to process.');
118 return;
119 }
120
121 // TODO(rnystrom): Note that the following lines get munged by create-sdk to
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 = 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 = copyDirectory(scriptDir.append('static'),
143 dartdoc.outputDir);
152 144
153 Futures.wait([filesCopied]).then((_) { 145 Futures.wait([compiled, filesCopied]).then((_) {
154 print('Documented ${dartdoc._totalLibraries} libraries, ' 146 print('Documented ${dartdoc._totalLibraries} libraries, '
155 '${dartdoc._totalTypes} types, and ' 147 '${dartdoc._totalTypes} types, and '
156 '${dartdoc._totalMembers} members.'); 148 '${dartdoc._totalMembers} members.');
157 }); 149 });
158 } 150 }
159 151
160 void printUsage() { 152 void printUsage() {
161 print(''' 153 print('''
162 Usage dartdoc [options] <entrypoint> 154 Usage dartdoc [options] <entrypoint>
163 [options] include 155 [options] include
164 --no-code Do not include source code in the documentation. 156 --no-code Do not include source code in the documentation.
165 157
166 --mode=static Generates completely static HTML containing 158 --mode=static Generates completely static HTML containing
167 everything you need to browse the docs. The only 159 everything you need to browse the docs. The only
168 client side behavior is trivial stuff like syntax 160 client side behavior is trivial stuff like syntax
169 highlighting code. 161 highlighting code.
170 162
171 --mode=live-nav (default) Generated docs do not include baked HTML 163 --mode=live-nav (default) Generated docs do not include baked HTML
172 navigation. Instead, a single `nav.json` file is 164 navigation. Instead, a single `nav.json` file is
173 created and the appropriate navigation is generated 165 created and the appropriate navigation is generated
174 client-side by parsing that and building HTML. 166 client-side by parsing that and building HTML.
175 This dramatically reduces the generated size of 167 This dramatically reduces the generated size of
176 the HTML since a large fraction of each static page 168 the HTML since a large fraction of each static page
177 is just redundant navigation links. 169 is just redundant navigation links.
178 In this mode, the browser will do a XHR for 170 In this mode, the browser will do a XHR for
179 nav.json which means that to preview docs locally, 171 nav.json which means that to preview docs locally,
180 you will need to enable requesting file:// links in 172 you will need to enable requesting file:// links in
181 your browser or run a little local server like 173 your browser or run a little local server like
182 `python -m SimpleHTTPServer`. 174 `python -m SimpleHTTPServer`.
183 175
184 --generate-app-cache Generates the App Cache manifest file, enabling 176 --generate-app-cache Generates the App Cache manifest file, enabling
185 offline doc viewing. 177 offline doc viewing.
186 178
187 --out=<dir> Generates files into directory <dir>. If omitted 179 --out=<dir> Generates files into directory <dir>. If omitted
188 the files are generated into ./docs/ 180 the files are generated into ./docs/
189 181
190 --verbose Print verbose information during generation. 182 --verbose Print verbose information during generation.
191 '''); 183 ''');
192 } 184 }
193 185
194 /** 186 /**
195 * Gets the full path to the directory containing the entrypoint of the current 187 * 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 188 * 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 189 * 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. 190 * that imports dartdoc, it will be the path to that script.
199 */ 191 */
200 String get scriptDir() { 192 // TODO(johnniwinther): Convert to final (lazily initialized) variables when
201 return dirname(new File(new Options().script).fullPathSync()); 193 // the feature is supported.
202 } 194 Path get scriptDir() =>
195 new Path.fromNative(new Options().script).directoryPath;
196
197 // TODO(johnniwinther): Trailing slashes matter due to the use of [libPath] as
198 // a base URI with [Uri.resolve].
199 Path get libPath() => IN_SDK
200 ? scriptDir.append('../dart2js/')
201 : scriptDir.append('../../');
203 202
204 /** 203 /**
205 * Deletes and recreates the output directory at [path] if it exists. 204 * Deletes and recreates the output directory at [path] if it exists.
206 */ 205 */
207 void cleanOutputDirectory(String path) { 206 void cleanOutputDirectory(Path path) {
208 final outputDir = new Directory(path); 207 final outputDir = new Directory.fromPath(path);
209 if (outputDir.existsSync()) { 208 if (outputDir.existsSync()) {
210 outputDir.deleteRecursivelySync(); 209 outputDir.deleteRecursivelySync();
211 } 210 }
212 211
213 try { 212 try {
214 // TODO(johnniwinther): Hack to avoid 'file already exists' exception thrown 213 // TODO(3914): Hack to avoid 'file already exists' exception thrown
215 // due to invalid result from dir.existsSync() (probably due to race 214 // due to invalid result from dir.existsSync() (probably due to race
216 // conditions). 215 // conditions).
217 outputDir.createSync(); 216 outputDir.createSync();
218 } catch (DirectoryIOException e) { 217 } catch (DirectoryIOException e) {
219 // Ignore. 218 // Ignore.
220 } 219 }
221 } 220 }
222 221
223 /** 222 /**
224 * Copies all of the files in the directory [from] to [to]. Does *not* 223 * Copies all of the files in the directory [from] to [to]. Does *not*
225 * recursively copy subdirectories. 224 * recursively copy subdirectories.
226 * 225 *
227 * Note: runs asynchronously, so you won't see any files copied until after the 226 * 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). 227 * event loop has had a chance to pump (i.e. after `main()` has returned).
229 */ 228 */
230 Future copyFiles(String from, String to) { 229 Future copyDirectory(Path from, Path to) {
231 final completer = new Completer(); 230 final completer = new Completer();
232 final fromDir = new Directory(from); 231 final fromDir = new Directory.fromPath(from);
233 final lister = fromDir.list(recursive: false); 232 final lister = fromDir.list(recursive: false);
234 233
235 lister.onFile = (path) { 234 lister.onFile = (String path) {
236 final name = basename(path); 235 final name = new Path.fromNative(path).filename;
237 // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store. 236 // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store.
238 if (name.startsWith('.')) return; 237 if (name.startsWith('.')) return;
239 238
240 new File(path).readAsBytes().then((bytes) { 239 File fromFile = new File(path);
241 final outFile = new File('$to/$name'); 240 File toFile = new File.fromPath(to.append(name));
242 final stream = outFile.openOutputStream(FileMode.WRITE); 241 fromFile.openInputStream().pipe(toFile.openOutputStream());
243 stream.write(bytes, copyBuffer: false);
244 stream.close();
245 });
246 }; 242 };
247 lister.onDone = (done) => completer.complete(true); 243 lister.onDone = (done) => completer.complete(true);
248 return completer.future; 244 return completer.future;
249 } 245 }
250 246
251 /** 247 /**
252 * Compiles the given Dart script to a JavaScript file at [jsPath] using the 248 * Compiles the given Dart script to a JavaScript file at [jsPath] using the
253 * Dart2js compiler. 249 * Dart2js compiler.
254 */ 250 */
255 void compileScript(String dartPath, String jsPath) { 251 Future<bool> compileScript(Path dartPath, Path jsPath) {
256 dart2js.compile([ 252 var completer = new Completer<bool>();
257 '--no-colors', 253 var compilation = new Compilation(dartPath, libPath);
258 // TODO(johnniwinther): The following lines get munged by create-sdk to 254 Future<String> result = compilation.compileToJavaScript();
259 // work with the SDK's different file layout. If you change, be sure to 255 result.then((jsCode) {
260 // test that dartdoc still works when run from the built SDK directory. 256 writeString(new File.fromPath(jsPath), jsCode);
261 '--library-root=${joinPaths(scriptDir, '../../')}', 257 completer.complete(true);
262 '--out=$jsPath', 258 });
263 '--throw-on-error', 259 result.handleException((e) => completer.completeException(e));
264 '--suppress-warnings', 260 return completer.future;
265 dartPath]);
266 } 261 }
267 262
268 class Dartdoc { 263 class Dartdoc {
269 264
270 /** Set to `false` to not include the source code in the generated docs. */ 265 /** Set to `false` to not include the source code in the generated docs. */
271 bool includeSource = true; 266 bool includeSource = true;
272 267
273 /** 268 /**
274 * Dartdoc can generate docs in a few different ways based on how dynamic you 269 * 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 270 * want the client-side behavior to be. The value for this should be one of
276 * the `MODE_` constants. 271 * the `MODE_` constants.
277 */ 272 */
278 int mode = MODE_LIVE_NAV; 273 int mode = MODE_LIVE_NAV;
279 274
280 /** 275 /**
281 * Generates the App Cache manifest file, enabling offline doc viewing. 276 * Generates the App Cache manifest file, enabling offline doc viewing.
282 */ 277 */
283 bool generateAppCache = false; 278 bool generateAppCache = false;
284 279
285 /** Path to generate HTML files into. */ 280 /** Path to generate HTML files into. */
286 String outputDir = 'docs'; 281 Path outputDir = const Path('docs');
287 282
288 /** 283 /**
289 * The title used for the overall generated output. Set this to change it. 284 * The title used for the overall generated output. Set this to change it.
290 */ 285 */
291 String mainTitle = 'Dart Documentation'; 286 String mainTitle = 'Dart Documentation';
292 287
293 /** 288 /**
294 * The URL that the Dart logo links to. Defaults "index.html", the main 289 * The URL that the Dart logo links to. Defaults "index.html", the main
295 * page for the generated docs, but can be anything. 290 * page for the generated docs, but can be anything.
296 */ 291 */
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 /** The library that we're currently generating docs for. */ 325 /** The library that we're currently generating docs for. */
331 LibraryMirror _currentLibrary; 326 LibraryMirror _currentLibrary;
332 327
333 /** The type that we're currently generating docs for. */ 328 /** The type that we're currently generating docs for. */
334 InterfaceMirror _currentType; 329 InterfaceMirror _currentType;
335 330
336 /** The member that we're currently generating docs for. */ 331 /** The member that we're currently generating docs for. */
337 MemberMirror _currentMember; 332 MemberMirror _currentMember;
338 333
339 /** The path to the file currently being written to, relative to [outdir]. */ 334 /** The path to the file currently being written to, relative to [outdir]. */
340 String _filePath; 335 Path _filePath;
341 336
342 /** The file currently being written to. */ 337 /** The file currently being written to. */
343 StringBuffer _file; 338 StringBuffer _file;
344 339
345 int _totalLibraries = 0; 340 int _totalLibraries = 0;
346 int _totalTypes = 0; 341 int _totalTypes = 0;
347 int _totalMembers = 0; 342 int _totalMembers = 0;
348 343
349 Dartdoc() 344 Dartdoc()
350 : _comments = new CommentMap() { 345 : _comments = new CommentMap() {
351 // Patch in support for [:...:]-style code to the markdown parser. 346 // Patch in support for [:...:]-style code to the markdown parser.
352 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? 347 // TODO(rnystrom): Markdown already has syntax for this. Phase this out?
353 md.InlineParser.syntaxes.insertRange(0, 1, 348 md.InlineParser.syntaxes.insertRange(0, 1,
354 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); 349 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]'));
355 350
356 md.setImplicitLinkResolver((name) => resolveNameReference(name, 351 md.setImplicitLinkResolver((name) => resolveNameReference(name,
357 library: _currentLibrary, type: _currentType, 352 currentLibrary: _currentLibrary, currentType: _currentType,
358 member: _currentMember)); 353 currentMember: _currentMember));
359 } 354 }
360 355
361 bool includeLibrary(LibraryMirror library) { 356 bool includeLibrary(LibraryMirror library) {
362 if (libraries != null) { 357 if (libraries != null) {
363 return libraries.indexOf(library.simpleName()) != -1; 358 return libraries.indexOf(library.simpleName()) != -1;
364 } 359 }
365 return true; 360 return true;
366 } 361 }
367 362
368 String get footerContent(){ 363 String get footerContent(){
369 var footerItems = []; 364 var footerItems = [];
370 if(!omitGenerationTime) { 365 if(!omitGenerationTime) {
371 footerItems.add("This page was generated at ${new Date.now()}"); 366 footerItems.add("This page was generated at ${new Date.now()}");
372 } 367 }
373 if(footerText != null) { 368 if(footerText != null) {
374 footerItems.add(footerText); 369 footerItems.add(footerText);
375 } 370 }
376 var content = ''; 371 var content = '';
377 for (int i = 0; i < footerItems.length; i++) { 372 for (int i = 0; i < footerItems.length; i++) {
378 if(i > 0){ 373 if(i > 0){
379 content = content.concat('\n'); 374 content = content.concat('\n');
380 } 375 }
381 content = content.concat('<div>${footerItems[i]}</div>'); 376 content = content.concat('<div>${footerItems[i]}</div>');
382 } 377 }
383 return content; 378 return content;
384 } 379 }
385 380
386 void documentEntryPoint(String entrypoint, String libPath) { 381 void documentEntryPoint(Path entrypoint, Path libPath) {
387 final compilation = new Compilation(entrypoint, libPath); 382 final compilation = new Compilation(entrypoint, libPath);
388 _document(compilation); 383 _document(compilation);
389 } 384 }
390 385
391 void documentLibraries(List<String> libraries, String libPath) { 386 void documentLibraries(List<Path> libraryList, Path libPath) {
392 final compilation = new Compilation.library(libraries, libPath); 387 final compilation = new Compilation.library(libraryList, libPath);
393 _document(compilation); 388 _document(compilation);
394 } 389 }
395 390
396 void _document(Compilation compilation) { 391 void _document(Compilation compilation) {
397 // Sort the libraries by name (not key). 392 // Sort the libraries by name (not key).
398 _sortedLibraries = new List<LibraryMirror>.from( 393 _sortedLibraries = new List<LibraryMirror>.from(
399 compilation.mirrors().libraries().getValues().filter(includeLibrary)); 394 compilation.mirrors().libraries().getValues().filter(includeLibrary));
400 _sortedLibraries.sort((x, y) { 395 _sortedLibraries.sort((x, y) {
401 return x.simpleName().toUpperCase().compareTo( 396 return x.simpleName().toUpperCase().compareTo(
402 y.simpleName().toUpperCase()); 397 y.simpleName().toUpperCase());
403 }); 398 });
404 399
405 // Generate the docs. 400 // Generate the docs.
406 if (mode == MODE_LIVE_NAV) docNavigationJson(); 401 if (mode == MODE_LIVE_NAV) docNavigationJson();
407 402
408 docIndex(); 403 docIndex();
409 for (final library in _sortedLibraries) { 404 for (final library in _sortedLibraries) {
410 docLibrary(library); 405 docLibrary(library);
411 } 406 }
412 407
413 if (generateAppCache) { 408 if (generateAppCache) {
414 generateAppCacheManifest(); 409 generateAppCacheManifest();
415 } 410 }
416 } 411 }
417 412
418 void startFile(String path) { 413 void startFile(String path) {
419 _filePath = path; 414 _filePath = new Path(path);
420 _file = new StringBuffer(); 415 _file = new StringBuffer();
421 } 416 }
422 417
423 void endFile() { 418 void endFile() {
424 final outPath = '$outputDir/$_filePath'; 419 final outPath = outputDir.join(_filePath);
425 final dir = new Directory(dirname(outPath)); 420 final dir = new Directory.fromPath(outPath.directoryPath);
426 if (!dir.existsSync()) { 421 if (!dir.existsSync()) {
427 // TODO(johnniwinther): Hack to avoid 'file already exists' exception 422 // TODO(3914): Hack to avoid 'file already exists' exception
428 // thrown due to invalid result from dir.existsSync() (probably due to 423 // thrown due to invalid result from dir.existsSync() (probably due to
429 // race conditions). 424 // race conditions).
430 try { 425 try {
431 dir.createSync(); 426 dir.createSync();
432 } catch (DirectoryIOException e) { 427 } catch (DirectoryIOException e) {
433 // Ignore. 428 // Ignore.
434 } 429 }
435 } 430 }
436 431
437 writeString(new File(outPath), _file.toString()); 432 writeString(new File.fromPath(outPath), _file.toString());
438 _filePath = null; 433 _filePath = null;
439 _file = null; 434 _file = null;
440 } 435 }
441 436
442 void write(String s) { 437 void write(String s) {
443 _file.add(s); 438 _file.add(s);
444 } 439 }
445 440
446 void writeln(String s) { 441 void writeln(String s) {
447 write(s); 442 write(s);
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 docLibraryNavigationJson(library, libraryMap); 583 docLibraryNavigationJson(library, libraryMap);
589 } 584 }
590 585
591 writeln(JSON.stringify(libraryMap)); 586 writeln(JSON.stringify(libraryMap));
592 endFile(); 587 endFile();
593 } 588 }
594 589
595 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) { 590 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) {
596 final types = []; 591 final types = [];
597 592
598 for (final type in orderByName(library.types().getValues())) { 593 for (InterfaceMirror type in orderByName(library.types().getValues())) {
599 if (type.isPrivate) continue; 594 if (type.isPrivate) continue;
600 595
601 final kind = type.isClass ? 'class' : 'interface'; 596 final kind = type.isClass ? 'class' : 'interface';
602 final url = typeUrl(type); 597 final url = typeUrl(type);
603 types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); 598 types.add({ 'name': typeName(type), 'kind': kind, 'url': url });
604 } 599 }
605 600
606 libraryMap[library.simpleName()] = types; 601 libraryMap[library.simpleName()] = types;
607 } 602 }
608 603
(...skipping 21 matching lines...) Expand all
630 625
631 writeln('</div>'); 626 writeln('</div>');
632 } 627 }
633 628
634 /** Writes the navigation for the types contained by the given library. */ 629 /** Writes the navigation for the types contained by the given library. */
635 void docLibraryNavigation(LibraryMirror library) { 630 void docLibraryNavigation(LibraryMirror library) {
636 // Show the exception types separately. 631 // Show the exception types separately.
637 final types = <InterfaceMirror>[]; 632 final types = <InterfaceMirror>[];
638 final exceptions = <InterfaceMirror>[]; 633 final exceptions = <InterfaceMirror>[];
639 634
640 for (final type in orderByName(library.types().getValues())) { 635 for (InterfaceMirror type in orderByName(library.types().getValues())) {
641 if (type.isPrivate) continue; 636 if (type.isPrivate) continue;
642 637
643 if (isException(type)) { 638 if (isException(type)) {
644 exceptions.add(type); 639 exceptions.add(type);
645 } else { 640 } else {
646 types.add(type); 641 types.add(type);
647 } 642 }
648 } 643 }
649 644
650 if ((types.length == 0) && (exceptions.length == 0)) return; 645 if ((types.length == 0) && (exceptions.length == 0)) return;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 } 690 }
696 691
697 // Document the top-level members. 692 // Document the top-level members.
698 docMembers(library); 693 docMembers(library);
699 694
700 // Document the types. 695 // Document the types.
701 final classes = <InterfaceMirror>[]; 696 final classes = <InterfaceMirror>[];
702 final interfaces = <InterfaceMirror>[]; 697 final interfaces = <InterfaceMirror>[];
703 final exceptions = <InterfaceMirror>[]; 698 final exceptions = <InterfaceMirror>[];
704 699
705 for (final type in orderByName(library.types().getValues())) { 700 for (InterfaceMirror type in orderByName(library.types().getValues())) {
706 if (type.isPrivate) continue; 701 if (type.isPrivate) continue;
707 702
708 if (isException(type)) { 703 if (isException(type)) {
709 exceptions.add(type); 704 exceptions.add(type);
710 } else if (type.isClass) { 705 } else if (type.isClass) {
711 classes.add(type); 706 classes.add(type);
712 } else { 707 } else {
713 interfaces.add(type); 708 interfaces.add(type);
714 } 709 }
715 } 710 }
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
945 } 940 }
946 } 941 }
947 942
948 void docMembers(ObjectMirror host) { 943 void docMembers(ObjectMirror host) {
949 // Collect the different kinds of members. 944 // Collect the different kinds of members.
950 final staticMethods = []; 945 final staticMethods = [];
951 final staticFields = []; 946 final staticFields = [];
952 final instanceMethods = []; 947 final instanceMethods = [];
953 final instanceFields = []; 948 final instanceFields = [];
954 949
955 for (final member in orderByName(host.declaredMembers().getValues())) { 950 for (MemberMirror member in orderByName(host.declaredMembers().getValues())) {
956 if (member.isPrivate) continue; 951 if (member.isPrivate) continue;
957 952
958 final methods = member.isStatic ? staticMethods : instanceMethods; 953 final methods = member.isStatic ? staticMethods : instanceMethods;
959 final fields = member.isStatic ? staticFields : instanceFields; 954 final fields = member.isStatic ? staticFields : instanceFields;
960 955
961 if (member.isMethod) { 956 if (member.isMethod) {
962 methods.add(member); 957 methods.add(member);
963 } else if (member.isField) { 958 } else if (member.isField) {
964 fields.add(member); 959 fields.add(member);
965 } 960 }
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
1169 /** 1164 /**
1170 * Converts [fullPath] which is understood to be a full path from the root of 1165 * Converts [fullPath] which is understood to be a full path from the root of
1171 * the generated docs to one relative to the current file. 1166 * the generated docs to one relative to the current file.
1172 */ 1167 */
1173 String relativePath(String fullPath) { 1168 String relativePath(String fullPath) {
1174 // Don't make it relative if it's an absolute path. 1169 // Don't make it relative if it's an absolute path.
1175 if (isAbsolute(fullPath)) return fullPath; 1170 if (isAbsolute(fullPath)) return fullPath;
1176 1171
1177 // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do 1172 // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do
1178 // this if the paths overlap. 1173 // this if the paths overlap.
1179 return '${repeat('../', countOccurrences(_filePath, '/'))}$fullPath'; 1174 return '${repeat('../',
1175 countOccurrences(_filePath.toString(), '/'))}$fullPath';
1180 } 1176 }
1181 1177
1182 /** Gets whether or not the given URL is absolute or relative. */ 1178 /** Gets whether or not the given URL is absolute or relative. */
1183 bool isAbsolute(String url) { 1179 bool isAbsolute(String url) {
1184 // TODO(rnystrom): Why don't we have a nice type in the platform for this? 1180 // TODO(rnystrom): Why don't we have a nice type in the platform for this?
1185 // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks 1181 // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks
1186 // a scheme to be relative. 1182 // a scheme to be relative.
1187 return const RegExp(@'^\w+:').hasMatch(url); 1183 return const RegExp(@'^\w+:').hasMatch(url);
1188 } 1184 }
1189 1185
1190 /** Gets the URL to the documentation for [library]. */ 1186 /** Gets the URL to the documentation for [library]. */
1191 String libraryUrl(LibraryMirror library) { 1187 String libraryUrl(LibraryMirror library) {
1192 return '${sanitize(library.simpleName())}.html'; 1188 return '${sanitize(library.simpleName())}.html';
1193 } 1189 }
1194 1190
1195 /** Gets the URL for the documentation for [type]. */ 1191 /** Gets the URL for the documentation for [type]. */
1196 String typeUrl(ObjectMirror type) { 1192 String typeUrl(ObjectMirror type) {
1197 if (type is LibraryMirror) return '${sanitize(type.simpleName())}.html'; 1193 if (type is LibraryMirror) {
1194 return '${sanitize(type.simpleName())}.html';
1195 }
1198 assert (type is TypeMirror); 1196 assert (type is TypeMirror);
1199 // Always get the generic type to strip off any type parameters or 1197 // Always get the generic type to strip off any type parameters or
1200 // arguments. If the type isn't generic, genericType returns `this`, so it 1198 // arguments. If the type isn't generic, genericType returns `this`, so it
1201 // works for non-generic types too. 1199 // works for non-generic types too.
1202 return '${sanitize(type.library().simpleName())}/' 1200 return '${sanitize(type.library().simpleName())}/'
1203 '${type.declaration.simpleName()}.html'; 1201 '${type.declaration.simpleName()}.html';
1204 } 1202 }
1205 1203
1206 /** Gets the URL for the documentation for [member]. */ 1204 /** Gets the URL for the documentation for [member]. */
1207 String memberUrl(MemberMirror member) { 1205 String memberUrl(MemberMirror member) {
1208 final url = typeUrl(member.surroundingDeclaration()); 1206 String url = typeUrl(member.surroundingDeclaration());
1209 if (!member.isConstructor) return '$url#${member.simpleName()}'; 1207 if (!member.isConstructor) {
1208 return '$url#${member.simpleName()}';
1209 }
1210 assert (member is MethodMirror); 1210 assert (member is MethodMirror);
1211 if (member.constructorName == '') return '$url#new:${member.simpleName()}'; 1211 if (member.constructorName == '') {
1212 return '$url#new:${member.simpleName()}';
1213 }
1212 return '$url#new:${member.simpleName()}.${member.constructorName}'; 1214 return '$url#new:${member.simpleName()}.${member.constructorName}';
1213 } 1215 }
1214 1216
1215 /** Gets the anchor id for the document for [member]. */ 1217 /** Gets the anchor id for the document for [member]. */
1216 String memberAnchor(MemberMirror member) { 1218 String memberAnchor(MemberMirror member) {
1217 return '${member.simpleName()}'; 1219 return '${member.simpleName()}';
1218 } 1220 }
1219 1221
1220 /** 1222 /**
1221 * Creates a hyperlink. Handles turning the [href] into an appropriate 1223 * Creates a hyperlink. Handles turning the [href] into an appropriate
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
1371 // Syntax highlight. 1373 // Syntax highlight.
1372 return classifySource(code); 1374 return classifySource(code);
1373 } 1375 }
1374 1376
1375 /** 1377 /**
1376 * This will be called whenever a doc comment hits a `[name]` in square 1378 * 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 1379 * brackets. It will try to figure out what the name refers to and link or
1378 * style it appropriately. 1380 * style it appropriately.
1379 */ 1381 */
1380 md.Node resolveNameReference(String name, 1382 md.Node resolveNameReference(String name,
1381 [MemberMirror member = null, 1383 [MemberMirror currentMember = null,
1382 ObjectMirror type = null, 1384 ObjectMirror currentType = null,
1383 LibraryMirror library = null]) { 1385 LibraryMirror currentLibrary = null]) {
1384 makeLink(String href) { 1386 makeLink(String href) {
1385 final anchor = new md.Element.text('a', name); 1387 final anchor = new md.Element.text('a', name);
1386 anchor.attributes['href'] = relativePath(href); 1388 anchor.attributes['href'] = relativePath(href);
1387 anchor.attributes['class'] = 'crossref'; 1389 anchor.attributes['class'] = 'crossref';
1388 return anchor; 1390 return anchor;
1389 } 1391 }
1390 1392
1391 // See if it's a parameter of the current method. 1393 // See if it's a parameter of the current method.
1392 if (member is MethodMirror) { 1394 if (currentMember is MethodMirror) {
1393 for (final parameter in member.parameters()) { 1395 for (final parameter in currentMember.parameters()) {
1394 if (parameter.simpleName() == name) { 1396 if (parameter.simpleName() == name) {
1395 final element = new md.Element.text('span', name); 1397 final element = new md.Element.text('span', name);
1396 element.attributes['class'] = 'param'; 1398 element.attributes['class'] = 'param';
1397 return element; 1399 return element;
1398 } 1400 }
1399 } 1401 }
1400 } 1402 }
1401 1403
1402 // See if it's another member of the current type. 1404 // See if it's another member of the current type.
1403 if (type != null) { 1405 if (currentType != null) {
1404 final member = findMirror(type.declaredMembers(), name); 1406 final foundMember = findMirror(currentType.declaredMembers(), name);
1405 if (member != null) { 1407 if (foundMember != null) {
1406 return makeLink(memberUrl(member)); 1408 return makeLink(memberUrl(foundMember));
1407 } 1409 }
1408 } 1410 }
1409 1411
1410 // See if it's another type or a member of another type in the current 1412 // See if it's another type or a member of another type in the current
1411 // library. 1413 // library.
1412 if (library != null) { 1414 if (currentLibrary != null) {
1413 // See if it's a constructor 1415 // See if it's a constructor
1414 final constructorLink = (() { 1416 final constructorLink = (() {
1415 final match = 1417 final match =
1416 new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); 1418 new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name);
1417 if (match == null) return; 1419 if (match == null) return;
1418 final type = findMirror(library.types(), match[1]); 1420 InterfaceMirror foundtype = findMirror(currentLibrary.types(), match[1]) ;
1419 if (type == null) return; 1421 if (foundtype == null) return;
1420 final constructor = 1422 final constructor =
1421 findMirror(type.constructors(), 1423 findMirror(foundtype.constructors(),
1422 match[2] == null ? '' : match[2]); 1424 match[2] == null ? '' : match[2]);
1423 if (constructor == null) return; 1425 if (constructor == null) return;
1424 return makeLink(memberUrl(constructor)); 1426 return makeLink(memberUrl(constructor));
1425 })(); 1427 })();
1426 if (constructorLink != null) return constructorLink; 1428 if (constructorLink != null) return constructorLink;
1427 1429
1428 // See if it's a member of another type 1430 // See if it's a member of another type
1429 final foreignMemberLink = (() { 1431 final foreignMemberLink = (() {
1430 final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name); 1432 final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name);
1431 if (match == null) return; 1433 if (match == null) return;
1432 final type = findMirror(library.types(), match[1]); 1434 InterfaceMirror foundtype = findMirror(currentLibrary.types(), match[1]) ;
1433 if (type == null) return; 1435 if (foundtype == null) return;
1434 final member = findMirror(type.declaredMembers(), match[2]); 1436 MemberMirror foundMember = findMirror(foundtype.declaredMembers(), match [2]);
1435 if (member == null) return; 1437 if (foundMember == null) return;
1436 return makeLink(memberUrl(member)); 1438 return makeLink(memberUrl(foundMember));
1437 })(); 1439 })();
1438 if (foreignMemberLink != null) return foreignMemberLink; 1440 if (foreignMemberLink != null) return foreignMemberLink;
1439 1441
1440 final type = findMirror(library.types(), name); 1442 InterfaceMirror foundType = findMirror(currentLibrary.types(), name);
1441 if (type != null) { 1443 if (foundType != null) {
1442 return makeLink(typeUrl(type)); 1444 return makeLink(typeUrl(foundType));
1443 } 1445 }
1444 1446
1445 // See if it's a top-level member in the current library. 1447 // See if it's a top-level member in the current library.
1446 final member = findMirror(library.declaredMembers(), name); 1448 MemberMirror foundMember = findMirror(currentLibrary.declaredMembers(), na me);
1447 if (member != null) { 1449 if (foundMember != null) {
1448 return makeLink(memberUrl(member)); 1450 return makeLink(memberUrl(foundMember));
1449 } 1451 }
1450 } 1452 }
1451 1453
1452 // TODO(rnystrom): Should also consider: 1454 // TODO(rnystrom): Should also consider:
1453 // * Names imported by libraries this library imports. 1455 // * Names imported by libraries this library imports.
1454 // * Type parameters of the enclosing type. 1456 // * Type parameters of the enclosing type.
1455 1457
1456 return new md.Element.text('code', name); 1458 return new md.Element.text('code', name);
1457 } 1459 }
1458 1460
1459 generateAppCacheManifest() { 1461 generateAppCacheManifest() {
1460 print('Generating app cache manifest from output $outputDir'); 1462 if (verbose) {
1463 print('Generating app cache manifest from output $outputDir');
1464 }
1461 startFile('appcache.manifest'); 1465 startFile('appcache.manifest');
1462 write("CACHE MANIFEST\n\n"); 1466 write("CACHE MANIFEST\n\n");
1463 write("# VERSION: ${new Date.now()}\n\n"); 1467 write("# VERSION: ${new Date.now()}\n\n");
1464 write("NETWORK:\n*\n\n"); 1468 write("NETWORK:\n*\n\n");
1465 write("CACHE:\n"); 1469 write("CACHE:\n");
1466 var toCache = new Directory(outputDir); 1470 var toCache = new Directory.fromPath(outputDir);
1467 var pathPrefix = new File(outputDir).fullPathSync(); 1471 var toCacheLister = toCache.list(recursive: true);
1468 var pathPrefixLength = pathPrefix.length; 1472 toCacheLister.onFile = (filename) {
1469 toCache.onFile = (filename) {
1470 if (filename.endsWith('appcache.manifest')) { 1473 if (filename.endsWith('appcache.manifest')) {
1471 return; 1474 return;
1472 } 1475 }
1473 var relativePath = filename.substring(pathPrefixLength + 1); 1476 // TODO(johnniwinther): If [outputDir] has trailing slashes, [filename]
1474 write("$relativePath\n"); 1477 // contains double (back)slashes for files in the immediate [toCache]
1478 // directory. These are not handled by [relativeTo] thus
1479 // wrongfully producing the path `/foo.html` for a file `foo.html` in
1480 // [toCache].
1481 //
1482 // This can be handled in two ways. 1) By ensuring that
1483 // [Directory.fromPath] does not receive a path with a trailing slash, or
1484 // better, by making [Directory.fromPath] handle such trailing slashes.
1485 // 2) By ensuring that [filePath] does not have double slashes before
1486 // calling [relativeTo], or better, by making [relativeTo] handle double
1487 // slashes correctly.
1488 Path filePath = new Path.fromNative(filename).canonicalize();
1489 Path relativeFilePath = filePath.relativeTo(outputDir);
1490 write("$relativeFilePath\n");
1475 }; 1491 };
1476 toCache.onDone = (done) => endFile(); 1492 toCacheLister.onDone = (done) => endFile();
1477 toCache.list(recursive: true);
1478 } 1493 }
1479 1494
1480 /** 1495 /**
1481 * Returns [:true:] if [type] should be regarded as an exception. 1496 * Returns [:true:] if [type] should be regarded as an exception.
1482 */ 1497 */
1483 bool isException(TypeMirror type) { 1498 bool isException(TypeMirror type) {
1484 return type.simpleName().endsWith('Exception'); 1499 return type.simpleName().endsWith('Exception');
1485 } 1500 }
1486 } 1501 }
1487 1502
OLDNEW
« no previous file with comments | « no previous file | lib/dartdoc/file_util.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698