| OLD | NEW |
| 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:async'; |
| 19 import 'dart:io'; | 20 import 'dart:io'; |
| 20 import 'dart:math'; | 21 import 'dart:math'; |
| 21 import 'dart:uri'; | 22 import 'dart:uri'; |
| 22 import 'dart:json'; | 23 import 'dart:json' as json; |
| 23 | 24 |
| 24 import '../../compiler/implementation/mirrors/mirrors.dart'; | 25 import '../../compiler/implementation/mirrors/mirrors.dart'; |
| 25 import '../../compiler/implementation/mirrors/mirrors_util.dart'; | 26 import '../../compiler/implementation/mirrors/mirrors_util.dart'; |
| 26 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; | 27 import '../../compiler/implementation/mirrors/dart2js_mirror.dart' as dart2js; |
| 27 import 'classify.dart'; | 28 import 'classify.dart'; |
| 28 import 'universe_serializer.dart'; | 29 import 'universe_serializer.dart'; |
| 29 import 'markdown.dart' as md; | 30 import 'markdown.dart' as md; |
| 30 import 'src/json_serializer.dart' as json_serializer; | 31 import 'src/json_serializer.dart' as json_serializer; |
| 31 import '../../compiler/implementation/scanner/scannerlib.dart' as dart2js; | 32 import '../../compiler/implementation/scanner/scannerlib.dart' as dart2js; |
| 32 import '../../libraries.dart'; | 33 import '../../libraries.dart'; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 'lib/_internal/dartdoc/lib/src/client/client-$clientScript.dart'); | 134 'lib/_internal/dartdoc/lib/src/client/client-$clientScript.dart'); |
| 134 var jsPath = outputDir.append('client-$clientScript.js'); | 135 var jsPath = outputDir.append('client-$clientScript.js'); |
| 135 | 136 |
| 136 var completer = new Completer<bool>(); | 137 var completer = new Completer<bool>(); |
| 137 var compilation = new Compilation(dartPath, libPath); | 138 var compilation = new Compilation(dartPath, libPath); |
| 138 Future<String> result = compilation.compileToJavaScript(); | 139 Future<String> result = compilation.compileToJavaScript(); |
| 139 result.then((jsCode) { | 140 result.then((jsCode) { |
| 140 writeString(new File.fromPath(jsPath), jsCode); | 141 writeString(new File.fromPath(jsPath), jsCode); |
| 141 completer.complete(true); | 142 completer.complete(true); |
| 142 }); | 143 }); |
| 143 result.handleException((e) => completer.completeException(e)); | 144 result.catchError((e) => completer.completeError(e.error, e.stackTrace)); |
| 144 return completer.future; | 145 return completer.future; |
| 145 } | 146 } |
| 146 | 147 |
| 147 class Dartdoc { | 148 class Dartdoc { |
| 148 | 149 |
| 149 /** Set to `false` to not include the source code in the generated docs. */ | 150 /** Set to `false` to not include the source code in the generated docs. */ |
| 150 bool includeSource = true; | 151 bool includeSource = true; |
| 151 | 152 |
| 152 /** | 153 /** |
| 153 * Dartdoc can generate docs in a few different ways based on how dynamic you | 154 * Dartdoc can generate docs in a few different ways based on how dynamic you |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 } | 330 } |
| 330 | 331 |
| 331 void documentLibraries(List<Path> libraryList, Path libPath, Path pkgPath) { | 332 void documentLibraries(List<Path> libraryList, Path libPath, Path pkgPath) { |
| 332 final compilation = new Compilation.library(libraryList, libPath, pkgPath); | 333 final compilation = new Compilation.library(libraryList, libPath, pkgPath); |
| 333 _document(compilation); | 334 _document(compilation); |
| 334 } | 335 } |
| 335 | 336 |
| 336 void _document(Compilation compilation) { | 337 void _document(Compilation compilation) { |
| 337 // Sort the libraries by name (not key). | 338 // Sort the libraries by name (not key). |
| 338 _sortedLibraries = new List<LibraryMirror>.from( | 339 _sortedLibraries = new List<LibraryMirror>.from( |
| 339 compilation.mirrors.libraries.values.filter( | 340 compilation.mirrors.libraries.values.where(shouldIncludeLibrary)); |
| 340 shouldIncludeLibrary)); | |
| 341 _sortedLibraries.sort((x, y) { | 341 _sortedLibraries.sort((x, y) { |
| 342 return displayName(x).toUpperCase().compareTo( | 342 return displayName(x).toUpperCase().compareTo( |
| 343 displayName(y).toUpperCase()); | 343 displayName(y).toUpperCase()); |
| 344 }); | 344 }); |
| 345 | 345 |
| 346 // Generate the docs. | 346 // Generate the docs. |
| 347 if (mode == MODE_LIVE_NAV) { | 347 if (mode == MODE_LIVE_NAV) { |
| 348 docNavigationJson(); | 348 docNavigationJson(); |
| 349 } else { | 349 } else { |
| 350 docNavigationDart(); | 350 docNavigationDart(); |
| 351 } | 351 } |
| 352 | 352 |
| 353 docIndex(); | 353 docIndex(); |
| 354 for (final library in _sortedLibraries) { | 354 for (final library in _sortedLibraries) { |
| 355 docLibrary(library); | 355 docLibrary(library); |
| 356 } | 356 } |
| 357 | 357 |
| 358 if (generateAppCache) { | 358 if (generateAppCache) { |
| 359 generateAppCacheManifest(); | 359 generateAppCacheManifest(); |
| 360 } | 360 } |
| 361 | 361 |
| 362 startFile("apidoc.json"); | 362 startFile("apidoc.json"); |
| 363 var libraries = _sortedLibraries.map( | 363 var libraries = _sortedLibraries.mappedBy( |
| 364 (lib) => new LibraryElement(lib.qualifiedName, lib, _comments)); | 364 (lib) => new LibraryElement(lib.qualifiedName, lib, _comments)) |
| 365 .toList(); |
| 365 write(json_serializer.serialize(libraries)); | 366 write(json_serializer.serialize(libraries)); |
| 366 endFile(); | 367 endFile(); |
| 367 } | 368 } |
| 368 | 369 |
| 369 void startFile(String path) { | 370 void startFile(String path) { |
| 370 _filePath = new Path(path); | 371 _filePath = new Path(path); |
| 371 _file = new StringBuffer(); | 372 _file = new StringBuffer(); |
| 372 } | 373 } |
| 373 | 374 |
| 374 void endFile() { | 375 void endFile() { |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 void docIndexLibrary(LibraryMirror library) { | 538 void docIndexLibrary(LibraryMirror library) { |
| 538 writeln('<h4>${a(libraryUrl(library), displayName(library))}</h4>'); | 539 writeln('<h4>${a(libraryUrl(library), displayName(library))}</h4>'); |
| 539 } | 540 } |
| 540 | 541 |
| 541 /** | 542 /** |
| 542 * Walks the libraries and creates a JSON object containing the data needed | 543 * Walks the libraries and creates a JSON object containing the data needed |
| 543 * to generate navigation for them. | 544 * to generate navigation for them. |
| 544 */ | 545 */ |
| 545 void docNavigationJson() { | 546 void docNavigationJson() { |
| 546 startFile('nav.json'); | 547 startFile('nav.json'); |
| 547 writeln(JSON.stringify(createNavigationInfo())); | 548 writeln(json.stringify(createNavigationInfo())); |
| 548 endFile(); | 549 endFile(); |
| 549 } | 550 } |
| 550 | 551 |
| 551 void docNavigationDart() { | 552 void docNavigationDart() { |
| 552 final dir = new Directory.fromPath(tmpPath); | 553 final dir = new Directory.fromPath(tmpPath); |
| 553 if (!dir.existsSync()) { | 554 if (!dir.existsSync()) { |
| 554 // TODO(3914): Hack to avoid 'file already exists' exception | 555 // TODO(3914): Hack to avoid 'file already exists' exception |
| 555 // thrown due to invalid result from dir.existsSync() (probably due to | 556 // thrown due to invalid result from dir.existsSync() (probably due to |
| 556 // race conditions). | 557 // race conditions). |
| 557 try { | 558 try { |
| 558 dir.createSync(); | 559 dir.createSync(); |
| 559 } on DirectoryIOException catch (e) { | 560 } on DirectoryIOException catch (e) { |
| 560 // Ignore. | 561 // Ignore. |
| 561 } | 562 } |
| 562 } | 563 } |
| 563 String jsonString = JSON.stringify(createNavigationInfo()); | 564 String jsonString = json.stringify(createNavigationInfo()); |
| 564 String dartString = jsonString.replaceAll(r"$", r"\$"); | 565 String dartString = jsonString.replaceAll(r"$", r"\$"); |
| 565 final filePath = tmpPath.append('nav.dart'); | 566 final filePath = tmpPath.append('nav.dart'); |
| 566 writeString(new File.fromPath(filePath), | 567 writeString(new File.fromPath(filePath), |
| 567 'get json => $dartString;'); | 568 'get json => $dartString;'); |
| 568 } | 569 } |
| 569 | 570 |
| 570 Path get tmpPath => dartdocPath.append('tmp'); | 571 Path get tmpPath => dartdocPath.append('tmp'); |
| 571 | 572 |
| 572 void cleanup() { | 573 void cleanup() { |
| 573 final dir = new Directory.fromPath(tmpPath); | 574 final dir = new Directory.fromPath(tmpPath); |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 897 void docInheritance(ClassMirror type) { | 898 void docInheritance(ClassMirror type) { |
| 898 // Don't show the inheritance details for Object. It doesn't have any base | 899 // Don't show the inheritance details for Object. It doesn't have any base |
| 899 // class (obviously) and it has too many subclasses to be useful. | 900 // class (obviously) and it has too many subclasses to be useful. |
| 900 if (type.isObject) return; | 901 if (type.isObject) return; |
| 901 | 902 |
| 902 // Writes an unordered list of references to types with an optional header. | 903 // Writes an unordered list of references to types with an optional header. |
| 903 listTypes(types, header) { | 904 listTypes(types, header) { |
| 904 if (types == null) return; | 905 if (types == null) return; |
| 905 | 906 |
| 906 // Filter out injected types. (JavaScriptIndexingBehavior) | 907 // Filter out injected types. (JavaScriptIndexingBehavior) |
| 907 types = new List.from(types.filter((t) => t.library != null)); | 908 types = new List.from(types.where((t) => t.library != null)); |
| 908 | 909 |
| 909 var publicTypes; | 910 var publicTypes; |
| 910 if (showPrivate) { | 911 if (showPrivate) { |
| 911 publicTypes = types; | 912 publicTypes = types; |
| 912 } else { | 913 } else { |
| 913 // Skip private types. | 914 // Skip private types. |
| 914 publicTypes = new List.from(types.filter((t) => !t.isPrivate)); | 915 publicTypes = new List.from(types.where((t) => !t.isPrivate)); |
| 915 } | 916 } |
| 916 if (publicTypes.length == 0) return; | 917 if (publicTypes.length == 0) return; |
| 917 | 918 |
| 918 writeln('<h3>$header</h3>'); | 919 writeln('<h3>$header</h3>'); |
| 919 writeln('<p>'); | 920 writeln('<p>'); |
| 920 bool first = true; | 921 bool first = true; |
| 921 for (final t in publicTypes) { | 922 for (final t in publicTypes) { |
| 922 if (!first) write(', '); | 923 if (!first) write(', '); |
| 923 typeSpan(t); | 924 typeSpan(t); |
| 924 first = false; | 925 first = false; |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1203 } | 1204 } |
| 1204 } else { | 1205 } else { |
| 1205 // Document as field. | 1206 // Document as field. |
| 1206 docProperty(host, getter, setter); | 1207 docProperty(host, getter, setter); |
| 1207 } | 1208 } |
| 1208 } | 1209 } |
| 1209 } | 1210 } |
| 1210 writeln('</div>'); | 1211 writeln('</div>'); |
| 1211 } | 1212 } |
| 1212 | 1213 |
| 1213 void docMethods(ContainerMirror host, String title, List<MethodMirror> methods
, | 1214 void docMethods(ContainerMirror host, String title, List<Mirror> methods, |
| 1214 {bool allInherited}) { | 1215 {bool allInherited}) { |
| 1215 if (methods.length > 0) { | 1216 if (methods.length > 0) { |
| 1216 writeln('<div${allInherited ? ' class="inherited"' : ''}>'); | 1217 writeln('<div${allInherited ? ' class="inherited"' : ''}>'); |
| 1217 writeln('<h3>$title</h3>'); | 1218 writeln('<h3>$title</h3>'); |
| 1218 for (final method in methods) { | 1219 for (MethodMirror method in methods) { |
| 1219 docMethod(host, method); | 1220 docMethod(host, method); |
| 1220 } | 1221 } |
| 1221 writeln('</div>'); | 1222 writeln('</div>'); |
| 1222 } | 1223 } |
| 1223 } | 1224 } |
| 1224 | 1225 |
| 1225 /** | 1226 /** |
| 1226 * Documents the [member] declared in [host]. Handles all kinds of members | 1227 * Documents the [member] declared in [host]. Handles all kinds of members |
| 1227 * including getters, setters, and constructors. If [member] is a | 1228 * including getters, setters, and constructors. If [member] is a |
| 1228 * [FieldMirror] it is documented as a getter or setter depending upon the | 1229 * [FieldMirror] it is documented as a getter or setter depending upon the |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1664 // TODO(rnystrom): Do we need to handle ParameterTypes here like | 1665 // TODO(rnystrom): Do we need to handle ParameterTypes here like |
| 1665 // annotation() does? | 1666 // annotation() does? |
| 1666 return a(typeUrl(type), typeName(type), 'crossref'); | 1667 return a(typeUrl(type), typeName(type), 'crossref'); |
| 1667 } | 1668 } |
| 1668 | 1669 |
| 1669 /** Generates a human-friendly string representation for a type. */ | 1670 /** Generates a human-friendly string representation for a type. */ |
| 1670 typeName(TypeMirror type, {bool showBounds: false}) { | 1671 typeName(TypeMirror type, {bool showBounds: false}) { |
| 1671 if (type.isVoid) { | 1672 if (type.isVoid) { |
| 1672 return 'void'; | 1673 return 'void'; |
| 1673 } | 1674 } |
| 1675 if (type.isDynamic) { |
| 1676 return 'dynamic'; |
| 1677 } |
| 1674 if (type is TypeVariableMirror) { | 1678 if (type is TypeVariableMirror) { |
| 1675 return type.simpleName; | 1679 return type.simpleName; |
| 1676 } | 1680 } |
| 1677 assert(type is ClassMirror); | 1681 assert(type is ClassMirror); |
| 1678 | 1682 |
| 1679 // See if it's a generic type. | 1683 // See if it's a generic type. |
| 1680 if (type.isOriginalDeclaration) { | 1684 if (type.isOriginalDeclaration) { |
| 1681 final typeParams = []; | 1685 final typeParams = []; |
| 1682 for (final typeParam in type.originalDeclaration.typeVariables) { | 1686 for (final typeParam in type.originalDeclaration.typeVariables) { |
| 1683 if (showBounds && | 1687 if (showBounds && |
| 1684 (typeParam.upperBound != null) && | 1688 (typeParam.upperBound != null) && |
| 1685 !typeParam.upperBound.isObject) { | 1689 !typeParam.upperBound.isObject) { |
| 1686 final bound = typeName(typeParam.upperBound, showBounds: true); | 1690 final bound = typeName(typeParam.upperBound, showBounds: true); |
| 1687 typeParams.add('${typeParam.simpleName} extends $bound'); | 1691 typeParams.add('${typeParam.simpleName} extends $bound'); |
| 1688 } else { | 1692 } else { |
| 1689 typeParams.add(typeParam.simpleName); | 1693 typeParams.add(typeParam.simpleName); |
| 1690 } | 1694 } |
| 1691 } | 1695 } |
| 1692 if (typeParams.isEmpty) { | 1696 if (typeParams.isEmpty) { |
| 1693 return type.simpleName; | 1697 return type.simpleName; |
| 1694 } | 1698 } |
| 1695 final params = Strings.join(typeParams, ', '); | 1699 final params = Strings.join(typeParams, ', '); |
| 1696 return '${type.simpleName}<$params>'; | 1700 return '${type.simpleName}<$params>'; |
| 1697 } | 1701 } |
| 1698 | 1702 |
| 1699 // See if it's an instantiation of a generic type. | 1703 // See if it's an instantiation of a generic type. |
| 1700 final typeArgs = type.typeArguments; | 1704 final typeArgs = type.typeArguments; |
| 1701 if (typeArgs.length > 0) { | 1705 if (typeArgs.length > 0) { |
| 1702 final args = Strings.join(typeArgs.map((arg) => typeName(arg)), ', '); | 1706 final args = |
| 1707 Strings.join(typeArgs.mappedBy((arg) => typeName(arg)), ', '); |
| 1703 return '${type.originalDeclaration.simpleName}<$args>'; | 1708 return '${type.originalDeclaration.simpleName}<$args>'; |
| 1704 } | 1709 } |
| 1705 | 1710 |
| 1706 // Regular type. | 1711 // Regular type. |
| 1707 return type.simpleName; | 1712 return type.simpleName; |
| 1708 } | 1713 } |
| 1709 | 1714 |
| 1710 /** | 1715 /** |
| 1711 * Remove leading indentation to line up with first line. | 1716 * Remove leading indentation to line up with first line. |
| 1712 */ | 1717 */ |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1880 final ClassMirror inheritedFrom; | 1885 final ClassMirror inheritedFrom; |
| 1881 | 1886 |
| 1882 DocComment(this.text, [this.inheritedFrom = null]) { | 1887 DocComment(this.text, [this.inheritedFrom = null]) { |
| 1883 assert(text != null && !text.trim().isEmpty); | 1888 assert(text != null && !text.trim().isEmpty); |
| 1884 } | 1889 } |
| 1885 | 1890 |
| 1886 String get html => md.markdownToHtml(text); | 1891 String get html => md.markdownToHtml(text); |
| 1887 | 1892 |
| 1888 String toString() => text; | 1893 String toString() => text; |
| 1889 } | 1894 } |
| OLD | NEW |