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

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

Powered by Google App Engine
This is Rietveld 408576698