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

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

Issue 10701091: Dartdoc and Apidoc updated to use dart2js through the mirror system. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fixed cf. rnystrom's 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
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:json'); 21 #import('dart:json');
21 #import('frog/lang.dart'); 22 #import('mirrors/mirrors.dart');
22 #import('frog/file_system.dart'); 23 #import('mirrors/mirrors_util.dart');
23 #import('frog/file_system_vm.dart'); 24 #import('mirrors/dart2js_mirror.dart', prefix: 'dart2js');
24 #import('classify.dart'); 25 #import('classify.dart');
25 #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',
29 prefix: 'dart2js');
30 #import('file_util.dart');
26 31
27 #source('comment_map.dart'); 32 #source('comment_map.dart');
28 #source('utils.dart'); 33 #source('utils.dart');
29 34
30 /** 35 /**
31 * Generates completely static HTML containing everything you need to browse 36 * Generates completely static HTML containing everything you need to browse
32 * the docs. The only client side behavior is trivial stuff like syntax 37 * the docs. The only client side behavior is trivial stuff like syntax
33 * highlighting code. 38 * highlighting code.
34 */ 39 */
35 final MODE_STATIC = 0; 40 final MODE_STATIC = 0;
(...skipping 17 matching lines...) Expand all
53 */ 58 */
54 void main() { 59 void main() {
55 final args = new Options().arguments; 60 final args = new Options().arguments;
56 61
57 // Parse the dartdoc options. 62 // Parse the dartdoc options.
58 bool includeSource; 63 bool includeSource;
59 int mode; 64 int mode;
60 String outputDir; 65 String outputDir;
61 bool generateAppCache; 66 bool generateAppCache;
62 bool omitGenerationTime; 67 bool omitGenerationTime;
68 bool verbose;
69
70 if (args.isEmpty()) {
71 print('No arguments provided.');
72 printUsage();
73 return;
74 }
63 75
64 for (int i = 0; i < args.length - 1; i++) { 76 for (int i = 0; i < args.length - 1; i++) {
65 final arg = args[i]; 77 final arg = args[i];
66 78
67 switch (arg) { 79 switch (arg) {
68 case '--no-code': 80 case '--no-code':
69 includeSource = false; 81 includeSource = false;
70 break; 82 break;
71 83
72 case '--mode=static': 84 case '--mode=static':
73 mode = MODE_STATIC; 85 mode = MODE_STATIC;
74 break; 86 break;
75 87
76 case '--mode=live-nav': 88 case '--mode=live-nav':
77 mode = MODE_LIVE_NAV; 89 mode = MODE_LIVE_NAV;
78 break; 90 break;
79 91
80 case '--generate-app-cache': 92 case '--generate-app-cache':
81 case '--generate-app-cache=true': 93 case '--generate-app-cache=true':
82 generateAppCache = true; 94 generateAppCache = true;
83 break; 95 break;
84 96
85 case '--omit-generation-time': 97 case '--omit-generation-time':
86 omitGenerationTime = true; 98 omitGenerationTime = true;
87 break; 99 break;
100 case '--verbose':
101 verbose = true;
102 break;
88 103
89 default: 104 default:
90 if (arg.startsWith('--out=')) { 105 if (arg.startsWith('--out=')) {
91 outputDir = arg.substring('--out='.length); 106 outputDir = arg.substring('--out='.length);
92 } else { 107 } else {
93 print('Unknown option: $arg'); 108 print('Unknown option: $arg');
109 printUsage();
94 return; 110 return;
95 } 111 }
96 break; 112 break;
97 } 113 }
98 } 114 }
99 115
100 if (args.length == 0) { 116 if (args.length == 0) {
101 print('Provide at least one dart file to process.'); 117 print('Provide at least one dart file to process.');
102 return; 118 return;
103 } 119 }
104 120
105 // The entrypoint of the library to generate docs for.
106 final entrypoint = args[args.length - 1];
107
108 final files = new VMFileSystem();
109
110 // TODO(rnystrom): Note that the following lines get munged by create-sdk to 121 // TODO(rnystrom): Note that the following lines get munged by create-sdk to
111 // work with the SDK's different file layout. If you change, be sure to test 122 // work with the SDK's different file layout. If you change, be sure to test
112 // that dartdoc still works when run from the built SDK directory. 123 // that dartdoc still works when run from the built SDK directory.
113 final frogPath = joinPaths(scriptDir, 'frog/'); 124 final String libPath = joinPaths(scriptDir, '../');
114 final libDir = joinPaths(scriptDir, '..');
115 final compilerPath
116 = Platform.operatingSystem == 'windows' ? 'dart2js.bat' : 'dart2js';
117 125
118 parseOptions(frogPath, ['', '', '--libdir=$libDir'], files); 126 // The entrypoint of the library to generate docs for.
119 initializeWorld(files); 127 // TODO(johnniwinther): Handle absolute/relative paths
128 final entrypoint = canonicalizePath(args[args.length - 1]);
120 129
121 final dartdoc = new Dartdoc(); 130 final dartdoc = new Dartdoc();
122 131
123 if (includeSource != null) dartdoc.includeSource = includeSource; 132 if (includeSource != null) dartdoc.includeSource = includeSource;
124 if (mode != null) dartdoc.mode = mode; 133 if (mode != null) dartdoc.mode = mode;
125 if (outputDir != null) dartdoc.outputDir = outputDir; 134 if (outputDir != null) dartdoc.outputDir = outputDir;
126 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache; 135 if (generateAppCache != null) dartdoc.generateAppCache = generateAppCache;
127 if (omitGenerationTime != null) { 136 if (omitGenerationTime != null) {
128 dartdoc.omitGenerationTime = omitGenerationTime; 137 dartdoc.omitGenerationTime = omitGenerationTime;
129 } 138 }
139 if (verbose != null) dartdoc.verbose = verbose;
130 140
131 cleanOutputDirectory(dartdoc.outputDir); 141 cleanOutputDirectory(dartdoc.outputDir);
132 142
143 dartdoc.documentEntryPoint(entrypoint, libPath);
144
133 // Compile the client-side code to JS. 145 // Compile the client-side code to JS.
134 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; 146 final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav';
135 final Future scriptCompiled = compileScript(compilerPath, 147 compileScript(
136 '$scriptDir/client-$clientScript.dart', 148 '$scriptDir/client-$clientScript.dart',
137 '${dartdoc.outputDir}/client-$clientScript.js'); 149 '${dartdoc.outputDir}/client-$clientScript.js');
138 150
139 final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir); 151 final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir);
140 152
141 Futures.wait([scriptCompiled, filesCopied]).then((_) { 153 Futures.wait([filesCopied]).then((_) {
142 dartdoc.document(entrypoint); 154 print('Documented ${dartdoc._totalLibraries} libraries, '
155 '${dartdoc._totalTypes} types, and '
156 '${dartdoc._totalMembers} members.');
157 });
158 }
143 159
144 print('Documented ${dartdoc._totalLibraries} libraries, ' 160 void printUsage() {
145 '${dartdoc._totalTypes} types, and ' 161 print('''
146 '${dartdoc._totalMembers} members.'); 162 Usage dartdoc [options] <entrypoint>
147 }); 163 [options] include
164 --no-code Do not include source code in the documentation.
165
166 --mode=static Generates completely static HTML containing
167 everything you need to browse the docs. The only
168 client side behavior is trivial stuff like syntax
169 highlighting code.
170
171 --mode=live-nav (default) Generated docs do not include baked HTML
172 navigation. Instead, a single `nav.json` file is
173 created and the appropriate navigation is generated
174 client-side by parsing that and building HTML.
175 This dramatically reduces the generated size of
176 the HTML since a large fraction of each static page
177 is just redundant navigation links.
178 In this mode, the browser will do a XHR for
179 nav.json which means that to preview docs locally,
180 you will need to enable requesting file:// links in
181 your browser or run a little local server like
182 `python -m SimpleHTTPServer`.
183
184 --generate-app-cache Generates the App Cache manifest file, enabling
185 offline doc viewing.
186
187 --out=<dir> Generates files into directory <dir>. If omitted
188 the files are generated into ./docs/
189
190 --verbose Print verbose information during generation.
191 ''');
148 } 192 }
149 193
150 /** 194 /**
151 * Gets the full path to the directory containing the entrypoint of the current 195 * Gets the full path to the directory containing the entrypoint of the current
152 * script. In other words, if you invoked dartdoc, directly, it will be the 196 * script. In other words, if you invoked dartdoc, directly, it will be the
153 * path to the directory containing `dartdoc.dart`. If you're running a script 197 * path to the directory containing `dartdoc.dart`. If you're running a script
154 * that imports dartdoc, it will be the path to that script. 198 * that imports dartdoc, it will be the path to that script.
155 */ 199 */
156 String get scriptDir() { 200 String get scriptDir() {
157 return dirname(new File(new Options().script).fullPathSync()); 201 return dirname(new File(new Options().script).fullPathSync());
158 } 202 }
159 203
160 /** 204 /**
161 * Deletes and recreates the output directory at [path] if it exists. 205 * Deletes and recreates the output directory at [path] if it exists.
162 */ 206 */
163 void cleanOutputDirectory(String path) { 207 void cleanOutputDirectory(String path) {
164 final outputDir = new Directory(path); 208 final outputDir = new Directory(path);
165 if (outputDir.existsSync()) { 209 if (outputDir.existsSync()) {
166 outputDir.deleteRecursivelySync(); 210 outputDir.deleteRecursivelySync();
167 } 211 }
168 212
169 outputDir.createSync(); 213 try {
214 // TODO(johnniwinther): Hack to avoid 'file already exists' exception thrown
215 // due to invalid result from dir.existsSync() (probably due to race
216 // conditions).
217 outputDir.createSync();
218 } catch (DirectoryIOException e) {
219 // Ignore.
220 }
170 } 221 }
171 222
172 /** 223 /**
173 * Copies all of the files in the directory [from] to [to]. Does *not* 224 * Copies all of the files in the directory [from] to [to]. Does *not*
174 * recursively copy subdirectories. 225 * recursively copy subdirectories.
175 * 226 *
176 * Note: runs asynchronously, so you won't see any files copied until after the 227 * Note: runs asynchronously, so you won't see any files copied until after the
177 * event loop has had a chance to pump (i.e. after `main()` has returned). 228 * event loop has had a chance to pump (i.e. after `main()` has returned).
178 */ 229 */
179 Future copyFiles(String from, String to) { 230 Future copyFiles(String from, String to) {
(...skipping 12 matching lines...) Expand all
192 stream.write(bytes, copyBuffer: false); 243 stream.write(bytes, copyBuffer: false);
193 stream.close(); 244 stream.close();
194 }); 245 });
195 }; 246 };
196 lister.onDone = (done) => completer.complete(true); 247 lister.onDone = (done) => completer.complete(true);
197 return completer.future; 248 return completer.future;
198 } 249 }
199 250
200 /** 251 /**
201 * Compiles the given Dart script to a JavaScript file at [jsPath] using the 252 * Compiles the given Dart script to a JavaScript file at [jsPath] using the
202 * Dart-to-JS compiler located at [compilerPath]. 253 * Dart2js compiler.
203 */ 254 */
204 Future compileScript(String compilerPath, String dartPath, String jsPath) { 255 void compileScript(String dartPath, String jsPath) {
205 final completer = new Completer(); 256 dart2js.compile([
206 onExit(ProcessResult result) { 257 '--no-colors',
207 if (result.exitCode != 0) { 258 // TODO(johnniwinther): The following lines get munged by create-sdk to
208 final message = 'Non-zero exit code from $compilerPath'; 259 // work with the SDK's different file layout. If you change, be sure to
209 print('$message.'); 260 // test that dartdoc still works when run from the built SDK directory.
210 print(result.stdout); 261 '--library-root=${joinPaths(scriptDir, '../../')}',
211 print(result.stderr); 262 '--out=$jsPath',
212 throw message; 263 '--throw-on-error',
213 } 264 '--suppress-warnings',
214 completer.complete(true); 265 dartPath]);
215 }
216
217 onError(error) {
218 final message = 'Error trying to execute $compilerPath. Error: $error';
219 print('$message.');
220 throw message;
221 }
222
223 print('Compiling $dartPath to $jsPath');
224 var processFuture = Process.run(compilerPath, ['--out=$jsPath', dartPath]);
225 processFuture.handleException(onError);
226 processFuture.then(onExit);
227
228 return completer.future;
229 } 266 }
230 267
231 class Dartdoc { 268 class Dartdoc {
232 269
233 /** Set to `false` to not include the source code in the generated docs. */ 270 /** Set to `false` to not include the source code in the generated docs. */
234 bool includeSource = true; 271 bool includeSource = true;
235 272
236 /** 273 /**
237 * Dartdoc can generate docs in a few different ways based on how dynamic you 274 * Dartdoc can generate docs in a few different ways based on how dynamic you
238 * want the client-side behavior to be. The value for this should be one of 275 * want the client-side behavior to be. The value for this should be one of
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 307
271 /** Set this to add footer text to each generated page. */ 308 /** Set this to add footer text to each generated page. */
272 String footerText = null; 309 String footerText = null;
273 310
274 /** Set this to add content before the footer */ 311 /** Set this to add content before the footer */
275 String preFooterText = ''; 312 String preFooterText = '';
276 313
277 /** Set this to omit generation timestamp from output */ 314 /** Set this to omit generation timestamp from output */
278 bool omitGenerationTime = false; 315 bool omitGenerationTime = false;
279 316
317 /** Set by Dartdoc user to print extra information during generation. */
318 bool verbose = false;
319
320 /** Set this to select the libraries to document */
321 List<String> libraries = null;
322
280 /** 323 /**
281 * From exposes the set of libraries in `world.libraries`. That maps library 324 * This list contains the libraries sorted in by the library name.
282 * *keys* to [Library] objects. The keys are *not* exactly the same as their
283 * names. This means if we order by key, we won't actually have them sorted
284 * correctly. This list contains the libraries in correct order by their
285 * *name*.
286 */ 325 */
287 List<Library> _sortedLibraries; 326 List<LibraryMirror> _sortedLibraries;
288 327
289 CommentMap _comments; 328 CommentMap _comments;
290 329
291 /** The library that we're currently generating docs for. */ 330 /** The library that we're currently generating docs for. */
292 Library _currentLibrary; 331 LibraryMirror _currentLibrary;
293 332
294 /** The type that we're currently generating docs for. */ 333 /** The type that we're currently generating docs for. */
295 Type _currentType; 334 InterfaceMirror _currentType;
296 335
297 /** The member that we're currently generating docs for. */ 336 /** The member that we're currently generating docs for. */
298 Member _currentMember; 337 MemberMirror _currentMember;
299 338
300 /** The path to the file currently being written to, relative to [outdir]. */ 339 /** The path to the file currently being written to, relative to [outdir]. */
301 String _filePath; 340 String _filePath;
302 341
303 /** The file currently being written to. */ 342 /** The file currently being written to. */
304 StringBuffer _file; 343 StringBuffer _file;
305 344
306 int _totalLibraries = 0; 345 int _totalLibraries = 0;
307 int _totalTypes = 0; 346 int _totalTypes = 0;
308 int _totalMembers = 0; 347 int _totalMembers = 0;
309 348
310 Dartdoc() 349 Dartdoc()
311 : _comments = new CommentMap() { 350 : _comments = new CommentMap() {
312 // Patch in support for [:...:]-style code to the markdown parser. 351 // Patch in support for [:...:]-style code to the markdown parser.
313 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? 352 // TODO(rnystrom): Markdown already has syntax for this. Phase this out?
314 md.InlineParser.syntaxes.insertRange(0, 1, 353 md.InlineParser.syntaxes.insertRange(0, 1,
315 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); 354 new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]'));
316 355
317 md.setImplicitLinkResolver((name) => resolveNameReference(name, 356 md.setImplicitLinkResolver((name) => resolveNameReference(name,
318 library: _currentLibrary, type: _currentType, 357 library: _currentLibrary, type: _currentType,
319 member: _currentMember)); 358 member: _currentMember));
320 } 359 }
321 360
361 bool includeLibrary(LibraryMirror library) {
362 if (libraries != null) {
363 return libraries.indexOf(library.simpleName()) != -1;
364 }
365 return true;
366 }
367
322 String get footerContent(){ 368 String get footerContent(){
323 var footerItems = []; 369 var footerItems = [];
324 if(!omitGenerationTime) { 370 if(!omitGenerationTime) {
325 footerItems.add("This page generated at ${new Date.now()}"); 371 footerItems.add("This page was generated at ${new Date.now()}");
326 } 372 }
327 if(footerText != null) { 373 if(footerText != null) {
328 footerItems.add(footerText); 374 footerItems.add(footerText);
329 } 375 }
330 var content = ''; 376 var content = '';
331 for (int i = 0; i < footerItems.length; i++) { 377 for (int i = 0; i < footerItems.length; i++) {
332 if(i > 0){ 378 if(i > 0){
333 content = content.concat('\n'); 379 content = content.concat('\n');
334 } 380 }
335 content = content.concat('<div>${footerItems[i]}</div>'); 381 content = content.concat('<div>${footerItems[i]}</div>');
336 } 382 }
337 return content; 383 return content;
338 } 384 }
339 385
340 void document([String entrypoint]) { 386 void documentEntryPoint(String entrypoint, String libPath) {
341 var oldDietParse = options.dietParse; 387 final compilation = new Compilation(entrypoint, libPath);
342 try { 388 _document(compilation);
343 options.dietParse = true; 389 }
344 390
345 // If we have an entrypoint, process it. Otherwise, just use whatever 391 void documentLibraries(List<String> libraries, String libPath) {
346 // libraries have been previously loaded by the calling code. 392 final compilation = new Compilation.library(libraries, libPath);
347 if (entrypoint != null) { 393 _document(compilation);
348 world.processDartScript(entrypoint); 394 }
349 }
350 395
351 world.resolveAll(); 396 void _document(Compilation compilation) {
397 // Sort the libraries by name (not key).
398 _sortedLibraries = new List<LibraryMirror>.from(
399 compilation.mirrors().libraries().getValues().filter(includeLibrary));
400 _sortedLibraries.sort((x, y) {
401 return x.simpleName().toUpperCase().compareTo(
402 y.simpleName().toUpperCase());
403 });
352 404
353 // Sort the libraries by name (not key). 405 // Generate the docs.
354 _sortedLibraries = world.libraries.getValues(); 406 if (mode == MODE_LIVE_NAV) docNavigationJson();
355 _sortedLibraries.sort((a, b) {
356 return a.name.toUpperCase().compareTo(b.name.toUpperCase());
357 });
358 407
359 // Generate the docs. 408 docIndex();
360 if (mode == MODE_LIVE_NAV) docNavigationJson(); 409 for (final library in _sortedLibraries) {
410 docLibrary(library);
411 }
361 412
362 docIndex(); 413 if (generateAppCache) {
363 for (final library in _sortedLibraries) { 414 generateAppCacheManifest();
364 docLibrary(library);
365 }
366
367 if (generateAppCache) {
368 generateAppCacheManifest();
369 }
370 } finally {
371 options.dietParse = oldDietParse;
372 } 415 }
373 } 416 }
374 417
375 void startFile(String path) { 418 void startFile(String path) {
376 _filePath = path; 419 _filePath = path;
377 _file = new StringBuffer(); 420 _file = new StringBuffer();
378 } 421 }
379 422
380 void endFile() { 423 void endFile() {
381 final outPath = '$outputDir/$_filePath'; 424 final outPath = '$outputDir/$_filePath';
382 final dir = new Directory(dirname(outPath)); 425 final dir = new Directory(dirname(outPath));
383 if (!dir.existsSync()) { 426 if (!dir.existsSync()) {
384 dir.createSync(); 427 // TODO(johnniwinther): Hack to avoid 'file already exists' exception
428 // thrown due to invalid result from dir.existsSync() (probably due to
429 // race conditions).
430 try {
431 dir.createSync();
432 } catch (DirectoryIOException e) {
433 // Ignore.
434 }
385 } 435 }
386 436
387 world.files.writeString(outPath, _file.toString()); 437 writeString(new File(outPath), _file.toString());
388 _filePath = null; 438 _filePath = null;
389 _file = null; 439 _file = null;
390 } 440 }
391 441
392 void write(String s) { 442 void write(String s) {
393 _file.add(s); 443 _file.add(s);
394 } 444 }
395 445
396 void writeln(String s) { 446 void writeln(String s) {
397 write(s); 447 write(s);
(...skipping 19 matching lines...) Expand all
417 ''' 467 '''
418 <!DOCTYPE html> 468 <!DOCTYPE html>
419 <html${htmlAttributes == '' ? '' : ' $htmlAttributes'}> 469 <html${htmlAttributes == '' ? '' : ' $htmlAttributes'}>
420 <head> 470 <head>
421 '''); 471 ''');
422 writeHeadContents(title); 472 writeHeadContents(title);
423 473
424 // Add data attributes describing what the page documents. 474 // Add data attributes describing what the page documents.
425 var data = ''; 475 var data = '';
426 if (_currentLibrary != null) { 476 if (_currentLibrary != null) {
427 data = '$data data-library="${md.escapeHtml(_currentLibrary.name)}"'; 477 data = '$data data-library='
478 '"${md.escapeHtml(_currentLibrary.simpleName())}"';
428 } 479 }
429 480
430 if (_currentType != null) { 481 if (_currentType != null) {
431 data = '$data data-type="${md.escapeHtml(typeName(_currentType))}"'; 482 data = '$data data-type="${md.escapeHtml(typeName(_currentType))}"';
432 } 483 }
433 484
434 write( 485 write(
435 ''' 486 '''
436 </head> 487 </head>
437 <body$data> 488 <body$data>
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 writeln('<h3>Libraries</h3>'); 564 writeln('<h3>Libraries</h3>');
514 565
515 for (final library in _sortedLibraries) { 566 for (final library in _sortedLibraries) {
516 docIndexLibrary(library); 567 docIndexLibrary(library);
517 } 568 }
518 569
519 writeFooter(); 570 writeFooter();
520 endFile(); 571 endFile();
521 } 572 }
522 573
523 void docIndexLibrary(Library library) { 574 void docIndexLibrary(LibraryMirror library) {
524 writeln('<h4>${a(libraryUrl(library), library.name)}</h4>'); 575 writeln('<h4>${a(libraryUrl(library), library.simpleName())}</h4>');
525 } 576 }
526 577
527 /** 578 /**
528 * Walks the libraries and creates a JSON object containing the data needed 579 * Walks the libraries and creates a JSON object containing the data needed
529 * to generate navigation for them. 580 * to generate navigation for them.
530 */ 581 */
531 void docNavigationJson() { 582 void docNavigationJson() {
532 startFile('nav.json'); 583 startFile('nav.json');
533 584
534 final libraries = {}; 585 final libraryMap = {};
535 586
536 for (final library in _sortedLibraries) { 587 for (final library in _sortedLibraries) {
537 docLibraryNavigationJson(library, libraries); 588 docLibraryNavigationJson(library, libraryMap);
538 } 589 }
539 590
540 writeln(JSON.stringify(libraries)); 591 writeln(JSON.stringify(libraryMap));
541 endFile(); 592 endFile();
542 } 593 }
543 594
544 void docLibraryNavigationJson(Library library, Map libraries) { 595 void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) {
545 final types = []; 596 final types = [];
546 597
547 for (final type in orderByName(library.types)) { 598 for (final type in orderByName(library.types().getValues())) {
548 if (type.isTop) continue; 599 if (type.isPrivate) continue;
549 if (type.name.startsWith('_')) continue;
550 600
551 final kind = type.isClass ? 'class' : 'interface'; 601 final kind = type.isClass ? 'class' : 'interface';
552 final url = typeUrl(type); 602 final url = typeUrl(type);
553 types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); 603 types.add({ 'name': typeName(type), 'kind': kind, 'url': url });
554 } 604 }
555 605
556 libraries[library.name] = types; 606 libraryMap[library.simpleName()] = types;
557 } 607 }
558 608
559 void docNavigation() { 609 void docNavigation() {
560 writeln( 610 writeln(
561 ''' 611 '''
562 <div class="nav"> 612 <div class="nav">
563 '''); 613 ''');
564 614
565 if (mode == MODE_STATIC) { 615 if (mode == MODE_STATIC) {
566 for (final library in _sortedLibraries) { 616 for (final library in _sortedLibraries) {
567 write('<h2><div class="icon-library"></div>'); 617 write('<h2><div class="icon-library"></div>');
568 618
569 if ((_currentLibrary == library) && (_currentType == null)) { 619 if ((_currentLibrary == library) && (_currentType == null)) {
570 write('<strong>${library.name}</strong>'); 620 write('<strong>${library.simpleName()}</strong>');
571 } else { 621 } else {
572 write('${a(libraryUrl(library), library.name)}'); 622 write('${a(libraryUrl(library), library.simpleName())}');
573 } 623 }
574 write('</h2>'); 624 write('</h2>');
575 625
576 // Only expand classes in navigation for current library. 626 // Only expand classes in navigation for current library.
577 if (_currentLibrary == library) docLibraryNavigation(library); 627 if (_currentLibrary == library) docLibraryNavigation(library);
578 } 628 }
579 } 629 }
580 630
581 writeln('</div>'); 631 writeln('</div>');
582 } 632 }
583 633
584 /** Writes the navigation for the types contained by the given library. */ 634 /** Writes the navigation for the types contained by the given library. */
585 void docLibraryNavigation(Library library) { 635 void docLibraryNavigation(LibraryMirror library) {
586 // Show the exception types separately. 636 // Show the exception types separately.
587 final types = <Type>[]; 637 final types = <InterfaceMirror>[];
588 final exceptions = <Type>[]; 638 final exceptions = <InterfaceMirror>[];
589 639
590 for (final type in orderByName(library.types)) { 640 for (final type in orderByName(library.types().getValues())) {
591 if (type.isTop) continue; 641 if (type.isPrivate) continue;
592 if (type.name.startsWith('_')) continue;
593 642
594 if (type.name.endsWith('Exception')) { 643 if (isException(type)) {
595 exceptions.add(type); 644 exceptions.add(type);
596 } else { 645 } else {
597 types.add(type); 646 types.add(type);
598 } 647 }
599 } 648 }
600 649
601 if ((types.length == 0) && (exceptions.length == 0)) return; 650 if ((types.length == 0) && (exceptions.length == 0)) return;
602 651
603 writeln('<ul class="icon">'); 652 writeln('<ul class="icon">');
604 types.forEach(docTypeNavigation); 653 types.forEach(docTypeNavigation);
605 exceptions.forEach(docTypeNavigation); 654 exceptions.forEach(docTypeNavigation);
606 writeln('</ul>'); 655 writeln('</ul>');
607 } 656 }
608 657
609 /** Writes a linked navigation list item for the given type. */ 658 /** Writes a linked navigation list item for the given type. */
610 void docTypeNavigation(Type type) { 659 void docTypeNavigation(InterfaceMirror type) {
611 var icon = 'interface'; 660 var icon = 'interface';
612 if (type.name.endsWith('Exception')) { 661 if (type.simpleName().endsWith('Exception')) {
613 icon = 'exception'; 662 icon = 'exception';
614 } else if (type.isClass) { 663 } else if (type.isClass) {
615 icon = 'class'; 664 icon = 'class';
616 } 665 }
617 666
618 write('<li>'); 667 write('<li>');
619 if (_currentType == type) { 668 if (_currentType == type) {
620 write( 669 write(
621 '<div class="icon-$icon"></div><strong>${typeName(type)}</strong>'); 670 '<div class="icon-$icon"></div><strong>${typeName(type)}</strong>');
622 } else { 671 } else {
623 write(a(typeUrl(type), 672 write(a(typeUrl(type),
624 '<div class="icon-$icon"></div>${typeName(type)}')); 673 '<div class="icon-$icon"></div>${typeName(type)}'));
625 } 674 }
626 writeln('</li>'); 675 writeln('</li>');
627 } 676 }
628 677
629 void docLibrary(Library library) { 678 void docLibrary(LibraryMirror library) {
679 if (verbose) {
680 print('Library \'${library.simpleName()}\':');
681 }
630 _totalLibraries++; 682 _totalLibraries++;
631 _currentLibrary = library; 683 _currentLibrary = library;
632 _currentType = null; 684 _currentType = null;
633 685
634 startFile(libraryUrl(library)); 686 startFile(libraryUrl(library));
635 writeHeader('${library.name} Library', 687 writeHeader('${library.simpleName()} Library',
636 [library.name, libraryUrl(library)]); 688 [library.simpleName(), libraryUrl(library)]);
637 writeln('<h2><strong>${library.name}</strong> library</h2>'); 689 writeln('<h2><strong>${library.simpleName()}</strong> library</h2>');
638 690
639 // Look for a comment for the entire library. 691 // Look for a comment for the entire library.
640 final comment = getLibraryComment(library); 692 final comment = getLibraryComment(library);
641 if (comment != null) { 693 if (comment != null) {
642 writeln('<div class="doc">$comment</div>'); 694 writeln('<div class="doc">$comment</div>');
643 } 695 }
644 696
645 // Document the top-level members. 697 // Document the top-level members.
646 docMembers(library.topType); 698 docMembers(library);
647 699
648 // Document the types. 700 // Document the types.
649 final classes = <Type>[]; 701 final classes = <InterfaceMirror>[];
650 final interfaces = <Type>[]; 702 final interfaces = <InterfaceMirror>[];
651 final exceptions = <Type>[]; 703 final exceptions = <InterfaceMirror>[];
652 704
653 for (final type in orderByName(library.types)) { 705 for (final type in orderByName(library.types().getValues())) {
654 if (type.isTop) continue; 706 if (type.isPrivate) continue;
655 if (type.name.startsWith('_')) continue;
656 707
657 if (type.name.endsWith('Exception')) { 708 if (isException(type)) {
658 exceptions.add(type); 709 exceptions.add(type);
659 } else if (type.isClass) { 710 } else if (type.isClass) {
660 classes.add(type); 711 classes.add(type);
661 } else { 712 } else {
662 interfaces.add(type); 713 interfaces.add(type);
663 } 714 }
664 } 715 }
665 716
666 docTypes(classes, 'Classes'); 717 docTypes(classes, 'Classes');
667 docTypes(interfaces, 'Interfaces'); 718 docTypes(interfaces, 'Interfaces');
668 docTypes(exceptions, 'Exceptions'); 719 docTypes(exceptions, 'Exceptions');
669 720
670 writeFooter(); 721 writeFooter();
671 endFile(); 722 endFile();
672 723
673 for (final type in library.types.getValues()) { 724 for (final type in library.types().getValues()) {
674 if (type.isTop) continue; 725 if (type.isPrivate) continue;
675 if (type.name.startsWith('_')) continue; 726
676 docType(type); 727 docType(type);
677 } 728 }
678 } 729 }
679 730
680 void docTypes(List<Type> types, String header) { 731 void docTypes(List<InterfaceMirror> types, String header) {
681 if (types.length == 0) return; 732 if (types.length == 0) return;
682 733
683 writeln('<h3>$header</h3>'); 734 writeln('<h3>$header</h3>');
684 735
685 for (final type in types) { 736 for (final type in types) {
686 writeln( 737 writeln(
687 ''' 738 '''
688 <div class="type"> 739 <div class="type">
689 <h4> 740 <h4>
690 ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} 741 ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")}
691 </h4> 742 </h4>
692 </div> 743 </div>
693 '''); 744 ''');
694 } 745 }
695 } 746 }
696 747
697 void docType(Type type) { 748 void docType(InterfaceMirror type) {
749 if (verbose) {
750 print('- ${type.simpleName()}');
751 }
698 _totalTypes++; 752 _totalTypes++;
699 _currentType = type; 753 _currentType = type;
700 754
701 startFile(typeUrl(type)); 755 startFile(typeUrl(type));
702 756
757 var kind = 'Interface';
758 if (type.isTypedef) {
759 kind = 'Typedef';
760 } else if (type.isClass) {
761 kind = 'Class';
762 }
763
703 final typeTitle = 764 final typeTitle =
704 '${typeName(type)} ${type.isClass ? "Class" : "Interface"}'; 765 '${typeName(type)} ${kind}';
705 writeHeader('$typeTitle / ${type.library.name} Library', 766 writeHeader('$typeTitle / ${type.library().simpleName()} Library',
706 [type.library.name, libraryUrl(type.library), 767 [type.library().simpleName(), libraryUrl(type.library()),
707 typeName(type), typeUrl(type)]); 768 typeName(type), typeUrl(type)]);
708 writeln( 769 writeln(
709 ''' 770 '''
710 <h2><strong>${typeName(type, showBounds: true)}</strong> 771 <h2><strong>${typeName(type, showBounds: true)}</strong>
711 ${type.isClass ? "Class" : "Interface"} 772 $kind
712 </h2> 773 </h2>
713 '''); 774 ''');
714 775
715 docCode(type.span, getTypeComment(type)); 776 docCode(type.location(), getTypeComment(type));
716 docInheritance(type); 777 docInheritance(type);
778 docTypedef(type);
717 docConstructors(type); 779 docConstructors(type);
718 docMembers(type); 780 docMembers(type);
719 781
720 writeTypeFooter(); 782 writeTypeFooter();
721 writeFooter(); 783 writeFooter();
722 endFile(); 784 endFile();
723 } 785 }
724 786
725 /** Override this to write additional content at the end of a type's page. */ 787 /** Override this to write additional content at the end of a type's page. */
726 void writeTypeFooter() { 788 void writeTypeFooter() {
727 // Do nothing. 789 // Do nothing.
728 } 790 }
729 791
730 /** 792 /**
731 * Writes an inline type span for the given type. This is a little box with 793 * Writes an inline type span for the given type. This is a little box with
732 * an icon and the type's name. It's similar to how types appear in the 794 * an icon and the type's name. It's similar to how types appear in the
733 * navigation, but is suitable for inline (as opposed to in a `<ul>`) use. 795 * navigation, but is suitable for inline (as opposed to in a `<ul>`) use.
734 */ 796 */
735 void typeSpan(Type type) { 797 void typeSpan(InterfaceMirror type) {
736 var icon = 'interface'; 798 var icon = 'interface';
737 if (type.name.endsWith('Exception')) { 799 if (type.simpleName().endsWith('Exception')) {
738 icon = 'exception'; 800 icon = 'exception';
739 } else if (type.isClass) { 801 } else if (type.isClass) {
740 icon = 'class'; 802 icon = 'class';
741 } 803 }
742 804
743 write('<span class="type-box"><span class="icon-$icon"></span>'); 805 write('<span class="type-box"><span class="icon-$icon"></span>');
744 if (_currentType == type) { 806 if (_currentType == type) {
745 write('<strong>${typeName(type)}</strong>'); 807 write('<strong>${typeName(type)}</strong>');
746 } else { 808 } else {
747 write(a(typeUrl(type), typeName(type))); 809 write(a(typeUrl(type), typeName(type)));
748 } 810 }
749 write('</span>'); 811 write('</span>');
750 } 812 }
751 813
752 /** 814 /**
753 * Document the other types that touch [Type] in the inheritance hierarchy: 815 * Document the other types that touch [Type] in the inheritance hierarchy:
754 * subclasses, superclasses, subinterfaces, superinferfaces, and default 816 * subclasses, superclasses, subinterfaces, superinferfaces, and default
755 * class. 817 * class.
756 */ 818 */
757 void docInheritance(Type type) { 819 void docInheritance(InterfaceMirror type) {
758 // Don't show the inheritance details for Object. It doesn't have any base 820 // Don't show the inheritance details for Object. It doesn't have any base
759 // class (obviously) and it has too many subclasses to be useful. 821 // class (obviously) and it has too many subclasses to be useful.
760 if (type.isObject) return; 822 if (type.isObject) return;
761 823
762 // Writes an unordered list of references to types with an optional header. 824 // Writes an unordered list of references to types with an optional header.
763 listTypes(types, header) { 825 listTypes(types, header) {
764 if (types == null) return; 826 if (types == null) return;
765 827
766 // Skip private types. 828 // Skip private types.
767 final publicTypes = types.filter((type) => !type.name.startsWith('_')); 829 final publicTypes = new List.from(types.filter((t) => !t.isPrivate));
768 if (publicTypes.length == 0) return; 830 if (publicTypes.length == 0) return;
769 831
770 writeln('<h3>$header</h3>'); 832 writeln('<h3>$header</h3>');
771 writeln('<p>'); 833 writeln('<p>');
772 bool first = true; 834 bool first = true;
773 for (final type in publicTypes) { 835 for (final t in publicTypes) {
774 if (!first) write(', '); 836 if (!first) write(', ');
775 typeSpan(type); 837 typeSpan(t);
776 first = false; 838 first = false;
777 } 839 }
778 writeln('</p>'); 840 writeln('</p>');
779 } 841 }
780 842
843 final subtypes = [];
844 for (final subtype in computeSubdeclarations(type)) {
845 subtypes.add(subtype);
846 }
847 subtypes.sort((x, y) => x.simpleName().compareTo(y.simpleName()));
781 if (type.isClass) { 848 if (type.isClass) {
782 // Show the chain of superclasses. 849 // Show the chain of superclasses.
783 if (!type.parent.isObject) { 850 if (!type.superclass().isObject) {
784 final supertypes = []; 851 final supertypes = [];
785 var thisType = type.parent; 852 var thisType = type.superclass();
786 // As a sanity check, only show up to five levels of nesting, otherwise 853 // As a sanity check, only show up to five levels of nesting, otherwise
787 // the box starts to get hideous. 854 // the box starts to get hideous.
788 do { 855 do {
789 supertypes.add(thisType); 856 supertypes.add(thisType);
790 thisType = thisType.parent; 857 thisType = thisType.superclass();
791 } while (!thisType.isObject); 858 } while (!thisType.isObject);
792 859
793 writeln('<h3>Extends</h3>'); 860 writeln('<h3>Extends</h3>');
794 writeln('<p>'); 861 writeln('<p>');
795 for (var i = supertypes.length - 1; i >= 0; i--) { 862 for (var i = supertypes.length - 1; i >= 0; i--) {
796 typeSpan(supertypes[i]); 863 typeSpan(supertypes[i]);
797 write('&nbsp;&gt;&nbsp;'); 864 write('&nbsp;&gt;&nbsp;');
798 } 865 }
799 866
800 // Write this class. 867 // Write this class.
801 typeSpan(type); 868 typeSpan(type);
802 writeln('</p>'); 869 writeln('</p>');
803 } 870 }
804 871
805 // Find the immediate declared subclasses (Type.subtypes includes many
806 // transitive subtypes).
807 final subtypes = [];
808 for (final subtype in type.subtypes) {
809 if (subtype.parent == type) subtypes.add(subtype);
810 }
811 subtypes.sort((a, b) => a.name.compareTo(b.name));
812
813 listTypes(subtypes, 'Subclasses'); 872 listTypes(subtypes, 'Subclasses');
814 listTypes(type.interfaces, 'Implements'); 873 listTypes(type.interfaces().getValues(), 'Implements');
815 } else { 874 } else {
816 // Show the default class. 875 // Show the default class.
817 if (type.genericType.defaultType != null) { 876 if (type.defaultType() != null) {
818 listTypes([type.genericType.defaultType], 'Default class'); 877 listTypes([type.defaultType()], 'Default class');
819 } 878 }
820 879
821 // List extended interfaces. 880 // List extended interfaces.
822 listTypes(type.interfaces, 'Extends'); 881 listTypes(type.interfaces().getValues(), 'Extends');
823 882
824 // List subinterfaces and implementing classes. 883 // List subinterfaces and implementing classes.
825 final subinterfaces = []; 884 final subinterfaces = [];
826 final implementing = []; 885 final implementing = [];
827 886
828 for (final subtype in type.subtypes) { 887 for (final subtype in subtypes) {
829 // We only want explicitly declared subinterfaces, so check that this 888 if (subtype.isClass) {
830 // type is a superinterface. 889 implementing.add(subtype);
831 for (final supertype in subtype.interfaces) { 890 } else {
832 if (supertype == type) { 891 subinterfaces.add(subtype);
833 if (subtype.isClass) {
834 implementing.add(subtype);
835 } else {
836 subinterfaces.add(subtype);
837 }
838 break;
839 }
840 } 892 }
841 } 893 }
842 894
843 listTypes(subinterfaces, 'Subinterfaces'); 895 listTypes(subinterfaces, 'Subinterfaces');
844 listTypes(implementing, 'Implemented by'); 896 listTypes(implementing, 'Implemented by');
845 } 897 }
846 } 898 }
847 899
900 /**
901 * Documents the definition of [type] if it is a typedef.
902 */
903 void docTypedef(TypeMirror type) {
904 if (type is! TypedefMirror) {
905 return;
906 }
907 writeln('<div class="method"><h4 id="${type.simpleName()}">');
908
909 if (includeSource) {
910 writeln('<span class="show-code">Code</span>');
911 }
912
913 if (type.definition() !== null) {
914 // TODO(johnniwinther): Implement [:TypedefMirror.definition():].
915 write('typedef ');
916 annotateType(type, type.definition(), type.simpleName());
917
918 write(''' <a class="anchor-link" href="#${type.simpleName()}"
919 title="Permalink to ${type.simpleName()}">#</a>''');
920 }
921 writeln('</h4>');
922
923 docCode(type.location(), null, showCode: true);
924
925 writeln('</div>');
926 }
927
848 /** Document the constructors for [Type], if any. */ 928 /** Document the constructors for [Type], if any. */
849 void docConstructors(Type type) { 929 void docConstructors(InterfaceMirror type) {
850 final names = type.constructors.getKeys().filter( 930 final constructors = <MethodMirror>[];
851 (name) => !name.startsWith('_')); 931 for (var constructor in type.constructors().getValues()) {
932 if (!constructor.isPrivate) {
933 constructors.add(constructor);
934 }
935 }
852 936
853 if (names.length > 0) { 937 if (constructors.length > 0) {
854 writeln('<h3>Constructors</h3>'); 938 writeln('<h3>Constructors</h3>');
855 names.sort((x, y) => x.toUpperCase().compareTo(y.toUpperCase())); 939 constructors.sort((x, y) => x.simpleName().toUpperCase().compareTo(
940 y.simpleName().toUpperCase()));
856 941
857 for (final name in names) { 942 for (final constructor in constructors) {
858 docMethod(type, type.constructors[name], constructorName: name); 943 docMethod(type, constructor);
859 } 944 }
860 } 945 }
861 } 946 }
862 947
863 void docMembers(Type type) { 948 void docMembers(ObjectMirror host) {
864 // Collect the different kinds of members. 949 // Collect the different kinds of members.
865 final staticMethods = []; 950 final staticMethods = [];
866 final staticFields = []; 951 final staticFields = [];
867 final instanceMethods = []; 952 final instanceMethods = [];
868 final instanceFields = []; 953 final instanceFields = [];
869 954
870 for (final member in orderByName(type.members)) { 955 for (final member in orderByName(host.declaredMembers().getValues())) {
871 if (member.name.startsWith('_')) continue; 956 if (member.isPrivate) continue;
872 957
873 final methods = member.isStatic ? staticMethods : instanceMethods; 958 final methods = member.isStatic ? staticMethods : instanceMethods;
874 final fields = member.isStatic ? staticFields : instanceFields; 959 final fields = member.isStatic ? staticFields : instanceFields;
875 960
876 if (member.isProperty) { 961 if (member.isMethod) {
877 if (member.canGet) methods.add(member.getter);
878 if (member.canSet) methods.add(member.setter);
879 } else if (member.isMethod) {
880 methods.add(member); 962 methods.add(member);
881 } else if (member.isField) { 963 } else if (member.isField) {
882 fields.add(member); 964 fields.add(member);
883 } 965 }
884 } 966 }
885 967
886 if (staticMethods.length > 0) { 968 if (staticMethods.length > 0) {
887 final title = type.isTop ? 'Functions' : 'Static Methods'; 969 final title = host is LibraryMirror ? 'Functions' : 'Static Methods';
888 writeln('<h3>$title</h3>'); 970 writeln('<h3>$title</h3>');
889 for (final method in staticMethods) docMethod(type, method); 971 for (final method in orderByName(staticMethods)) {
972 docMethod(host, method);
973 }
890 } 974 }
891 975
892 if (staticFields.length > 0) { 976 if (staticFields.length > 0) {
893 final title = type.isTop ? 'Variables' : 'Static Fields'; 977 final title = host is LibraryMirror ? 'Variables' : 'Static Fields';
894 writeln('<h3>$title</h3>'); 978 writeln('<h3>$title</h3>');
895 for (final field in staticFields) docField(type, field); 979 for (final field in orderByName(staticFields)) {
980 docField(host, field);
981 }
896 } 982 }
897 983
898 if (instanceMethods.length > 0) { 984 if (instanceMethods.length > 0) {
899 writeln('<h3>Methods</h3>'); 985 writeln('<h3>Methods</h3>');
900 for (final method in instanceMethods) docMethod(type, method); 986 for (final method in orderByName(instanceMethods)) {
987 docMethod(host, method);
988 }
901 } 989 }
902 990
903 if (instanceFields.length > 0) { 991 if (instanceFields.length > 0) {
904 writeln('<h3>Fields</h3>'); 992 writeln('<h3>Fields</h3>');
905 for (final field in instanceFields) docField(type, field); 993 for (final field in orderByName(instanceFields)) {
994 docField(host, field);
995 }
906 } 996 }
907 } 997 }
908 998
909 /** 999 /**
910 * Documents the [method] in type [type]. Handles all kinds of methods 1000 * Documents the [method] in type [type]. Handles all kinds of methods
911 * including getters, setters, and constructors. 1001 * including getters, setters, and constructors.
912 */ 1002 */
913 void docMethod(Type type, MethodMember method, 1003 void docMethod(ObjectMirror host, MethodMirror method) {
914 [String constructorName = null]) {
915 _totalMembers++; 1004 _totalMembers++;
916 _currentMember = method; 1005 _currentMember = method;
917 1006
918 writeln('<div class="method"><h4 id="${memberAnchor(method)}">'); 1007 writeln('<div class="method"><h4 id="${memberAnchor(method)}">');
919 1008
920 if (includeSource) { 1009 if (includeSource) {
921 writeln('<span class="show-code">Code</span>'); 1010 writeln('<span class="show-code">Code</span>');
922 } 1011 }
923 1012
924 if (method.isConstructor) { 1013 if (method.isConstructor) {
925 write(method.isConst ? 'const ' : 'new '); 1014 if (method.isFactory) {
1015 write('factory ');
1016 } else {
1017 write(method.isConst ? 'const ' : 'new ');
1018 }
926 } 1019 }
927 1020
928 if (constructorName == null) { 1021 if (method.constructorName == null) {
929 annotateType(type, method.returnType); 1022 annotateType(host, method.returnType());
930 } 1023 }
931 1024
1025 var name = method.simpleName();
932 // Translate specially-named methods: getters, setters, operators. 1026 // Translate specially-named methods: getters, setters, operators.
933 var name = method.name; 1027 if (method.isGetter) {
934 if (name.startsWith('get:')) {
935 // Getter. 1028 // Getter.
936 name = 'get ${name.substring(4)}'; 1029 name = 'get $name';
937 } else if (name.startsWith('set:')) { 1030 } else if (method.isSetter) {
938 // Setter. 1031 // Setter.
939 name = 'set ${name.substring(4)}'; 1032 name = 'set $name';
940 } else if (name == ':negate') { 1033 } else if (method.isOperator) {
941 // Dart uses 'negate' for prefix negate operators, not '!'. 1034 name = 'operator ${method.operatorName}';
942 name = 'operator negate';
943 } else if (name == ':call') {
944 name = 'operator call';
945 } else {
946 // See if it's an operator.
947 name = TokenKind.rawOperatorFromMethod(name);
948 if (name == null) {
949 name = method.name;
950 } else {
951 name = 'operator $name';
952 }
953 } 1035 }
954 1036
955 write('<strong>$name</strong>'); 1037 write('<strong>$name</strong>');
956 1038
957 // Named constructors. 1039 // Named constructors.
958 if (constructorName != null && constructorName != '') { 1040 if (method.constructorName != null && method.constructorName != '') {
959 write('.'); 1041 write('.');
960 write(constructorName); 1042 write(method.constructorName);
961 } 1043 }
962 1044
963 docParamList(type, method); 1045 docParamList(host, method.parameters());
964 1046
1047 var prefix = host is LibraryMirror ? '' : '${typeName(host)}.';
965 write(''' <a class="anchor-link" href="#${memberAnchor(method)}" 1048 write(''' <a class="anchor-link" href="#${memberAnchor(method)}"
966 title="Permalink to ${typeName(type)}.$name">#</a>'''); 1049 title="Permalink to $prefix$name">#</a>''');
967 writeln('</h4>'); 1050 writeln('</h4>');
968 1051
969 docCode(method.span, getMethodComment(method), showCode: true); 1052 docCode(method.location(), getMethodComment(method), showCode: true);
970 1053
971 writeln('</div>'); 1054 writeln('</div>');
972 } 1055 }
973 1056
974 /** Documents the field [field] of type [type]. */ 1057 /** Documents the field [field] of type [type]. */
975 void docField(Type type, FieldMember field) { 1058 void docField(ObjectMirror host, FieldMirror field) {
976 _totalMembers++; 1059 _totalMembers++;
977 _currentMember = field; 1060 _currentMember = field;
978 1061
979 writeln('<div class="field"><h4 id="${memberAnchor(field)}">'); 1062 writeln('<div class="field"><h4 id="${memberAnchor(field)}">');
980 1063
981 if (includeSource) { 1064 if (includeSource) {
982 writeln('<span class="show-code">Code</span>'); 1065 writeln('<span class="show-code">Code</span>');
983 } 1066 }
984 1067
985 if (field.isFinal) { 1068 if (field.isFinal) {
986 write('final '); 1069 write('final ');
987 } else if (field.type.name == 'Dynamic') { 1070 } else if (field.type().isDynamic) {
988 write('var '); 1071 write('var ');
989 } 1072 }
990 1073
991 annotateType(type, field.type); 1074 annotateType(host, field.type());
1075 var prefix = host is LibraryMirror ? '' : '${typeName(host)}.';
992 write( 1076 write(
993 ''' 1077 '''
994 <strong>${field.name}</strong> <a class="anchor-link" 1078 <strong>${field.simpleName()}</strong> <a class="anchor-link"
995 href="#${memberAnchor(field)}" 1079 href="#${memberAnchor(field)}"
996 title="Permalink to ${typeName(type)}.${field.name}">#</a> 1080 title="Permalink to $prefix${field.simpleName()}">#</a>
997 </h4> 1081 </h4>
998 '''); 1082 ''');
999 1083
1000 docCode(field.span, getFieldComment(field), showCode: true); 1084 docCode(field.location(), getFieldComment(field), showCode: true);
1001 writeln('</div>'); 1085 writeln('</div>');
1002 } 1086 }
1003 1087
1004 void docParamList(Type enclosingType, MethodMember member) { 1088 void docParamList(ObjectMirror enclosingType,
1089 List<ParameterMirror> parameters) {
1005 write('('); 1090 write('(');
1006 bool first = true; 1091 bool first = true;
1007 bool inOptionals = false; 1092 bool inOptionals = false;
1008 for (final parameter in member.parameters) { 1093 for (final parameter in parameters) {
1009 if (!first) write(', '); 1094 if (!first) write(', ');
1010 1095
1011 if (!inOptionals && parameter.isOptional) { 1096 if (!inOptionals && parameter.isOptional()) {
1012 write('['); 1097 write('[');
1013 inOptionals = true; 1098 inOptionals = true;
1014 } 1099 }
1015 1100
1016 annotateType(enclosingType, parameter.type, parameter.name); 1101 annotateType(enclosingType, parameter.type(), parameter.simpleName());
1017 1102
1018 // Show the default value for named optional parameters. 1103 // Show the default value for named optional parameters.
1019 if (parameter.isOptional && parameter.hasDefaultValue) { 1104 if (parameter.isOptional() && parameter.hasDefaultValue()) {
1020 write(' = '); 1105 write(' = ');
1021 // TODO(rnystrom): Using the definition text here is a bit cheap. 1106 write(parameter.defaultValue());
1022 // We really should be pretty-printing the AST so that if you have:
1023 // foo([arg = 1 + /* comment */ 2])
1024 // the docs should just show:
1025 // foo([arg = 1 + 2])
1026 // For now, we'll assume you don't do that.
1027 write(parameter.definition.value.span.text);
1028 } 1107 }
1029 1108
1030 first = false; 1109 first = false;
1031 } 1110 }
1032 1111
1033 if (inOptionals) write(']'); 1112 if (inOptionals) write(']');
1034 write(')'); 1113 write(')');
1035 } 1114 }
1036 1115
1037 /** 1116 /**
1038 * Documents the code contained within [span] with [comment]. If [showCode] 1117 * Documents the code contained within [span] with [comment]. If [showCode]
1039 * is `true` (and [includeSource] is set), also includes the source code. 1118 * is `true` (and [includeSource] is set), also includes the source code.
1040 */ 1119 */
1041 void docCode(SourceSpan span, String comment, [bool showCode = false]) { 1120 void docCode(Location location, String comment, [bool showCode = false]) {
1042 writeln('<div class="doc">'); 1121 writeln('<div class="doc">');
1043 if (comment != null) { 1122 if (comment != null) {
1044 writeln(comment); 1123 writeln(comment);
1045 } 1124 }
1046 1125
1047 if (includeSource && showCode) { 1126 if (includeSource && showCode) {
1048 writeln('<pre class="source">'); 1127 writeln('<pre class="source">');
1049 writeln(md.escapeHtml(unindentCode(span))); 1128 writeln(md.escapeHtml(unindentCode(location)));
1050 writeln('</pre>'); 1129 writeln('</pre>');
1051 } 1130 }
1052 1131
1053 writeln('</div>'); 1132 writeln('</div>');
1054 } 1133 }
1055 1134
1056 1135
1057 /** Get the doc comment associated with the given library. */ 1136 /** Get the doc comment associated with the given library. */
1058 String getLibraryComment(Library library) { 1137 String getLibraryComment(LibraryMirror library) {
1059 // Look for a comment for the entire library. 1138 // Look for a comment for the entire library.
1060 final comment = _comments.findLibrary(library.baseSource); 1139 final comment = _comments.findLibrary(library.location().source());
1061 if (comment != null) { 1140 if (comment != null) {
1062 return md.markdownToHtml(comment); 1141 return md.markdownToHtml(comment);
1063 } 1142 }
1064 return null; 1143 return null;
1065 } 1144 }
1066 1145
1067 /** Get the doc comment associated with the given type. */ 1146 /** Get the doc comment associated with the given type. */
1068 String getTypeComment(Type type) { 1147 String getTypeComment(TypeMirror type) {
1069 String comment = _comments.find(type.span); 1148 String comment = _comments.find(type.location());
1070 if (comment == null) return null; 1149 if (comment == null) return null;
1071 return commentToHtml(comment); 1150 return commentToHtml(comment);
1072 } 1151 }
1073 1152
1074 /** Get the doc comment associated with the given method. */ 1153 /** Get the doc comment associated with the given method. */
1075 String getMethodComment(MethodMember method) { 1154 String getMethodComment(MethodMirror method) {
1076 String comment = _comments.find(method.span); 1155 String comment = _comments.find(method.location());
1077 if (comment == null) return null; 1156 if (comment == null) return null;
1078 return commentToHtml(comment); 1157 return commentToHtml(comment);
1079 } 1158 }
1080 1159
1081 /** Get the doc comment associated with the given field. */ 1160 /** Get the doc comment associated with the given field. */
1082 String getFieldComment(FieldMember field) { 1161 String getFieldComment(FieldMirror field) {
1083 String comment = _comments.find(field.span); 1162 String comment = _comments.find(field.location());
1084 if (comment == null) return null; 1163 if (comment == null) return null;
1085 return commentToHtml(comment); 1164 return commentToHtml(comment);
1086 } 1165 }
1087 1166
1088 String commentToHtml(String comment) => md.markdownToHtml(comment); 1167 String commentToHtml(String comment) => md.markdownToHtml(comment);
1089 1168
1090 /** 1169 /**
1091 * Converts [fullPath] which is understood to be a full path from the root of 1170 * Converts [fullPath] which is understood to be a full path from the root of
1092 * the generated docs to one relative to the current file. 1171 * the generated docs to one relative to the current file.
1093 */ 1172 */
1094 String relativePath(String fullPath) { 1173 String relativePath(String fullPath) {
1095 // Don't make it relative if it's an absolute path. 1174 // Don't make it relative if it's an absolute path.
1096 if (isAbsolute(fullPath)) return fullPath; 1175 if (isAbsolute(fullPath)) return fullPath;
1097 1176
1098 // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do 1177 // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do
1099 // this if the paths overlap. 1178 // this if the paths overlap.
1100 return '${repeat('../', countOccurrences(_filePath, '/'))}$fullPath'; 1179 return '${repeat('../', countOccurrences(_filePath, '/'))}$fullPath';
1101 } 1180 }
1102 1181
1103 /** Gets whether or not the given URL is absolute or relative. */ 1182 /** Gets whether or not the given URL is absolute or relative. */
1104 bool isAbsolute(String url) { 1183 bool isAbsolute(String url) {
1105 // TODO(rnystrom): Why don't we have a nice type in the platform for this? 1184 // TODO(rnystrom): Why don't we have a nice type in the platform for this?
1106 // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks 1185 // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks
1107 // a scheme to be relative. 1186 // a scheme to be relative.
1108 return const RegExp(@'^\w+:').hasMatch(url); 1187 return const RegExp(@'^\w+:').hasMatch(url);
1109 } 1188 }
1110 1189
1111 /** Gets the URL to the documentation for [library]. */ 1190 /** Gets the URL to the documentation for [library]. */
1112 String libraryUrl(Library library) { 1191 String libraryUrl(LibraryMirror library) {
1113 return '${sanitize(library.name)}.html'; 1192 return '${sanitize(library.simpleName())}.html';
1114 } 1193 }
1115 1194
1116 /** Gets the URL for the documentation for [type]. */ 1195 /** Gets the URL for the documentation for [type]. */
1117 String typeUrl(Type type) { 1196 String typeUrl(ObjectMirror type) {
1118 if (type.isTop) return '${sanitize(type.library.name)}.html'; 1197 if (type is LibraryMirror) return '${sanitize(type.simpleName())}.html';
1198 assert (type is TypeMirror);
1119 // Always get the generic type to strip off any type parameters or 1199 // Always get the generic type to strip off any type parameters or
1120 // arguments. If the type isn't generic, genericType returns `this`, so it 1200 // arguments. If the type isn't generic, genericType returns `this`, so it
1121 // works for non-generic types too. 1201 // works for non-generic types too.
1122 return '${sanitize(type.library.name)}/${type.genericType.name}.html'; 1202 return '${sanitize(type.library().simpleName())}/'
1203 '${type.declaration.simpleName()}.html';
1123 } 1204 }
1124 1205
1125 /** Gets the URL for the documentation for [member]. */ 1206 /** Gets the URL for the documentation for [member]. */
1126 String memberUrl(Member member) { 1207 String memberUrl(MemberMirror member) {
1127 final typeUrl = typeUrl(member.declaringType); 1208 final url = typeUrl(member.surroundingDeclaration());
1128 if (!member.isConstructor) return '$typeUrl#${member.name}'; 1209 if (!member.isConstructor) return '$url#${member.simpleName()}';
1129 if (member.constructorName == '') return '$typeUrl#new:${member.name}'; 1210 assert (member is MethodMirror);
1130 return '$typeUrl#new:${member.name}.${member.constructorName}'; 1211 if (member.constructorName == '') return '$url#new:${member.simpleName()}';
1212 return '$url#new:${member.simpleName()}.${member.constructorName}';
1131 } 1213 }
1132 1214
1133 /** Gets the anchor id for the document for [member]. */ 1215 /** Gets the anchor id for the document for [member]. */
1134 String memberAnchor(Member member) { 1216 String memberAnchor(MemberMirror member) {
1135 return '${member.name}'; 1217 return '${member.simpleName()}';
1136 } 1218 }
1137 1219
1138 /** 1220 /**
1139 * Creates a hyperlink. Handles turning the [href] into an appropriate 1221 * Creates a hyperlink. Handles turning the [href] into an appropriate
1140 * relative path from the current file. 1222 * relative path from the current file.
1141 */ 1223 */
1142 String a(String href, String contents, [String css]) { 1224 String a(String href, String contents, [String css]) {
1143 // Mark outgoing external links, mainly so we can style them. 1225 // Mark outgoing external links, mainly so we can style them.
1144 final rel = isAbsolute(href) ? ' ref="external"' : ''; 1226 final rel = isAbsolute(href) ? ' ref="external"' : '';
1145 final cssClass = css == null ? '' : ' class="$css"'; 1227 final cssClass = css == null ? '' : ' class="$css"';
1146 return '<a href="${relativePath(href)}"$cssClass$rel>$contents</a>'; 1228 return '<a href="${relativePath(href)}"$cssClass$rel>$contents</a>';
1147 } 1229 }
1148 1230
1149 /** 1231 /**
1150 * Writes a type annotation for the given type and (optional) parameter name. 1232 * Writes a type annotation for the given type and (optional) parameter name.
1151 */ 1233 */
1152 annotateType(Type enclosingType, Type type, [String paramName = null]) { 1234 annotateType(ObjectMirror enclosingType,
1235 TypeMirror type,
1236 [String paramName = null]) {
1153 // Don't bother explicitly displaying Dynamic. 1237 // Don't bother explicitly displaying Dynamic.
1154 if (type.isVar) { 1238 if (type.isDynamic) {
1155 if (paramName !== null) write(paramName); 1239 if (paramName !== null) write(paramName);
1156 return; 1240 return;
1157 } 1241 }
1158 1242
1159 // For parameters, handle non-typedefed function types. 1243 // For parameters, handle non-typedefed function types.
1160 if (paramName !== null) { 1244 if (paramName !== null && type is FunctionTypeMirror) {
1161 final call = type.getCallMethod(); 1245 annotateType(enclosingType, type.returnType());
1162 if (call != null) { 1246 write(paramName);
1163 annotateType(enclosingType, call.returnType);
1164 write(paramName);
1165 1247
1166 docParamList(enclosingType, call); 1248 docParamList(enclosingType, type.parameters());
1167 return; 1249 return;
1168 }
1169 } 1250 }
1170 1251
1171 linkToType(enclosingType, type); 1252 linkToType(enclosingType, type);
1172 1253
1173 write(' '); 1254 write(' ');
1174 if (paramName !== null) write(paramName); 1255 if (paramName !== null) write(paramName);
1175 } 1256 }
1176 1257
1177 /** Writes a link to a human-friendly string representation for a type. */ 1258 /** Writes a link to a human-friendly string representation for a type. */
1178 linkToType(Type enclosingType, Type type) { 1259 linkToType(ObjectMirror enclosingType, TypeMirror type) {
1179 if (type is ParameterType) { 1260 if (type.isVoid) {
1180 // If we're using a type parameter within the body of a generic class then 1261 // Do not generate links for void.
1181 // just link back up to the class. 1262 // TODO(johnniwinter): Generate span for specific style?
1182 write(a(typeUrl(enclosingType), type.name)); 1263 write('void');
1183 return; 1264 return;
1184 } 1265 }
1185 1266
1267 if (type.isTypeVariable) {
1268 // If we're using a type parameter within the body of a generic class then
1269 // just link back up to the class.
1270 write(a(typeUrl(enclosingType), type.simpleName()));
1271 return;
1272 }
1273
1274 assert(type is InterfaceMirror);
1275
1186 // Link to the type. 1276 // Link to the type.
1187 // Use .genericType to avoid writing the <...> here. 1277 if (includeLibrary(type.library())) {
1188 write(a(typeUrl(type), type.genericType.name)); 1278 write(a(typeUrl(type), type.simpleName()));
1279 } else {
1280 write(type.simpleName());
1281 }
1189 1282
1190 // See if it's a generic type. 1283 if (type.isDeclaration) {
1191 if (type.isGeneric) { 1284 // Avoid calling [:typeArguments():] on a declaration.
1192 // TODO(rnystrom): This relies on a weird corner case of frog. Currently,
1193 // the only time we get into this case is when we have a "raw" generic
1194 // that's been instantiated with Dynamic for all type arguments. It's kind
1195 // of strange that frog works that way, but we take advantage of it to
1196 // show raw types without any type arguments.
1197 return; 1285 return;
1198 } 1286 }
1199 1287
1200 // See if it's an instantiation of a generic type. 1288 // See if it's an instantiation of a generic type.
1201 final typeArgs = type.typeArgsInOrder; 1289 final typeArgs = type.typeArguments();
1202 if (typeArgs.length > 0) { 1290 if (typeArgs.length > 0) {
1203 write('&lt;'); 1291 write('&lt;');
1204 bool first = true; 1292 bool first = true;
1205 for (final arg in typeArgs) { 1293 for (final arg in typeArgs) {
1206 if (!first) write(', '); 1294 if (!first) write(', ');
1207 first = false; 1295 first = false;
1208 linkToType(enclosingType, arg); 1296 linkToType(enclosingType, arg);
1209 } 1297 }
1210 write('&gt;'); 1298 write('&gt;');
1211 } 1299 }
1212 } 1300 }
1213 1301
1214 /** Creates a linked cross reference to [type]. */ 1302 /** Creates a linked cross reference to [type]. */
1215 typeReference(Type type) { 1303 typeReference(InterfaceMirror type) {
1216 // TODO(rnystrom): Do we need to handle ParameterTypes here like 1304 // TODO(rnystrom): Do we need to handle ParameterTypes here like
1217 // annotation() does? 1305 // annotation() does?
1218 return a(typeUrl(type), typeName(type), css: 'crossref'); 1306 return a(typeUrl(type), typeName(type), css: 'crossref');
1219 } 1307 }
1220 1308
1221 /** Generates a human-friendly string representation for a type. */ 1309 /** Generates a human-friendly string representation for a type. */
1222 typeName(Type type, [bool showBounds = false]) { 1310 typeName(TypeMirror type, [bool showBounds = false]) {
1311 if (type.isVoid) {
1312 return 'void';
1313 }
1314 if (type is TypeVariableMirror) {
1315 return type.simpleName();
1316 }
1317 assert (type is InterfaceMirror);
1318
1223 // See if it's a generic type. 1319 // See if it's a generic type.
1224 if (type.isGeneric) { 1320 if (type.isDeclaration) {
1225 final typeParams = []; 1321 final typeParams = [];
1226 for (final typeParam in type.genericType.typeParameters) { 1322 for (final typeParam in type.declaration.typeVariables()) {
1227 if (showBounds && 1323 if (showBounds &&
1228 (typeParam.extendsType != null) && 1324 (typeParam.bound() != null) &&
1229 !typeParam.extendsType.isObject) { 1325 !typeParam.bound().isObject) {
1230 final bound = typeName(typeParam.extendsType, showBounds: true); 1326 final bound = typeName(typeParam.bound(), showBounds: true);
1231 typeParams.add('${typeParam.name} extends $bound'); 1327 typeParams.add('${typeParam.simpleName()} extends $bound');
1232 } else { 1328 } else {
1233 typeParams.add(typeParam.name); 1329 typeParams.add(typeParam.simpleName());
1234 } 1330 }
1235 } 1331 }
1236 1332 if (typeParams.isEmpty()) {
1333 return type.simpleName();
1334 }
1237 final params = Strings.join(typeParams, ', '); 1335 final params = Strings.join(typeParams, ', ');
1238 return '${type.name}&lt;$params&gt;'; 1336 return '${type.simpleName()}&lt;$params&gt;';
1239 } 1337 }
1240 1338
1241 // See if it's an instantiation of a generic type. 1339 // See if it's an instantiation of a generic type.
1242 final typeArgs = type.typeArgsInOrder; 1340 final typeArgs = type.typeArguments();
1243 if (typeArgs.length > 0) { 1341 if (typeArgs.length > 0) {
1244 final args = Strings.join(map(typeArgs, (arg) => typeName(arg)), ', '); 1342 final args = Strings.join(typeArgs.map((arg) => typeName(arg)), ', ');
1245 return '${type.genericType.name}&lt;$args&gt;'; 1343 return '${type.declaration.simpleName()}&lt;$args&gt;';
1246 } 1344 }
1247 1345
1248 // Regular type. 1346 // Regular type.
1249 return type.name; 1347 return type.simpleName();
1250 } 1348 }
1251 1349
1252 /** 1350 /**
1253 * Remove leading indentation to line up with first line. 1351 * Remove leading indentation to line up with first line.
1254 */ 1352 */
1255 unindentCode(SourceSpan span) { 1353 unindentCode(Location span) {
1256 final column = getSpanColumn(span); 1354 final column = getLocationColumn(span);
1257 final lines = span.text.split('\n'); 1355 final lines = span.text().split('\n');
1258 // TODO(rnystrom): Dirty hack. 1356 // TODO(rnystrom): Dirty hack.
1259 for (var i = 1; i < lines.length; i++) { 1357 for (var i = 1; i < lines.length; i++) {
1260 lines[i] = unindent(lines[i], column); 1358 lines[i] = unindent(lines[i], column);
1261 } 1359 }
1262 1360
1263 final code = Strings.join(lines, '\n'); 1361 final code = Strings.join(lines, '\n');
1264 return code; 1362 return code;
1265 } 1363 }
1266 1364
1267 /** 1365 /**
1268 * Takes a string of Dart code and turns it into sanitized HTML. 1366 * Takes a string of Dart code and turns it into sanitized HTML.
1269 */ 1367 */
1270 formatCode(SourceSpan span) { 1368 formatCode(Location span) {
1271 final code = unindentCode(span); 1369 final code = unindentCode(span);
1272 1370
1273 // Syntax highlight. 1371 // Syntax highlight.
1274 return classifySource(new SourceFile('', code)); 1372 return classifySource(code);
1275 } 1373 }
1276 1374
1277 /** 1375 /**
1278 * This will be called whenever a doc comment hits a `[name]` in square 1376 * This will be called whenever a doc comment hits a `[name]` in square
1279 * brackets. It will try to figure out what the name refers to and link or 1377 * brackets. It will try to figure out what the name refers to and link or
1280 * style it appropriately. 1378 * style it appropriately.
1281 */ 1379 */
1282 md.Node resolveNameReference(String name, [Member member = null, 1380 md.Node resolveNameReference(String name,
1283 Type type = null, Library library = null]) { 1381 [MemberMirror member = null,
1382 ObjectMirror type = null,
1383 LibraryMirror library = null]) {
1284 makeLink(String href) { 1384 makeLink(String href) {
1285 final anchor = new md.Element.text('a', name); 1385 final anchor = new md.Element.text('a', name);
1286 anchor.attributes['href'] = relativePath(href); 1386 anchor.attributes['href'] = relativePath(href);
1287 anchor.attributes['class'] = 'crossref'; 1387 anchor.attributes['class'] = 'crossref';
1288 return anchor; 1388 return anchor;
1289 } 1389 }
1290 1390
1291 findMember(Type type, String memberName) {
1292 final member = type.members[memberName];
1293 if (member == null) return null;
1294
1295 // Special case: if the member we've resolved is a property (i.e. it wraps
1296 // a getter and/or setter then *that* member itself won't be on the docs,
1297 // just the getter or setter will be. So pick one of those to link to.
1298 if (member.isProperty) {
1299 return member.canGet ? member.getter : member.setter;
1300 }
1301
1302 return member;
1303 }
1304
1305 // See if it's a parameter of the current method. 1391 // See if it's a parameter of the current method.
1306 if (member != null) { 1392 if (member is MethodMirror) {
1307 for (final parameter in member.parameters) { 1393 for (final parameter in member.parameters()) {
1308 if (parameter.name == name) { 1394 if (parameter.simpleName() == name) {
1309 final element = new md.Element.text('span', name); 1395 final element = new md.Element.text('span', name);
1310 element.attributes['class'] = 'param'; 1396 element.attributes['class'] = 'param';
1311 return element; 1397 return element;
1312 } 1398 }
1313 } 1399 }
1314 } 1400 }
1315 1401
1316 // See if it's another member of the current type. 1402 // See if it's another member of the current type.
1317 if (type != null) { 1403 if (type != null) {
1318 final member = findMember(type, name); 1404 final member = findMirror(type.declaredMembers(), name);
1319 if (member != null) { 1405 if (member != null) {
1320 return makeLink(memberUrl(member)); 1406 return makeLink(memberUrl(member));
1321 } 1407 }
1322 } 1408 }
1323 1409
1324 // See if it's another type or a member of another type in the current 1410 // See if it's another type or a member of another type in the current
1325 // library. 1411 // library.
1326 if (library != null) { 1412 if (library != null) {
1327 // See if it's a constructor 1413 // See if it's a constructor
1328 final constructorLink = (() { 1414 final constructorLink = (() {
1329 final match = new RegExp(@'new (\w+)(?:\.(\w+))?').firstMatch(name); 1415 final match =
1416 new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name);
1330 if (match == null) return; 1417 if (match == null) return;
1331 final type = library.types[match[1]]; 1418 final type = findMirror(library.types(), match[1]);
1332 if (type == null) return; 1419 if (type == null) return;
1333 final constructor = type.getConstructor( 1420 final constructor =
1334 match[2] == null ? '' : match[2]); 1421 findMirror(type.constructors(),
1422 match[2] == null ? '' : match[2]);
1335 if (constructor == null) return; 1423 if (constructor == null) return;
1336 return makeLink(memberUrl(constructor)); 1424 return makeLink(memberUrl(constructor));
1337 })(); 1425 })();
1338 if (constructorLink != null) return constructorLink; 1426 if (constructorLink != null) return constructorLink;
1339 1427
1340 // See if it's a member of another type 1428 // See if it's a member of another type
1341 final foreignMemberLink = (() { 1429 final foreignMemberLink = (() {
1342 final match = new RegExp(@'(\w+)\.(\w+)').firstMatch(name); 1430 final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name);
1343 if (match == null) return; 1431 if (match == null) return;
1344 final type = library.types[match[1]]; 1432 final type = findMirror(library.types(), match[1]);
1345 if (type == null) return; 1433 if (type == null) return;
1346 final member = findMember(type, match[2]); 1434 final member = findMirror(type.declaredMembers(), match[2]);
1347 if (member == null) return; 1435 if (member == null) return;
1348 return makeLink(memberUrl(member)); 1436 return makeLink(memberUrl(member));
1349 })(); 1437 })();
1350 if (foreignMemberLink != null) return foreignMemberLink; 1438 if (foreignMemberLink != null) return foreignMemberLink;
1351 1439
1352 final type = library.types[name]; 1440 final type = findMirror(library.types(), name);
1353 if (type != null) { 1441 if (type != null) {
1354 return makeLink(typeUrl(type)); 1442 return makeLink(typeUrl(type));
1355 } 1443 }
1356 1444
1357 // See if it's a top-level member in the current library. 1445 // See if it's a top-level member in the current library.
1358 final member = findMember(library.topType, name); 1446 final member = findMirror(library.declaredMembers(), name);
1359 if (member != null) { 1447 if (member != null) {
1360 return makeLink(memberUrl(member)); 1448 return makeLink(memberUrl(member));
1361 } 1449 }
1362 } 1450 }
1363 1451
1364 // TODO(rnystrom): Should also consider: 1452 // TODO(rnystrom): Should also consider:
1365 // * Names imported by libraries this library imports. 1453 // * Names imported by libraries this library imports.
1366 // * Type parameters of the enclosing type. 1454 // * Type parameters of the enclosing type.
1367 1455
1368 return new md.Element.text('code', name); 1456 return new md.Element.text('code', name);
1369 } 1457 }
1370 1458
1371 // TODO(rnystrom): Move into SourceSpan?
1372 int getSpanColumn(SourceSpan span) {
1373 final line = span.file.getLine(span.start);
1374 return span.file.getColumn(line, span.start);
1375 }
1376
1377 generateAppCacheManifest() { 1459 generateAppCacheManifest() {
1378 print('Generating app cache manifest from output $outputDir'); 1460 print('Generating app cache manifest from output $outputDir');
1379 startFile('appcache.manifest'); 1461 startFile('appcache.manifest');
1380 write("CACHE MANIFEST\n\n"); 1462 write("CACHE MANIFEST\n\n");
1381 write("# VERSION: ${new Date.now()}\n\n"); 1463 write("# VERSION: ${new Date.now()}\n\n");
1382 write("NETWORK:\n*\n\n"); 1464 write("NETWORK:\n*\n\n");
1383 write("CACHE:\n"); 1465 write("CACHE:\n");
1384 var toCache = new Directory(outputDir); 1466 var toCache = new Directory(outputDir);
1385 var pathPrefix = new File(outputDir).fullPathSync(); 1467 var pathPrefix = new File(outputDir).fullPathSync();
1386 var pathPrefixLength = pathPrefix.length; 1468 var pathPrefixLength = pathPrefix.length;
1387 toCache.onFile = (filename) { 1469 toCache.onFile = (filename) {
1388 if (filename.endsWith('appcache.manifest')) { 1470 if (filename.endsWith('appcache.manifest')) {
1389 return; 1471 return;
1390 } 1472 }
1391 var relativePath = filename.substring(pathPrefixLength + 1); 1473 var relativePath = filename.substring(pathPrefixLength + 1);
1392 write("$relativePath\n"); 1474 write("$relativePath\n");
1393 }; 1475 };
1394 toCache.onDone = (done) => endFile(); 1476 toCache.onDone = (done) => endFile();
1395 toCache.list(recursive: true); 1477 toCache.list(recursive: true);
1396 } 1478 }
1479
1480 /**
1481 * Returns [:true:] if [type] should be regarded as an exception.
1482 */
1483 bool isException(TypeMirror type) {
1484 return type.simpleName().endsWith('Exception');
1485 }
1397 } 1486 }
1487
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698