Index: sdk/lib/_internal/dartdoc/lib/dartdoc.dart |
=================================================================== |
--- sdk/lib/_internal/dartdoc/lib/dartdoc.dart (revision 32349) |
+++ sdk/lib/_internal/dartdoc/lib/dartdoc.dart (working copy) |
@@ -1,2365 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/** |
- * To generate docs for a library, run this script with the path to an |
- * entrypoint .dart file, like: |
- * |
- * $ dart dartdoc.dart foo.dart |
- * |
- * This will create a "docs" directory with the docs for your libraries. To |
- * create these beautiful docs, dartdoc parses your library and every library |
- * it imports (recursively). From each library, it parses all classes and |
- * members, finds the associated doc comments and builds crosslinked docs from |
- * them. |
- */ |
-library dartdoc; |
- |
-import 'dart:async'; |
-import 'dart:convert'; |
-import 'dart:io'; |
- |
-import 'package:path/path.dart' as path; |
- |
-import 'classify.dart'; |
-import 'markdown.dart' as md; |
-import 'universe_serializer.dart'; |
- |
-import 'src/dartdoc/nav.dart'; |
-import 'src/dartdoc/utils.dart'; |
-import 'src/export_map.dart'; |
-import 'src/json_serializer.dart' as json_serializer; |
- |
-// TODO(rnystrom): Use "package:" URL (#4968). |
-import 'src/dart2js_mirrors.dart' as dart2js; |
-import '../../compiler/implementation/mirrors/source_mirrors.dart'; |
-import '../../compiler/implementation/mirrors/mirrors_util.dart'; |
-import '../../libraries.dart'; |
- |
-/** |
- * Generates completely static HTML containing everything you need to browse |
- * the docs. The only client side behavior is trivial stuff like syntax |
- * highlighting code. |
- */ |
-const MODE_STATIC = 0; |
- |
-/** |
- * Generated docs do not include baked HTML navigation. Instead, a single |
- * `nav.json` file is created and the appropriate navigation is generated |
- * client-side by parsing that and building HTML. |
- * |
- * This dramatically reduces the generated size of the HTML since a large |
- * fraction of each static page is just redundant navigation links. |
- * |
- * In this mode, the browser will do a XHR for nav.json which means that to |
- * preview docs locally, you will need to enable requesting file:// links in |
- * your browser or run a little local server like `python -m SimpleHTTPServer`. |
- */ |
-const MODE_LIVE_NAV = 1; |
- |
-const API_LOCATION = 'http://api.dartlang.org/'; |
- |
-/** |
- * Gets the full path to the directory containing the entrypoint of the current |
- * script. In other words, if you invoked dartdoc, directly, it will be the |
- * path to the directory containing `dartdoc.dart`. If you're running a script |
- * that imports dartdoc, it will be the path to that script. |
- */ |
-// TODO(johnniwinther): Convert to final (lazily initialized) variables when |
-// the feature is supported. |
-String get scriptDir => path.dirname(Platform.script.toFilePath()); |
- |
-/** |
- * Deletes and recreates the output directory at [path] if it exists. |
- */ |
-void cleanOutputDirectory(String path) { |
- final outputDir = new Directory(path); |
- if (outputDir.existsSync()) { |
- outputDir.deleteSync(recursive: true); |
- } |
- outputDir.createSync(); |
-} |
- |
-/** |
- * Copies all of the files in the directory [from] to [to]. Does *not* |
- * recursively copy subdirectories. |
- * |
- * Note: runs asynchronously, so you won't see any files copied until after the |
- * event loop has had a chance to pump (i.e. after `main()` has returned). |
- */ |
-Future copyDirectory(String from, String to) { |
- print('Copying static files...'); |
- final completer = new Completer(); |
- final fromDir = new Directory(from); |
- var futureList = []; |
- fromDir.list(recursive: false).listen( |
- (FileSystemEntity entity) { |
- if (entity is File) { |
- final name = path.basename(entity.path); |
- // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store. |
- if (name.startsWith('.')) return; |
- |
- File fromFile = entity; |
- File toFile = new File(path.join(to, name)); |
- futureList.add(fromFile.openRead().pipe(toFile.openWrite())); |
- } |
- }, |
- onDone: () => Future.wait(futureList).then((_) => completer.complete()), |
- onError: completer.completeError); |
- return completer.future; |
-} |
- |
-/** |
- * Compiles the dartdoc client-side code to JavaScript using Dart2js. |
- */ |
-Future compileScript(int mode, String outputDir, String libPath, String tmpPath) { |
- print('Compiling client JavaScript...'); |
- var clientScript = (mode == MODE_STATIC) ? 'static' : 'live-nav'; |
- var dartdocLibPath = path.join(libPath, 'lib', '_internal', 'dartdoc', 'lib'); |
- var dartPath = mode == MODE_STATIC ? |
- path.join(tmpPath, 'client.dart') : |
- path.join(dartdocLibPath, 'src', 'client', 'client-live-nav.dart'); |
- |
- var jsPath = path.join(outputDir, 'client-$clientScript.js'); |
- |
- // dart2js takes a String, but it expects that to be a Uri, not a file |
- // system path. |
- libPath = path.toUri(libPath).toString(); |
- dartPath = path.toUri(dartPath).toString(); |
- |
- return dart2js.compile( |
- dartPath, libPath, |
- options: const <String>['--categories=Client,Server', '--minify']) |
- .then((jsCode) { |
- if (jsCode == null) throw new StateError("No javascript was generated."); |
- writeString(new File(jsPath), jsCode); |
- }); |
-} |
- |
-/** |
- * Package manifest containing all information required to render the main page |
- * for a package. |
- * |
- * The manifest specifies where to load the [LibraryElement]s describing each |
- * library rather than including them directly. |
- * For our purposes we treat the core Dart libraries as a package. |
- */ |
-class PackageManifest { |
- /** Package name. */ |
- final name; |
- /** Package description */ |
- final description; |
- /** Libraries contained in this package. */ |
- final List<Reference> libraries = <Reference>[]; |
- /** |
- * Descriptive string describing the version# of the package. |
- * |
- * The current format for dart-sdk versions is |
- * $MAJOR.$MINOR.$BUILD.$PATCH$revisionString$userNameString |
- * For example: 0.1.2.0_r18233_johndoe |
- */ |
- final String fullVersion; |
- /** |
- * Source control revision number for the package. For SVN this is a number |
- * while for GIT it is a hash. |
- */ |
- final String revision; |
- /** |
- * Path to the directory containing data files for each library. |
- * |
- * Currently this is the serialized json version of the LibraryElement for |
- * the library. |
- */ |
- String location; |
- /** |
- * Packages depended on by this package. |
- * We currently store the entire manifest for the depency here the manifests |
- * are small. We may want to change this to a reference in the future. |
- */ |
- final List<PackageManifest> dependencies = <PackageManifest>[]; |
- |
- PackageManifest(this.name, this.description, this.fullVersion, this.revision); |
-} |
- |
-class Dartdoc { |
- |
- /** Set to `false` to not include the source code in the generated docs. */ |
- bool includeSource = true; |
- |
- /** |
- * Dartdoc can generate docs in a few different ways based on how dynamic you |
- * want the client-side behavior to be. The value for this should be one of |
- * the `MODE_` constants. |
- */ |
- int mode = MODE_LIVE_NAV; |
- |
- /** |
- * Generates the App Cache manifest file, enabling offline doc viewing. |
- */ |
- bool generateAppCache = false; |
- |
- /** Path to the dartdoc directory. */ |
- String dartdocPath; |
- |
- /** Path to generate HTML files into. */ |
- String outputDir = 'docs'; |
- |
- /** |
- * The title used for the overall generated output. Set this to change it. |
- */ |
- String mainTitle = 'Dart Documentation'; |
- |
- /** |
- * The URL that the Dart logo links to. Defaults "index.html", the main |
- * page for the generated docs, but can be anything. |
- */ |
- String mainUrl = 'index.html'; |
- |
- /** |
- * The Google Custom Search ID that should be used for the search box. If |
- * this is `null` then no search box will be shown. |
- */ |
- String searchEngineId = null; |
- |
- /* The URL that the embedded search results should be displayed on. */ |
- String searchResultsUrl = 'results.html'; |
- |
- /** Set this to add footer text to each generated page. */ |
- String footerText = null; |
- |
- /** Set this to omit generation timestamp from output */ |
- bool omitGenerationTime = false; |
- |
- /** Set by Dartdoc user to print extra information during generation. */ |
- bool verbose = false; |
- |
- /** Set this to include API libraries in the documentation. */ |
- bool includeApi = false; |
- |
- /** Set this to generate links to the online API. */ |
- bool linkToApi = false; |
- |
- /** Set this to generate docs for private types and members. */ |
- bool showPrivate = false; |
- |
- /** Set this to inherit from Object. */ |
- bool inheritFromObject = false; |
- |
- /** Version of the sdk or package docs are being generated for. */ |
- String version; |
- |
- /** Set this to select the libraries to include in the documentation. */ |
- List<String> includedLibraries = const <String>[]; |
- |
- /** Set this to select the libraries to exclude from the documentation. */ |
- List<String> excludedLibraries = const <String>[]; |
- |
- /** The package root for `package:` imports. */ |
- String _packageRoot; |
- |
- /** The map containing all the exports for each library. */ |
- ExportMap _exports; |
- |
- /** The path to a temporary directory used by Dartdoc. */ |
- String tmpPath; |
- |
- /** |
- * This list contains the libraries sorted in by the library name. |
- */ |
- List<LibraryMirror> _sortedLibraries; |
- |
- /** |
- * A map from hidden libraries to lists of [Export]s that export those |
- * libraries from visible libraries. This is used to determine what public |
- * library any given entity belongs to. |
- * |
- * The lists of exports are sorted so that exports that hide the fewest number |
- * of members come first. |
- */ |
- Map<LibraryMirror, List<Export>> _hiddenLibraryExports; |
- |
- /** The mirror system that we're currently generating docs for. */ |
- MirrorSystem _currentMirrorSystem; |
- |
- /** The library that we're currently generating docs for. */ |
- LibraryMirror _currentLibrary; |
- |
- /** The type that we're currently generating docs for. */ |
- TypeMirror _currentType; |
- |
- /** The member that we're currently generating docs for. */ |
- DeclarationMirror _currentMember; |
- |
- /** The path to the file currently being written to, relative to [outdir]. */ |
- String _filePath; |
- |
- /** The file currently being written to. */ |
- StringBuffer _file; |
- |
- int _totalLibraries = 0; |
- int _totalTypes = 0; |
- int _totalMembers = 0; |
- |
- int get totalLibraries => _totalLibraries; |
- int get totalTypes => _totalTypes; |
- int get totalMembers => _totalMembers; |
- |
- // Check if the compilation has started and finished. |
- bool _started = false; |
- bool _finished = false; |
- |
- /** |
- * Prints the status of dartdoc. |
- * |
- * Prints whether dartdoc is running, whether dartdoc has finished |
- * succesfully or not, and how many libraries, types, and members were |
- * documented. |
- */ |
- String get status { |
- // TODO(amouravski): Make this more full featured and remove all other |
- // prints and put them under verbose flag. |
- if (!_started) { |
- return 'Documentation has not yet started.'; |
- } else if (!_finished) { |
- return 'Documentation in progress -- documented $_statisticsSummary so far.'; |
- } else { |
- if (totals == 0) { |
- return 'Documentation complete -- warning: nothing was documented!'; |
- } else { |
- return 'Documentation complete -- documented $_statisticsSummary.'; |
- } |
- } |
- } |
- |
- int get totals => totalLibraries + totalTypes + totalMembers; |
- |
- String get _statisticsSummary => |
- '${totalLibraries} libraries, ${totalTypes} types, and ' |
- '${totalMembers} members'; |
- |
- static const List<String> COMPILER_OPTIONS = |
- const <String>['--preserve-comments', '--categories=Client,Server']; |
- |
- /// Resolves Dart links to the correct Node. |
- md.Resolver dartdocResolver; |
- |
- // Add support for [:...:]-style code to the markdown parser. |
- List<md.InlineSyntax> dartdocSyntaxes = |
- [new md.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')]; |
- |
- Dartdoc() { |
- tmpPath = Directory.systemTemp.createTempSync('dartdoc_').path; |
- dartdocResolver = (String name) => resolveNameReference(name, |
- currentLibrary: _currentLibrary, currentType: _currentType, |
- currentMember: _currentMember); |
- } |
- |
- /** |
- * Returns `true` if [library] is included in the generated documentation. |
- */ |
- bool shouldIncludeLibrary(LibraryMirror library) { |
- if (shouldLinkToPublicApi(library)) { |
- return false; |
- } |
- var includeByDefault = true; |
- String libraryName = displayName(library); |
- if (excludedLibraries.contains(libraryName)) { |
- return false; |
- } |
- if (!includedLibraries.isEmpty) { |
- includeByDefault = false; |
- if (includedLibraries.contains(libraryName)) { |
- return true; |
- } |
- } |
- Uri uri = library.uri; |
- if (uri.scheme == 'dart') { |
- String suffix = uri.path; |
- LibraryInfo info = LIBRARIES[suffix]; |
- if (info != null) { |
- return info.documented && includeApi; |
- } |
- } |
- return includeByDefault; |
- } |
- |
- /** |
- * Returns `true` if links to the public API should be generated for |
- * [library]. |
- */ |
- bool shouldLinkToPublicApi(LibraryMirror library) { |
- if (linkToApi) { |
- Uri uri = library.uri; |
- if (uri.scheme == 'dart') { |
- String suffix = uri.path; |
- LibraryInfo info = LIBRARIES[suffix]; |
- if (info != null) { |
- return info.documented; |
- } |
- } |
- } |
- return false; |
- } |
- |
- String get footerContent{ |
- var footerItems = []; |
- if (!omitGenerationTime) { |
- footerItems.add("This page was generated at ${new DateTime.now()}"); |
- } |
- if (footerText != null) { |
- footerItems.add(footerText); |
- } |
- var content = ''; |
- for (int i = 0; i < footerItems.length; i++) { |
- if (i > 0) { |
- content += '\n'; |
- } |
- content += '<div>${footerItems[i]}</div>'; |
- } |
- return content; |
- } |
- |
- Future documentLibraries(List<Uri> libraryList, String libPath, |
- String packageRoot) { |
- _packageRoot = packageRoot; |
- |
- // dart2js takes a String, but it expects that to be a Uri, not a file |
- // system path. |
- libPath = path.toUri(libPath).toString(); |
- |
- if (packageRoot != null) { |
- packageRoot = path.toUri(packageRoot).toString(); |
- } |
- |
- // TODO(amouravski): make all of these print statements into logging |
- // statements. |
- print('Analyzing libraries...'); |
- return dart2js.analyze( |
- libraryList.map((uri) => uri.toString()).toList(), |
- libPath, packageRoot: packageRoot, options: COMPILER_OPTIONS) |
- .then((MirrorSystem mirrors) { |
- print('Generating documentation...'); |
- _document(mirrors); |
- }); |
- } |
- |
- void _document(MirrorSystem mirrors) { |
- _currentMirrorSystem = mirrors; |
- _exports = new ExportMap(mirrors); |
- _started = true; |
- |
- |
- // Remove duplicated libraries. This is a hack because libraries can |
- // get picked up multiple times (dartbug.com/11826) which will go away |
- // with the new docgen. The reason we hit this issue is that we attempt |
- // to dart2js.analyze all packages in the repo together, which results |
- // in packages getting referenced with different URI's (../../pkg versus |
- // ../../out/ReleaseIA32/packages versus package:). |
- _sortedLibraries = new Map.fromIterable( |
- mirrors.libraries.values.where(shouldIncludeLibrary), |
- key: displayName).values.toList(); |
- |
- // Sort the libraries by name (not key). |
- _sortedLibraries.sort((x, y) { |
- return displayName(x).toUpperCase().compareTo( |
- displayName(y).toUpperCase()); |
- }); |
- |
- _hiddenLibraryExports = _generateHiddenLibraryExports(); |
- |
- // Generate the docs. |
- if (mode == MODE_LIVE_NAV) { |
- docNavigationJson(); |
- } else { |
- docNavigationDart(); |
- } |
- |
- docIndex(); |
- for (final library in _sortedLibraries) { |
- docLibrary(library); |
- } |
- |
- if (generateAppCache) { |
- generateAppCacheManifest(); |
- } |
- |
- // TODO(nweiz): properly handle exports when generating JSON. |
- // TODO(jacobr): handle arbitrary pub packages rather than just the system |
- // libraries. |
- var revision = '0'; |
- if (version != null) { |
- var match = new RegExp(r"_r(\d+)").firstMatch(version); |
- if (match != null) { |
- revision = match.group(1); |
- } else { |
- print("Warning: could not parse version: $version"); |
- } |
- } |
- var packageManifest = new PackageManifest('dart:', 'Dart System Libraries', |
- version, revision); |
- |
- for (final lib in _sortedLibraries) { |
- var libraryElement = new LibraryElement( |
- lib, lookupMdnComment: lookupMdnComment) |
- ..stripDuplicateUris(null, null); |
- packageManifest.libraries.add(new Reference.fromElement(libraryElement)); |
- startFile("$revision/${libraryElement.id}.json"); |
- write(json_serializer.serialize(libraryElement)); |
- endFile(); |
- } |
- |
- startFile("$revision/apidoc.json"); |
- write(json_serializer.serialize(packageManifest)); |
- endFile(); |
- |
- // Write out top level json file with a relative path to the library json |
- // files. |
- startFile("apidoc.json"); |
- packageManifest.location = revision; |
- write(json_serializer.serialize(packageManifest)); |
- endFile(); |
- |
- _finished = true; |
- _currentMirrorSystem = null; |
- } |
- |
- /** |
- * Generate [_hiddenLibraryExports] from [_exports]. |
- */ |
- Map<LibraryMirror, List<Export>> _generateHiddenLibraryExports() { |
- // First generate a map `exported library => exporter library => Export`. |
- // The inner map makes it easier to merge multiple exports of the same |
- // library by the same exporter. |
- var hiddenLibraryExportMaps = |
- new Map<LibraryMirror, Map<LibraryMirror, Export>>(); |
- |
- _exports.exports.forEach((exporter, exports) { |
- if (!shouldIncludeLibrary(exporter)) return; |
- for (var export in exports) { |
- var exported = export.exported; |
- if (shouldIncludeLibrary(exported)) continue; |
- |
- var hiddenExports = _exports.transitiveExports(exported) |
- .map((transitiveExport) => export.compose(transitiveExport)) |
- .toList(); |
- hiddenExports.add(export); |
- |
- for (var hiddenExport in hiddenExports) { |
- var exportsByExporter = hiddenLibraryExportMaps.putIfAbsent( |
- hiddenExport.exported, () => new Map<LibraryMirror, Export>()); |
- addOrMergeExport(exportsByExporter, exporter, hiddenExport); |
- } |
- } |
- }); |
- |
- // Now sort the values of the inner maps of `hiddenLibraryExportMaps` to get |
- // the final value of `_hiddenLibraryExports`. |
- var hiddenLibraryExports = new Map<LibraryMirror, List<Export>>(); |
- hiddenLibraryExportMaps.forEach((exportee, exportsByExporter) { |
- int rank(Export export) { |
- if (export.show.isEmpty && export.hide.isEmpty) return 0; |
- if (export.show.isEmpty) return export.hide.length; |
- // Multiply by 1000 to ensure this sorts after an export with hides. |
- return 1000 * export.show.length; |
- } |
- |
- var exports = exportsByExporter.values.toList(); |
- exports.sort((export1, export2) { |
- var comparison = Comparable.compare(rank(export1), rank(export2)); |
- if (comparison != 0) return comparison; |
- |
- var library1 = export1.exporter; |
- var library2 = export2.exporter; |
- return Comparable.compare(displayName(library1), displayName(library2)); |
- }); |
- hiddenLibraryExports[exportee] = exports; |
- }); |
- return hiddenLibraryExports; |
- } |
- |
- MdnComment lookupMdnComment(Mirror mirror) => null; |
- |
- void startFile(String path) { |
- _filePath = path; |
- _file = new StringBuffer(); |
- } |
- |
- void endFile() { |
- final outPath = path.join(outputDir, _filePath); |
- final dir = new Directory(path.dirname(outPath)); |
- if (!dir.existsSync()) { |
- // TODO(3914): Hack to avoid 'file already exists' exception |
- // thrown due to invalid result from dir.existsSync() (probably due to |
- // race conditions). |
- try { |
- dir.createSync(); |
- } on FileSystemException catch (e) { |
- // Ignore. |
- } |
- } |
- |
- writeString(new File(outPath), _file.toString()); |
- _filePath = null; |
- _file = null; |
- } |
- |
- void write(String s) { |
- _file.write(s); |
- } |
- |
- void writeln(String s) { |
- write(s); |
- write('\n'); |
- } |
- |
- /** |
- * Writes the page header with the given [title] and [breadcrumbs]. The |
- * breadcrumbs are an interleaved list of links and titles. If a link is null, |
- * then no link will be generated. For example, given: |
- * |
- * ['foo', 'foo.html', 'bar', null] |
- * |
- * It will output: |
- * |
- * <a href="foo.html">foo</a> › bar |
- */ |
- void writeHeader(String title, List<String> breadcrumbs) { |
- final htmlAttributes = generateAppCache ? |
- 'manifest="/appcache.manifest"' : ''; |
- |
- write( |
- ''' |
- <!DOCTYPE html> |
- <html${htmlAttributes == '' ? '' : ' $htmlAttributes'}> |
- <head> |
- '''); |
- writeHeadContents(title); |
- |
- // Add data attributes describing what the page documents. |
- var data = ''; |
- if (_currentLibrary != null) { |
- data = '$data data-library=' |
- '"${md.escapeHtml(displayName(_currentLibrary))}"'; |
- } |
- |
- if (_currentType != null) { |
- data = '$data data-type="${md.escapeHtml(typeName(_currentType))}"'; |
- } |
- |
- write( |
- ''' |
- </head> |
- <body$data> |
- <div class="page"> |
- <div class="header"> |
- ${a(mainUrl, '<div class="logo"></div>')} |
- ${a('index.html', mainTitle)} |
- '''); |
- |
- // Write the breadcrumb trail. |
- for (int i = 0; i < breadcrumbs.length; i += 2) { |
- if (breadcrumbs[i + 1] == null) { |
- write(' › ${breadcrumbs[i]}'); |
- } else { |
- write(' › ${a(breadcrumbs[i + 1], breadcrumbs[i])}'); |
- } |
- } |
- |
- if (searchEngineId != null) { |
- writeln( |
- ''' |
- <form action="$searchResultsUrl" id="search-box"> |
- <input type="hidden" name="cx" value="$searchEngineId"> |
- <input type="hidden" name="ie" value="UTF-8"> |
- <input type="hidden" name="hl" value="en"> |
- <input type="search" name="q" id="q" autocomplete="off" |
- class="search-input" placeholder="Search API"> |
- </form> |
- '''); |
- } else { |
- writeln( |
- ''' |
- <div id="search-box"> |
- <input type="search" name="q" id="q" autocomplete="off" |
- class="search-input" placeholder="Search API"> |
- </div> |
- '''); |
- } |
- |
- writeln( |
- ''' |
- </div> |
- <div class="drop-down" id="drop-down"></div> |
- '''); |
- |
- docNavigation(); |
- writeln('<div class="content">'); |
- } |
- |
- String get clientScript { |
- switch (mode) { |
- case MODE_STATIC: return 'client-static'; |
- case MODE_LIVE_NAV: return 'client-live-nav'; |
- default: throw 'Unknown mode $mode.'; |
- } |
- } |
- |
- void writeHeadContents(String title) { |
- writeln( |
- ''' |
- <meta charset="utf-8"> |
- <title>$title / $mainTitle</title> |
- <link rel="stylesheet" type="text/css" |
- href="${relativePath('styles.css')}"> |
- <link href="//fonts.googleapis.com/css?family=Open+Sans:400,600,700,800" rel="stylesheet" type="text/css"> |
- <link rel="shortcut icon" href="${relativePath('favicon.ico')}"> |
- '''); |
- } |
- |
- void writeFooter() { |
- writeln( |
- ''' |
- </div> |
- <div class="clear"></div> |
- </div> |
- <div class="footer"> |
- $footerContent |
- </div> |
- <script async src="${relativePath('$clientScript.js')}"></script> |
- </body></html> |
- '''); |
- } |
- |
- void docIndex() { |
- startFile('index.html'); |
- |
- writeHeader(mainTitle, []); |
- |
- writeln('<h2>$mainTitle</h2>'); |
- writeln('<h3>Libraries</h3>'); |
- |
- for (final library in _sortedLibraries) { |
- docIndexLibrary(library); |
- } |
- |
- writeFooter(); |
- endFile(); |
- } |
- |
- void docIndexLibrary(LibraryMirror library) { |
- writeln('<h4>${a(libraryUrl(library), displayName(library))}</h4>'); |
- } |
- |
- /** |
- * Walks the libraries and creates a JSON object containing the data needed |
- * to generate navigation for them. |
- */ |
- void docNavigationJson() { |
- startFile('nav.json'); |
- writeln(JSON.encode(createNavigationInfo())); |
- endFile(); |
- } |
- /// Whether dartdoc is running from within the Dart SDK or the |
- /// Dart source repository. |
- bool get runningFromSdk => |
- path.extension(Platform.script.toFilePath()) == '.snapshot'; |
- |
- /// Gets the path to the root directory of the SDK. |
- String get sdkDir => |
- path.dirname(path.dirname(Platform.executable)); |
- |
- /// Gets the path to the dartdoc directory normalized for running in different |
- /// places. |
- String get normalizedDartdocPath => path.normalize( |
- path.absolute(runningFromSdk ? |
- path.join(sdkDir, 'lib', '_internal', 'dartdoc') : |
- dartdocPath.toString())); |
- |
- void docNavigationDart() { |
- var tmpDir = new Directory(tmpPath); |
- if (!tmpDir.existsSync()) { |
- tmpDir.createSync(); |
- } |
- String jsonString = JSON.encode(createNavigationInfo()); |
- String dartString = jsonString.replaceAll(r"$", r"\$"); |
- var filePath = path.join(tmpPath, 'client.dart'); |
- |
- var clientDir = path.join(normalizedDartdocPath,'lib', 'src', 'client'); |
- |
- writeString(new File(filePath), |
- '''library client; |
- import 'dart:html'; |
- import r'${path.toUri(path.join(clientDir, 'client-shared.dart'))}'; |
- import r'${path.toUri(path.join(clientDir, 'dropdown.dart'))}'; |
- |
- main() { |
- setup(); |
- setupSearch(json); |
- } |
- |
- get json => $dartString;'''); |
- } |
- |
- void cleanup() { |
- var tmpDir = new Directory(tmpPath); |
- if (tmpDir.existsSync()) { |
- tmpDir.deleteSync(recursive: true); |
- } |
- tmpPath = null; |
- } |
- |
- List createNavigationInfo() { |
- final libraryList = []; |
- for (final library in _sortedLibraries) { |
- docLibraryNavigationJson(library, libraryList); |
- } |
- return libraryList; |
- } |
- |
- void docLibraryNavigationJson(LibraryMirror library, List libraryList) { |
- var libraryInfo = {}; |
- libraryInfo[NAME] = displayName(library); |
- final List members = docMembersJson(membersOf(library.declarations)); |
- if (!members.isEmpty) { |
- libraryInfo[MEMBERS] = members; |
- } |
- |
- final types = []; |
- var classes = |
- library.declarations.values.where((mirror) => mirror is TypeMirror); |
- for (TypeMirror type in orderByName(classes)) { |
- |
- if (!showPrivate && type.isPrivate) continue; |
- |
- var typeInfo = {}; |
- typeInfo[NAME] = displayName(type); |
- if (type is ClassMirror) { |
- typeInfo[KIND] = CLASS; |
- |
- final List typeMembers = docMembersJson(membersOf(type.declarations)); |
- if (!typeMembers.isEmpty) { |
- typeInfo[MEMBERS] = typeMembers; |
- } |
- } else { |
- assert(type is TypedefMirror); |
- typeInfo[KIND] = TYPEDEF; |
- } |
- |
- if (!type.typeVariables.isEmpty) { |
- final typeVariables = []; |
- for (final typeVariable in type.typeVariables) { |
- typeVariables.add(displayName(typeVariable)); |
- } |
- typeInfo[ARGS] = typeVariables.join(', '); |
- } |
- types.add(typeInfo); |
- } |
- if (!types.isEmpty) { |
- libraryInfo[TYPES] = types; |
- } |
- |
- libraryList.add(libraryInfo); |
- } |
- |
- List docMembersJson(Iterable<DeclarationMirror> declarations) { |
- final members = []; |
- for (DeclarationMirror member in orderByName(declarations)) { |
- if (!showPrivate && member.isPrivate) continue; |
- |
- var memberInfo = {}; |
- if (member is VariableMirror) { |
- memberInfo[KIND] = FIELD; |
- } else { |
- MethodMirror method = member; |
- if (method.isConstructor) { |
- memberInfo[KIND] = CONSTRUCTOR; |
- } else if (method.isSetter) { |
- memberInfo[KIND] = SETTER; |
- } else if (method.isGetter) { |
- memberInfo[KIND] = GETTER; |
- } else { |
- memberInfo[KIND] = METHOD; |
- } |
- if (method.parameters.isEmpty) { |
- memberInfo[NO_PARAMS] = true; |
- } |
- } |
- memberInfo[NAME] = displayName(member); |
- var anchor = memberAnchor(member); |
- if (anchor != memberInfo[NAME]) { |
- memberInfo[LINK_NAME] = anchor; |
- } |
- members.add(memberInfo); |
- } |
- return members; |
- } |
- |
- void docNavigation() { |
- writeln( |
- ''' |
- <div class="nav"> |
- '''); |
- |
- if (mode == MODE_STATIC) { |
- for (final library in _sortedLibraries) { |
- write('<h2><div class="icon-library"></div>'); |
- |
- if ((_currentLibrary == library) && (_currentType == null)) { |
- write('<strong>${displayName(library)}</strong>'); |
- } else { |
- write('${a(libraryUrl(library), displayName(library))}'); |
- } |
- write('</h2>'); |
- |
- // Only expand classes in navigation for current library. |
- if (_currentLibrary == library) docLibraryNavigation(library); |
- } |
- } |
- |
- writeln('</div>'); |
- } |
- |
- /** Writes the navigation for the types contained by the given library. */ |
- void docLibraryNavigation(LibraryMirror library) { |
- // Show the exception types separately. |
- final types = <TypeMirror>[]; |
- final exceptions = <TypeMirror>[]; |
- |
- for (TypeMirror type in orderByName(typesOf(library.declarations))) { |
- if (!showPrivate && type.isPrivate) continue; |
- |
- if (isException(type)) { |
- exceptions.add(type); |
- } else { |
- types.add(type); |
- } |
- } |
- |
- if ((types.length == 0) && (exceptions.length == 0)) return; |
- |
- writeln('<ul class="icon">'); |
- types.forEach(docTypeNavigation); |
- exceptions.forEach(docTypeNavigation); |
- writeln('</ul>'); |
- } |
- |
- /** Writes a linked navigation list item for the given type. */ |
- void docTypeNavigation(TypeMirror type) { |
- var icon = 'interface'; |
- if (isException(type)) { |
- icon = 'exception'; |
- } else if (type is ClassMirror) { |
- icon = 'class'; |
- } |
- |
- write('<li>'); |
- if (_currentType == type) { |
- write( |
- '<div class="icon-$icon"></div><strong>${typeName(type)}</strong>'); |
- } else { |
- write(a(typeUrl(type), |
- '<div class="icon-$icon"></div>${typeName(type)}')); |
- } |
- writeln('</li>'); |
- } |
- |
- void docLibrary(LibraryMirror library) { |
- if (verbose) { |
- print('Library \'${displayName(library)}\':'); |
- } |
- _totalLibraries++; |
- _currentLibrary = library; |
- |
- startFile(libraryUrl(library)); |
- writeHeader('${displayName(library)} Library', |
- [displayName(library), libraryUrl(library)]); |
- writeln('<h2><strong>${displayName(library)}</strong> library</h2>'); |
- |
- // Look for a comment for the entire library. |
- final comment = getLibraryComment(library); |
- if (comment != null) { |
- writeln('<div class="doc">${comment.html}</div>'); |
- } |
- |
- // Document the visible libraries exported by this library. |
- docExports(library); |
- |
- // Document the top-level members. |
- docMembers(library); |
- |
- // Document the types. |
- final abstractClasses = <ClassMirror>[]; |
- final classes = <ClassMirror>[]; |
- final typedefs = <TypedefMirror>[]; |
- final exceptions = <ClassMirror>[]; |
- |
- var allClasses = _libraryClasses(library); |
- for (TypeMirror type in orderByName(allClasses)) { |
- if (!showPrivate && type.isPrivate) continue; |
- |
- if (isException(type)) { |
- exceptions.add(type); |
- } else if (type is TypedefMirror) { |
- typedefs.add(type); |
- } else if (type is ClassSourceMirror) { |
- if (type.isAbstract) { |
- abstractClasses.add(type); |
- } else { |
- classes.add(type); |
- } |
- } else { |
- throw new InternalError("internal error: unknown type $type."); |
- } |
- } |
- |
- docTypes(abstractClasses, 'Abstract Classes'); |
- docTypes(classes, 'Classes'); |
- docTypes(typedefs, 'Typedefs'); |
- docTypes(exceptions, 'Exceptions'); |
- |
- writeFooter(); |
- endFile(); |
- |
- for (final type in allClasses) { |
- if (!showPrivate && type.isPrivate) continue; |
- |
- docType(type); |
- } |
- |
- _currentLibrary = null; |
- } |
- |
- void docTypes(List types, String header) { |
- if (types.length == 0) return; |
- |
- writeln('<div>'); |
- writeln('<h3>$header</h3>'); |
- |
- for (final type in types) { |
- writeln( |
- ''' |
- <div class="type"> |
- <h4> |
- ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} |
- </h4> |
- </div> |
- '''); |
- } |
- writeln('</div>'); |
- } |
- |
- void docType(TypeMirror type) { |
- if (verbose) { |
- print('- ${nameOf(type)}'); |
- } |
- _totalTypes++; |
- _currentType = type; |
- |
- startFile(typePath(type)); |
- |
- var kind; |
- if (type is TypedefMirror) { |
- kind = 'typedef'; |
- } else if (type is ClassSourceMirror) { |
- if (type.isAbstract) { |
- kind = 'abstract class'; |
- } else { |
- kind = 'class'; |
- } |
- } else { |
- assert(false); |
- } |
- |
- final typeTitle = |
- '${typeName(type)} ${kind}'; |
- var library = _libraryFor(type); |
- writeHeader('$typeTitle / ${displayName(library)} Library', |
- [displayName(library), libraryUrl(library), |
- typeName(type), typeUrl(type)]); |
- writeln( |
- ''' |
- <h2><strong>${typeName(type, showBounds: true)}</strong> |
- $kind |
- </h2> |
- '''); |
- writeln('<button id="show-inherited" class="show-inherited">' |
- 'Hide inherited</button>'); |
- |
- writeln('<div class="doc">'); |
- docComment(type, getTypeComment(type)); |
- docCode(type.location); |
- writeln('</div>'); |
- |
- |
- if (type is TypedefMirror) { |
- docTypedef(type); |
- } else { |
- docInheritance(type); |
- docMembers(type); |
- } |
- |
- writeTypeFooter(); |
- writeFooter(); |
- endFile(); |
- |
- _currentType = null; |
- } |
- |
- /** Override this to write additional content at the end of a type's page. */ |
- void writeTypeFooter() { |
- // Do nothing. |
- } |
- |
- /** |
- * Writes an inline type span for the given type. This is a little box with |
- * an icon and the type's name. It's similar to how types appear in the |
- * navigation, but is suitable for inline (as opposed to in a `<ul>`) use. |
- */ |
- void typeSpan(TypeMirror type) { |
- var icon = 'interface'; |
- if (isException(type)) { |
- icon = 'exception'; |
- } else if (type is ClassMirror) { |
- icon = 'class'; |
- } |
- |
- write('<span class="type-box"><span class="icon-$icon"></span>'); |
- if (_currentType == type) { |
- write('<strong>${typeName(type)}</strong>'); |
- } else { |
- write(a(typeUrl(type), typeName(type))); |
- } |
- write('</span>'); |
- } |
- |
- /** |
- * Document the other types that touch [Type] in the inheritance hierarchy: |
- * subclasses, superclasses, subinterfaces, superinferfaces, and default |
- * class. |
- */ |
- void docInheritance(ClassMirror type) { |
- // Don't show the inheritance details for Object. It doesn't have any base |
- // class (obviously) and it has too many subclasses to be useful. |
- if (isObject(type)) return; |
- |
- // Writes an unordered list of references to types with an optional header. |
- listTypes(types, header) { |
- if (types == null) return; |
- |
- // Filter out types from private dart libraries. |
- types = new List.from(types.where((t) => !isFromPrivateDartLibrary(t))); |
- |
- var publicTypes; |
- if (showPrivate) { |
- publicTypes = types; |
- } else { |
- // Skip private types. |
- publicTypes = new List.from(types.where((t) => !t.isPrivate)); |
- } |
- if (publicTypes.length == 0) return; |
- |
- writeln('<h3>$header</h3>'); |
- writeln('<p>'); |
- bool first = true; |
- for (final t in publicTypes) { |
- if (!first) write(', '); |
- typeSpan(t); |
- first = false; |
- } |
- writeln('</p>'); |
- } |
- |
- final subtypes = []; |
- for (final subtype in computeSubdeclarations(_currentMirrorSystem, type)) { |
- subtypes.add(subtype); |
- } |
- subtypes.sort((x, y) { |
- String xName = nameOf(x); |
- String yName = nameOf(y); |
- return xName.compareTo(yName); |
- }); |
- |
- // Show the chain of superclasses. |
- var superclass = getSuperclass(type); |
- if (!isObject(superclass)) { |
- final supertypes = []; |
- var thisType = superclass; |
- do { |
- if (!isFromPrivateDartLibrary(thisType)) { |
- supertypes.add(thisType); |
- } |
- thisType = getSuperclass(thisType); |
- } while (!isObject(thisType)); |
- |
- writeln('<h3>Extends</h3>'); |
- writeln('<p>'); |
- for (var i = supertypes.length - 1; i >= 0; i--) { |
- typeSpan(supertypes[i]); |
- write(' > '); |
- } |
- |
- // Write this class. |
- typeSpan(type); |
- writeln('</p>'); |
- } |
- |
- listTypes(subtypes, 'Subclasses'); |
- listTypes(getAppliedMixins(type), 'Mixins'); |
- listTypes(getExplicitInterfaces(type), 'Implements'); |
- } |
- |
- /** |
- * Documents the definition of [type] if it is a typedef. |
- */ |
- void docTypedef(TypedefMirror type) { |
- String name = nameOf(type); |
- |
- writeln('<div class="method"><h4 id="${name}">'); |
- |
- if (includeSource) { |
- writeln('<button class="show-code">Code</button>'); |
- } |
- |
- write('typedef '); |
- annotateType(type, type.referent, name); |
- |
- write(''' <a class="anchor-link" href="#${name}" |
- title="Permalink to ${name}">#</a>'''); |
- writeln('</h4>'); |
- |
- writeln('<div class="doc">'); |
- docCode(type.location); |
- writeln('</div>'); |
- |
- writeln('</div>'); |
- } |
- |
- static const operatorOrder = const <String>[ |
- '[]', '[]=', // Indexing. |
- '+', 'unary-', '-', '*', '/', '~/', '%', // Arithmetic. |
- '&', '|', '^', '~', // Bitwise. |
- '<<', '>>', // Shift. |
- '<', '<=', '>', '>=', // Relational. |
- '==', // Equality. |
- ]; |
- |
- static final Map<String, int> operatorOrderMap = (){ |
- var map = new Map<String, int>(); |
- var index = 0; |
- for (String operator in operatorOrder) { |
- map[operator] = index++; |
- } |
- return map; |
- }(); |
- |
- void docExports(LibraryMirror library) { |
- var exportLinks = _exports.transitiveExports(library).map((export) { |
- var library = export.exported; |
- // Only link to publically visible libraries. |
- if (!shouldIncludeLibrary(library)) return null; |
- |
- var memberNames = export.show.isEmpty ? export.hide : export.show; |
- var memberLinks = memberNames.map((name) { |
- return md.renderToHtml([resolveNameReference( |
- name, currentLibrary: library)]); |
- }).join(', '); |
- var combinator = ''; |
- if (!export.show.isEmpty) { |
- combinator = ' show $memberLinks'; |
- } else if (!export.hide.isEmpty) { |
- combinator = ' hide $memberLinks'; |
- } |
- |
- return '<ul>${a(libraryUrl(library), displayName(library))}' |
- '$combinator</ul>'; |
- }).where((link) => link != null); |
- |
- if (!exportLinks.isEmpty) { |
- writeln('<h3>Exports</h3>'); |
- writeln('<ul>'); |
- writeln(exportLinks.join('\n')); |
- writeln('</ul>'); |
- } |
- } |
- |
- void docMembers(DeclarationMirror host) { |
- // Collect the different kinds of members. |
- final staticMethods = []; |
- final staticGetters = new Map<String, DeclarationMirror>(); |
- final staticSetters = new Map<String, DeclarationMirror>(); |
- final memberMap = new Map<String, DeclarationMirror>(); |
- final instanceMethods = []; |
- final instanceOperators = []; |
- final instanceGetters = new Map<String, DeclarationMirror>(); |
- final instanceSetters = new Map<String, DeclarationMirror>(); |
- final constructors = []; |
- |
- var hostMembers = host is ClassMirror ? |
- membersOf(host.declarations) : _libraryMembers(host); |
- for (var member in hostMembers) { |
- if (!showPrivate && member.isPrivate) continue; |
- if (host is LibraryMirror || member.isStatic) { |
- if (member is MethodMirror) { |
- if (member.isGetter) { |
- staticGetters[displayName(member)] = member; |
- } else if (member.isSetter) { |
- staticSetters[displayName(member)] = member; |
- } else { |
- staticMethods.add(member); |
- } |
- } else if (member is VariableMirror) { |
- staticGetters[displayName(member)] = member; |
- } |
- } |
- } |
- |
- if (host is ClassMirror) { |
- var iterable = new HierarchyIterable(host, includeType: true); |
- for (ClassMirror type in iterable) { |
- if (!isObject(host) && !inheritFromObject && isObject(type)) continue; |
- if (isFromPrivateDartLibrary(type)) continue; |
- |
- membersOf(type.declarations).forEach((member) { |
- if (member.isStatic) return; |
- if (!showPrivate && member.isPrivate) return; |
- |
- bool inherit = true; |
- if (type != host) { |
- if (member.isPrivate) { |
- // Don't inherit private members. |
- inherit = false; |
- } |
- if (member is MethodMirror && member.isConstructor) { |
- // Don't inherit constructors. |
- inherit = false; |
- } |
- } |
- if (!inherit) return; |
- |
- String name = nameOf(member); |
- if (member is VariableMirror) { |
- // Fields override both getters and setters. |
- memberMap.putIfAbsent(name, () => member); |
- memberMap.putIfAbsent('${name}=', () => member); |
- } else if (member is MethodMirror && member.isConstructor) { |
- constructors.add(member); |
- } else { |
- memberMap.putIfAbsent(name, () => member); |
- } |
- }); |
- } |
- } |
- |
- bool allMethodsInherited = true; |
- bool allPropertiesInherited = true; |
- bool allOperatorsInherited = true; |
- memberMap.forEach((_, member) { |
- if (member is MethodMirror) { |
- if (member.isGetter) { |
- instanceGetters[displayName(member)] = member; |
- if (_ownerFor(member) == host) { |
- allPropertiesInherited = false; |
- } |
- } else if (member.isSetter) { |
- instanceSetters[displayName(member)] = member; |
- if (_ownerFor(member) == host) { |
- allPropertiesInherited = false; |
- } |
- } else if (member.isOperator) { |
- instanceOperators.add(member); |
- if (_ownerFor(member) == host) { |
- allOperatorsInherited = false; |
- } |
- } else { |
- instanceMethods.add(member); |
- if (_ownerFor(member) == host) { |
- allMethodsInherited = false; |
- } |
- } |
- } else if (member is VariableMirror) { |
- instanceGetters[displayName(member)] = member; |
- if (_ownerFor(member) == host) { |
- allPropertiesInherited = false; |
- } |
- } |
- }); |
- |
- instanceOperators.sort((MethodMirror a, MethodMirror b) { |
- return operatorOrderMap[nameOf(a)].compareTo( |
- operatorOrderMap[nameOf(b)]); |
- }); |
- |
- docProperties(host, |
- host is LibraryMirror ? 'Properties' : 'Static Properties', |
- staticGetters, staticSetters, allInherited: false); |
- docMethods(host, |
- host is LibraryMirror ? 'Functions' : 'Static Methods', |
- staticMethods, allInherited: false); |
- |
- docMethods(host, 'Constructors', orderByName(constructors), |
- allInherited: false); |
- docProperties(host, 'Properties', instanceGetters, instanceSetters, |
- allInherited: allPropertiesInherited); |
- docMethods(host, 'Operators', instanceOperators, |
- allInherited: allOperatorsInherited); |
- docMethods(host, 'Methods', orderByName(instanceMethods), |
- allInherited: allMethodsInherited); |
- } |
- |
- /** |
- * Documents fields, getters, and setters as properties. |
- */ |
- void docProperties(DeclarationMirror host, String title, |
- Map<String, DeclarationMirror> getters, |
- Map<String, DeclarationMirror> setters, |
- {bool allInherited}) { |
- if (getters.isEmpty && setters.isEmpty) return; |
- |
- var nameSet = new Set<String>.from(getters.keys); |
- nameSet.addAll(setters.keys); |
- var nameList = new List<String>.from(nameSet); |
- nameList.sort((String a, String b) { |
- return a.toLowerCase().compareTo(b.toLowerCase()); |
- }); |
- |
- writeln('<div${allInherited ? ' class="inherited"' : ''}>'); |
- writeln('<h3>$title</h3>'); |
- for (String name in nameList) { |
- DeclarationMirror getter = getters[name]; |
- DeclarationMirror setter = setters[name]; |
- if (setter == null) { |
- if (getter is VariableMirror) { |
- // We have a field. |
- docField(host, getter); |
- } else { |
- // We only have a getter. |
- assert(getter is MethodMirror); |
- docProperty(host, getter, null); |
- } |
- } else if (getter == null) { |
- // We only have a setter => Document as a method. |
- assert(setter is MethodMirror); |
- docMethod(host, setter); |
- } else { |
- DocComment getterComment = getMemberComment(getter); |
- DocComment setterComment = getMemberComment(setter); |
- if (_ownerFor(getter) != _ownerFor(setter) || |
- getterComment != null && setterComment != null) { |
- // Both have comments or are not declared in the same class |
- // => Documents separately. |
- if (getter is VariableMirror) { |
- // Document field as a getter (setter is inherited). |
- docField(host, getter, asGetter: true); |
- } else { |
- docMethod(host, getter); |
- } |
- if (setter is VariableMirror) { |
- // Document field as a setter (getter is inherited). |
- docField(host, setter, asSetter: true); |
- } else { |
- docMethod(host, setter); |
- } |
- } else { |
- // Document as field. |
- docProperty(host, getter, setter); |
- } |
- } |
- } |
- writeln('</div>'); |
- } |
- |
- void docMethods(DeclarationMirror host, String title, List<Mirror> methods, |
- {bool allInherited}) { |
- if (methods.length > 0) { |
- writeln('<div${allInherited ? ' class="inherited"' : ''}>'); |
- writeln('<h3>$title</h3>'); |
- for (MethodMirror method in methods) { |
- docMethod(host, method); |
- } |
- writeln('</div>'); |
- } |
- } |
- |
- /** |
- * Documents the [member] declared in [host]. Handles all kinds of members |
- * including getters, setters, and constructors. If [member] is a |
- * [FieldMirror] it is documented as a getter or setter depending upon the |
- * value of [asGetter]. |
- */ |
- void docMethod(DeclarationMirror host, DeclarationMirror member, |
- {bool asGetter: false}) { |
- _totalMembers++; |
- _currentMember = member; |
- |
- bool isAbstract = false; |
- String name = displayName(member); |
- if (member is VariableMirror) { |
- if (asGetter) { |
- // Getter. |
- name = 'get $name'; |
- } else { |
- // Setter. |
- name = 'set $name'; |
- } |
- } else if (member is MethodMirror) { |
- isAbstract = member.isAbstract; |
- if (member.isGetter) { |
- // Getter. |
- name = 'get $name'; |
- } else if (member.isSetter) { |
- // Setter. |
- name = 'set $name'; |
- } |
- } else { |
- assert(false); |
- } |
- |
- bool showCode = includeSource && !isAbstract; |
- bool inherited = host != member.owner && member.owner is! LibraryMirror; |
- |
- writeln('<div class="method${inherited ? ' inherited': ''}">' |
- '<h4 id="${memberAnchor(member)}">'); |
- |
- if (showCode) { |
- writeln('<button class="show-code">Code</button>'); |
- } |
- |
- if (member is MethodMirror) { |
- if (member.isConstructor) { |
- if (member.isFactoryConstructor) { |
- write('factory '); |
- } else { |
- write(member.isConstConstructor ? 'const ' : 'new '); |
- } |
- } else if (member.isAbstract) { |
- write('abstract '); |
- } |
- |
- if (!member.isConstructor) { |
- annotateDynamicType(host, member.returnType); |
- } |
- } else if (member is VariableMirror) { |
- if (asGetter) { |
- annotateDynamicType(host, member.type); |
- } else { |
- write('void '); |
- } |
- } else { |
- assert(false); |
- } |
- |
- write('<strong>$name</strong>'); |
- |
- if (member is MethodMirror) { |
- if (!member.isGetter) { |
- docParamList(host, member.parameters); |
- } |
- } else if (member is VariableMirror) { |
- if (!asGetter) { |
- write('('); |
- annotateType(host, member.type); |
- write(' value)'); |
- } |
- } else { |
- assert(false); |
- } |
- |
- var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
- write(''' <a class="anchor-link" href="#${memberAnchor(member)}" |
- title="Permalink to $prefix$name">#</a>'''); |
- writeln('</h4>'); |
- |
- if (inherited) { |
- docInherited(host, member.owner); |
- } |
- |
- writeln('<div class="doc">'); |
- docComment(host, getMemberComment(member)); |
- if (showCode) { |
- docCode(member.location); |
- } |
- writeln('</div>'); |
- |
- writeln('</div>'); |
- |
- _currentMember = null; |
- } |
- |
- /** |
- * Annotate a member as inherited or mixed in from [owner]. |
- */ |
- void docInherited(DeclarationMirror host, ClassMirror owner) { |
- if (isMixinApplication(owner)) { |
- write('<div class="inherited-from">mixed in from '); |
- annotateType(host, owner.mixin); |
- write('</div>'); |
- } else { |
- write('<div class="inherited-from">inherited from '); |
- annotateType(host, owner); |
- write('</div>'); |
- } |
- } |
- |
- void docField(DeclarationMirror host, VariableMirror field, |
- {bool asGetter: false, bool asSetter: false}) { |
- if (asGetter) { |
- docMethod(host, field, asGetter: true); |
- } else if (asSetter) { |
- docMethod(host, field, asGetter: false); |
- } else { |
- docProperty(host, field, null); |
- } |
- } |
- |
- /** |
- * Documents the property defined by [getter] and [setter] of declared in |
- * [host]. If [getter] is a [FieldMirror], [setter] must be [:null:]. |
- * Otherwise, if [getter] is a [MethodMirror], the property is considered |
- * final if [setter] is [:null:]. |
- */ |
- void docProperty(DeclarationMirror host, |
- DeclarationMirror getter, DeclarationMirror setter) { |
- assert(getter != null); |
- _totalMembers++; |
- _currentMember = getter; |
- |
- bool inherited = host != getter.owner && getter.owner is! LibraryMirror; |
- |
- writeln('<div class="field${inherited ? ' inherited' : ''}">' |
- '<h4 id="${memberAnchor(getter)}">'); |
- |
- if (includeSource) { |
- writeln('<button class="show-code">Code</button>'); |
- } |
- |
- bool isConst = false; |
- bool isFinal; |
- TypeMirror type; |
- if (getter is VariableMirror) { |
- assert(setter == null); |
- isConst = getter.isConst; |
- isFinal = getter.isFinal; |
- type = getter.type; |
- } else if (getter is MethodMirror) { |
- isFinal = setter == null; |
- type = getter.returnType; |
- } else { |
- assert(false); |
- } |
- |
- if (isConst) { |
- write('const '); |
- } else if (isFinal) { |
- write('final '); |
- } else if (type.isDynamic) { |
- write('var '); |
- } |
- |
- |
- var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
- String name = nameOf(getter); |
- write( |
- ''' |
- <strong>${name}</strong> <a class="anchor-link" |
- href="#${memberAnchor(getter)}" |
- title="Permalink to $prefix${name}">#</a> |
- </h4> |
- '''); |
- |
- if (inherited) { |
- docInherited(host, getter.owner); |
- } |
- |
- DocComment comment = getMemberComment(getter); |
- if (comment == null && setter != null) { |
- comment = getMemberComment(setter); |
- } |
- writeln('<div class="doc">'); |
- docComment(host, comment); |
- docCode(getter.location); |
- if (setter != null) { |
- docCode(setter.location); |
- } |
- writeln('</div>'); |
- |
- writeln('</div>'); |
- |
- _currentMember = null; |
- } |
- |
- void docParamList(DeclarationMirror enclosingType, |
- List<ParameterMirror> parameters) { |
- write('('); |
- bool first = true; |
- bool inOptionals = false; |
- bool isNamed = false; |
- for (final parameter in parameters) { |
- if (!first) write(', '); |
- |
- if (!inOptionals && parameter.isOptional) { |
- isNamed = parameter.isNamed; |
- write(isNamed ? '{' : '['); |
- inOptionals = true; |
- } |
- |
- annotateType(enclosingType, parameter.type, |
- nameOf(parameter)); |
- |
- // Show the default value for named optional parameters. |
- if (parameter.isOptional && parameter.hasDefaultValue) { |
- write(isNamed ? ': ' : ' = '); |
- write('${parameter.defaultValue}'); |
- } |
- |
- first = false; |
- } |
- |
- if (inOptionals) { |
- write(isNamed ? '}' : ']'); |
- } |
- write(')'); |
- } |
- |
- void docComment(DeclarationMirror host, DocComment comment) { |
- if (comment != null) { |
- var html = comment.html; |
- |
- if (comment.inheritedFrom != null) { |
- writeln('<div class="inherited">'); |
- writeln(html); |
- write('<div class="docs-inherited-from">docs inherited from '); |
- annotateType(host, comment.inheritedFrom); |
- write('</div>'); |
- writeln('</div>'); |
- } else { |
- writeln(html); |
- } |
- } |
- } |
- |
- /** |
- * Documents the source code contained within [location]. |
- */ |
- void docCode(SourceLocation location) { |
- if (includeSource) { |
- writeln('<pre class="source">'); |
- writeln(md.escapeHtml(unindentCode(location))); |
- writeln('</pre>'); |
- } |
- } |
- |
- /** Get the doc comment associated with the given library. */ |
- DocComment getLibraryComment(LibraryMirror library) => getComment(library); |
- |
- /** Get the doc comment associated with the given type. */ |
- DocComment getTypeComment(TypeMirror type) => getComment(type); |
- |
- /** |
- * Get the doc comment associated with the given member. |
- * |
- * If no comment was found on the member, the hierarchy is traversed to find |
- * an inherited comment, favouring comments inherited from classes over |
- * comments inherited from interfaces. |
- */ |
- DocComment getMemberComment(DeclarationMirror member) => getComment(member); |
- |
- /** |
- * Get the doc comment associated with the given declaration. |
- * |
- * If no comment was found on a member, the hierarchy is traversed to find |
- * an inherited comment, favouring comments inherited from classes over |
- * comments inherited from interfaces. |
- */ |
- DocComment getComment(DeclarationMirror mirror) { |
- String comment = computeComment(mirror); |
- ClassMirror inheritedFrom = null; |
- if (comment == null) { |
- if (mirror.owner is ClassMirror) { |
- var iterable = |
- new HierarchyIterable(mirror.owner, |
- includeType: false); |
- for (ClassMirror type in iterable) { |
- if (isFromPrivateDartLibrary(type)) continue; |
- var inheritedMember = type.declarations[mirror.simpleName]; |
- if (inheritedMember is MethodMirror || |
- inheritedMember is VariableMirror) { |
- comment = computeComment(inheritedMember); |
- if (comment != null) { |
- inheritedFrom = type; |
- break; |
- } |
- } |
- } |
- } |
- } |
- if (comment == null) return null; |
- if (isMixinApplication(inheritedFrom)) { |
- inheritedFrom = inheritedFrom.mixin; |
- } |
- return new DocComment(comment, inheritedFrom, dartdocSyntaxes, |
- dartdocResolver); |
- } |
- |
- /** |
- * Converts [fullPath] which is understood to be a full path from the root of |
- * the generated docs to one relative to the current file. |
- */ |
- String relativePath(String fullPath) { |
- // Don't make it relative if it's an absolute path. |
- if (isAbsolute(fullPath)) return fullPath; |
- |
- // TODO(rnystrom): Walks all the way up to root each time. Shouldn't do |
- // this if the paths overlap. |
- return '${repeat('../', |
- countOccurrences(_filePath.toString(), '/'))}$fullPath'; |
- } |
- |
- /** Gets whether or not the given URL is absolute or relative. */ |
- bool isAbsolute(String url) { |
- // TODO(rnystrom): Why don't we have a nice type in the platform for this? |
- // TODO(rnystrom): This is a bit hackish. We consider any URL that lacks |
- // a scheme to be relative. |
- return new RegExp(r'^\w+:').hasMatch(url); |
- } |
- |
- /** Gets the URL to the documentation for [library]. */ |
- String libraryUrl(LibraryMirror library) { |
- return '${sanitize(displayName(library))}.html'; |
- } |
- |
- /** |
- * Gets the URL for the documentation for [type] or `null` if there is no |
- * link to the documentation of [type]. |
- */ |
- String typeUrl(DeclarationMirror type) { |
- var library = type is LibraryMirror ? type : _libraryFor(type); |
- if (shouldLinkToPublicApi(library)) { |
- return "$API_LOCATION${typePath(type)}"; |
- } else if (shouldIncludeLibrary(library)) { |
- return typePath(type); |
- } else { |
- return null; |
- } |
- } |
- |
- /** Gets the relative path for the documentation for [type]. */ |
- String typePath(DeclarationMirror type) { |
- String name = nameOf(type); |
- if (type is LibraryMirror) { |
- return '${sanitize(name)}.html'; |
- } |
- if (getLibrary(type) == null) { |
- return ''; |
- } |
- // Always get the generic type to strip off any type parameters or |
- // arguments. If the type isn't generic, genericType returns `this`, so it |
- // works for non-generic types too. |
- return '${sanitize(displayName(_libraryFor(type)))}/${name}.html'; |
- } |
- |
- /** Gets the URL for the documentation for [member]. */ |
- String memberUrl(DeclarationMirror member) { |
- String url = typeUrl(_ownerFor(member)); |
- return url != null ? '$url#${memberAnchor(member)}' : null; |
- } |
- |
- /** Gets the anchor id for the document for [member]. */ |
- String memberAnchor(DeclarationMirror member) { |
- return nameOf(member); |
- } |
- |
- /** |
- * Creates a hyperlink. Handles turning the [href] into an appropriate |
- * relative path from the current file. If [href] is `null`, [contents] is |
- * not embedded in an anchor tag, and thus no link is created. |
- */ |
- String a(String href, String contents, [String css]) { |
- if (href != null) { |
- // Mark outgoing external links, mainly so we can style them. |
- final rel = isAbsolute(href) ? ' ref="external"' : ''; |
- final cssClass = css == null ? '' : ' class="$css"'; |
- return '<a href="${relativePath(href)}"$cssClass$rel>$contents</a>'; |
- } |
- return contents; |
- } |
- |
- /** |
- * Writes a type annotation, preferring to print dynamic. |
- */ |
- annotateDynamicType(DeclarationMirror enclosingType, |
- TypeMirror type) { |
- annotateType(enclosingType, type, type.isDynamic ? 'dynamic ' : null); |
- } |
- |
- /** |
- * Writes a type annotation for the given type and (optional) parameter name. |
- */ |
- annotateType(DeclarationMirror enclosingType, |
- TypeMirror type, |
- [String paramName = null]) { |
- // Don't bother explicitly displaying dynamic. |
- if (type.isDynamic) { |
- if (paramName != null) write(paramName); |
- return; |
- } |
- |
- // For parameters, handle non-typedefed function types. |
- if (paramName != null && type is FunctionTypeMirror) { |
- annotateType(enclosingType, type.returnType); |
- write(paramName); |
- |
- docParamList(enclosingType, type.parameters); |
- return; |
- } |
- |
- linkToType(enclosingType, type); |
- |
- write(' '); |
- if (paramName != null) write(paramName); |
- } |
- |
- /** Writes a link to a human-friendly string representation for a type. */ |
- linkToType(DeclarationMirror enclosingType, TypeMirror type) { |
- if (type.isVoid) { |
- // Do not generate links for void. |
- // TODO(johnniwinter): Generate span for specific style? |
- write('void'); |
- return; |
- } |
- if (type.isDynamic) { |
- // Do not generate links for dynamic. |
- write('dynamic'); |
- return; |
- } |
- |
- String name = nameOf(type); |
- if (type is TypeVariableMirror) { |
- // If we're using a type parameter within the body of a generic class then |
- // just link back up to the class. |
- write(a(typeUrl(enclosingType), name)); |
- return; |
- } |
- |
- // Link to the type. |
- write(a(typeUrl(type), name)); |
- |
- if (type.isOriginalDeclaration) { |
- // Avoid calling [:typeArguments():] on a declaration. |
- return; |
- } |
- |
- // See if it's an instantiation of a generic type. |
- final typeArgs = type.typeArguments; |
- if (typeArgs.length > 0) { |
- write('<'); |
- bool first = true; |
- for (final arg in typeArgs) { |
- if (!first) write(', '); |
- first = false; |
- linkToType(enclosingType, arg); |
- } |
- write('>'); |
- } |
- } |
- |
- /** Creates a linked cross reference to [type]. */ |
- typeReference(ClassMirror type) { |
- // TODO(rnystrom): Do we need to handle ParameterTypes here like |
- // annotation() does? |
- return a(typeUrl(type), typeName(type), 'crossref'); |
- } |
- |
- /** Generates a human-friendly string representation for a type. */ |
- String typeName(TypeMirror type, {bool showBounds: false}) { |
- if (type.isVoid) { |
- return 'void'; |
- } |
- if (type.isDynamic) { |
- return 'dynamic'; |
- } |
- String name = nameOf(type); |
- if (type is TypeVariableMirror) { |
- return name; |
- } |
- |
- // See if it's a generic type. |
- if (type.isOriginalDeclaration) { |
- final typeParams = []; |
- for (final typeParam in type.originalDeclaration.typeVariables) { |
- String typeParamName = nameOf(typeParam); |
- if (showBounds && |
- (typeParam.upperBound != null) && |
- !isObject(typeParam.upperBound)) { |
- final bound = typeName(typeParam.upperBound, showBounds: true); |
- typeParams.add('${typeParamName} extends $bound'); |
- } else { |
- typeParams.add(typeParamName); |
- } |
- } |
- if (typeParams.isEmpty) { |
- return name; |
- } |
- final params = typeParams.join(', '); |
- return '${name}<$params>'; |
- } |
- |
- // See if it's an instantiation of a generic type. |
- final typeArgs = type.typeArguments; |
- if (typeArgs.length > 0) { |
- final args = typeArgs.map((arg) => typeName(arg)).join(', '); |
- return '${name}<$args>'; |
- } |
- |
- // Regular type. |
- return name; |
- } |
- |
- /** |
- * Remove leading indentation to line up with first line. |
- */ |
- unindentCode(SourceLocation span) { |
- final column = span.column; |
- final lines = span.text.split('\n'); |
- // TODO(rnystrom): Dirty hack. |
- for (var i = 1; i < lines.length; i++) { |
- lines[i] = unindent(lines[i], column); |
- } |
- |
- final code = lines.join('\n'); |
- return code; |
- } |
- |
- /** |
- * Takes a string of Dart code and turns it into sanitized HTML. |
- */ |
- formatCode(SourceLocation span) { |
- final code = unindentCode(span); |
- |
- // Syntax highlight. |
- return classifySource(code); |
- } |
- |
- /** |
- * This will be called whenever a doc comment hits a `[name]` in square |
- * brackets. It will try to figure out what the name refers to and link or |
- * style it appropriately. |
- */ |
- md.Node resolveNameReference(String name, |
- {DeclarationMirror currentMember, |
- TypeMirror currentType, |
- LibraryMirror currentLibrary}) { |
- makeLink(String href) { |
- if (href != null) { |
- final anchor = new md.Element.text('a', name); |
- anchor.attributes['href'] = relativePath(href); |
- anchor.attributes['class'] = 'crossref'; |
- return anchor; |
- } else { |
- return new md.Element.text('code', name); |
- } |
- } |
- |
- DeclarationMirror declaration = currentMember; |
- if (declaration == null) declaration = currentType; |
- if (declaration == null) declaration = currentLibrary; |
- if (declaration != null) { |
- declaration = lookupQualifiedInScope(declaration, name); |
- } |
- |
- if (declaration != null) { |
- if (declaration is TypeVariableMirror) { |
- return makeLink(typeUrl(declaration.owner)); |
- } if (declaration is TypeMirror) { |
- return makeLink(typeUrl(declaration)); |
- } else if (declaration is MethodMirror) { |
- return makeLink(memberUrl(declaration)); |
- } |
- } |
- |
- // TODO(johnniwinther): Handle private names. |
- Symbol symbol = new Symbol(name); |
- |
- // See if it's a parameter of the current method. |
- if (currentMember is MethodMirror) { |
- for (final parameter in currentMember.parameters) { |
- if (parameter.simpleName == symbol) { |
- final element = new md.Element.text('span', name); |
- element.attributes['class'] = 'param'; |
- return element; |
- } |
- } |
- } |
- |
- // See if it's another member of the current type. |
- if (currentType is ClassMirror) { |
- final foundMember = currentType.declarations[symbol]; |
- if (foundMember is MethodMirror || foundMember is VariableMirror) { |
- return makeLink(memberUrl(foundMember)); |
- } |
- } |
- |
- // See if it's another type or a member of another type in the current |
- // library. |
- if (currentLibrary != null) { |
- // See if it's a constructor |
- final constructorLink = (() { |
- final match = |
- new RegExp(r'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); |
- if (match == null) return null; |
- // TODO(johnniwinther): Handle private names. |
- Symbol typeName = new Symbol(match[1]); |
- var foundtype = currentLibrary.declarations[typeName]; |
- if (foundtype is! ClassMirror) return null; |
- // TODO(johnniwinther): Handle private names. |
- Symbol constructorName = |
- (match[2] == null) ? const Symbol('') : new Symbol(match[2]); |
- final constructor = |
- foundtype.declarations[constructorName]; |
- if (constructor is! MethodMirror) return null; |
- if (!constructor.isConstructor) return null; |
- return makeLink(memberUrl(constructor)); |
- })(); |
- if (constructorLink != null) return constructorLink; |
- |
- // See if it's a member of another type |
- final foreignMemberLink = (() { |
- final match = new RegExp(r'([\w$]+)\.([\w$]+)').firstMatch(name); |
- if (match == null) return null; |
- // TODO(johnniwinther): Handle private names. |
- Symbol typeName = new Symbol(match[1]); |
- var foundtype = currentLibrary.declarations[typeName]; |
- if (foundtype == null) return null; |
- // TODO(johnniwinther): Handle private names. |
- Symbol memberName = new Symbol(match[2]); |
- final foundMember = foundtype.declarations[memberName]; |
- if (foundMember is! MethodMirror && |
- foundMember is! VariableMirror) return null; |
- return makeLink(memberUrl(foundMember)); |
- })(); |
- if (foreignMemberLink != null) return foreignMemberLink; |
- |
- var foundType = currentLibrary.declarations[symbol]; |
- if (foundType is TypeMirror) { |
- return makeLink(typeUrl(foundType)); |
- } |
- |
- // See if it's a top-level member in the current library. |
- var foundMember = currentLibrary.declarations[symbol]; |
- if (foundMember is MethodMirror || foundMember is VariableMirror) { |
- return makeLink(memberUrl(foundMember)); |
- } |
- } |
- |
- // TODO(rnystrom): Should also consider: |
- // * Names imported by libraries this library imports. |
- // * Type parameters of the enclosing type. |
- |
- return new md.Element.text('code', name); |
- } |
- |
- generateAppCacheManifest() { |
- if (verbose) { |
- print('Generating app cache manifest from output $outputDir'); |
- } |
- startFile('appcache.manifest'); |
- write("CACHE MANIFEST\n\n"); |
- write("# VERSION: ${new DateTime.now()}\n\n"); |
- write("NETWORK:\n*\n\n"); |
- write("CACHE:\n"); |
- var toCache = new Directory(outputDir); |
- toCache.list(recursive: true).listen( |
- (FileSystemEntity entity) { |
- if (entity is File) { |
- var filename = entity.path; |
- if (filename.endsWith('appcache.manifest')) { |
- return; |
- } |
- String relativeFilePath = path.relative(filename, from: outputDir); |
- write("$relativeFilePath\n"); |
- } |
- }, |
- onDone: () => endFile()); |
- } |
- |
- /** |
- * Returns [:true:] if [type] should be regarded as an exception. |
- */ |
- bool isException(TypeMirror type) { |
- String name = nameOf(type); |
- return name.endsWith('Exception') || name.endsWith('Error'); |
- } |
- |
- /** |
- * Returns the absolute path to [library] on the filesystem, or `null` if the |
- * library doesn't exist on the local filesystem. |
- */ |
- String _libraryPath(LibraryMirror library) => |
- importUriToPath(library.uri, packageRoot: _packageRoot); |
- |
- /** |
- * Returns a list of classes in [library], including classes it exports from |
- * hidden libraries. |
- */ |
- List<ClassMirror> _libraryClasses(LibraryMirror library) => |
- _libraryContents(library, (lib) => typesOf(lib.declarations).toList()); |
- |
- /** |
- * Returns a list of top-level members in [library], including members it |
- * exports from hidden libraries. |
- */ |
- List<DeclarationMirror> _libraryMembers(LibraryMirror library) => |
- _libraryContents(library, (lib) => membersOf(lib.declarations).toList()); |
- |
- |
- /** |
- * Returns a list of elements in [library], including elements it exports from |
- * hidden libraries. [fn] should return the element list for a single library, |
- * which will then be merged across all exported libraries. |
- */ |
- List<DeclarationMirror> _libraryContents(LibraryMirror library, |
- List<DeclarationMirror> fn(LibraryMirror)) { |
- var contents = fn(library).toList(); |
- var exports = _exports.exports[library]; |
- if (exports == null) return contents; |
- |
- contents.addAll(exports.expand((export) { |
- var exportedLibrary = export.exported; |
- if (shouldIncludeLibrary(exportedLibrary)) return []; |
- return fn(exportedLibrary).where((declaration) => |
- export.isMemberVisible(displayName(declaration))); |
- })); |
- return contents; |
- } |
- |
- /** |
- * Returns the library in which [type] was defined. If [type] was defined in a |
- * hidden library that was exported by another library, this returns the |
- * exporter. |
- */ |
- LibraryMirror _libraryFor(TypeMirror type) => |
- _visibleLibrary(getLibrary(type), displayName(type)); |
- |
- /** |
- * Returns the owner of [declaration]. If [declaration]'s owner is a hidden |
- * library that was exported by another library, this returns the exporter. |
- */ |
- DeclarationMirror _ownerFor(DeclarationMirror declaration) { |
- var owner = declaration.owner; |
- if (owner is! LibraryMirror) return owner; |
- return _visibleLibrary(owner, displayName(declaration)); |
- } |
- |
- /** |
- * Returns the best visible library that exports [name] from [library]. If |
- * [library] is public, it will be returned. |
- */ |
- LibraryMirror _visibleLibrary(LibraryMirror library, String name) { |
- if (library == null) return null; |
- |
- var exports = _hiddenLibraryExports[library]; |
- if (exports == null) return library; |
- |
- var export = exports.firstWhere( |
- (exp) => exp.isMemberVisible(name), |
- orElse: () => null); |
- if (export == null) return library; |
- return export.exporter; |
- } |
-} |
- |
-/** |
- * Used to report an unexpected error in the DartDoc tool or the |
- * underlying data |
- */ |
-class InternalError { |
- final String message; |
- const InternalError(this.message); |
- String toString() => "InternalError: '$message'"; |
-} |
- |
-/** |
- * Computes the doc comment for the declaration mirror. |
- * |
- * Multiple comments are concatenated with newlines in between. |
- */ |
-String computeComment(DeclarationMirror mirror) { |
- String text; |
- for (InstanceMirror metadata in mirror.metadata) { |
- if (metadata is CommentInstanceMirror) { |
- CommentInstanceMirror comment = metadata; |
- if (comment.isDocComment) { |
- if (text == null) { |
- text = comment.trimmedText; |
- } else { |
- text = '$text\n${comment.trimmedText}'; |
- } |
- } |
- } |
- } |
- return text; |
-} |
- |
-/** |
- * Computes the doc comment for the declaration mirror as a list. |
- */ |
-List<String> computeUntrimmedCommentAsList(DeclarationMirror mirror) { |
- var text = <String>[]; |
- for (InstanceMirror metadata in mirror.metadata) { |
- if (metadata is CommentInstanceMirror) { |
- CommentInstanceMirror comment = metadata; |
- if (comment.isDocComment) { |
- text.add(comment.text); |
- } |
- } |
- } |
- return text; |
-} |
- |
-class DocComment { |
- final String text; |
- md.Resolver dartdocResolver; |
- List<md.InlineSyntax> dartdocSyntaxes; |
- |
- /** |
- * Non-null if the comment is inherited from another declaration. |
- */ |
- final ClassMirror inheritedFrom; |
- |
- DocComment(this.text, [this.inheritedFrom = null, this.dartdocSyntaxes, |
- this.dartdocResolver]) { |
- assert(text != null && !text.trim().isEmpty); |
- } |
- |
- String toString() => text; |
- |
- String get html { |
- return md.markdownToHtml(text, |
- inlineSyntaxes: dartdocSyntaxes, |
- linkResolver: dartdocResolver); |
- } |
-} |
- |
-class MdnComment implements DocComment { |
- final String mdnComment; |
- final String mdnUrl; |
- |
- MdnComment(String this.mdnComment, String this.mdnUrl); |
- |
- String get text => mdnComment; |
- |
- ClassMirror get inheritedFrom => null; |
- |
- String get html { |
- // Wrap the mdn comment so we can highlight it and so we handle MDN scraped |
- // content that lacks a top-level block tag. |
- return ''' |
- <div class="mdn"> |
- $mdnComment |
- <div class="mdn-note"><a href="$mdnUrl">from MDN</a></div> |
- </div> |
- '''; |
- } |
- |
- String toString() => mdnComment; |
-} |