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:json' as json; | 21 import 'dart:json' as json; |
22 import 'dart:math'; | 22 import 'dart:math'; |
23 import 'dart:uri'; | 23 import 'dart:uri'; |
24 | 24 |
| 25 import 'package:pathos/path.dart' as pathos; |
| 26 |
25 import 'classify.dart'; | 27 import 'classify.dart'; |
26 import 'markdown.dart' as md; | 28 import 'markdown.dart' as md; |
27 import 'universe_serializer.dart'; | 29 import 'universe_serializer.dart'; |
28 | 30 |
29 import 'src/dartdoc/nav.dart'; | 31 import 'src/dartdoc/nav.dart'; |
| 32 import 'src/dartdoc/utils.dart'; |
| 33 import 'src/export_map.dart'; |
30 import 'src/json_serializer.dart' as json_serializer; | 34 import 'src/json_serializer.dart' as json_serializer; |
31 | 35 |
32 // TODO(rnystrom): Use "package:" URL (#4968). | 36 // TODO(rnystrom): Use "package:" URL (#4968). |
33 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; | 37 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; |
34 import '../../compiler/implementation/mirrors/mirrors.dart'; | 38 import '../../compiler/implementation/mirrors/mirrors.dart'; |
35 import '../../compiler/implementation/mirrors/mirrors_util.dart'; | 39 import '../../compiler/implementation/mirrors/mirrors_util.dart'; |
36 import '../../libraries.dart'; | 40 import '../../libraries.dart'; |
37 | 41 |
38 part 'src/dartdoc/utils.dart'; | |
39 | |
40 /** | 42 /** |
41 * Generates completely static HTML containing everything you need to browse | 43 * Generates completely static HTML containing everything you need to browse |
42 * the docs. The only client side behavior is trivial stuff like syntax | 44 * the docs. The only client side behavior is trivial stuff like syntax |
43 * highlighting code. | 45 * highlighting code. |
44 */ | 46 */ |
45 const MODE_STATIC = 0; | 47 const MODE_STATIC = 0; |
46 | 48 |
47 /** | 49 /** |
48 * Generated docs do not include baked HTML navigation. Instead, a single | 50 * Generated docs do not include baked HTML navigation. Instead, a single |
49 * `nav.json` file is created and the appropriate navigation is generated | 51 * `nav.json` file is created and the appropriate navigation is generated |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 | 256 |
255 /** Version of the sdk or package docs are being generated for. */ | 257 /** Version of the sdk or package docs are being generated for. */ |
256 String version; | 258 String version; |
257 | 259 |
258 /** Set this to select the libraries to include in the documentation. */ | 260 /** Set this to select the libraries to include in the documentation. */ |
259 List<String> includedLibraries = const <String>[]; | 261 List<String> includedLibraries = const <String>[]; |
260 | 262 |
261 /** Set this to select the libraries to exclude from the documentation. */ | 263 /** Set this to select the libraries to exclude from the documentation. */ |
262 List<String> excludedLibraries = const <String>[]; | 264 List<String> excludedLibraries = const <String>[]; |
263 | 265 |
| 266 /** The package root for `package:` imports. */ |
| 267 String _packageRoot; |
| 268 |
| 269 /** The map containing all the exports for each library. */ |
| 270 ExportMap _exports; |
| 271 |
264 /** | 272 /** |
265 * This list contains the libraries sorted in by the library name. | 273 * This list contains the libraries sorted in by the library name. |
266 */ | 274 */ |
267 List<LibraryMirror> _sortedLibraries; | 275 List<LibraryMirror> _sortedLibraries; |
268 | 276 |
| 277 /** A map from absolute paths of libraries to the libraries at those paths. */ |
| 278 Map<String, LibraryMirror> _librariesByPath; |
| 279 |
| 280 /** |
| 281 * A map from absolute paths of hidden libraries to lists of [Export]s that |
| 282 * export those libraries from visible libraries. This is used to determine |
| 283 * what public library any given entity belongs to. |
| 284 * |
| 285 * The lists of exports are sorted so that exports that hide the fewest number |
| 286 * of members come first. |
| 287 */ |
| 288 Map<String, List<Export>> _hiddenLibraryExports; |
| 289 |
269 /** The library that we're currently generating docs for. */ | 290 /** The library that we're currently generating docs for. */ |
270 LibraryMirror _currentLibrary; | 291 LibraryMirror _currentLibrary; |
271 | 292 |
272 /** The type that we're currently generating docs for. */ | 293 /** The type that we're currently generating docs for. */ |
273 ClassMirror _currentType; | 294 ClassMirror _currentType; |
274 | 295 |
275 /** The member that we're currently generating docs for. */ | 296 /** The member that we're currently generating docs for. */ |
276 MemberMirror _currentMember; | 297 MemberMirror _currentMember; |
277 | 298 |
278 /** The path to the file currently being written to, relative to [outdir]. */ | 299 /** 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 = ''; | 417 var content = ''; |
397 for (int i = 0; i < footerItems.length; i++) { | 418 for (int i = 0; i < footerItems.length; i++) { |
398 if (i > 0) { | 419 if (i > 0) { |
399 content += '\n'; | 420 content += '\n'; |
400 } | 421 } |
401 content += '<div>${footerItems[i]}</div>'; | 422 content += '<div>${footerItems[i]}</div>'; |
402 } | 423 } |
403 return content; | 424 return content; |
404 } | 425 } |
405 | 426 |
406 Future documentEntryPoint(Path entrypoint, Path libPath, Path packageRoot) { | 427 Future documentLibraries(List<Uri> libraryList, Path libPath, |
407 return documentLibraries(<Path>[entrypoint], libPath, packageRoot); | 428 String packageRoot) { |
408 } | 429 _packageRoot = packageRoot; |
| 430 _exports = new ExportMap.parse(libraryList, packageRoot); |
| 431 var librariesToAnalyze = _exports.allExportedFiles.toList(); |
| 432 librariesToAnalyze.addAll(libraryList.map((uri) { |
| 433 if (uri.scheme == 'file') return fileUriToPath(uri); |
| 434 // dart2js takes "dart:*" URIs as Path objects for some reason. |
| 435 return uri.toString(); |
| 436 })); |
409 | 437 |
410 Future documentLibraries(List<Path> libraryList, Path libPath, Path packageRoo
t) { | |
411 // TODO(amouravski): make all of these print statements into logging | 438 // TODO(amouravski): make all of these print statements into logging |
412 // statements. | 439 // statements. |
413 print('Analyzing libraries...'); | 440 print('Analyzing libraries...'); |
414 return dart2js.analyze(libraryList, libPath, packageRoot: packageRoot, | 441 return dart2js.analyze( |
415 options: COMPILER_OPTIONS) | 442 librariesToAnalyze.map((path) => new Path(path)).toList(), libPath, |
| 443 packageRoot: new Path(packageRoot), options: COMPILER_OPTIONS) |
416 .then((MirrorSystem mirrors) { | 444 .then((MirrorSystem mirrors) { |
417 print('Generating documentation...'); | 445 print('Generating documentation...'); |
418 _document(mirrors); | 446 _document(mirrors); |
419 }); | 447 }); |
420 } | 448 } |
421 | 449 |
422 void _document(MirrorSystem mirrors) { | 450 void _document(MirrorSystem mirrors) { |
423 _started = true; | 451 _started = true; |
424 | 452 |
425 // Sort the libraries by name (not key). | 453 // Sort the libraries by name (not key). |
426 _sortedLibraries = new List<LibraryMirror>.from( | 454 _sortedLibraries = new List<LibraryMirror>.from( |
427 mirrors.libraries.values.where(shouldIncludeLibrary)); | 455 mirrors.libraries.values.where(shouldIncludeLibrary)); |
428 _sortedLibraries.sort((x, y) { | 456 _sortedLibraries.sort((x, y) { |
429 return displayName(x).toUpperCase().compareTo( | 457 return displayName(x).toUpperCase().compareTo( |
430 displayName(y).toUpperCase()); | 458 displayName(y).toUpperCase()); |
431 }); | 459 }); |
432 | 460 |
| 461 _librariesByPath = <String, LibraryMirror>{}; |
| 462 for (var library in mirrors.libraries.values) { |
| 463 var path = _libraryPath(library); |
| 464 if (path == null) continue; |
| 465 path = pathos.normalize(pathos.absolute(path)); |
| 466 _librariesByPath[path] = library; |
| 467 } |
| 468 |
| 469 _hiddenLibraryExports = _generateHiddenLibraryExports(); |
| 470 |
433 // Generate the docs. | 471 // Generate the docs. |
434 if (mode == MODE_LIVE_NAV) { | 472 if (mode == MODE_LIVE_NAV) { |
435 docNavigationJson(); | 473 docNavigationJson(); |
436 } else { | 474 } else { |
437 docNavigationDart(); | 475 docNavigationDart(); |
438 } | 476 } |
439 | 477 |
440 docIndex(); | 478 docIndex(); |
441 for (final library in _sortedLibraries) { | 479 for (final library in _sortedLibraries) { |
442 docLibrary(library); | 480 docLibrary(library); |
443 } | 481 } |
444 | 482 |
445 if (generateAppCache) { | 483 if (generateAppCache) { |
446 generateAppCacheManifest(); | 484 generateAppCacheManifest(); |
447 } | 485 } |
448 | 486 |
| 487 // TODO(nweiz): properly handle exports when generating JSON. |
449 // TODO(jacobr): handle arbitrary pub packages rather than just the system | 488 // TODO(jacobr): handle arbitrary pub packages rather than just the system |
450 // libraries. | 489 // libraries. |
451 var revision = '0'; | 490 var revision = '0'; |
452 if (version != null) { | 491 if (version != null) { |
453 var match = new RegExp(r"_r(\d+)").firstMatch(version); | 492 var match = new RegExp(r"_r(\d+)").firstMatch(version); |
454 if (match != null) { | 493 if (match != null) { |
455 revision = match.group(1); | 494 revision = match.group(1); |
456 } else { | 495 } else { |
457 print("Warning: could not parse version: $version"); | 496 print("Warning: could not parse version: $version"); |
458 } | 497 } |
(...skipping 18 matching lines...) Expand all Loading... |
477 // Write out top level json file with a relative path to the library json | 516 // Write out top level json file with a relative path to the library json |
478 // files. | 517 // files. |
479 startFile("apidoc.json"); | 518 startFile("apidoc.json"); |
480 packageManifest.location = revision; | 519 packageManifest.location = revision; |
481 write(json_serializer.serialize(packageManifest)); | 520 write(json_serializer.serialize(packageManifest)); |
482 endFile(); | 521 endFile(); |
483 | 522 |
484 _finished = true; | 523 _finished = true; |
485 } | 524 } |
486 | 525 |
| 526 /** |
| 527 * Generate [_hiddenLibraryExports] from [_exports] and [_librariesByPath]. |
| 528 */ |
| 529 Map<String, List<Export>> _generateHiddenLibraryExports() { |
| 530 // First generate a map `exported path => exporter path => Export`. The |
| 531 // inner map makes it easier to merge multiple exports of the same library |
| 532 // by the same exporter. |
| 533 var hiddenLibraryExportMaps = <String, Map<String, Export>>{}; |
| 534 _exports.exports.forEach((exporter, exports) { |
| 535 var library = _librariesByPath[exporter]; |
| 536 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 537 if (library == null) return; |
| 538 if (!shouldIncludeLibrary(library)) return; |
| 539 for (var export in exports) { |
| 540 var library = _librariesByPath[export.path]; |
| 541 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 542 if (library == null) continue; |
| 543 if (shouldIncludeLibrary(library)) continue; |
| 544 |
| 545 var hiddenExports = _exports.transitiveExports(export.path) |
| 546 .map((transitiveExport) => export.compose(transitiveExport)) |
| 547 .toList(); |
| 548 hiddenExports.add(export); |
| 549 |
| 550 for (var hiddenExport in hiddenExports) { |
| 551 var exportsByExporterPath = hiddenLibraryExportMaps |
| 552 .putIfAbsent(hiddenExport.path, () => <String, Export>{}); |
| 553 addOrMergeExport(exportsByExporterPath, exporter, hiddenExport); |
| 554 } |
| 555 } |
| 556 }); |
| 557 |
| 558 // Now sort the values of the inner maps of `hiddenLibraryExportMaps` to get |
| 559 // the final value of `_hiddenLibraryExports`. |
| 560 var hiddenLibraryExports = <String, List<Export>>{}; |
| 561 hiddenLibraryExportMaps.forEach((exporteePath, exportsByExporterPath) { |
| 562 int rank(Export export) { |
| 563 if (export.show.isEmpty && export.hide.isEmpty) return 0; |
| 564 if (export.show.isEmpty) return export.hide.length; |
| 565 // Multiply by 1000 to ensure this sorts after an export with hides. |
| 566 return 1000 * export.show.length; |
| 567 } |
| 568 |
| 569 var exports = exportsByExporterPath.values.toList(); |
| 570 exports.sort((export1, export2) { |
| 571 var comparison = Comparable.compare(rank(export1), rank(export2)); |
| 572 if (comparison != 0) return comparison; |
| 573 |
| 574 var library1 = _librariesByPath[export1.exporter]; |
| 575 var library2 = _librariesByPath[export2.exporter]; |
| 576 return Comparable.compare(library1.displayName, library2.displayName); |
| 577 }); |
| 578 hiddenLibraryExports[exporteePath] = exports; |
| 579 }); |
| 580 return hiddenLibraryExports; |
| 581 } |
| 582 |
487 MdnComment lookupMdnComment(Mirror mirror) => null; | 583 MdnComment lookupMdnComment(Mirror mirror) => null; |
488 | 584 |
489 void startFile(String path) { | 585 void startFile(String path) { |
490 _filePath = new Path(path); | 586 _filePath = new Path(path); |
491 _file = new StringBuffer(); | 587 _file = new StringBuffer(); |
492 } | 588 } |
493 | 589 |
494 void endFile() { | 590 void endFile() { |
495 final outPath = outputDir.join(_filePath); | 591 final outPath = outputDir.join(_filePath); |
496 final dir = new Directory.fromPath(outPath.directoryPath); | 592 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', | 957 writeHeader('${displayName(library)} Library', |
862 [displayName(library), libraryUrl(library)]); | 958 [displayName(library), libraryUrl(library)]); |
863 writeln('<h2><strong>${displayName(library)}</strong> library</h2>'); | 959 writeln('<h2><strong>${displayName(library)}</strong> library</h2>'); |
864 | 960 |
865 // Look for a comment for the entire library. | 961 // Look for a comment for the entire library. |
866 final comment = getLibraryComment(library); | 962 final comment = getLibraryComment(library); |
867 if (comment != null) { | 963 if (comment != null) { |
868 writeln('<div class="doc">${comment.html}</div>'); | 964 writeln('<div class="doc">${comment.html}</div>'); |
869 } | 965 } |
870 | 966 |
| 967 // Document the visible libraries exported by this library. |
| 968 docExports(library); |
| 969 |
871 // Document the top-level members. | 970 // Document the top-level members. |
872 docMembers(library); | 971 docMembers(library); |
873 | 972 |
874 // Document the types. | 973 // Document the types. |
875 final interfaces = <ClassMirror>[]; | 974 final interfaces = <ClassMirror>[]; |
876 final abstractClasses = <ClassMirror>[]; | 975 final abstractClasses = <ClassMirror>[]; |
877 final classes = <ClassMirror>[]; | 976 final classes = <ClassMirror>[]; |
878 final typedefs = <TypedefMirror>[]; | 977 final typedefs = <TypedefMirror>[]; |
879 final exceptions = <ClassMirror>[]; | 978 final exceptions = <ClassMirror>[]; |
880 | 979 |
881 for (ClassMirror type in orderByName(library.classes.values)) { | 980 var allClasses = _libraryClasses(library); |
| 981 for (ClassMirror type in orderByName(allClasses)) { |
882 if (!showPrivate && type.isPrivate) continue; | 982 if (!showPrivate && type.isPrivate) continue; |
883 | 983 |
884 if (isException(type)) { | 984 if (isException(type)) { |
885 exceptions.add(type); | 985 exceptions.add(type); |
886 } else if (type.isClass) { | 986 } else if (type.isClass) { |
887 if (type.isAbstract) { | 987 if (type.isAbstract) { |
888 abstractClasses.add(type); | 988 abstractClasses.add(type); |
889 } else { | 989 } else { |
890 classes.add(type); | 990 classes.add(type); |
891 } | 991 } |
892 } else if (type.isInterface){ | 992 } else if (type.isInterface){ |
893 interfaces.add(type); | 993 interfaces.add(type); |
894 } else if (type is TypedefMirror) { | 994 } else if (type is TypedefMirror) { |
895 typedefs.add(type); | 995 typedefs.add(type); |
896 } else { | 996 } else { |
897 throw new InternalError("internal error: unknown type $type."); | 997 throw new InternalError("internal error: unknown type $type."); |
898 } | 998 } |
899 } | 999 } |
900 | 1000 |
901 docTypes(interfaces, 'Interfaces'); | 1001 docTypes(interfaces, 'Interfaces'); |
902 docTypes(abstractClasses, 'Abstract Classes'); | 1002 docTypes(abstractClasses, 'Abstract Classes'); |
903 docTypes(classes, 'Classes'); | 1003 docTypes(classes, 'Classes'); |
904 docTypes(typedefs, 'Typedefs'); | 1004 docTypes(typedefs, 'Typedefs'); |
905 docTypes(exceptions, 'Exceptions'); | 1005 docTypes(exceptions, 'Exceptions'); |
906 | 1006 |
907 writeFooter(); | 1007 writeFooter(); |
908 endFile(); | 1008 endFile(); |
909 | 1009 |
910 for (final type in library.classes.values) { | 1010 for (final type in allClasses) { |
911 if (!showPrivate && type.isPrivate) continue; | 1011 if (!showPrivate && type.isPrivate) continue; |
912 | 1012 |
913 docType(type); | 1013 docType(type); |
914 } | 1014 } |
915 } | 1015 } |
916 | 1016 |
917 void docTypes(List types, String header) { | 1017 void docTypes(List types, String header) { |
918 if (types.length == 0) return; | 1018 if (types.length == 0) return; |
919 | 1019 |
920 writeln('<div>'); | 1020 writeln('<div>'); |
(...skipping 27 matching lines...) Expand all Loading... |
948 } else if (type.isClass) { | 1048 } else if (type.isClass) { |
949 if (type.isAbstract) { | 1049 if (type.isAbstract) { |
950 kind = 'abstract class'; | 1050 kind = 'abstract class'; |
951 } else { | 1051 } else { |
952 kind = 'class'; | 1052 kind = 'class'; |
953 } | 1053 } |
954 } | 1054 } |
955 | 1055 |
956 final typeTitle = | 1056 final typeTitle = |
957 '${typeName(type)} ${kind}'; | 1057 '${typeName(type)} ${kind}'; |
958 writeHeader('$typeTitle / ${displayName(type.library)} Library', | 1058 var library = _libraryFor(type); |
959 [displayName(type.library), libraryUrl(type.library), | 1059 writeHeader('$typeTitle / ${displayName(library)} Library', |
| 1060 [displayName(library), libraryUrl(library), |
960 typeName(type), typeUrl(type)]); | 1061 typeName(type), typeUrl(type)]); |
961 writeln( | 1062 writeln( |
962 ''' | 1063 ''' |
963 <h2><strong>${typeName(type, showBounds: true)}</strong> | 1064 <h2><strong>${typeName(type, showBounds: true)}</strong> |
964 $kind | 1065 $kind |
965 </h2> | 1066 </h2> |
966 '''); | 1067 '''); |
967 writeln('<button id="show-inherited" class="show-inherited">' | 1068 writeln('<button id="show-inherited" class="show-inherited">' |
968 'Hide inherited</button>'); | 1069 'Hide inherited</button>'); |
969 | 1070 |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1141 | 1242 |
1142 static final Map<String, int> operatorOrderMap = (){ | 1243 static final Map<String, int> operatorOrderMap = (){ |
1143 var map = new Map<String, int>(); | 1244 var map = new Map<String, int>(); |
1144 var index = 0; | 1245 var index = 0; |
1145 for (String operator in operatorOrder) { | 1246 for (String operator in operatorOrder) { |
1146 map[operator] = index++; | 1247 map[operator] = index++; |
1147 } | 1248 } |
1148 return map; | 1249 return map; |
1149 }(); | 1250 }(); |
1150 | 1251 |
| 1252 void docExports(LibraryMirror library) { |
| 1253 // TODO(nweiz): show `dart:` library exports. |
| 1254 var exportLinks = _exports.transitiveExports(_libraryPath(library)) |
| 1255 .map((export) { |
| 1256 var library = _librariesByPath[export.path]; |
| 1257 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 1258 if (library == null) return null; |
| 1259 // Only link to publically visible libraries. |
| 1260 if (!shouldIncludeLibrary(library)) return null; |
| 1261 |
| 1262 var memberNames = export.show.isEmpty ? export.hide : export.show; |
| 1263 var memberLinks = memberNames.map((name) { |
| 1264 return md.renderToHtml([resolveNameReference( |
| 1265 name, currentLibrary: library)]); |
| 1266 }).join(', '); |
| 1267 var combinator = ''; |
| 1268 if (!export.show.isEmpty) { |
| 1269 combinator = ' show $memberLinks'; |
| 1270 } else if (!export.hide.isEmpty) { |
| 1271 combinator = ' hide $memberLinks'; |
| 1272 } |
| 1273 |
| 1274 return '<ul>${a(libraryUrl(library), displayName(library))}' |
| 1275 '$combinator</ul>'; |
| 1276 }).where((link) => link != null); |
| 1277 |
| 1278 if (!exportLinks.isEmpty) { |
| 1279 writeln('<h3>Exports</h3>'); |
| 1280 writeln('<ul>'); |
| 1281 writeln(exportLinks.join('\n')); |
| 1282 writeln('</ul>'); |
| 1283 } |
| 1284 } |
| 1285 |
1151 void docMembers(ContainerMirror host) { | 1286 void docMembers(ContainerMirror host) { |
1152 // Collect the different kinds of members. | 1287 // Collect the different kinds of members. |
1153 final staticMethods = []; | 1288 final staticMethods = []; |
1154 final staticGetters = new Map<String,MemberMirror>(); | 1289 final staticGetters = new Map<String,MemberMirror>(); |
1155 final staticSetters = new Map<String,MemberMirror>(); | 1290 final staticSetters = new Map<String,MemberMirror>(); |
1156 final memberMap = new Map<String,MemberMirror>(); | 1291 final memberMap = new Map<String,MemberMirror>(); |
1157 final instanceMethods = []; | 1292 final instanceMethods = []; |
1158 final instanceOperators = []; | 1293 final instanceOperators = []; |
1159 final instanceGetters = new Map<String,MemberMirror>(); | 1294 final instanceGetters = new Map<String,MemberMirror>(); |
1160 final instanceSetters = new Map<String,MemberMirror>(); | 1295 final instanceSetters = new Map<String,MemberMirror>(); |
1161 final constructors = []; | 1296 final constructors = []; |
1162 | 1297 |
1163 host.members.forEach((_, MemberMirror member) { | 1298 var hostMembers = host is LibraryMirror ? |
1164 if (!showPrivate && member.isPrivate) return; | 1299 _libraryMembers(host) : host.members.values; |
| 1300 for (var member in hostMembers) { |
| 1301 if (!showPrivate && member.isPrivate) continue; |
1165 if (host is LibraryMirror || member.isStatic) { | 1302 if (host is LibraryMirror || member.isStatic) { |
1166 if (member is MethodMirror) { | 1303 if (member is MethodMirror) { |
1167 if (member.isGetter) { | 1304 if (member.isGetter) { |
1168 staticGetters[member.displayName] = member; | 1305 staticGetters[member.displayName] = member; |
1169 } else if (member.isSetter) { | 1306 } else if (member.isSetter) { |
1170 staticSetters[member.displayName] = member; | 1307 staticSetters[member.displayName] = member; |
1171 } else { | 1308 } else { |
1172 staticMethods.add(member); | 1309 staticMethods.add(member); |
1173 } | 1310 } |
1174 } else if (member is VariableMirror) { | 1311 } else if (member is VariableMirror) { |
1175 staticGetters[member.displayName] = member; | 1312 staticGetters[member.displayName] = member; |
1176 } | 1313 } |
1177 } | 1314 } |
1178 }); | 1315 } |
1179 | 1316 |
1180 if (host is ClassMirror) { | 1317 if (host is ClassMirror) { |
1181 var iterable = new HierarchyIterable(host, includeType: true); | 1318 var iterable = new HierarchyIterable(host, includeType: true); |
1182 for (ClassMirror type in iterable) { | 1319 for (ClassMirror type in iterable) { |
1183 if (!host.isObject && !inheritFromObject && type.isObject) continue; | 1320 if (!host.isObject && !inheritFromObject && type.isObject) continue; |
1184 | 1321 |
1185 type.members.forEach((_, MemberMirror member) { | 1322 type.members.forEach((_, MemberMirror member) { |
1186 if (member.isStatic) return; | 1323 if (member.isStatic) return; |
1187 if (!showPrivate && member.isPrivate) return; | 1324 if (!showPrivate && member.isPrivate) return; |
1188 | 1325 |
(...skipping 23 matching lines...) Expand all Loading... |
1212 } | 1349 } |
1213 } | 1350 } |
1214 | 1351 |
1215 bool allMethodsInherited = true; | 1352 bool allMethodsInherited = true; |
1216 bool allPropertiesInherited = true; | 1353 bool allPropertiesInherited = true; |
1217 bool allOperatorsInherited = true; | 1354 bool allOperatorsInherited = true; |
1218 memberMap.forEach((_, MemberMirror member) { | 1355 memberMap.forEach((_, MemberMirror member) { |
1219 if (member is MethodMirror) { | 1356 if (member is MethodMirror) { |
1220 if (member.isGetter) { | 1357 if (member.isGetter) { |
1221 instanceGetters[member.displayName] = member; | 1358 instanceGetters[member.displayName] = member; |
1222 if (member.owner == host) { | 1359 if (_ownerFor(member) == host) { |
1223 allPropertiesInherited = false; | 1360 allPropertiesInherited = false; |
1224 } | 1361 } |
1225 } else if (member.isSetter) { | 1362 } else if (member.isSetter) { |
1226 instanceSetters[member.displayName] = member; | 1363 instanceSetters[member.displayName] = member; |
1227 if (member.owner == host) { | 1364 if (_ownerFor(member) == host) { |
1228 allPropertiesInherited = false; | 1365 allPropertiesInherited = false; |
1229 } | 1366 } |
1230 } else if (member.isOperator) { | 1367 } else if (member.isOperator) { |
1231 instanceOperators.add(member); | 1368 instanceOperators.add(member); |
1232 if (member.owner == host) { | 1369 if (_ownerFor(member) == host) { |
1233 allOperatorsInherited = false; | 1370 allOperatorsInherited = false; |
1234 } | 1371 } |
1235 } else { | 1372 } else { |
1236 instanceMethods.add(member); | 1373 instanceMethods.add(member); |
1237 if (member.owner == host) { | 1374 if (_ownerFor(member) == host) { |
1238 allMethodsInherited = false; | 1375 allMethodsInherited = false; |
1239 } | 1376 } |
1240 } | 1377 } |
1241 } else if (member is VariableMirror) { | 1378 } else if (member is VariableMirror) { |
1242 instanceGetters[member.displayName] = member; | 1379 instanceGetters[member.displayName] = member; |
1243 if (member.owner == host) { | 1380 if (_ownerFor(member) == host) { |
1244 allPropertiesInherited = false; | 1381 allPropertiesInherited = false; |
1245 } | 1382 } |
1246 } | 1383 } |
1247 }); | 1384 }); |
1248 | 1385 |
1249 instanceOperators.sort((MethodMirror a, MethodMirror b) { | 1386 instanceOperators.sort((MethodMirror a, MethodMirror b) { |
1250 return operatorOrderMap[a.simpleName].compareTo( | 1387 return operatorOrderMap[a.simpleName].compareTo( |
1251 operatorOrderMap[b.simpleName]); | 1388 operatorOrderMap[b.simpleName]); |
1252 }); | 1389 }); |
1253 | 1390 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1298 assert(getter is MethodMirror); | 1435 assert(getter is MethodMirror); |
1299 docProperty(host, getter, null); | 1436 docProperty(host, getter, null); |
1300 } | 1437 } |
1301 } else if (getter == null) { | 1438 } else if (getter == null) { |
1302 // We only have a setter => Document as a method. | 1439 // We only have a setter => Document as a method. |
1303 assert(setter is MethodMirror); | 1440 assert(setter is MethodMirror); |
1304 docMethod(host, setter); | 1441 docMethod(host, setter); |
1305 } else { | 1442 } else { |
1306 DocComment getterComment = getMemberComment(getter); | 1443 DocComment getterComment = getMemberComment(getter); |
1307 DocComment setterComment = getMemberComment(setter); | 1444 DocComment setterComment = getMemberComment(setter); |
1308 if (getter.owner != setter.owner || | 1445 if (_ownerFor(getter) != _ownerFor(setter) || |
1309 getterComment != null && setterComment != null) { | 1446 getterComment != null && setterComment != null) { |
1310 // Both have comments or are not declared in the same class | 1447 // Both have comments or are not declared in the same class |
1311 // => Documents separately. | 1448 // => Documents separately. |
1312 if (getter is VariableMirror) { | 1449 if (getter is VariableMirror) { |
1313 // Document field as a getter (setter is inherited). | 1450 // Document field as a getter (setter is inherited). |
1314 docField(host, getter, asGetter: true); | 1451 docField(host, getter, asGetter: true); |
1315 } else { | 1452 } else { |
1316 docMethod(host, getter); | 1453 docMethod(host, getter); |
1317 } | 1454 } |
1318 if (setter is VariableMirror) { | 1455 if (setter is VariableMirror) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1369 if (member.isGetter) { | 1506 if (member.isGetter) { |
1370 // Getter. | 1507 // Getter. |
1371 name = 'get $name'; | 1508 name = 'get $name'; |
1372 } else if (member.isSetter) { | 1509 } else if (member.isSetter) { |
1373 // Setter. | 1510 // Setter. |
1374 name = 'set $name'; | 1511 name = 'set $name'; |
1375 } | 1512 } |
1376 } | 1513 } |
1377 | 1514 |
1378 bool showCode = includeSource && !isAbstract; | 1515 bool showCode = includeSource && !isAbstract; |
1379 bool inherited = host != member.owner; | 1516 bool inherited = host != member.owner && member.owner is! LibraryMirror; |
1380 | 1517 |
1381 writeln('<div class="method${inherited ? ' inherited': ''}">' | 1518 writeln('<div class="method${inherited ? ' inherited': ''}">' |
1382 '<h4 id="${memberAnchor(member)}">'); | 1519 '<h4 id="${memberAnchor(member)}">'); |
1383 | 1520 |
1384 if (showCode) { | 1521 if (showCode) { |
1385 writeln('<button class="show-code">Code</button>'); | 1522 writeln('<button class="show-code">Code</button>'); |
1386 } | 1523 } |
1387 | 1524 |
1388 if (member is MethodMirror) { | 1525 if (member is MethodMirror) { |
1389 if (member.isConstructor) { | 1526 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:]. | 1597 * [host]. If [getter] is a [FieldMirror], [setter] must be [:null:]. |
1461 * Otherwise, if [getter] is a [MethodMirror], the property is considered | 1598 * Otherwise, if [getter] is a [MethodMirror], the property is considered |
1462 * final if [setter] is [:null:]. | 1599 * final if [setter] is [:null:]. |
1463 */ | 1600 */ |
1464 void docProperty(ContainerMirror host, | 1601 void docProperty(ContainerMirror host, |
1465 MemberMirror getter, MemberMirror setter) { | 1602 MemberMirror getter, MemberMirror setter) { |
1466 assert(getter != null); | 1603 assert(getter != null); |
1467 _totalMembers++; | 1604 _totalMembers++; |
1468 _currentMember = getter; | 1605 _currentMember = getter; |
1469 | 1606 |
1470 bool inherited = host != getter.owner; | 1607 bool inherited = host != getter.owner && getter.owner is! LibraryMirror; |
1471 | 1608 |
1472 writeln('<div class="field${inherited ? ' inherited' : ''}">' | 1609 writeln('<div class="field${inherited ? ' inherited' : ''}">' |
1473 '<h4 id="${memberAnchor(getter)}">'); | 1610 '<h4 id="${memberAnchor(getter)}">'); |
1474 | 1611 |
1475 if (includeSource) { | 1612 if (includeSource) { |
1476 writeln('<button class="show-code">Code</button>'); | 1613 writeln('<button class="show-code">Code</button>'); |
1477 } | 1614 } |
1478 | 1615 |
1479 bool isConst = false; | 1616 bool isConst = false; |
1480 bool isFinal; | 1617 bool isFinal; |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1667 String typeUrl(ContainerMirror type) { | 1804 String typeUrl(ContainerMirror type) { |
1668 if (type is LibraryMirror) { | 1805 if (type is LibraryMirror) { |
1669 return '${sanitize(type.simpleName)}.html'; | 1806 return '${sanitize(type.simpleName)}.html'; |
1670 } | 1807 } |
1671 if (type.library == null) { | 1808 if (type.library == null) { |
1672 return ''; | 1809 return ''; |
1673 } | 1810 } |
1674 // Always get the generic type to strip off any type parameters or | 1811 // 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 | 1812 // arguments. If the type isn't generic, genericType returns `this`, so it |
1676 // works for non-generic types too. | 1813 // works for non-generic types too. |
1677 return '${sanitize(displayName(type.library))}/' | 1814 return '${sanitize(displayName(_libraryFor(type)))}/' |
1678 '${type.originalDeclaration.simpleName}.html'; | 1815 '${type.originalDeclaration.simpleName}.html'; |
1679 } | 1816 } |
1680 | 1817 |
1681 /** Gets the URL for the documentation for [member]. */ | 1818 /** Gets the URL for the documentation for [member]. */ |
1682 String memberUrl(MemberMirror member) { | 1819 String memberUrl(MemberMirror member) { |
1683 String url = typeUrl(member.owner); | 1820 String url = typeUrl(_ownerFor(member)); |
1684 return '$url#${memberAnchor(member)}'; | 1821 return '$url#${memberAnchor(member)}'; |
1685 } | 1822 } |
1686 | 1823 |
1687 /** Gets the anchor id for the document for [member]. */ | 1824 /** Gets the anchor id for the document for [member]. */ |
1688 String memberAnchor(MemberMirror member) { | 1825 String memberAnchor(MemberMirror member) { |
1689 return member.simpleName; | 1826 return member.simpleName; |
1690 } | 1827 } |
1691 | 1828 |
1692 /** | 1829 /** |
1693 * Creates a hyperlink. Handles turning the [href] into an appropriate | 1830 * 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) { | 1889 if (type.isTypeVariable) { |
1753 // If we're using a type parameter within the body of a generic class then | 1890 // If we're using a type parameter within the body of a generic class then |
1754 // just link back up to the class. | 1891 // just link back up to the class. |
1755 write(a(typeUrl(enclosingType), type.simpleName)); | 1892 write(a(typeUrl(enclosingType), type.simpleName)); |
1756 return; | 1893 return; |
1757 } | 1894 } |
1758 | 1895 |
1759 assert(type is ClassMirror); | 1896 assert(type is ClassMirror); |
1760 | 1897 |
1761 // Link to the type. | 1898 // Link to the type. |
1762 if (shouldLinkToPublicApi(type.library)) { | 1899 var library = _libraryFor(type); |
| 1900 if (shouldLinkToPublicApi(library)) { |
1763 write('<a href="$API_LOCATION${typeUrl(type)}">${type.simpleName}</a>'); | 1901 write('<a href="$API_LOCATION${typeUrl(type)}">${type.simpleName}</a>'); |
1764 } else if (shouldIncludeLibrary(type.library)) { | 1902 } else if (shouldIncludeLibrary(library)) { |
1765 write(a(typeUrl(type), type.simpleName)); | 1903 write(a(typeUrl(type), type.simpleName)); |
1766 } else { | 1904 } else { |
1767 write(type.simpleName); | 1905 write(type.simpleName); |
1768 } | 1906 } |
1769 | 1907 |
1770 if (type.isOriginalDeclaration) { | 1908 if (type.isOriginalDeclaration) { |
1771 // Avoid calling [:typeArguments():] on a declaration. | 1909 // Avoid calling [:typeArguments():] on a declaration. |
1772 return; | 1910 return; |
1773 } | 1911 } |
1774 | 1912 |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1972 onDone: () => endFile()); | 2110 onDone: () => endFile()); |
1973 } | 2111 } |
1974 | 2112 |
1975 /** | 2113 /** |
1976 * Returns [:true:] if [type] should be regarded as an exception. | 2114 * Returns [:true:] if [type] should be regarded as an exception. |
1977 */ | 2115 */ |
1978 bool isException(TypeMirror type) { | 2116 bool isException(TypeMirror type) { |
1979 return type.simpleName.endsWith('Exception') || | 2117 return type.simpleName.endsWith('Exception') || |
1980 type.simpleName.endsWith('Error'); | 2118 type.simpleName.endsWith('Error'); |
1981 } | 2119 } |
| 2120 |
| 2121 /** |
| 2122 * Returns the absolute path to [library] on the filesystem, or `null` if the |
| 2123 * library doesn't exist on the local filesystem. |
| 2124 */ |
| 2125 String _libraryPath(LibraryMirror library) => |
| 2126 importUriToPath(library.uri, packageRoot: _packageRoot); |
| 2127 |
| 2128 /** |
| 2129 * Returns a list of classes in [library], including classes it exports from |
| 2130 * hidden libraries. |
| 2131 */ |
| 2132 List<ClassMirror> _libraryClasses(LibraryMirror library) => |
| 2133 _libraryContents(library, (lib) => lib.classes.values); |
| 2134 |
| 2135 /** |
| 2136 * Returns a list of top-level members in [library], including members it |
| 2137 * exports from hidden libraries. |
| 2138 */ |
| 2139 List<MemberMirror> _libraryMembers(LibraryMirror library) => |
| 2140 _libraryContents(library, (lib) => lib.members.values); |
| 2141 |
| 2142 |
| 2143 /** |
| 2144 * Returns a list of elements in [library], including elements it exports from |
| 2145 * hidden libraries. [fn] should return the element list for a single library, |
| 2146 * which will then be merged across all exported libraries. |
| 2147 */ |
| 2148 List<DeclarationMirror> _libraryContents(LibraryMirror library, |
| 2149 List<DeclarationMirror> fn(LibraryMirror)) { |
| 2150 var contents = fn(library).toList(); |
| 2151 var path = _libraryPath(library); |
| 2152 if (path == null) return contents; |
| 2153 |
| 2154 contents.addAll(_exports.exports[path].expand((export) { |
| 2155 var exportedLibrary = _librariesByPath[export.path]; |
| 2156 // TODO(nweiz): remove this check when issue 9645 is fixed. |
| 2157 if (exportedLibrary == null) return []; |
| 2158 if (shouldIncludeLibrary(exportedLibrary)) return []; |
| 2159 return fn(exportedLibrary).where((declaration) => |
| 2160 export.isMemberVisible(declaration.displayName)); |
| 2161 })); |
| 2162 return contents; |
| 2163 } |
| 2164 |
| 2165 /** |
| 2166 * Returns the library in which [type] was defined. If [type] was defined in a |
| 2167 * hidden library that was exported by another library, this returns the |
| 2168 * exporter. |
| 2169 */ |
| 2170 LibraryMirror _libraryFor(TypeMirror type) => |
| 2171 _visibleLibrary(type.library, type.displayName); |
| 2172 |
| 2173 /** |
| 2174 * Returns the owner of [declaration]. If [declaration]'s owner is a hidden |
| 2175 * library that was exported by another library, this returns the exporter. |
| 2176 */ |
| 2177 DeclarationMirror _ownerFor(DeclarationMirror declaration) { |
| 2178 var owner = declaration.owner; |
| 2179 if (owner is! LibraryMirror) return owner; |
| 2180 return _visibleLibrary(owner, declaration.displayName); |
| 2181 } |
| 2182 |
| 2183 /** |
| 2184 * Returns the best visible library that exports [name] from [library]. If |
| 2185 * [library] is public, it will be returned. |
| 2186 */ |
| 2187 LibraryMirror _visibleLibrary(LibraryMirror library, String name) { |
| 2188 if (library == null) return null; |
| 2189 |
| 2190 var exports = _hiddenLibraryExports[_libraryPath(library)]; |
| 2191 if (exports == null) return library; |
| 2192 |
| 2193 var export = exports.firstWhere( |
| 2194 (exp) => exp.isMemberVisible(name), |
| 2195 orElse: () => null); |
| 2196 if (export == null) return library; |
| 2197 return _librariesByPath[export.exporter]; |
| 2198 } |
1982 } | 2199 } |
1983 | 2200 |
1984 /** | 2201 /** |
1985 * Used to report an unexpected error in the DartDoc tool or the | 2202 * Used to report an unexpected error in the DartDoc tool or the |
1986 * underlying data | 2203 * underlying data |
1987 */ | 2204 */ |
1988 class InternalError { | 2205 class InternalError { |
1989 final String message; | 2206 final String message; |
1990 const InternalError(this.message); | 2207 const InternalError(this.message); |
1991 String toString() => "InternalError: '$message'"; | 2208 String toString() => "InternalError: '$message'"; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2069 return ''' | 2286 return ''' |
2070 <div class="mdn"> | 2287 <div class="mdn"> |
2071 $mdnComment | 2288 $mdnComment |
2072 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> | 2289 <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> |
2073 </div> | 2290 </div> |
2074 '''; | 2291 '''; |
2075 } | 2292 } |
2076 | 2293 |
2077 String toString() => mdnComment; | 2294 String toString() => mdnComment; |
2078 } | 2295 } |
OLD | NEW |