OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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:async'; | 19 import 'dart:async'; |
20 import 'dart:io'; | 20 import 'dart:io'; |
| 21 import 'dart:isolate'; |
21 import 'dart:json' as json; | 22 import 'dart:json' as json; |
22 import 'dart:math'; | 23 import 'dart:math'; |
23 import 'dart:uri'; | 24 import 'dart:uri'; |
24 | 25 |
| 26 import 'package:pathos/path.dart' as pathos; |
| 27 |
25 import 'classify.dart'; | 28 import 'classify.dart'; |
26 import 'markdown.dart' as md; | 29 import 'markdown.dart' as md; |
27 import 'universe_serializer.dart'; | 30 import 'universe_serializer.dart'; |
28 | 31 |
29 import 'src/dartdoc/nav.dart'; | 32 import 'src/dartdoc/nav.dart'; |
| 33 import 'src/dartdoc/utils.dart'; |
| 34 import 'src/export_map.dart'; |
30 import 'src/json_serializer.dart' as json_serializer; | 35 import 'src/json_serializer.dart' as json_serializer; |
31 | 36 |
32 // TODO(rnystrom): Use "package:" URL (#4968). | 37 // TODO(rnystrom): Use "package:" URL (#4968). |
33 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; | 38 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; |
34 import '../../compiler/implementation/mirrors/mirrors.dart'; | 39 import '../../compiler/implementation/mirrors/mirrors.dart'; |
35 import '../../compiler/implementation/mirrors/mirrors_util.dart'; | 40 import '../../compiler/implementation/mirrors/mirrors_util.dart'; |
36 import '../../libraries.dart'; | 41 import '../../libraries.dart'; |
37 | 42 |
38 part 'src/dartdoc/utils.dart'; | |
39 | |
40 /** | 43 /** |
41 * Generates completely static HTML containing everything you need to browse | 44 * Generates completely static HTML containing everything you need to browse |
42 * the docs. The only client side behavior is trivial stuff like syntax | 45 * the docs. The only client side behavior is trivial stuff like syntax |
43 * highlighting code. | 46 * highlighting code. |
44 */ | 47 */ |
45 const MODE_STATIC = 0; | 48 const MODE_STATIC = 0; |
46 | 49 |
47 /** | 50 /** |
48 * Generated docs do not include baked HTML navigation. Instead, a single | 51 * Generated docs do not include baked HTML navigation. Instead, a single |
49 * `nav.json` file is created and the appropriate navigation is generated | 52 * `nav.json` file is created and the appropriate navigation is generated |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 onDone: () => completer.complete(), | 127 onDone: () => completer.complete(), |
125 onError: (e) => completer.completeError(e.error, e.stackTrace)); | 128 onError: (e) => completer.completeError(e.error, e.stackTrace)); |
126 return completer.future; | 129 return completer.future; |
127 } | 130 } |
128 | 131 |
129 /** | 132 /** |
130 * Compiles the dartdoc client-side code to JavaScript using Dart2js. | 133 * Compiles the dartdoc client-side code to JavaScript using Dart2js. |
131 */ | 134 */ |
132 Future compileScript(int mode, Path outputDir, Path libPath) { | 135 Future compileScript(int mode, Path outputDir, Path libPath) { |
133 print('Compiling client JavaScript...'); | 136 print('Compiling client JavaScript...'); |
134 var clientScript = (mode == MODE_STATIC) ? 'static' : 'live-nav'; | |
135 var dartPath = libPath.append( | |
136 'lib/_internal/dartdoc/lib/src/client/client-$clientScript.dart'); | |
137 var jsPath = outputDir.append('client-$clientScript.js'); | |
138 | 137 |
139 return dart2js.compile(dartPath, libPath, | 138 // TODO(nweiz): don't run this in an isolate when issue 9815 is fixed. |
140 options: const <String>['--categories=Client,Server']) | 139 return spawnFunction(_compileScript).call({ |
141 .then((jsCode) { | 140 'mode': mode, |
142 writeString(new File.fromPath(jsPath), jsCode); | 141 'outputDir': outputDir.toNativePath(), |
| 142 'libPath': libPath.toNativePath() |
| 143 }).then((result) { |
| 144 if (result.first == 'success') return; |
| 145 throw new AsyncError(result[1], result[2]); |
| 146 }); |
| 147 } |
| 148 |
| 149 void _compileScript() { |
| 150 port.receive((message, replyTo) { |
| 151 new Future.of(() { |
| 152 var clientScript = (message['mode'] == MODE_STATIC) ? |
| 153 'static' : 'live-nav'; |
| 154 var dartPath = pathos.join(message['libPath'], 'lib', '_internal', |
| 155 'dartdoc', 'lib', 'src', 'client', 'client-$clientScript.dart'); |
| 156 var jsPath = pathos.join(message['outputDir'], 'client-$clientScript.js'); |
| 157 |
| 158 return dart2js.compile( |
| 159 new Path(dartPath), new Path(message['libPath']), |
| 160 options: const <String>['--categories=Client,Server']).then((jsCode) { |
| 161 writeString(new File(jsPath), jsCode); |
| 162 }); |
| 163 }).then((_) { |
| 164 replyTo.send(['success']); |
| 165 }).catchError((e) { |
| 166 replyTo.send(['error', e.error.toString(), e.stackTrace.toString()]); |
143 }); | 167 }); |
| 168 }); |
144 } | 169 } |
145 | 170 |
146 /** | 171 /** |
147 * Package manifest containing all information required to render the main page | 172 * Package manifest containing all information required to render the main page |
148 * for a package. | 173 * for a package. |
149 * | 174 * |
150 * The manifest specifies where to load the [LibraryElement]s describing each | 175 * The manifest specifies where to load the [LibraryElement]s describing each |
151 * library rather than including them directly. | 176 * library rather than including them directly. |
152 * For our purposes we treat the core Dart libraries as a package. | 177 * For our purposes we treat the core Dart libraries as a package. |
153 */ | 178 */ |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 | 279 |
255 /** Version of the sdk or package docs are being generated for. */ | 280 /** Version of the sdk or package docs are being generated for. */ |
256 String version; | 281 String version; |
257 | 282 |
258 /** Set this to select the libraries to include in the documentation. */ | 283 /** Set this to select the libraries to include in the documentation. */ |
259 List<String> includedLibraries = const <String>[]; | 284 List<String> includedLibraries = const <String>[]; |
260 | 285 |
261 /** Set this to select the libraries to exclude from the documentation. */ | 286 /** Set this to select the libraries to exclude from the documentation. */ |
262 List<String> excludedLibraries = const <String>[]; | 287 List<String> excludedLibraries = const <String>[]; |
263 | 288 |
| 289 /** The package root for `package:` imports. */ |
| 290 String _packageRoot; |
| 291 |
| 292 /** The map containing all the exports for each library. */ |
| 293 ExportMap _exports; |
| 294 |
264 /** | 295 /** |
265 * This list contains the libraries sorted in by the library name. | 296 * This list contains the libraries sorted in by the library name. |
266 */ | 297 */ |
267 List<LibraryMirror> _sortedLibraries; | 298 List<LibraryMirror> _sortedLibraries; |
268 | 299 |
| 300 /** A map from absolute paths of libraries to the libraries at those paths. */ |
| 301 Map<String, LibraryMirror> _librariesByPath; |
| 302 |
| 303 /** |
| 304 * A map from absolute paths of hidden libraries to lists of [Export]s that |
| 305 * export those libraries from visible libraries. This is used to determine |
| 306 * what public library any given entity belongs to. |
| 307 * |
| 308 * The lists of exports are sorted so that exports that hide the fewest number |
| 309 * of members come first. |
| 310 */ |
| 311 Map<String, List<Export>> _hiddenLibraryExports; |
| 312 |
269 /** The library that we're currently generating docs for. */ | 313 /** The library that we're currently generating docs for. */ |
270 LibraryMirror _currentLibrary; | 314 LibraryMirror _currentLibrary; |
271 | 315 |
272 /** The type that we're currently generating docs for. */ | 316 /** The type that we're currently generating docs for. */ |
273 ClassMirror _currentType; | 317 ClassMirror _currentType; |
274 | 318 |
275 /** The member that we're currently generating docs for. */ | 319 /** The member that we're currently generating docs for. */ |
276 MemberMirror _currentMember; | 320 MemberMirror _currentMember; |
277 | 321 |
278 /** The path to the file currently being written to, relative to [outdir]. */ | 322 /** The path to the file currently being written to, relative to [outdir]. */ |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 var content = ''; | 440 var content = ''; |
397 for (int i = 0; i < footerItems.length; i++) { | 441 for (int i = 0; i < footerItems.length; i++) { |
398 if (i > 0) { | 442 if (i > 0) { |
399 content += '\n'; | 443 content += '\n'; |
400 } | 444 } |
401 content += '<div>${footerItems[i]}</div>'; | 445 content += '<div>${footerItems[i]}</div>'; |
402 } | 446 } |
403 return content; | 447 return content; |
404 } | 448 } |
405 | 449 |
406 Future documentEntryPoint(Path entrypoint, Path libPath, Path packageRoot) { | 450 Future documentLibraries(List<Uri> libraryList, Path libPath, |
407 return documentLibraries(<Path>[entrypoint], libPath, packageRoot); | 451 String packageRoot) { |
408 } | 452 _packageRoot = packageRoot; |
| 453 _exports = new ExportMap.parse(libraryList, packageRoot); |
| 454 var librariesToAnalyze = _exports.allExportedFiles.toList(); |
| 455 librariesToAnalyze.addAll(libraryList.map((uri) { |
| 456 if (uri.scheme == 'file') return fileUriToPath(uri); |
| 457 // dart2js takes "dart:*" URIs as Path objects for some reason. |
| 458 return uri.toString(); |
| 459 })); |
409 | 460 |
410 Future documentLibraries(List<Path> libraryList, Path libPath, Path packageRoo
t) { | 461 var packageRootPath = packageRoot == null ? null : new Path(packageRoot); |
| 462 |
411 // TODO(amouravski): make all of these print statements into logging | 463 // TODO(amouravski): make all of these print statements into logging |
412 // statements. | 464 // statements. |
413 print('Analyzing libraries...'); | 465 print('Analyzing libraries...'); |
414 return dart2js.analyze(libraryList, libPath, packageRoot: packageRoot, | 466 return dart2js.analyze( |
415 options: COMPILER_OPTIONS) | 467 librariesToAnalyze.map((path) => new Path(path)).toList(), libPath, |
| 468 packageRoot: packageRootPath, options: COMPILER_OPTIONS) |
416 .then((MirrorSystem mirrors) { | 469 .then((MirrorSystem mirrors) { |
417 print('Generating documentation...'); | 470 print('Generating documentation...'); |
418 _document(mirrors); | 471 _document(mirrors); |
419 }); | 472 }); |
420 } | 473 } |
421 | 474 |
422 void _document(MirrorSystem mirrors) { | 475 void _document(MirrorSystem mirrors) { |
423 _started = true; | 476 _started = true; |
424 | 477 |
425 // Sort the libraries by name (not key). | 478 // Sort the libraries by name (not key). |
426 _sortedLibraries = new List<LibraryMirror>.from( | 479 _sortedLibraries = new List<LibraryMirror>.from( |
427 mirrors.libraries.values.where(shouldIncludeLibrary)); | 480 mirrors.libraries.values.where(shouldIncludeLibrary)); |
428 _sortedLibraries.sort((x, y) { | 481 _sortedLibraries.sort((x, y) { |
429 return displayName(x).toUpperCase().compareTo( | 482 return displayName(x).toUpperCase().compareTo( |
430 displayName(y).toUpperCase()); | 483 displayName(y).toUpperCase()); |
431 }); | 484 }); |
432 | 485 |
| 486 _librariesByPath = <String, LibraryMirror>{}; |
| 487 for (var library in mirrors.libraries.values) { |
| 488 var path = _libraryPath(library); |
| 489 if (path == null) continue; |
| 490 path = pathos.normalize(pathos.absolute(path)); |
| 491 _librariesByPath[path] = library; |
| 492 } |
| 493 |
| 494 _hiddenLibraryExports = _generateHiddenLibraryExports(); |
| 495 |
433 // Generate the docs. | 496 // Generate the docs. |
434 if (mode == MODE_LIVE_NAV) { | 497 if (mode == MODE_LIVE_NAV) { |
435 docNavigationJson(); | 498 docNavigationJson(); |
436 } else { | 499 } else { |
437 docNavigationDart(); | 500 docNavigationDart(); |
438 } | 501 } |
439 | 502 |
440 docIndex(); | 503 docIndex(); |
441 for (final library in _sortedLibraries) { | 504 for (final library in _sortedLibraries) { |
442 docLibrary(library); | 505 docLibrary(library); |
443 } | 506 } |
444 | 507 |
445 if (generateAppCache) { | 508 if (generateAppCache) { |
446 generateAppCacheManifest(); | 509 generateAppCacheManifest(); |
447 } | 510 } |
448 | 511 |
| 512 // TODO(nweiz): properly handle exports when generating JSON. |
449 // TODO(jacobr): handle arbitrary pub packages rather than just the system | 513 // TODO(jacobr): handle arbitrary pub packages rather than just the system |
450 // libraries. | 514 // libraries. |
451 var revision = '0'; | 515 var revision = '0'; |
452 if (version != null) { | 516 if (version != null) { |
453 var match = new RegExp(r"_r(\d+)").firstMatch(version); | 517 var match = new RegExp(r"_r(\d+)").firstMatch(version); |
454 if (match != null) { | 518 if (match != null) { |
455 revision = match.group(1); | 519 revision = match.group(1); |
456 } else { | 520 } else { |
457 print("Warning: could not parse version: $version"); | 521 print("Warning: could not parse version: $version"); |
458 } | 522 } |
(...skipping 18 matching lines...) Expand all Loading... |
477 // Write out top level json file with a relative path to the library json | 541 // Write out top level json file with a relative path to the library json |
478 // files. | 542 // files. |
479 startFile("apidoc.json"); | 543 startFile("apidoc.json"); |
480 packageManifest.location = revision; | 544 packageManifest.location = revision; |
481 write(json_serializer.serialize(packageManifest)); | 545 write(json_serializer.serialize(packageManifest)); |
482 endFile(); | 546 endFile(); |
483 | 547 |
484 _finished = true; | 548 _finished = true; |
485 } | 549 } |
486 | 550 |
| 551 /** |
| 552 * Generate [_hiddenLibraryExports] from [_exports] and [_librariesByPath]. |
| 553 */ |
| 554 Map<String, List<Export>> _generateHiddenLibraryExports() { |
| 555 // First generate a map `exported path => exporter path => Export`. The |
| 556 // inner map makes it easier to merge multiple exports of the same library |
| 557 // by the same exporter. |
| 558 var hiddenLibraryExportMaps = <String, Map<String, Export>>{}; |
| 559 _exports.exports.forEach((exporter, exports) { |
| 560 var library = _librariesByPath[exporter]; |
| 561 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 562 if (library == null) return; |
| 563 if (!shouldIncludeLibrary(library)) return; |
| 564 for (var export in exports) { |
| 565 var library = _librariesByPath[export.path]; |
| 566 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 567 if (library == null) continue; |
| 568 if (shouldIncludeLibrary(library)) continue; |
| 569 |
| 570 var hiddenExports = _exports.transitiveExports(export.path) |
| 571 .map((transitiveExport) => export.compose(transitiveExport)) |
| 572 .toList(); |
| 573 hiddenExports.add(export); |
| 574 |
| 575 for (var hiddenExport in hiddenExports) { |
| 576 var exportsByExporterPath = hiddenLibraryExportMaps |
| 577 .putIfAbsent(hiddenExport.path, () => <String, Export>{}); |
| 578 addOrMergeExport(exportsByExporterPath, exporter, hiddenExport); |
| 579 } |
| 580 } |
| 581 }); |
| 582 |
| 583 // Now sort the values of the inner maps of `hiddenLibraryExportMaps` to get |
| 584 // the final value of `_hiddenLibraryExports`. |
| 585 var hiddenLibraryExports = <String, List<Export>>{}; |
| 586 hiddenLibraryExportMaps.forEach((exporteePath, exportsByExporterPath) { |
| 587 int rank(Export export) { |
| 588 if (export.show.isEmpty && export.hide.isEmpty) return 0; |
| 589 if (export.show.isEmpty) return export.hide.length; |
| 590 // Multiply by 1000 to ensure this sorts after an export with hides. |
| 591 return 1000 * export.show.length; |
| 592 } |
| 593 |
| 594 var exports = exportsByExporterPath.values.toList(); |
| 595 exports.sort((export1, export2) { |
| 596 var comparison = Comparable.compare(rank(export1), rank(export2)); |
| 597 if (comparison != 0) return comparison; |
| 598 |
| 599 var library1 = _librariesByPath[export1.exporter]; |
| 600 var library2 = _librariesByPath[export2.exporter]; |
| 601 return Comparable.compare(library1.displayName, library2.displayName); |
| 602 }); |
| 603 hiddenLibraryExports[exporteePath] = exports; |
| 604 }); |
| 605 return hiddenLibraryExports; |
| 606 } |
| 607 |
487 MdnComment lookupMdnComment(Mirror mirror) => null; | 608 MdnComment lookupMdnComment(Mirror mirror) => null; |
488 | 609 |
489 void startFile(String path) { | 610 void startFile(String path) { |
490 _filePath = new Path(path); | 611 _filePath = new Path(path); |
491 _file = new StringBuffer(); | 612 _file = new StringBuffer(); |
492 } | 613 } |
493 | 614 |
494 void endFile() { | 615 void endFile() { |
495 final outPath = outputDir.join(_filePath); | 616 final outPath = outputDir.join(_filePath); |
496 final dir = new Directory.fromPath(outPath.directoryPath); | 617 final dir = new Directory.fromPath(outPath.directoryPath); |
(...skipping 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
861 writeHeader('${displayName(library)} Library', | 982 writeHeader('${displayName(library)} Library', |
862 [displayName(library), libraryUrl(library)]); | 983 [displayName(library), libraryUrl(library)]); |
863 writeln('<h2><strong>${displayName(library)}</strong> library</h2>'); | 984 writeln('<h2><strong>${displayName(library)}</strong> library</h2>'); |
864 | 985 |
865 // Look for a comment for the entire library. | 986 // Look for a comment for the entire library. |
866 final comment = getLibraryComment(library); | 987 final comment = getLibraryComment(library); |
867 if (comment != null) { | 988 if (comment != null) { |
868 writeln('<div class="doc">${comment.html}</div>'); | 989 writeln('<div class="doc">${comment.html}</div>'); |
869 } | 990 } |
870 | 991 |
| 992 // Document the visible libraries exported by this library. |
| 993 docExports(library); |
| 994 |
871 // Document the top-level members. | 995 // Document the top-level members. |
872 docMembers(library); | 996 docMembers(library); |
873 | 997 |
874 // Document the types. | 998 // Document the types. |
875 final interfaces = <ClassMirror>[]; | 999 final interfaces = <ClassMirror>[]; |
876 final abstractClasses = <ClassMirror>[]; | 1000 final abstractClasses = <ClassMirror>[]; |
877 final classes = <ClassMirror>[]; | 1001 final classes = <ClassMirror>[]; |
878 final typedefs = <TypedefMirror>[]; | 1002 final typedefs = <TypedefMirror>[]; |
879 final exceptions = <ClassMirror>[]; | 1003 final exceptions = <ClassMirror>[]; |
880 | 1004 |
881 for (ClassMirror type in orderByName(library.classes.values)) { | 1005 var allClasses = _libraryClasses(library); |
| 1006 for (ClassMirror type in orderByName(allClasses)) { |
882 if (!showPrivate && type.isPrivate) continue; | 1007 if (!showPrivate && type.isPrivate) continue; |
883 | 1008 |
884 if (isException(type)) { | 1009 if (isException(type)) { |
885 exceptions.add(type); | 1010 exceptions.add(type); |
886 } else if (type.isClass) { | 1011 } else if (type.isClass) { |
887 if (type.isAbstract) { | 1012 if (type.isAbstract) { |
888 abstractClasses.add(type); | 1013 abstractClasses.add(type); |
889 } else { | 1014 } else { |
890 classes.add(type); | 1015 classes.add(type); |
891 } | 1016 } |
892 } else if (type.isInterface){ | 1017 } else if (type.isInterface){ |
893 interfaces.add(type); | 1018 interfaces.add(type); |
894 } else if (type is TypedefMirror) { | 1019 } else if (type is TypedefMirror) { |
895 typedefs.add(type); | 1020 typedefs.add(type); |
896 } else { | 1021 } else { |
897 throw new InternalError("internal error: unknown type $type."); | 1022 throw new InternalError("internal error: unknown type $type."); |
898 } | 1023 } |
899 } | 1024 } |
900 | 1025 |
901 docTypes(interfaces, 'Interfaces'); | 1026 docTypes(interfaces, 'Interfaces'); |
902 docTypes(abstractClasses, 'Abstract Classes'); | 1027 docTypes(abstractClasses, 'Abstract Classes'); |
903 docTypes(classes, 'Classes'); | 1028 docTypes(classes, 'Classes'); |
904 docTypes(typedefs, 'Typedefs'); | 1029 docTypes(typedefs, 'Typedefs'); |
905 docTypes(exceptions, 'Exceptions'); | 1030 docTypes(exceptions, 'Exceptions'); |
906 | 1031 |
907 writeFooter(); | 1032 writeFooter(); |
908 endFile(); | 1033 endFile(); |
909 | 1034 |
910 for (final type in library.classes.values) { | 1035 for (final type in allClasses) { |
911 if (!showPrivate && type.isPrivate) continue; | 1036 if (!showPrivate && type.isPrivate) continue; |
912 | 1037 |
913 docType(type); | 1038 docType(type); |
914 } | 1039 } |
915 } | 1040 } |
916 | 1041 |
917 void docTypes(List types, String header) { | 1042 void docTypes(List types, String header) { |
918 if (types.length == 0) return; | 1043 if (types.length == 0) return; |
919 | 1044 |
920 writeln('<div>'); | 1045 writeln('<div>'); |
(...skipping 27 matching lines...) Expand all Loading... |
948 } else if (type.isClass) { | 1073 } else if (type.isClass) { |
949 if (type.isAbstract) { | 1074 if (type.isAbstract) { |
950 kind = 'abstract class'; | 1075 kind = 'abstract class'; |
951 } else { | 1076 } else { |
952 kind = 'class'; | 1077 kind = 'class'; |
953 } | 1078 } |
954 } | 1079 } |
955 | 1080 |
956 final typeTitle = | 1081 final typeTitle = |
957 '${typeName(type)} ${kind}'; | 1082 '${typeName(type)} ${kind}'; |
958 writeHeader('$typeTitle / ${displayName(type.library)} Library', | 1083 var library = _libraryFor(type); |
959 [displayName(type.library), libraryUrl(type.library), | 1084 writeHeader('$typeTitle / ${displayName(library)} Library', |
| 1085 [displayName(library), libraryUrl(library), |
960 typeName(type), typeUrl(type)]); | 1086 typeName(type), typeUrl(type)]); |
961 writeln( | 1087 writeln( |
962 ''' | 1088 ''' |
963 <h2><strong>${typeName(type, showBounds: true)}</strong> | 1089 <h2><strong>${typeName(type, showBounds: true)}</strong> |
964 $kind | 1090 $kind |
965 </h2> | 1091 </h2> |
966 '''); | 1092 '''); |
967 writeln('<button id="show-inherited" class="show-inherited">' | 1093 writeln('<button id="show-inherited" class="show-inherited">' |
968 'Hide inherited</button>'); | 1094 'Hide inherited</button>'); |
969 | 1095 |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1141 | 1267 |
1142 static final Map<String, int> operatorOrderMap = (){ | 1268 static final Map<String, int> operatorOrderMap = (){ |
1143 var map = new Map<String, int>(); | 1269 var map = new Map<String, int>(); |
1144 var index = 0; | 1270 var index = 0; |
1145 for (String operator in operatorOrder) { | 1271 for (String operator in operatorOrder) { |
1146 map[operator] = index++; | 1272 map[operator] = index++; |
1147 } | 1273 } |
1148 return map; | 1274 return map; |
1149 }(); | 1275 }(); |
1150 | 1276 |
| 1277 void docExports(LibraryMirror library) { |
| 1278 // TODO(nweiz): show `dart:` library exports. |
| 1279 var exportLinks = _exports.transitiveExports(_libraryPath(library)) |
| 1280 .map((export) { |
| 1281 var library = _librariesByPath[export.path]; |
| 1282 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 1283 if (library == null) return null; |
| 1284 // Only link to publically visible libraries. |
| 1285 if (!shouldIncludeLibrary(library)) return null; |
| 1286 |
| 1287 var memberNames = export.show.isEmpty ? export.hide : export.show; |
| 1288 var memberLinks = memberNames.map((name) { |
| 1289 return md.renderToHtml([resolveNameReference( |
| 1290 name, currentLibrary: library)]); |
| 1291 }).join(', '); |
| 1292 var combinator = ''; |
| 1293 if (!export.show.isEmpty) { |
| 1294 combinator = ' show $memberLinks'; |
| 1295 } else if (!export.hide.isEmpty) { |
| 1296 combinator = ' hide $memberLinks'; |
| 1297 } |
| 1298 |
| 1299 return '<ul>${a(libraryUrl(library), displayName(library))}' |
| 1300 '$combinator</ul>'; |
| 1301 }).where((link) => link != null); |
| 1302 |
| 1303 if (!exportLinks.isEmpty) { |
| 1304 writeln('<h3>Exports</h3>'); |
| 1305 writeln('<ul>'); |
| 1306 writeln(exportLinks.join('\n')); |
| 1307 writeln('</ul>'); |
| 1308 } |
| 1309 } |
| 1310 |
1151 void docMembers(ContainerMirror host) { | 1311 void docMembers(ContainerMirror host) { |
1152 // Collect the different kinds of members. | 1312 // Collect the different kinds of members. |
1153 final staticMethods = []; | 1313 final staticMethods = []; |
1154 final staticGetters = new Map<String,MemberMirror>(); | 1314 final staticGetters = new Map<String,MemberMirror>(); |
1155 final staticSetters = new Map<String,MemberMirror>(); | 1315 final staticSetters = new Map<String,MemberMirror>(); |
1156 final memberMap = new Map<String,MemberMirror>(); | 1316 final memberMap = new Map<String,MemberMirror>(); |
1157 final instanceMethods = []; | 1317 final instanceMethods = []; |
1158 final instanceOperators = []; | 1318 final instanceOperators = []; |
1159 final instanceGetters = new Map<String,MemberMirror>(); | 1319 final instanceGetters = new Map<String,MemberMirror>(); |
1160 final instanceSetters = new Map<String,MemberMirror>(); | 1320 final instanceSetters = new Map<String,MemberMirror>(); |
1161 final constructors = []; | 1321 final constructors = []; |
1162 | 1322 |
1163 host.members.forEach((_, MemberMirror member) { | 1323 var hostMembers = host is LibraryMirror ? |
1164 if (!showPrivate && member.isPrivate) return; | 1324 _libraryMembers(host) : host.members.values; |
| 1325 for (var member in hostMembers) { |
| 1326 if (!showPrivate && member.isPrivate) continue; |
1165 if (host is LibraryMirror || member.isStatic) { | 1327 if (host is LibraryMirror || member.isStatic) { |
1166 if (member is MethodMirror) { | 1328 if (member is MethodMirror) { |
1167 if (member.isGetter) { | 1329 if (member.isGetter) { |
1168 staticGetters[member.displayName] = member; | 1330 staticGetters[member.displayName] = member; |
1169 } else if (member.isSetter) { | 1331 } else if (member.isSetter) { |
1170 staticSetters[member.displayName] = member; | 1332 staticSetters[member.displayName] = member; |
1171 } else { | 1333 } else { |
1172 staticMethods.add(member); | 1334 staticMethods.add(member); |
1173 } | 1335 } |
1174 } else if (member is VariableMirror) { | 1336 } else if (member is VariableMirror) { |
1175 staticGetters[member.displayName] = member; | 1337 staticGetters[member.displayName] = member; |
1176 } | 1338 } |
1177 } | 1339 } |
1178 }); | 1340 } |
1179 | 1341 |
1180 if (host is ClassMirror) { | 1342 if (host is ClassMirror) { |
1181 var iterable = new HierarchyIterable(host, includeType: true); | 1343 var iterable = new HierarchyIterable(host, includeType: true); |
1182 for (ClassMirror type in iterable) { | 1344 for (ClassMirror type in iterable) { |
1183 if (!host.isObject && !inheritFromObject && type.isObject) continue; | 1345 if (!host.isObject && !inheritFromObject && type.isObject) continue; |
1184 | 1346 |
1185 type.members.forEach((_, MemberMirror member) { | 1347 type.members.forEach((_, MemberMirror member) { |
1186 if (member.isStatic) return; | 1348 if (member.isStatic) return; |
1187 if (!showPrivate && member.isPrivate) return; | 1349 if (!showPrivate && member.isPrivate) return; |
1188 | 1350 |
(...skipping 23 matching lines...) Expand all Loading... |
1212 } | 1374 } |
1213 } | 1375 } |
1214 | 1376 |
1215 bool allMethodsInherited = true; | 1377 bool allMethodsInherited = true; |
1216 bool allPropertiesInherited = true; | 1378 bool allPropertiesInherited = true; |
1217 bool allOperatorsInherited = true; | 1379 bool allOperatorsInherited = true; |
1218 memberMap.forEach((_, MemberMirror member) { | 1380 memberMap.forEach((_, MemberMirror member) { |
1219 if (member is MethodMirror) { | 1381 if (member is MethodMirror) { |
1220 if (member.isGetter) { | 1382 if (member.isGetter) { |
1221 instanceGetters[member.displayName] = member; | 1383 instanceGetters[member.displayName] = member; |
1222 if (member.owner == host) { | 1384 if (_ownerFor(member) == host) { |
1223 allPropertiesInherited = false; | 1385 allPropertiesInherited = false; |
1224 } | 1386 } |
1225 } else if (member.isSetter) { | 1387 } else if (member.isSetter) { |
1226 instanceSetters[member.displayName] = member; | 1388 instanceSetters[member.displayName] = member; |
1227 if (member.owner == host) { | 1389 if (_ownerFor(member) == host) { |
1228 allPropertiesInherited = false; | 1390 allPropertiesInherited = false; |
1229 } | 1391 } |
1230 } else if (member.isOperator) { | 1392 } else if (member.isOperator) { |
1231 instanceOperators.add(member); | 1393 instanceOperators.add(member); |
1232 if (member.owner == host) { | 1394 if (_ownerFor(member) == host) { |
1233 allOperatorsInherited = false; | 1395 allOperatorsInherited = false; |
1234 } | 1396 } |
1235 } else { | 1397 } else { |
1236 instanceMethods.add(member); | 1398 instanceMethods.add(member); |
1237 if (member.owner == host) { | 1399 if (_ownerFor(member) == host) { |
1238 allMethodsInherited = false; | 1400 allMethodsInherited = false; |
1239 } | 1401 } |
1240 } | 1402 } |
1241 } else if (member is VariableMirror) { | 1403 } else if (member is VariableMirror) { |
1242 instanceGetters[member.displayName] = member; | 1404 instanceGetters[member.displayName] = member; |
1243 if (member.owner == host) { | 1405 if (_ownerFor(member) == host) { |
1244 allPropertiesInherited = false; | 1406 allPropertiesInherited = false; |
1245 } | 1407 } |
1246 } | 1408 } |
1247 }); | 1409 }); |
1248 | 1410 |
1249 instanceOperators.sort((MethodMirror a, MethodMirror b) { | 1411 instanceOperators.sort((MethodMirror a, MethodMirror b) { |
1250 return operatorOrderMap[a.simpleName].compareTo( | 1412 return operatorOrderMap[a.simpleName].compareTo( |
1251 operatorOrderMap[b.simpleName]); | 1413 operatorOrderMap[b.simpleName]); |
1252 }); | 1414 }); |
1253 | 1415 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1298 assert(getter is MethodMirror); | 1460 assert(getter is MethodMirror); |
1299 docProperty(host, getter, null); | 1461 docProperty(host, getter, null); |
1300 } | 1462 } |
1301 } else if (getter == null) { | 1463 } else if (getter == null) { |
1302 // We only have a setter => Document as a method. | 1464 // We only have a setter => Document as a method. |
1303 assert(setter is MethodMirror); | 1465 assert(setter is MethodMirror); |
1304 docMethod(host, setter); | 1466 docMethod(host, setter); |
1305 } else { | 1467 } else { |
1306 DocComment getterComment = getMemberComment(getter); | 1468 DocComment getterComment = getMemberComment(getter); |
1307 DocComment setterComment = getMemberComment(setter); | 1469 DocComment setterComment = getMemberComment(setter); |
1308 if (getter.owner != setter.owner || | 1470 if (_ownerFor(getter) != _ownerFor(setter) || |
1309 getterComment != null && setterComment != null) { | 1471 getterComment != null && setterComment != null) { |
1310 // Both have comments or are not declared in the same class | 1472 // Both have comments or are not declared in the same class |
1311 // => Documents separately. | 1473 // => Documents separately. |
1312 if (getter is VariableMirror) { | 1474 if (getter is VariableMirror) { |
1313 // Document field as a getter (setter is inherited). | 1475 // Document field as a getter (setter is inherited). |
1314 docField(host, getter, asGetter: true); | 1476 docField(host, getter, asGetter: true); |
1315 } else { | 1477 } else { |
1316 docMethod(host, getter); | 1478 docMethod(host, getter); |
1317 } | 1479 } |
1318 if (setter is VariableMirror) { | 1480 if (setter is VariableMirror) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1369 if (member.isGetter) { | 1531 if (member.isGetter) { |
1370 // Getter. | 1532 // Getter. |
1371 name = 'get $name'; | 1533 name = 'get $name'; |
1372 } else if (member.isSetter) { | 1534 } else if (member.isSetter) { |
1373 // Setter. | 1535 // Setter. |
1374 name = 'set $name'; | 1536 name = 'set $name'; |
1375 } | 1537 } |
1376 } | 1538 } |
1377 | 1539 |
1378 bool showCode = includeSource && !isAbstract; | 1540 bool showCode = includeSource && !isAbstract; |
1379 bool inherited = host != member.owner; | 1541 bool inherited = host != member.owner && member.owner is! LibraryMirror; |
1380 | 1542 |
1381 writeln('<div class="method${inherited ? ' inherited': ''}">' | 1543 writeln('<div class="method${inherited ? ' inherited': ''}">' |
1382 '<h4 id="${memberAnchor(member)}">'); | 1544 '<h4 id="${memberAnchor(member)}">'); |
1383 | 1545 |
1384 if (showCode) { | 1546 if (showCode) { |
1385 writeln('<button class="show-code">Code</button>'); | 1547 writeln('<button class="show-code">Code</button>'); |
1386 } | 1548 } |
1387 | 1549 |
1388 if (member is MethodMirror) { | 1550 if (member is MethodMirror) { |
1389 if (member.isConstructor) { | 1551 if (member.isConstructor) { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1460 * [host]. If [getter] is a [FieldMirror], [setter] must be [:null:]. | 1622 * [host]. If [getter] is a [FieldMirror], [setter] must be [:null:]. |
1461 * Otherwise, if [getter] is a [MethodMirror], the property is considered | 1623 * Otherwise, if [getter] is a [MethodMirror], the property is considered |
1462 * final if [setter] is [:null:]. | 1624 * final if [setter] is [:null:]. |
1463 */ | 1625 */ |
1464 void docProperty(ContainerMirror host, | 1626 void docProperty(ContainerMirror host, |
1465 MemberMirror getter, MemberMirror setter) { | 1627 MemberMirror getter, MemberMirror setter) { |
1466 assert(getter != null); | 1628 assert(getter != null); |
1467 _totalMembers++; | 1629 _totalMembers++; |
1468 _currentMember = getter; | 1630 _currentMember = getter; |
1469 | 1631 |
1470 bool inherited = host != getter.owner; | 1632 bool inherited = host != getter.owner && getter.owner is! LibraryMirror; |
1471 | 1633 |
1472 writeln('<div class="field${inherited ? ' inherited' : ''}">' | 1634 writeln('<div class="field${inherited ? ' inherited' : ''}">' |
1473 '<h4 id="${memberAnchor(getter)}">'); | 1635 '<h4 id="${memberAnchor(getter)}">'); |
1474 | 1636 |
1475 if (includeSource) { | 1637 if (includeSource) { |
1476 writeln('<button class="show-code">Code</button>'); | 1638 writeln('<button class="show-code">Code</button>'); |
1477 } | 1639 } |
1478 | 1640 |
1479 bool isConst = false; | 1641 bool isConst = false; |
1480 bool isFinal; | 1642 bool isFinal; |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1667 String typeUrl(ContainerMirror type) { | 1829 String typeUrl(ContainerMirror type) { |
1668 if (type is LibraryMirror) { | 1830 if (type is LibraryMirror) { |
1669 return '${sanitize(type.simpleName)}.html'; | 1831 return '${sanitize(type.simpleName)}.html'; |
1670 } | 1832 } |
1671 if (type.library == null) { | 1833 if (type.library == null) { |
1672 return ''; | 1834 return ''; |
1673 } | 1835 } |
1674 // Always get the generic type to strip off any type parameters or | 1836 // Always get the generic type to strip off any type parameters or |
1675 // arguments. If the type isn't generic, genericType returns `this`, so it | 1837 // arguments. If the type isn't generic, genericType returns `this`, so it |
1676 // works for non-generic types too. | 1838 // works for non-generic types too. |
1677 return '${sanitize(displayName(type.library))}/' | 1839 return '${sanitize(displayName(_libraryFor(type)))}/' |
1678 '${type.originalDeclaration.simpleName}.html'; | 1840 '${type.originalDeclaration.simpleName}.html'; |
1679 } | 1841 } |
1680 | 1842 |
1681 /** Gets the URL for the documentation for [member]. */ | 1843 /** Gets the URL for the documentation for [member]. */ |
1682 String memberUrl(MemberMirror member) { | 1844 String memberUrl(MemberMirror member) { |
1683 String url = typeUrl(member.owner); | 1845 String url = typeUrl(_ownerFor(member)); |
1684 return '$url#${memberAnchor(member)}'; | 1846 return '$url#${memberAnchor(member)}'; |
1685 } | 1847 } |
1686 | 1848 |
1687 /** Gets the anchor id for the document for [member]. */ | 1849 /** Gets the anchor id for the document for [member]. */ |
1688 String memberAnchor(MemberMirror member) { | 1850 String memberAnchor(MemberMirror member) { |
1689 return member.simpleName; | 1851 return member.simpleName; |
1690 } | 1852 } |
1691 | 1853 |
1692 /** | 1854 /** |
1693 * Creates a hyperlink. Handles turning the [href] into an appropriate | 1855 * Creates a hyperlink. Handles turning the [href] into an appropriate |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1752 if (type.isTypeVariable) { | 1914 if (type.isTypeVariable) { |
1753 // If we're using a type parameter within the body of a generic class then | 1915 // If we're using a type parameter within the body of a generic class then |
1754 // just link back up to the class. | 1916 // just link back up to the class. |
1755 write(a(typeUrl(enclosingType), type.simpleName)); | 1917 write(a(typeUrl(enclosingType), type.simpleName)); |
1756 return; | 1918 return; |
1757 } | 1919 } |
1758 | 1920 |
1759 assert(type is ClassMirror); | 1921 assert(type is ClassMirror); |
1760 | 1922 |
1761 // Link to the type. | 1923 // Link to the type. |
1762 if (shouldLinkToPublicApi(type.library)) { | 1924 var library = _libraryFor(type); |
| 1925 if (shouldLinkToPublicApi(library)) { |
1763 write('<a href="$API_LOCATION${typeUrl(type)}">${type.simpleName}</a>'); | 1926 write('<a href="$API_LOCATION${typeUrl(type)}">${type.simpleName}</a>'); |
1764 } else if (shouldIncludeLibrary(type.library)) { | 1927 } else if (shouldIncludeLibrary(library)) { |
1765 write(a(typeUrl(type), type.simpleName)); | 1928 write(a(typeUrl(type), type.simpleName)); |
1766 } else { | 1929 } else { |
1767 write(type.simpleName); | 1930 write(type.simpleName); |
1768 } | 1931 } |
1769 | 1932 |
1770 if (type.isOriginalDeclaration) { | 1933 if (type.isOriginalDeclaration) { |
1771 // Avoid calling [:typeArguments():] on a declaration. | 1934 // Avoid calling [:typeArguments():] on a declaration. |
1772 return; | 1935 return; |
1773 } | 1936 } |
1774 | 1937 |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1972 onDone: () => endFile()); | 2135 onDone: () => endFile()); |
1973 } | 2136 } |
1974 | 2137 |
1975 /** | 2138 /** |
1976 * Returns [:true:] if [type] should be regarded as an exception. | 2139 * Returns [:true:] if [type] should be regarded as an exception. |
1977 */ | 2140 */ |
1978 bool isException(TypeMirror type) { | 2141 bool isException(TypeMirror type) { |
1979 return type.simpleName.endsWith('Exception') || | 2142 return type.simpleName.endsWith('Exception') || |
1980 type.simpleName.endsWith('Error'); | 2143 type.simpleName.endsWith('Error'); |
1981 } | 2144 } |
| 2145 |
| 2146 /** |
| 2147 * Returns the absolute path to [library] on the filesystem, or `null` if the |
| 2148 * library doesn't exist on the local filesystem. |
| 2149 */ |
| 2150 String _libraryPath(LibraryMirror library) => |
| 2151 importUriToPath(library.uri, packageRoot: _packageRoot); |
| 2152 |
| 2153 /** |
| 2154 * Returns a list of classes in [library], including classes it exports from |
| 2155 * hidden libraries. |
| 2156 */ |
| 2157 List<ClassMirror> _libraryClasses(LibraryMirror library) => |
| 2158 _libraryContents(library, (lib) => lib.classes.values); |
| 2159 |
| 2160 /** |
| 2161 * Returns a list of top-level members in [library], including members it |
| 2162 * exports from hidden libraries. |
| 2163 */ |
| 2164 List<MemberMirror> _libraryMembers(LibraryMirror library) => |
| 2165 _libraryContents(library, (lib) => lib.members.values); |
| 2166 |
| 2167 |
| 2168 /** |
| 2169 * Returns a list of elements in [library], including elements it exports from |
| 2170 * hidden libraries. [fn] should return the element list for a single library, |
| 2171 * which will then be merged across all exported libraries. |
| 2172 */ |
| 2173 List<DeclarationMirror> _libraryContents(LibraryMirror library, |
| 2174 List<DeclarationMirror> fn(LibraryMirror)) { |
| 2175 var contents = fn(library).toList(); |
| 2176 var path = _libraryPath(library); |
| 2177 if (path == null || _exports.exports[path] == null) return contents; |
| 2178 |
| 2179 |
| 2180 contents.addAll(_exports.exports[path].expand((export) { |
| 2181 var exportedLibrary = _librariesByPath[export.path]; |
| 2182 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 2183 if (exportedLibrary == null) return []; |
| 2184 if (shouldIncludeLibrary(exportedLibrary)) return []; |
| 2185 return fn(exportedLibrary).where((declaration) => |
| 2186 export.isMemberVisible(declaration.displayName)); |
| 2187 })); |
| 2188 return contents; |
| 2189 } |
| 2190 |
| 2191 /** |
| 2192 * Returns the library in which [type] was defined. If [type] was defined in a |
| 2193 * hidden library that was exported by another library, this returns the |
| 2194 * exporter. |
| 2195 */ |
| 2196 LibraryMirror _libraryFor(TypeMirror type) => |
| 2197 _visibleLibrary(type.library, type.displayName); |
| 2198 |
| 2199 /** |
| 2200 * Returns the owner of [declaration]. If [declaration]'s owner is a hidden |
| 2201 * library that was exported by another library, this returns the exporter. |
| 2202 */ |
| 2203 DeclarationMirror _ownerFor(DeclarationMirror declaration) { |
| 2204 var owner = declaration.owner; |
| 2205 if (owner is! LibraryMirror) return owner; |
| 2206 return _visibleLibrary(owner, declaration.displayName); |
| 2207 } |
| 2208 |
| 2209 /** |
| 2210 * Returns the best visible library that exports [name] from [library]. If |
| 2211 * [library] is public, it will be returned. |
| 2212 */ |
| 2213 LibraryMirror _visibleLibrary(LibraryMirror library, String name) { |
| 2214 if (library == null) return null; |
| 2215 |
| 2216 var exports = _hiddenLibraryExports[_libraryPath(library)]; |
| 2217 if (exports == null) return library; |
| 2218 |
| 2219 var export = exports.firstWhere( |
| 2220 (exp) => exp.isMemberVisible(name), |
| 2221 orElse: () => null); |
| 2222 if (export == null) return library; |
| 2223 return _librariesByPath[export.exporter]; |
| 2224 } |
1982 } | 2225 } |
1983 | 2226 |
1984 /** | 2227 /** |
1985 * Used to report an unexpected error in the DartDoc tool or the | 2228 * Used to report an unexpected error in the DartDoc tool or the |
1986 * underlying data | 2229 * underlying data |
1987 */ | 2230 */ |
1988 class InternalError { | 2231 class InternalError { |
1989 final String message; | 2232 final String message; |
1990 const InternalError(this.message); | 2233 const InternalError(this.message); |
1991 String toString() => "InternalError: '$message'"; | 2234 String toString() => "InternalError: '$message'"; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2069 return ''' | 2312 return ''' |
2070 <div class="mdn"> | 2313 <div class="mdn"> |
2071 $mdnComment | 2314 $mdnComment |
2072 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> | 2315 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> |
2073 </div> | 2316 </div> |
2074 '''; | 2317 '''; |
2075 } | 2318 } |
2076 | 2319 |
2077 String toString() => mdnComment; | 2320 String toString() => mdnComment; |
2078 } | 2321 } |
OLD | NEW |