Index: pkg/dartdoc/dartdoc.dart |
diff --git a/pkg/dartdoc/dartdoc.dart b/pkg/dartdoc/dartdoc.dart |
deleted file mode 100644 |
index 62bf63ad6688b1d00e2de012ac6f46c4a1d39540..0000000000000000000000000000000000000000 |
--- a/pkg/dartdoc/dartdoc.dart |
+++ /dev/null |
@@ -1,1738 +0,0 @@ |
-// Copyright (c) 2012, 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:io'); |
-#import('dart:math'); |
-#import('dart:uri'); |
-#import('dart:json'); |
-#import('mirrors/mirrors.dart'); |
-#import('mirrors/mirrors_util.dart'); |
-#import('mirrors/dart2js_mirror.dart', prefix: 'dart2js'); |
-#import('classify.dart'); |
-#import('markdown.dart', prefix: 'md'); |
-#import('../../lib/compiler/implementation/scanner/scannerlib.dart', |
- prefix: 'dart2js'); |
-#import('../../lib/_internal/libraries.dart'); |
- |
-#source('comment_map.dart'); |
-#source('nav.dart'); |
-#source('utils.dart'); |
- |
-// TODO(johnniwinther): Note that [IN_SDK] gets initialized to true when this |
-// file is modified by the SDK deployment script. If you change, be sure to test |
-// that dartdoc still works when run from the built SDK directory. |
-const bool IN_SDK = false; |
- |
-/** |
- * 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/'; |
- |
-/** |
- * Run this from the `pkg/dartdoc` directory. |
- */ |
-void main() { |
- final args = new Options().arguments; |
- |
- final dartdoc = new Dartdoc(); |
- |
- if (args.isEmpty()) { |
- print('No arguments provided.'); |
- printUsage(); |
- return; |
- } |
- |
- final entrypoints = <Path>[]; |
- |
- var i = 0; |
- while (i < args.length) { |
- final arg = args[i]; |
- if (!arg.startsWith('--')) { |
- // The remaining arguments must be entry points. |
- break; |
- } |
- |
- switch (arg) { |
- case '--no-code': |
- dartdoc.includeSource = false; |
- break; |
- |
- case '--mode=static': |
- dartdoc.mode = MODE_STATIC; |
- break; |
- |
- case '--mode=live-nav': |
- dartdoc.mode = MODE_LIVE_NAV; |
- break; |
- |
- case '--generate-app-cache': |
- case '--generate-app-cache=true': |
- dartdoc.generateAppCache = true; |
- break; |
- |
- case '--omit-generation-time': |
- dartdoc.omitGenerationTime = true; |
- break; |
- case '--verbose': |
- dartdoc.verbose = true; |
- break; |
- case '--include-api': |
- dartdoc.includeApi = true; |
- break; |
- case '--link-api': |
- dartdoc.linkToApi = true; |
- break; |
- |
- default: |
- if (arg.startsWith('--out=')) { |
- dartdoc.outputDir = |
- new Path.fromNative(arg.substring('--out='.length)); |
- } else if (arg.startsWith('--include-lib=')) { |
- dartdoc.includedLibraries = |
- arg.substring('--include-lib='.length).split(','); |
- } else if (arg.startsWith('--exclude-lib=')) { |
- dartdoc.excludedLibraries = |
- arg.substring('--exclude-lib='.length).split(','); |
- } else { |
- print('Unknown option: $arg'); |
- printUsage(); |
- return; |
- } |
- break; |
- } |
- i++; |
- } |
- while (i < args.length) { |
- final arg = args[i]; |
- entrypoints.add(new Path.fromNative(arg)); |
- i++; |
- } |
- |
- if (entrypoints.isEmpty()) { |
- print('No entrypoints provided.'); |
- printUsage(); |
- return; |
- } |
- |
- cleanOutputDirectory(dartdoc.outputDir); |
- |
- dartdoc.documentLibraries(entrypoints, libPath); |
- |
- // Compile the client-side code to JS. |
- final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; |
- Future compiled = compileScript( |
- scriptDir.append('client-$clientScript.dart'), |
- dartdoc.outputDir.append('client-$clientScript.js')); |
- |
- Future filesCopied = copyDirectory(scriptDir.append('static'), |
- dartdoc.outputDir); |
- |
- Futures.wait([compiled, filesCopied]).then((_) { |
- dartdoc.cleanup(); |
- print('Documented ${dartdoc._totalLibraries} libraries, ' |
- '${dartdoc._totalTypes} types, and ' |
- '${dartdoc._totalMembers} members.'); |
- }); |
-} |
- |
-void printUsage() { |
- print(''' |
-Usage dartdoc [options] <entrypoint(s)> |
-[options] include |
- --no-code Do not include source code in the documentation. |
- |
- --mode=static Generates completely static HTML containing |
- everything you need to browse the docs. The only |
- client side behavior is trivial stuff like syntax |
- highlighting code. |
- |
- --mode=live-nav (default) 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`. |
- |
- --generate-app-cache Generates the App Cache manifest file, enabling |
- offline doc viewing. |
- |
- --out=<dir> Generates files into directory <dir>. If omitted |
- the files are generated into ./docs/ |
- |
- --link-api Link to the online language API in the generated |
- documentation. The option overrides inclusion |
- through --include-api or --include-lib. |
- |
- --include-api Include the used API libraries in the generated |
- documentation. If the --link-api option is used, |
- this option is ignored. |
- |
- --include-lib=<libs> Use this option to explicitly specify which |
- libraries to include in the documentation. If |
- omitted, all used libraries are included by |
- default. <libs> is comma-separated list of library |
- names. |
- |
- --exclude-lib=<libs> Use this option to explicitly specify which |
- libraries to exclude from the documentation. If |
- omitted, no libraries are excluded. <libs> is |
- comma-separated list of library names. |
- |
- --verbose Print verbose information during generation. |
-'''); |
-} |
- |
-/** |
- * 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. |
-Path get scriptDir => |
- new Path.fromNative(new Options().script).directoryPath; |
- |
-// TODO(johnniwinther): Trailing slashes matter due to the use of [libPath] as |
-// a base URI with [Uri.resolve]. |
-/// Relative path to the library in which dart2js resides. |
-Path get libPath => IN_SDK |
- ? scriptDir.append('../../lib/dart2js/') |
- : scriptDir.append('../../'); |
- |
-/** |
- * Deletes and recreates the output directory at [path] if it exists. |
- */ |
-void cleanOutputDirectory(Path path) { |
- final outputDir = new Directory.fromPath(path); |
- if (outputDir.existsSync()) { |
- outputDir.deleteRecursivelySync(); |
- } |
- |
- try { |
- // TODO(3914): Hack to avoid 'file already exists' exception thrown |
- // due to invalid result from dir.existsSync() (probably due to race |
- // conditions). |
- outputDir.createSync(); |
- } on DirectoryIOException catch (e) { |
- // Ignore. |
- } |
-} |
- |
-/** |
- * 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(Path from, Path to) { |
- final completer = new Completer(); |
- final fromDir = new Directory.fromPath(from); |
- final lister = fromDir.list(recursive: false); |
- |
- lister.onFile = (String path) { |
- final name = new Path.fromNative(path).filename; |
- // TODO(rnystrom): Hackish. Ignore 'hidden' files like .DS_Store. |
- if (name.startsWith('.')) return; |
- |
- File fromFile = new File(path); |
- File toFile = new File.fromPath(to.append(name)); |
- fromFile.openInputStream().pipe(toFile.openOutputStream()); |
- }; |
- lister.onDone = (done) => completer.complete(true); |
- return completer.future; |
-} |
- |
-/** |
- * Compiles the given Dart script to a JavaScript file at [jsPath] using the |
- * Dart2js compiler. |
- */ |
-Future<bool> compileScript(Path dartPath, Path jsPath) { |
- var completer = new Completer<bool>(); |
- var compilation = new Compilation(dartPath, libPath); |
- Future<String> result = compilation.compileToJavaScript(); |
- result.then((jsCode) { |
- writeString(new File.fromPath(jsPath), jsCode); |
- completer.complete(true); |
- }); |
- result.handleException((e) => completer.completeException(e)); |
- return completer.future; |
-} |
- |
-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. */ |
- Path dartdocPath; |
- |
- /** Path to generate HTML files into. */ |
- Path outputDir = const Path('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 add content before the footer */ |
- String preFooterText = ''; |
- |
- /** 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 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>[]; |
- |
- /** |
- * This list contains the libraries sorted in by the library name. |
- */ |
- List<LibraryMirror> _sortedLibraries; |
- |
- CommentMap _comments; |
- |
- /** The library that we're currently generating docs for. */ |
- LibraryMirror _currentLibrary; |
- |
- /** The type that we're currently generating docs for. */ |
- InterfaceMirror _currentType; |
- |
- /** The member that we're currently generating docs for. */ |
- MemberMirror _currentMember; |
- |
- /** The path to the file currently being written to, relative to [outdir]. */ |
- Path _filePath; |
- |
- /** The file currently being written to. */ |
- StringBuffer _file; |
- |
- int _totalLibraries = 0; |
- int _totalTypes = 0; |
- int _totalMembers = 0; |
- |
- Dartdoc() |
- : _comments = new CommentMap(), |
- dartdocPath = scriptDir { |
- // Patch in support for [:...:]-style code to the markdown parser. |
- // TODO(rnystrom): Markdown already has syntax for this. Phase this out? |
- md.InlineParser.syntaxes.insertRange(0, 1, |
- new md.CodeSyntax(@'\[\:((?:.|\n)*?)\:\]')); |
- |
- md.setImplicitLinkResolver((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 = library.simpleName; |
- if (!includedLibraries.isEmpty()) { |
- includeByDefault = false; |
- if (includedLibraries.indexOf(libraryName) != -1) { |
- return true; |
- } |
- } |
- if (excludedLibraries.indexOf(libraryName) != -1) { |
- return false; |
- } |
- if (libraryName.startsWith('dart:')) { |
- String suffix = libraryName.substring('dart:'.length); |
- 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) { |
- String libraryName = library.simpleName; |
- if (libraryName.startsWith('dart:')) { |
- String suffix = libraryName.substring('dart:'.length); |
- 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 Date.now()}"); |
- } |
- if (footerText != null) { |
- footerItems.add(footerText); |
- } |
- var content = ''; |
- for (int i = 0; i < footerItems.length; i++) { |
- if (i > 0) { |
- content = content.concat('\n'); |
- } |
- content = content.concat('<div>${footerItems[i]}</div>'); |
- } |
- return content; |
- } |
- |
- void documentEntryPoint(Path entrypoint, Path libPath) { |
- final compilation = new Compilation(entrypoint, libPath); |
- _document(compilation); |
- } |
- |
- void documentLibraries(List<Path> libraryList, Path libPath) { |
- final compilation = new Compilation.library(libraryList, libPath); |
- _document(compilation); |
- } |
- |
- void _document(Compilation compilation) { |
- // Sort the libraries by name (not key). |
- _sortedLibraries = new List<LibraryMirror>.from( |
- compilation.mirrors.libraries.getValues().filter( |
- shouldIncludeLibrary)); |
- _sortedLibraries.sort((x, y) { |
- return x.simpleName.toUpperCase().compareTo( |
- y.simpleName.toUpperCase()); |
- }); |
- |
- // Generate the docs. |
- if (mode == MODE_LIVE_NAV) { |
- docNavigationJson(); |
- } else { |
- docNavigationDart(); |
- } |
- |
- docIndex(); |
- for (final library in _sortedLibraries) { |
- docLibrary(library); |
- } |
- |
- if (generateAppCache) { |
- generateAppCacheManifest(); |
- } |
- } |
- |
- void startFile(String path) { |
- _filePath = new Path(path); |
- _file = new StringBuffer(); |
- } |
- |
- void endFile() { |
- final outPath = outputDir.join(_filePath); |
- final dir = new Directory.fromPath(outPath.directoryPath); |
- 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 DirectoryIOException catch (e) { |
- // Ignore. |
- } |
- } |
- |
- writeString(new File.fromPath(outPath), _file.toString()); |
- _filePath = null; |
- _file = null; |
- } |
- |
- void write(String s) { |
- _file.add(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(_currentLibrary.simpleName)}"'; |
- } |
- |
- 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="http://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> |
- ${preFooterText} |
- <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), library.simpleName)}</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.stringify(createNavigationInfo())); |
- endFile(); |
- } |
- |
- void docNavigationDart() { |
- final dir = new Directory.fromPath(tmpPath); |
- 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 DirectoryIOException catch (e) { |
- // Ignore. |
- } |
- } |
- String jsonString = JSON.stringify(createNavigationInfo()); |
- String dartString = jsonString.replaceAll(@"$", @"\$"); |
- final filePath = tmpPath.append('nav.dart'); |
- writeString(new File.fromPath(filePath), |
- 'get json => $dartString;'); |
- } |
- |
- Path get tmpPath => dartdocPath.append('tmp'); |
- |
- void cleanup() { |
- final dir = new Directory.fromPath(tmpPath); |
- if (dir.existsSync()) { |
- dir.deleteRecursivelySync(); |
- } |
- } |
- |
- List createNavigationInfo() { |
- final libraryList = []; |
- for (final library in _sortedLibraries) { |
- docLibraryNavigationJson(library, libraryList); |
- } |
- return libraryList; |
- } |
- |
- void docLibraryNavigationJson(LibraryMirror library, List libraryList) { |
- var libraryInfo = {}; |
- libraryInfo[NAME] = library.simpleName; |
- final List members = docMembersJson(library.declaredMembers); |
- if (!members.isEmpty()) { |
- libraryInfo[MEMBERS] = members; |
- } |
- |
- final types = []; |
- for (InterfaceMirror type in orderByName(library.types.getValues())) { |
- if (type.isPrivate) continue; |
- |
- var typeInfo = {}; |
- typeInfo[NAME] = type.simpleName; |
- if (type.isClass) { |
- typeInfo[KIND] = CLASS; |
- } else if (type.isInterface) { |
- typeInfo[KIND] = INTERFACE; |
- } else { |
- assert(type.isTypedef); |
- typeInfo[KIND] = TYPEDEF; |
- } |
- final List typeMembers = docMembersJson(type.declaredMembers); |
- if (!typeMembers.isEmpty()) { |
- typeInfo[MEMBERS] = typeMembers; |
- } |
- |
- if (!type.declaration.typeVariables.isEmpty()) { |
- final typeVariables = []; |
- for (final typeVariable in type.declaration.typeVariables) { |
- typeVariables.add(typeVariable.simpleName); |
- } |
- typeInfo[ARGS] = Strings.join(typeVariables, ', '); |
- } |
- types.add(typeInfo); |
- } |
- if (!types.isEmpty()) { |
- libraryInfo[TYPES] = types; |
- } |
- |
- libraryList.add(libraryInfo); |
- } |
- |
- List docMembersJson(Map<Object,MemberMirror> memberMap) { |
- final members = []; |
- for (MemberMirror member in orderByName(memberMap.getValues())) { |
- if (member.isPrivate) continue; |
- |
- var memberInfo = {}; |
- if (member.isField) { |
- memberInfo[NAME] = member.simpleName; |
- memberInfo[KIND] = FIELD; |
- } else { |
- MethodMirror method = member; |
- if (method.isConstructor) { |
- if (method.constructorName != '') { |
- memberInfo[NAME] = '${method.simpleName}.${method.constructorName}'; |
- memberInfo[KIND] = CONSTRUCTOR; |
- } else { |
- memberInfo[NAME] = member.simpleName; |
- memberInfo[KIND] = CONSTRUCTOR; |
- } |
- } else if (method.isOperator) { |
- memberInfo[NAME] = '${method.simpleName} ${method.operatorName}'; |
- memberInfo[KIND] = METHOD; |
- } else if (method.isSetter) { |
- memberInfo[NAME] = member.simpleName; |
- memberInfo[KIND] = SETTER; |
- } else if (method.isGetter) { |
- memberInfo[NAME] = member.simpleName; |
- memberInfo[KIND] = GETTER; |
- } else { |
- memberInfo[NAME] = member.simpleName; |
- memberInfo[KIND] = METHOD; |
- } |
- } |
- 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>${library.simpleName}</strong>'); |
- } else { |
- write('${a(libraryUrl(library), library.simpleName)}'); |
- } |
- 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 = <InterfaceMirror>[]; |
- final exceptions = <InterfaceMirror>[]; |
- |
- for (InterfaceMirror type in orderByName(library.types.getValues())) { |
- if (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(InterfaceMirror type) { |
- var icon = 'interface'; |
- if (type.simpleName.endsWith('Exception')) { |
- icon = 'exception'; |
- } else if (type.isClass) { |
- 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 \'${library.simpleName}\':'); |
- } |
- _totalLibraries++; |
- _currentLibrary = library; |
- _currentType = null; |
- |
- startFile(libraryUrl(library)); |
- writeHeader('${library.simpleName} Library', |
- [library.simpleName, libraryUrl(library)]); |
- writeln('<h2><strong>${library.simpleName}</strong> library</h2>'); |
- |
- // Look for a comment for the entire library. |
- final comment = getLibraryComment(library); |
- if (comment != null) { |
- writeln('<div class="doc">$comment</div>'); |
- } |
- |
- // Document the top-level members. |
- docMembers(library); |
- |
- // Document the types. |
- final classes = <InterfaceMirror>[]; |
- final interfaces = <InterfaceMirror>[]; |
- final typedefs = <TypedefMirror>[]; |
- final exceptions = <InterfaceMirror>[]; |
- |
- for (InterfaceMirror type in orderByName(library.types.getValues())) { |
- if (type.isPrivate) continue; |
- |
- if (isException(type)) { |
- exceptions.add(type); |
- } else if (type.isClass) { |
- classes.add(type); |
- } else if (type.isInterface){ |
- interfaces.add(type); |
- } else if (type is TypedefMirror) { |
- typedefs.add(type); |
- } else { |
- throw new InternalError("internal error: unknown type $type."); |
- } |
- } |
- |
- docTypes(classes, 'Classes'); |
- docTypes(interfaces, 'Interfaces'); |
- docTypes(typedefs, 'Typedefs'); |
- docTypes(exceptions, 'Exceptions'); |
- |
- writeFooter(); |
- endFile(); |
- |
- for (final type in library.types.getValues()) { |
- if (type.isPrivate) continue; |
- |
- docType(type); |
- } |
- } |
- |
- void docTypes(List types, String header) { |
- if (types.length == 0) return; |
- |
- writeln('<h3>$header</h3>'); |
- |
- for (final type in types) { |
- writeln( |
- ''' |
- <div class="type"> |
- <h4> |
- ${a(typeUrl(type), "<strong>${typeName(type)}</strong>")} |
- </h4> |
- </div> |
- '''); |
- } |
- } |
- |
- void docType(InterfaceMirror type) { |
- if (verbose) { |
- print('- ${type.simpleName}'); |
- } |
- _totalTypes++; |
- _currentType = type; |
- |
- startFile(typeUrl(type)); |
- |
- var kind = 'Interface'; |
- if (type.isTypedef) { |
- kind = 'Typedef'; |
- } else if (type.isClass) { |
- kind = 'Class'; |
- } |
- |
- final typeTitle = |
- '${typeName(type)} ${kind}'; |
- writeHeader('$typeTitle / ${type.library.simpleName} Library', |
- [type.library.simpleName, libraryUrl(type.library), |
- typeName(type), typeUrl(type)]); |
- writeln( |
- ''' |
- <h2><strong>${typeName(type, showBounds: true)}</strong> |
- $kind |
- </h2> |
- '''); |
- |
- docCode(type.location, getTypeComment(type)); |
- docInheritance(type); |
- docTypedef(type); |
- docConstructors(type); |
- docMembers(type); |
- |
- writeTypeFooter(); |
- writeFooter(); |
- endFile(); |
- } |
- |
- /** 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(InterfaceMirror type) { |
- var icon = 'interface'; |
- if (type.simpleName.endsWith('Exception')) { |
- icon = 'exception'; |
- } else if (type.isClass) { |
- 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(InterfaceMirror 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 (type.isObject) return; |
- |
- // Writes an unordered list of references to types with an optional header. |
- listTypes(types, header) { |
- if (types == null) return; |
- |
- // Skip private types. |
- final publicTypes = new List.from(types.filter((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(type)) { |
- subtypes.add(subtype); |
- } |
- subtypes.sort((x, y) => x.simpleName.compareTo(y.simpleName)); |
- if (type.isClass) { |
- // Show the chain of superclasses. |
- if (!type.superclass.isObject) { |
- final supertypes = []; |
- var thisType = type.superclass; |
- // As a sanity check, only show up to five levels of nesting, otherwise |
- // the box starts to get hideous. |
- do { |
- supertypes.add(thisType); |
- thisType = thisType.superclass; |
- } while (!thisType.isObject); |
- |
- 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(type.interfaces.getValues(), 'Implements'); |
- } else { |
- // Show the default class. |
- if (type.defaultType != null) { |
- listTypes([type.defaultType], 'Default class'); |
- } |
- |
- // List extended interfaces. |
- listTypes(type.interfaces.getValues(), 'Extends'); |
- |
- // List subinterfaces and implementing classes. |
- final subinterfaces = []; |
- final implementing = []; |
- |
- for (final subtype in subtypes) { |
- if (subtype.isClass) { |
- implementing.add(subtype); |
- } else { |
- subinterfaces.add(subtype); |
- } |
- } |
- |
- listTypes(subinterfaces, 'Subinterfaces'); |
- listTypes(implementing, 'Implemented by'); |
- } |
- } |
- |
- /** |
- * Documents the definition of [type] if it is a typedef. |
- */ |
- void docTypedef(TypeMirror type) { |
- if (type is! TypedefMirror) { |
- return; |
- } |
- writeln('<div class="method"><h4 id="${type.simpleName}">'); |
- |
- if (includeSource) { |
- writeln('<span class="show-code">Code</span>'); |
- } |
- |
- write('typedef '); |
- annotateType(type, type.definition, type.simpleName); |
- |
- write(''' <a class="anchor-link" href="#${type.simpleName}" |
- title="Permalink to ${type.simpleName}">#</a>'''); |
- writeln('</h4>'); |
- |
- docCode(type.location, null, showCode: true); |
- |
- writeln('</div>'); |
- } |
- |
- /** Document the constructors for [Type], if any. */ |
- void docConstructors(InterfaceMirror type) { |
- final constructors = <MethodMirror>[]; |
- for (var constructor in type.constructors.getValues()) { |
- if (!constructor.isPrivate) { |
- constructors.add(constructor); |
- } |
- } |
- |
- if (constructors.length > 0) { |
- writeln('<h3>Constructors</h3>'); |
- constructors.sort((x, y) => x.simpleName.toUpperCase().compareTo( |
- y.simpleName.toUpperCase())); |
- |
- for (final constructor in constructors) { |
- docMethod(type, constructor); |
- } |
- } |
- } |
- |
- void docMembers(ObjectMirror host) { |
- // Collect the different kinds of members. |
- final staticMethods = []; |
- final staticFields = []; |
- final instanceMethods = []; |
- final instanceFields = []; |
- |
- for (MemberMirror member in orderByName(host.declaredMembers.getValues())) { |
- if (member.isPrivate) continue; |
- |
- final methods = member.isStatic ? staticMethods : instanceMethods; |
- final fields = member.isStatic ? staticFields : instanceFields; |
- |
- if (member.isMethod) { |
- methods.add(member); |
- } else if (member.isField) { |
- fields.add(member); |
- } |
- } |
- |
- if (staticMethods.length > 0) { |
- final title = host is LibraryMirror ? 'Functions' : 'Static Methods'; |
- writeln('<h3>$title</h3>'); |
- for (final method in orderByName(staticMethods)) { |
- docMethod(host, method); |
- } |
- } |
- |
- if (staticFields.length > 0) { |
- final title = host is LibraryMirror ? 'Variables' : 'Static Fields'; |
- writeln('<h3>$title</h3>'); |
- for (final field in orderByName(staticFields)) { |
- docField(host, field); |
- } |
- } |
- |
- if (instanceMethods.length > 0) { |
- writeln('<h3>Methods</h3>'); |
- for (final method in orderByName(instanceMethods)) { |
- docMethod(host, method); |
- } |
- } |
- |
- if (instanceFields.length > 0) { |
- writeln('<h3>Fields</h3>'); |
- for (final field in orderByName(instanceFields)) { |
- docField(host, field); |
- } |
- } |
- } |
- |
- /** |
- * Documents the [method] in type [type]. Handles all kinds of methods |
- * including getters, setters, and constructors. |
- */ |
- void docMethod(ObjectMirror host, MethodMirror method) { |
- _totalMembers++; |
- _currentMember = method; |
- |
- writeln('<div class="method"><h4 id="${memberAnchor(method)}">'); |
- |
- if (includeSource) { |
- writeln('<span class="show-code">Code</span>'); |
- } |
- |
- if (method.isConstructor) { |
- if (method.isFactory) { |
- write('factory '); |
- } else { |
- write(method.isConst ? 'const ' : 'new '); |
- } |
- } |
- |
- if (method.constructorName == null) { |
- annotateType(host, method.returnType); |
- } |
- |
- var name = method.simpleName; |
- // Translate specially-named methods: getters, setters, operators. |
- if (method.isGetter) { |
- // Getter. |
- name = 'get $name'; |
- } else if (method.isSetter) { |
- // Setter. |
- name = 'set $name'; |
- } else if (method.isOperator) { |
- name = 'operator ${method.operatorName}'; |
- } |
- |
- write('<strong>$name</strong>'); |
- |
- // Named constructors. |
- if (method.constructorName != null && method.constructorName != '') { |
- write('.'); |
- write(method.constructorName); |
- } |
- |
- docParamList(host, method.parameters); |
- |
- var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
- write(''' <a class="anchor-link" href="#${memberAnchor(method)}" |
- title="Permalink to $prefix$name">#</a>'''); |
- writeln('</h4>'); |
- |
- docCode(method.location, getMethodComment(method), showCode: true); |
- |
- writeln('</div>'); |
- } |
- |
- /** Documents the field [field] of type [type]. */ |
- void docField(ObjectMirror host, FieldMirror field) { |
- _totalMembers++; |
- _currentMember = field; |
- |
- writeln('<div class="field"><h4 id="${memberAnchor(field)}">'); |
- |
- if (includeSource) { |
- writeln('<span class="show-code">Code</span>'); |
- } |
- |
- if (field.isFinal) { |
- write('final '); |
- } else if (field.type.isDynamic) { |
- write('var '); |
- } |
- |
- annotateType(host, field.type); |
- var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
- write( |
- ''' |
- <strong>${field.simpleName}</strong> <a class="anchor-link" |
- href="#${memberAnchor(field)}" |
- title="Permalink to $prefix${field.simpleName}">#</a> |
- </h4> |
- '''); |
- |
- docCode(field.location, getFieldComment(field), showCode: true); |
- writeln('</div>'); |
- } |
- |
- void docParamList(ObjectMirror enclosingType, |
- List<ParameterMirror> parameters) { |
- write('('); |
- bool first = true; |
- bool inOptionals = false; |
- for (final parameter in parameters) { |
- if (!first) write(', '); |
- |
- if (!inOptionals && parameter.isOptional) { |
- write('['); |
- inOptionals = true; |
- } |
- |
- annotateType(enclosingType, parameter.type, parameter.simpleName); |
- |
- // Show the default value for named optional parameters. |
- if (parameter.isOptional && parameter.hasDefaultValue) { |
- write(' = '); |
- write(parameter.defaultValue); |
- } |
- |
- first = false; |
- } |
- |
- if (inOptionals) write(']'); |
- write(')'); |
- } |
- |
- /** |
- * Documents the code contained within [span] with [comment]. If [showCode] |
- * is `true` (and [includeSource] is set), also includes the source code. |
- */ |
- void docCode(Location location, String comment, [bool showCode = false]) { |
- writeln('<div class="doc">'); |
- if (comment != null) { |
- writeln(comment); |
- } |
- |
- if (includeSource && showCode) { |
- writeln('<pre class="source">'); |
- writeln(md.escapeHtml(unindentCode(location))); |
- writeln('</pre>'); |
- } |
- |
- writeln('</div>'); |
- } |
- |
- |
- /** Get the doc comment associated with the given library. */ |
- String getLibraryComment(LibraryMirror library) { |
- // Look for a comment for the entire library. |
- final comment = _comments.findLibrary(library.location.source); |
- if (comment != null) { |
- return md.markdownToHtml(comment); |
- } |
- return null; |
- } |
- |
- /** Get the doc comment associated with the given type. */ |
- String getTypeComment(TypeMirror type) { |
- String comment = _comments.find(type.location); |
- if (comment == null) return null; |
- return commentToHtml(comment); |
- } |
- |
- /** Get the doc comment associated with the given method. */ |
- String getMethodComment(MethodMirror method) { |
- String comment = _comments.find(method.location); |
- if (comment == null) return null; |
- return commentToHtml(comment); |
- } |
- |
- /** Get the doc comment associated with the given field. */ |
- String getFieldComment(FieldMirror field) { |
- String comment = _comments.find(field.location); |
- if (comment == null) return null; |
- return commentToHtml(comment); |
- } |
- |
- String commentToHtml(String comment) => md.markdownToHtml(comment); |
- |
- /** |
- * 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 const RegExp(@'^\w+:').hasMatch(url); |
- } |
- |
- /** Gets the URL to the documentation for [library]. */ |
- String libraryUrl(LibraryMirror library) { |
- return '${sanitize(library.simpleName)}.html'; |
- } |
- |
- /** Gets the URL for the documentation for [type]. */ |
- String typeUrl(ObjectMirror type) { |
- if (type is LibraryMirror) { |
- return '${sanitize(type.simpleName)}.html'; |
- } |
- assert (type is TypeMirror); |
- // 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(type.library.simpleName)}/' |
- '${type.declaration.simpleName}.html'; |
- } |
- |
- /** Gets the URL for the documentation for [member]. */ |
- String memberUrl(MemberMirror member) { |
- String url = typeUrl(member.surroundingDeclaration); |
- return '$url#${memberAnchor(member)}'; |
- } |
- |
- /** Gets the anchor id for the document for [member]. */ |
- String memberAnchor(MemberMirror member) { |
- if (member.isField) { |
- return member.simpleName; |
- } |
- MethodMirror method = member; |
- if (method.isConstructor) { |
- if (method.constructorName == '') { |
- return method.simpleName; |
- } else { |
- return '${method.simpleName}.${method.constructorName}'; |
- } |
- } else if (method.isOperator) { |
- return '${method.simpleName} ${method.operatorName}'; |
- } else if (method.isSetter) { |
- return '${method.simpleName}='; |
- } else { |
- return method.simpleName; |
- } |
- } |
- |
- /** |
- * Creates a hyperlink. Handles turning the [href] into an appropriate |
- * relative path from the current file. |
- */ |
- String a(String href, String contents, [String css]) { |
- // 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>'; |
- } |
- |
- /** |
- * Writes a type annotation for the given type and (optional) parameter name. |
- */ |
- annotateType(ObjectMirror 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(ObjectMirror 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; |
- } |
- |
- if (type.isTypeVariable) { |
- // 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), type.simpleName)); |
- return; |
- } |
- |
- assert(type is InterfaceMirror); |
- |
- // Link to the type. |
- if (shouldLinkToPublicApi(type.library)) { |
- write('<a href="$API_LOCATION${typeUrl(type)}">${type.simpleName}</a>'); |
- } else if (shouldIncludeLibrary(type.library)) { |
- write(a(typeUrl(type), type.simpleName)); |
- } else { |
- write(type.simpleName); |
- } |
- |
- if (type.isDeclaration) { |
- // 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(InterfaceMirror type) { |
- // TODO(rnystrom): Do we need to handle ParameterTypes here like |
- // annotation() does? |
- return a(typeUrl(type), typeName(type), css: 'crossref'); |
- } |
- |
- /** Generates a human-friendly string representation for a type. */ |
- typeName(TypeMirror type, [bool showBounds = false]) { |
- if (type.isVoid) { |
- return 'void'; |
- } |
- if (type is TypeVariableMirror) { |
- return type.simpleName; |
- } |
- assert(type is InterfaceMirror); |
- |
- // See if it's a generic type. |
- if (type.isDeclaration) { |
- final typeParams = []; |
- for (final typeParam in type.declaration.typeVariables) { |
- if (showBounds && |
- (typeParam.bound != null) && |
- !typeParam.bound.isObject) { |
- final bound = typeName(typeParam.bound, showBounds: true); |
- typeParams.add('${typeParam.simpleName} extends $bound'); |
- } else { |
- typeParams.add(typeParam.simpleName); |
- } |
- } |
- if (typeParams.isEmpty()) { |
- return type.simpleName; |
- } |
- final params = Strings.join(typeParams, ', '); |
- return '${type.simpleName}<$params>'; |
- } |
- |
- // See if it's an instantiation of a generic type. |
- final typeArgs = type.typeArguments; |
- if (typeArgs.length > 0) { |
- final args = Strings.join(typeArgs.map((arg) => typeName(arg)), ', '); |
- return '${type.declaration.simpleName}<$args>'; |
- } |
- |
- // Regular type. |
- return type.simpleName; |
- } |
- |
- /** |
- * Remove leading indentation to line up with first line. |
- */ |
- unindentCode(Location span) { |
- final column = getLocationColumn(span); |
- 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 = Strings.join(lines, '\n'); |
- return code; |
- } |
- |
- /** |
- * Takes a string of Dart code and turns it into sanitized HTML. |
- */ |
- formatCode(Location 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, |
- [MemberMirror currentMember = null, |
- ObjectMirror currentType = null, |
- LibraryMirror currentLibrary = null]) { |
- makeLink(String href) { |
- final anchor = new md.Element.text('a', name); |
- anchor.attributes['href'] = relativePath(href); |
- anchor.attributes['class'] = 'crossref'; |
- return anchor; |
- } |
- |
- // See if it's a parameter of the current method. |
- if (currentMember is MethodMirror) { |
- for (final parameter in currentMember.parameters) { |
- if (parameter.simpleName == name) { |
- 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 != null) { |
- final foundMember = findMirror(currentType.declaredMembers, name); |
- if (foundMember != null) { |
- 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(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); |
- if (match == null) return; |
- InterfaceMirror foundtype = findMirror(currentLibrary.types, match[1]); |
- if (foundtype == null) return; |
- final constructor = |
- findMirror(foundtype.constructors, |
- match[2] == null ? '' : match[2]); |
- if (constructor == null) return; |
- return makeLink(memberUrl(constructor)); |
- })(); |
- if (constructorLink != null) return constructorLink; |
- |
- // See if it's a member of another type |
- final foreignMemberLink = (() { |
- final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name); |
- if (match == null) return; |
- InterfaceMirror foundtype = findMirror(currentLibrary.types, match[1]); |
- if (foundtype == null) return; |
- MemberMirror foundMember = findMirror(foundtype.declaredMembers, match[2]); |
- if (foundMember == null) return; |
- return makeLink(memberUrl(foundMember)); |
- })(); |
- if (foreignMemberLink != null) return foreignMemberLink; |
- |
- InterfaceMirror foundType = findMirror(currentLibrary.types, name); |
- if (foundType != null) { |
- return makeLink(typeUrl(foundType)); |
- } |
- |
- // See if it's a top-level member in the current library. |
- MemberMirror foundMember = findMirror(currentLibrary.declaredMembers, name); |
- if (foundMember != null) { |
- 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 Date.now()}\n\n"); |
- write("NETWORK:\n*\n\n"); |
- write("CACHE:\n"); |
- var toCache = new Directory.fromPath(outputDir); |
- var toCacheLister = toCache.list(recursive: true); |
- toCacheLister.onFile = (filename) { |
- if (filename.endsWith('appcache.manifest')) { |
- return; |
- } |
- // TODO(johnniwinther): If [outputDir] has trailing slashes, [filename] |
- // contains double (back)slashes for files in the immediate [toCache] |
- // directory. These are not handled by [relativeTo] thus |
- // wrongfully producing the path `/foo.html` for a file `foo.html` in |
- // [toCache]. |
- // |
- // This can be handled in two ways. 1) By ensuring that |
- // [Directory.fromPath] does not receive a path with a trailing slash, or |
- // better, by making [Directory.fromPath] handle such trailing slashes. |
- // 2) By ensuring that [filePath] does not have double slashes before |
- // calling [relativeTo], or better, by making [relativeTo] handle double |
- // slashes correctly. |
- Path filePath = new Path.fromNative(filename).canonicalize(); |
- Path relativeFilePath = filePath.relativeTo(outputDir); |
- write("$relativeFilePath\n"); |
- }; |
- toCacheLister.onDone = (done) => endFile(); |
- } |
- |
- /** |
- * Returns [:true:] if [type] should be regarded as an exception. |
- */ |
- bool isException(TypeMirror type) { |
- return type.simpleName.endsWith('Exception'); |
- } |
-} |
- |
-/** |
- * 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'"; |
-} |