Index: lib/dartdoc/dartdoc.dart |
diff --git a/lib/dartdoc/dartdoc.dart b/lib/dartdoc/dartdoc.dart |
index 63eb2b2f3ca7371cf218222f3b2f747797282413..90e3b705329c7aaa5ae7c8ac930c373aa3c8dd19 100644 |
--- a/lib/dartdoc/dartdoc.dart |
+++ b/lib/dartdoc/dartdoc.dart |
@@ -17,12 +17,17 @@ |
#library('dartdoc'); |
#import('dart:io'); |
+#import('dart:uri'); |
#import('dart:json'); |
-#import('frog/lang.dart'); |
-#import('frog/file_system.dart'); |
-#import('frog/file_system_vm.dart'); |
+#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('../compiler/implementation/dart2js.dart', prefix: 'dart2js'); |
+#import('../compiler/implementation/scanner/scannerlib.dart', |
+ prefix: 'dart2js'); |
+#import('file_util.dart'); |
#source('comment_map.dart'); |
#source('utils.dart'); |
@@ -60,6 +65,13 @@ void main() { |
String outputDir; |
bool generateAppCache; |
bool omitGenerationTime; |
+ bool verbose; |
+ |
+ if (args.isEmpty()) { |
+ print('No arguments provided.'); |
+ printUsage(); |
+ return; |
+ } |
for (int i = 0; i < args.length - 1; i++) { |
final arg = args[i]; |
@@ -85,12 +97,16 @@ void main() { |
case '--omit-generation-time': |
omitGenerationTime = true; |
break; |
+ case '--verbose': |
+ verbose = true; |
+ break; |
default: |
if (arg.startsWith('--out=')) { |
outputDir = arg.substring('--out='.length); |
} else { |
print('Unknown option: $arg'); |
+ printUsage(); |
return; |
} |
break; |
@@ -102,21 +118,14 @@ void main() { |
return; |
} |
- // The entrypoint of the library to generate docs for. |
- final entrypoint = args[args.length - 1]; |
- |
- final files = new VMFileSystem(); |
- |
// TODO(rnystrom): Note that the following lines get munged by create-sdk to |
// work with the SDK's different file layout. If you change, be sure to test |
// that dartdoc still works when run from the built SDK directory. |
- final frogPath = joinPaths(scriptDir, 'frog/'); |
- final libDir = joinPaths(scriptDir, '..'); |
- final compilerPath |
- = Platform.operatingSystem == 'windows' ? 'dart2js.bat' : 'dart2js'; |
+ final String libPath = joinPaths(scriptDir, '../'); |
- parseOptions(frogPath, ['', '', '--libdir=$libDir'], files); |
- initializeWorld(files); |
+ // The entrypoint of the library to generate docs for. |
+ // TODO(johnniwinther): Handle absolute/relative paths |
+ final entrypoint = canonicalizePath(args[args.length - 1]); |
final dartdoc = new Dartdoc(); |
@@ -127,26 +136,61 @@ void main() { |
if (omitGenerationTime != null) { |
dartdoc.omitGenerationTime = omitGenerationTime; |
} |
+ if (verbose != null) dartdoc.verbose = verbose; |
cleanOutputDirectory(dartdoc.outputDir); |
+ dartdoc.documentEntryPoint(entrypoint, libPath); |
+ |
// Compile the client-side code to JS. |
final clientScript = (dartdoc.mode == MODE_STATIC) ? 'static' : 'live-nav'; |
- final Future scriptCompiled = compileScript(compilerPath, |
- '$scriptDir/client-$clientScript.dart', |
- '${dartdoc.outputDir}/client-$clientScript.js'); |
+ compileScript( |
+ '$scriptDir/client-$clientScript.dart', |
+ '${dartdoc.outputDir}/client-$clientScript.js'); |
final Future filesCopied = copyFiles('$scriptDir/static', dartdoc.outputDir); |
- Futures.wait([scriptCompiled, filesCopied]).then((_) { |
- dartdoc.document(entrypoint); |
- |
+ Futures.wait([filesCopied]).then((_) { |
print('Documented ${dartdoc._totalLibraries} libraries, ' |
- '${dartdoc._totalTypes} types, and ' |
- '${dartdoc._totalMembers} members.'); |
+ '${dartdoc._totalTypes} types, and ' |
+ '${dartdoc._totalMembers} members.'); |
}); |
} |
+void printUsage() { |
+ print(''' |
+Usage dartdoc [options] <entrypoint> |
+[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/ |
+ |
+ --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 |
@@ -166,7 +210,14 @@ void cleanOutputDirectory(String path) { |
outputDir.deleteRecursivelySync(); |
} |
- outputDir.createSync(); |
+ try { |
+ // TODO(johnniwinther): Hack to avoid 'file already exists' exception thrown |
+ // due to invalid result from dir.existsSync() (probably due to race |
+ // conditions). |
+ outputDir.createSync(); |
+ } catch (DirectoryIOException e) { |
+ // Ignore. |
+ } |
} |
/** |
@@ -199,33 +250,19 @@ Future copyFiles(String from, String to) { |
/** |
* Compiles the given Dart script to a JavaScript file at [jsPath] using the |
- * Dart-to-JS compiler located at [compilerPath]. |
+ * Dart2js compiler. |
*/ |
-Future compileScript(String compilerPath, String dartPath, String jsPath) { |
- final completer = new Completer(); |
- onExit(ProcessResult result) { |
- if (result.exitCode != 0) { |
- final message = 'Non-zero exit code from $compilerPath'; |
- print('$message.'); |
- print(result.stdout); |
- print(result.stderr); |
- throw message; |
- } |
- completer.complete(true); |
- } |
- |
- onError(error) { |
- final message = 'Error trying to execute $compilerPath. Error: $error'; |
- print('$message.'); |
- throw message; |
- } |
- |
- print('Compiling $dartPath to $jsPath'); |
- var processFuture = Process.run(compilerPath, ['--out=$jsPath', dartPath]); |
- processFuture.handleException(onError); |
- processFuture.then(onExit); |
- |
- return completer.future; |
+void compileScript(String dartPath, String jsPath) { |
+ dart2js.compile([ |
+ '--no-colors', |
+ // TODO(johnniwinther): The following lines get munged by create-sdk to |
+ // work with the SDK's different file layout. If you change, be sure to |
+ // test that dartdoc still works when run from the built SDK directory. |
+ '--library-root=${joinPaths(scriptDir, '../../')}', |
+ '--out=$jsPath', |
+ '--throw-on-error', |
+ '--suppress-warnings', |
+ dartPath]); |
} |
class Dartdoc { |
@@ -277,25 +314,27 @@ class Dartdoc { |
/** 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 select the libraries to document */ |
+ List<String> libraries = null; |
+ |
/** |
- * From exposes the set of libraries in `world.libraries`. That maps library |
- * *keys* to [Library] objects. The keys are *not* exactly the same as their |
- * names. This means if we order by key, we won't actually have them sorted |
- * correctly. This list contains the libraries in correct order by their |
- * *name*. |
+ * This list contains the libraries sorted in by the library name. |
*/ |
- List<Library> _sortedLibraries; |
+ List<LibraryMirror> _sortedLibraries; |
CommentMap _comments; |
/** The library that we're currently generating docs for. */ |
- Library _currentLibrary; |
+ LibraryMirror _currentLibrary; |
/** The type that we're currently generating docs for. */ |
- Type _currentType; |
+ InterfaceMirror _currentType; |
/** The member that we're currently generating docs for. */ |
- Member _currentMember; |
+ MemberMirror _currentMember; |
/** The path to the file currently being written to, relative to [outdir]. */ |
String _filePath; |
@@ -319,10 +358,17 @@ class Dartdoc { |
member: _currentMember)); |
} |
+ bool includeLibrary(LibraryMirror library) { |
+ if (libraries != null) { |
+ return libraries.indexOf(library.simpleName()) != -1; |
+ } |
+ return true; |
+ } |
+ |
String get footerContent(){ |
var footerItems = []; |
if(!omitGenerationTime) { |
- footerItems.add("This page generated at ${new Date.now()}"); |
+ footerItems.add("This page was generated at ${new Date.now()}"); |
} |
if(footerText != null) { |
footerItems.add(footerText); |
@@ -337,38 +383,35 @@ class Dartdoc { |
return content; |
} |
- void document([String entrypoint]) { |
- var oldDietParse = options.dietParse; |
- try { |
- options.dietParse = true; |
- |
- // If we have an entrypoint, process it. Otherwise, just use whatever |
- // libraries have been previously loaded by the calling code. |
- if (entrypoint != null) { |
- world.processDartScript(entrypoint); |
- } |
+ void documentEntryPoint(String entrypoint, String libPath) { |
+ final compilation = new Compilation(entrypoint, libPath); |
+ _document(compilation); |
+ } |
- world.resolveAll(); |
+ void documentLibraries(List<String> libraries, String libPath) { |
+ final compilation = new Compilation.library(libraries, libPath); |
+ _document(compilation); |
+ } |
- // Sort the libraries by name (not key). |
- _sortedLibraries = world.libraries.getValues(); |
- _sortedLibraries.sort((a, b) { |
- return a.name.toUpperCase().compareTo(b.name.toUpperCase()); |
- }); |
+ void _document(Compilation compilation) { |
+ // Sort the libraries by name (not key). |
+ _sortedLibraries = new List<LibraryMirror>.from( |
+ compilation.mirrors().libraries().getValues().filter(includeLibrary)); |
+ _sortedLibraries.sort((x, y) { |
+ return x.simpleName().toUpperCase().compareTo( |
+ y.simpleName().toUpperCase()); |
+ }); |
- // Generate the docs. |
- if (mode == MODE_LIVE_NAV) docNavigationJson(); |
+ // Generate the docs. |
+ if (mode == MODE_LIVE_NAV) docNavigationJson(); |
- docIndex(); |
- for (final library in _sortedLibraries) { |
- docLibrary(library); |
- } |
+ docIndex(); |
+ for (final library in _sortedLibraries) { |
+ docLibrary(library); |
+ } |
- if (generateAppCache) { |
- generateAppCacheManifest(); |
- } |
- } finally { |
- options.dietParse = oldDietParse; |
+ if (generateAppCache) { |
+ generateAppCacheManifest(); |
} |
} |
@@ -381,10 +424,17 @@ class Dartdoc { |
final outPath = '$outputDir/$_filePath'; |
final dir = new Directory(dirname(outPath)); |
if (!dir.existsSync()) { |
- dir.createSync(); |
+ // TODO(johnniwinther): Hack to avoid 'file already exists' exception |
+ // thrown due to invalid result from dir.existsSync() (probably due to |
+ // race conditions). |
+ try { |
+ dir.createSync(); |
+ } catch (DirectoryIOException e) { |
+ // Ignore. |
+ } |
} |
- world.files.writeString(outPath, _file.toString()); |
+ writeString(new File(outPath), _file.toString()); |
_filePath = null; |
_file = null; |
} |
@@ -424,7 +474,8 @@ class Dartdoc { |
// Add data attributes describing what the page documents. |
var data = ''; |
if (_currentLibrary != null) { |
- data = '$data data-library="${md.escapeHtml(_currentLibrary.name)}"'; |
+ data = '$data data-library=' |
+ '"${md.escapeHtml(_currentLibrary.simpleName())}"'; |
} |
if (_currentType != null) { |
@@ -520,8 +571,8 @@ class Dartdoc { |
endFile(); |
} |
- void docIndexLibrary(Library library) { |
- writeln('<h4>${a(libraryUrl(library), library.name)}</h4>'); |
+ void docIndexLibrary(LibraryMirror library) { |
+ writeln('<h4>${a(libraryUrl(library), library.simpleName())}</h4>'); |
} |
/** |
@@ -531,29 +582,28 @@ class Dartdoc { |
void docNavigationJson() { |
startFile('nav.json'); |
- final libraries = {}; |
+ final libraryMap = {}; |
for (final library in _sortedLibraries) { |
- docLibraryNavigationJson(library, libraries); |
+ docLibraryNavigationJson(library, libraryMap); |
} |
- writeln(JSON.stringify(libraries)); |
+ writeln(JSON.stringify(libraryMap)); |
endFile(); |
} |
- void docLibraryNavigationJson(Library library, Map libraries) { |
+ void docLibraryNavigationJson(LibraryMirror library, Map libraryMap) { |
final types = []; |
- for (final type in orderByName(library.types)) { |
- if (type.isTop) continue; |
- if (type.name.startsWith('_')) continue; |
+ for (final type in orderByName(library.types().getValues())) { |
+ if (type.isPrivate) continue; |
final kind = type.isClass ? 'class' : 'interface'; |
final url = typeUrl(type); |
types.add({ 'name': typeName(type), 'kind': kind, 'url': url }); |
} |
- libraries[library.name] = types; |
+ libraryMap[library.simpleName()] = types; |
} |
void docNavigation() { |
@@ -567,9 +617,9 @@ class Dartdoc { |
write('<h2><div class="icon-library"></div>'); |
if ((_currentLibrary == library) && (_currentType == null)) { |
- write('<strong>${library.name}</strong>'); |
+ write('<strong>${library.simpleName()}</strong>'); |
} else { |
- write('${a(libraryUrl(library), library.name)}'); |
+ write('${a(libraryUrl(library), library.simpleName())}'); |
} |
write('</h2>'); |
@@ -582,16 +632,15 @@ class Dartdoc { |
} |
/** Writes the navigation for the types contained by the given library. */ |
- void docLibraryNavigation(Library library) { |
+ void docLibraryNavigation(LibraryMirror library) { |
// Show the exception types separately. |
- final types = <Type>[]; |
- final exceptions = <Type>[]; |
+ final types = <InterfaceMirror>[]; |
+ final exceptions = <InterfaceMirror>[]; |
- for (final type in orderByName(library.types)) { |
- if (type.isTop) continue; |
- if (type.name.startsWith('_')) continue; |
+ for (final type in orderByName(library.types().getValues())) { |
+ if (type.isPrivate) continue; |
- if (type.name.endsWith('Exception')) { |
+ if (isException(type)) { |
exceptions.add(type); |
} else { |
types.add(type); |
@@ -607,9 +656,9 @@ class Dartdoc { |
} |
/** Writes a linked navigation list item for the given type. */ |
- void docTypeNavigation(Type type) { |
+ void docTypeNavigation(InterfaceMirror type) { |
var icon = 'interface'; |
- if (type.name.endsWith('Exception')) { |
+ if (type.simpleName().endsWith('Exception')) { |
icon = 'exception'; |
} else if (type.isClass) { |
icon = 'class'; |
@@ -626,15 +675,18 @@ class Dartdoc { |
writeln('</li>'); |
} |
- void docLibrary(Library library) { |
+ void docLibrary(LibraryMirror library) { |
+ if (verbose) { |
+ print('Library \'${library.simpleName()}\':'); |
+ } |
_totalLibraries++; |
_currentLibrary = library; |
_currentType = null; |
startFile(libraryUrl(library)); |
- writeHeader('${library.name} Library', |
- [library.name, libraryUrl(library)]); |
- writeln('<h2><strong>${library.name}</strong> library</h2>'); |
+ 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); |
@@ -643,18 +695,17 @@ class Dartdoc { |
} |
// Document the top-level members. |
- docMembers(library.topType); |
+ docMembers(library); |
// Document the types. |
- final classes = <Type>[]; |
- final interfaces = <Type>[]; |
- final exceptions = <Type>[]; |
+ final classes = <InterfaceMirror>[]; |
+ final interfaces = <InterfaceMirror>[]; |
+ final exceptions = <InterfaceMirror>[]; |
- for (final type in orderByName(library.types)) { |
- if (type.isTop) continue; |
- if (type.name.startsWith('_')) continue; |
+ for (final type in orderByName(library.types().getValues())) { |
+ if (type.isPrivate) continue; |
- if (type.name.endsWith('Exception')) { |
+ if (isException(type)) { |
exceptions.add(type); |
} else if (type.isClass) { |
classes.add(type); |
@@ -670,14 +721,14 @@ class Dartdoc { |
writeFooter(); |
endFile(); |
- for (final type in library.types.getValues()) { |
- if (type.isTop) continue; |
- if (type.name.startsWith('_')) continue; |
+ for (final type in library.types().getValues()) { |
+ if (type.isPrivate) continue; |
+ |
docType(type); |
} |
} |
- void docTypes(List<Type> types, String header) { |
+ void docTypes(List<InterfaceMirror> types, String header) { |
if (types.length == 0) return; |
writeln('<h3>$header</h3>'); |
@@ -694,26 +745,37 @@ class Dartdoc { |
} |
} |
- void docType(Type type) { |
+ 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)} ${type.isClass ? "Class" : "Interface"}'; |
- writeHeader('$typeTitle / ${type.library.name} Library', |
- [type.library.name, libraryUrl(type.library), |
+ '${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> |
- ${type.isClass ? "Class" : "Interface"} |
+ $kind |
</h2> |
'''); |
- docCode(type.span, getTypeComment(type)); |
+ docCode(type.location(), getTypeComment(type)); |
docInheritance(type); |
+ docTypedef(type); |
docConstructors(type); |
docMembers(type); |
@@ -732,9 +794,9 @@ class Dartdoc { |
* 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(Type type) { |
+ void typeSpan(InterfaceMirror type) { |
var icon = 'interface'; |
- if (type.name.endsWith('Exception')) { |
+ if (type.simpleName().endsWith('Exception')) { |
icon = 'exception'; |
} else if (type.isClass) { |
icon = 'class'; |
@@ -754,7 +816,7 @@ class Dartdoc { |
* subclasses, superclasses, subinterfaces, superinferfaces, and default |
* class. |
*/ |
- void docInheritance(Type type) { |
+ 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; |
@@ -764,30 +826,35 @@ class Dartdoc { |
if (types == null) return; |
// Skip private types. |
- final publicTypes = types.filter((type) => !type.name.startsWith('_')); |
+ 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 type in publicTypes) { |
+ for (final t in publicTypes) { |
if (!first) write(', '); |
- typeSpan(type); |
+ 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.parent.isObject) { |
+ if (!type.superclass().isObject) { |
final supertypes = []; |
- var thisType = type.parent; |
+ 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.parent; |
+ thisType = thisType.superclass(); |
} while (!thisType.isObject); |
writeln('<h3>Extends</h3>'); |
@@ -802,41 +869,26 @@ class Dartdoc { |
writeln('</p>'); |
} |
- // Find the immediate declared subclasses (Type.subtypes includes many |
- // transitive subtypes). |
- final subtypes = []; |
- for (final subtype in type.subtypes) { |
- if (subtype.parent == type) subtypes.add(subtype); |
- } |
- subtypes.sort((a, b) => a.name.compareTo(b.name)); |
- |
listTypes(subtypes, 'Subclasses'); |
- listTypes(type.interfaces, 'Implements'); |
+ listTypes(type.interfaces().getValues(), 'Implements'); |
} else { |
// Show the default class. |
- if (type.genericType.defaultType != null) { |
- listTypes([type.genericType.defaultType], 'Default class'); |
+ if (type.defaultType() != null) { |
+ listTypes([type.defaultType()], 'Default class'); |
} |
// List extended interfaces. |
- listTypes(type.interfaces, 'Extends'); |
+ listTypes(type.interfaces().getValues(), 'Extends'); |
// List subinterfaces and implementing classes. |
final subinterfaces = []; |
final implementing = []; |
- for (final subtype in type.subtypes) { |
- // We only want explicitly declared subinterfaces, so check that this |
- // type is a superinterface. |
- for (final supertype in subtype.interfaces) { |
- if (supertype == type) { |
- if (subtype.isClass) { |
- implementing.add(subtype); |
- } else { |
- subinterfaces.add(subtype); |
- } |
- break; |
- } |
+ for (final subtype in subtypes) { |
+ if (subtype.isClass) { |
+ implementing.add(subtype); |
+ } else { |
+ subinterfaces.add(subtype); |
} |
} |
@@ -845,38 +897,68 @@ class Dartdoc { |
} |
} |
+ /** |
+ * 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>'); |
+ } |
+ |
+ if (type.definition() !== null) { |
+ // TODO(johnniwinther): Implement [:TypedefMirror.definition():]. |
+ 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(Type type) { |
- final names = type.constructors.getKeys().filter( |
- (name) => !name.startsWith('_')); |
+ void docConstructors(InterfaceMirror type) { |
+ final constructors = <MethodMirror>[]; |
+ for (var constructor in type.constructors().getValues()) { |
+ if (!constructor.isPrivate) { |
+ constructors.add(constructor); |
+ } |
+ } |
- if (names.length > 0) { |
+ if (constructors.length > 0) { |
writeln('<h3>Constructors</h3>'); |
- names.sort((x, y) => x.toUpperCase().compareTo(y.toUpperCase())); |
+ constructors.sort((x, y) => x.simpleName().toUpperCase().compareTo( |
+ y.simpleName().toUpperCase())); |
- for (final name in names) { |
- docMethod(type, type.constructors[name], constructorName: name); |
+ for (final constructor in constructors) { |
+ docMethod(type, constructor); |
} |
} |
} |
- void docMembers(Type type) { |
+ void docMembers(ObjectMirror host) { |
// Collect the different kinds of members. |
final staticMethods = []; |
final staticFields = []; |
final instanceMethods = []; |
final instanceFields = []; |
- for (final member in orderByName(type.members)) { |
- if (member.name.startsWith('_')) continue; |
+ for (final member in orderByName(host.declaredMembers().getValues())) { |
+ if (member.isPrivate) continue; |
final methods = member.isStatic ? staticMethods : instanceMethods; |
final fields = member.isStatic ? staticFields : instanceFields; |
- if (member.isProperty) { |
- if (member.canGet) methods.add(member.getter); |
- if (member.canSet) methods.add(member.setter); |
- } else if (member.isMethod) { |
+ if (member.isMethod) { |
methods.add(member); |
} else if (member.isField) { |
fields.add(member); |
@@ -884,25 +966,33 @@ class Dartdoc { |
} |
if (staticMethods.length > 0) { |
- final title = type.isTop ? 'Functions' : 'Static Methods'; |
+ final title = host is LibraryMirror ? 'Functions' : 'Static Methods'; |
writeln('<h3>$title</h3>'); |
- for (final method in staticMethods) docMethod(type, method); |
+ for (final method in orderByName(staticMethods)) { |
+ docMethod(host, method); |
+ } |
} |
if (staticFields.length > 0) { |
- final title = type.isTop ? 'Variables' : 'Static Fields'; |
+ final title = host is LibraryMirror ? 'Variables' : 'Static Fields'; |
writeln('<h3>$title</h3>'); |
- for (final field in staticFields) docField(type, field); |
+ for (final field in orderByName(staticFields)) { |
+ docField(host, field); |
+ } |
} |
if (instanceMethods.length > 0) { |
writeln('<h3>Methods</h3>'); |
- for (final method in instanceMethods) docMethod(type, method); |
+ for (final method in orderByName(instanceMethods)) { |
+ docMethod(host, method); |
+ } |
} |
if (instanceFields.length > 0) { |
writeln('<h3>Fields</h3>'); |
- for (final field in instanceFields) docField(type, field); |
+ for (final field in orderByName(instanceFields)) { |
+ docField(host, field); |
+ } |
} |
} |
@@ -910,8 +1000,7 @@ class Dartdoc { |
* Documents the [method] in type [type]. Handles all kinds of methods |
* including getters, setters, and constructors. |
*/ |
- void docMethod(Type type, MethodMember method, |
- [String constructorName = null]) { |
+ void docMethod(ObjectMirror host, MethodMirror method) { |
_totalMembers++; |
_currentMember = method; |
@@ -922,57 +1011,51 @@ class Dartdoc { |
} |
if (method.isConstructor) { |
- write(method.isConst ? 'const ' : 'new '); |
+ if (method.isFactory) { |
+ write('factory '); |
+ } else { |
+ write(method.isConst ? 'const ' : 'new '); |
+ } |
} |
- if (constructorName == null) { |
- annotateType(type, method.returnType); |
+ if (method.constructorName == null) { |
+ annotateType(host, method.returnType()); |
} |
+ var name = method.simpleName(); |
// Translate specially-named methods: getters, setters, operators. |
- var name = method.name; |
- if (name.startsWith('get:')) { |
+ if (method.isGetter) { |
// Getter. |
- name = 'get ${name.substring(4)}'; |
- } else if (name.startsWith('set:')) { |
+ name = 'get $name'; |
+ } else if (method.isSetter) { |
// Setter. |
- name = 'set ${name.substring(4)}'; |
- } else if (name == ':negate') { |
- // Dart uses 'negate' for prefix negate operators, not '!'. |
- name = 'operator negate'; |
- } else if (name == ':call') { |
- name = 'operator call'; |
- } else { |
- // See if it's an operator. |
- name = TokenKind.rawOperatorFromMethod(name); |
- if (name == null) { |
- name = method.name; |
- } else { |
- name = 'operator $name'; |
- } |
+ name = 'set $name'; |
+ } else if (method.isOperator) { |
+ name = 'operator ${method.operatorName}'; |
} |
write('<strong>$name</strong>'); |
// Named constructors. |
- if (constructorName != null && constructorName != '') { |
+ if (method.constructorName != null && method.constructorName != '') { |
write('.'); |
- write(constructorName); |
+ write(method.constructorName); |
} |
- docParamList(type, method); |
+ docParamList(host, method.parameters()); |
+ var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
write(''' <a class="anchor-link" href="#${memberAnchor(method)}" |
- title="Permalink to ${typeName(type)}.$name">#</a>'''); |
+ title="Permalink to $prefix$name">#</a>'''); |
writeln('</h4>'); |
- docCode(method.span, getMethodComment(method), showCode: true); |
+ docCode(method.location(), getMethodComment(method), showCode: true); |
writeln('</div>'); |
} |
/** Documents the field [field] of type [type]. */ |
- void docField(Type type, FieldMember field) { |
+ void docField(ObjectMirror host, FieldMirror field) { |
_totalMembers++; |
_currentMember = field; |
@@ -984,47 +1067,43 @@ class Dartdoc { |
if (field.isFinal) { |
write('final '); |
- } else if (field.type.name == 'Dynamic') { |
+ } else if (field.type().isDynamic) { |
write('var '); |
} |
- annotateType(type, field.type); |
+ annotateType(host, field.type()); |
+ var prefix = host is LibraryMirror ? '' : '${typeName(host)}.'; |
write( |
''' |
- <strong>${field.name}</strong> <a class="anchor-link" |
+ <strong>${field.simpleName()}</strong> <a class="anchor-link" |
href="#${memberAnchor(field)}" |
- title="Permalink to ${typeName(type)}.${field.name}">#</a> |
+ title="Permalink to $prefix${field.simpleName()}">#</a> |
</h4> |
'''); |
- docCode(field.span, getFieldComment(field), showCode: true); |
+ docCode(field.location(), getFieldComment(field), showCode: true); |
writeln('</div>'); |
} |
- void docParamList(Type enclosingType, MethodMember member) { |
+ void docParamList(ObjectMirror enclosingType, |
+ List<ParameterMirror> parameters) { |
write('('); |
bool first = true; |
bool inOptionals = false; |
- for (final parameter in member.parameters) { |
+ for (final parameter in parameters) { |
if (!first) write(', '); |
- if (!inOptionals && parameter.isOptional) { |
+ if (!inOptionals && parameter.isOptional()) { |
write('['); |
inOptionals = true; |
} |
- annotateType(enclosingType, parameter.type, parameter.name); |
+ annotateType(enclosingType, parameter.type(), parameter.simpleName()); |
// Show the default value for named optional parameters. |
- if (parameter.isOptional && parameter.hasDefaultValue) { |
+ if (parameter.isOptional() && parameter.hasDefaultValue()) { |
write(' = '); |
- // TODO(rnystrom): Using the definition text here is a bit cheap. |
- // We really should be pretty-printing the AST so that if you have: |
- // foo([arg = 1 + /* comment */ 2]) |
- // the docs should just show: |
- // foo([arg = 1 + 2]) |
- // For now, we'll assume you don't do that. |
- write(parameter.definition.value.span.text); |
+ write(parameter.defaultValue()); |
} |
first = false; |
@@ -1038,7 +1117,7 @@ class Dartdoc { |
* Documents the code contained within [span] with [comment]. If [showCode] |
* is `true` (and [includeSource] is set), also includes the source code. |
*/ |
- void docCode(SourceSpan span, String comment, [bool showCode = false]) { |
+ void docCode(Location location, String comment, [bool showCode = false]) { |
writeln('<div class="doc">'); |
if (comment != null) { |
writeln(comment); |
@@ -1046,7 +1125,7 @@ class Dartdoc { |
if (includeSource && showCode) { |
writeln('<pre class="source">'); |
- writeln(md.escapeHtml(unindentCode(span))); |
+ writeln(md.escapeHtml(unindentCode(location))); |
writeln('</pre>'); |
} |
@@ -1055,9 +1134,9 @@ class Dartdoc { |
/** Get the doc comment associated with the given library. */ |
- String getLibraryComment(Library library) { |
+ String getLibraryComment(LibraryMirror library) { |
// Look for a comment for the entire library. |
- final comment = _comments.findLibrary(library.baseSource); |
+ final comment = _comments.findLibrary(library.location().source()); |
if (comment != null) { |
return md.markdownToHtml(comment); |
} |
@@ -1065,22 +1144,22 @@ class Dartdoc { |
} |
/** Get the doc comment associated with the given type. */ |
- String getTypeComment(Type type) { |
- String comment = _comments.find(type.span); |
+ 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(MethodMember method) { |
- String comment = _comments.find(method.span); |
+ 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(FieldMember field) { |
- String comment = _comments.find(field.span); |
+ String getFieldComment(FieldMirror field) { |
+ String comment = _comments.find(field.location()); |
if (comment == null) return null; |
return commentToHtml(comment); |
} |
@@ -1109,30 +1188,33 @@ class Dartdoc { |
} |
/** Gets the URL to the documentation for [library]. */ |
- String libraryUrl(Library library) { |
- return '${sanitize(library.name)}.html'; |
+ String libraryUrl(LibraryMirror library) { |
+ return '${sanitize(library.simpleName())}.html'; |
} |
/** Gets the URL for the documentation for [type]. */ |
- String typeUrl(Type type) { |
- if (type.isTop) return '${sanitize(type.library.name)}.html'; |
+ 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.name)}/${type.genericType.name}.html'; |
+ return '${sanitize(type.library().simpleName())}/' |
+ '${type.declaration.simpleName()}.html'; |
} |
/** Gets the URL for the documentation for [member]. */ |
- String memberUrl(Member member) { |
- final typeUrl = typeUrl(member.declaringType); |
- if (!member.isConstructor) return '$typeUrl#${member.name}'; |
- if (member.constructorName == '') return '$typeUrl#new:${member.name}'; |
- return '$typeUrl#new:${member.name}.${member.constructorName}'; |
+ String memberUrl(MemberMirror member) { |
+ final url = typeUrl(member.surroundingDeclaration()); |
+ if (!member.isConstructor) return '$url#${member.simpleName()}'; |
+ assert (member is MethodMirror); |
+ if (member.constructorName == '') return '$url#new:${member.simpleName()}'; |
+ return '$url#new:${member.simpleName()}.${member.constructorName}'; |
} |
/** Gets the anchor id for the document for [member]. */ |
- String memberAnchor(Member member) { |
- return '${member.name}'; |
+ String memberAnchor(MemberMirror member) { |
+ return '${member.simpleName()}'; |
} |
/** |
@@ -1149,23 +1231,22 @@ class Dartdoc { |
/** |
* Writes a type annotation for the given type and (optional) parameter name. |
*/ |
- annotateType(Type enclosingType, Type type, [String paramName = null]) { |
+ annotateType(ObjectMirror enclosingType, |
+ TypeMirror type, |
+ [String paramName = null]) { |
// Don't bother explicitly displaying Dynamic. |
- if (type.isVar) { |
+ if (type.isDynamic) { |
if (paramName !== null) write(paramName); |
return; |
} |
// For parameters, handle non-typedefed function types. |
- if (paramName !== null) { |
- final call = type.getCallMethod(); |
- if (call != null) { |
- annotateType(enclosingType, call.returnType); |
- write(paramName); |
+ if (paramName !== null && type is FunctionTypeMirror) { |
+ annotateType(enclosingType, type.returnType()); |
+ write(paramName); |
- docParamList(enclosingType, call); |
- return; |
- } |
+ docParamList(enclosingType, type.parameters()); |
+ return; |
} |
linkToType(enclosingType, type); |
@@ -1175,30 +1256,37 @@ class Dartdoc { |
} |
/** Writes a link to a human-friendly string representation for a type. */ |
- linkToType(Type enclosingType, Type type) { |
- if (type is ParameterType) { |
+ 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.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.name)); |
+ write(a(typeUrl(enclosingType), type.simpleName())); |
return; |
} |
+ assert(type is InterfaceMirror); |
+ |
// Link to the type. |
- // Use .genericType to avoid writing the <...> here. |
- write(a(typeUrl(type), type.genericType.name)); |
+ if (includeLibrary(type.library())) { |
+ write(a(typeUrl(type), type.simpleName())); |
+ } else { |
+ write(type.simpleName()); |
+ } |
- // See if it's a generic type. |
- if (type.isGeneric) { |
- // TODO(rnystrom): This relies on a weird corner case of frog. Currently, |
- // the only time we get into this case is when we have a "raw" generic |
- // that's been instantiated with Dynamic for all type arguments. It's kind |
- // of strange that frog works that way, but we take advantage of it to |
- // show raw types without any type arguments. |
+ if (type.isDeclaration) { |
+ // Avoid calling [:typeArguments():] on a declaration. |
return; |
} |
// See if it's an instantiation of a generic type. |
- final typeArgs = type.typeArgsInOrder; |
+ final typeArgs = type.typeArguments(); |
if (typeArgs.length > 0) { |
write('<'); |
bool first = true; |
@@ -1212,49 +1300,59 @@ class Dartdoc { |
} |
/** Creates a linked cross reference to [type]. */ |
- typeReference(Type 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(Type type, [bool showBounds = false]) { |
+ 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.isGeneric) { |
+ if (type.isDeclaration) { |
final typeParams = []; |
- for (final typeParam in type.genericType.typeParameters) { |
+ for (final typeParam in type.declaration.typeVariables()) { |
if (showBounds && |
- (typeParam.extendsType != null) && |
- !typeParam.extendsType.isObject) { |
- final bound = typeName(typeParam.extendsType, showBounds: true); |
- typeParams.add('${typeParam.name} extends $bound'); |
+ (typeParam.bound() != null) && |
+ !typeParam.bound().isObject) { |
+ final bound = typeName(typeParam.bound(), showBounds: true); |
+ typeParams.add('${typeParam.simpleName()} extends $bound'); |
} else { |
- typeParams.add(typeParam.name); |
+ typeParams.add(typeParam.simpleName()); |
} |
} |
- |
+ if (typeParams.isEmpty()) { |
+ return type.simpleName(); |
+ } |
final params = Strings.join(typeParams, ', '); |
- return '${type.name}<$params>'; |
+ return '${type.simpleName()}<$params>'; |
} |
// See if it's an instantiation of a generic type. |
- final typeArgs = type.typeArgsInOrder; |
+ final typeArgs = type.typeArguments(); |
if (typeArgs.length > 0) { |
- final args = Strings.join(map(typeArgs, (arg) => typeName(arg)), ', '); |
- return '${type.genericType.name}<$args>'; |
+ final args = Strings.join(typeArgs.map((arg) => typeName(arg)), ', '); |
+ return '${type.declaration.simpleName()}<$args>'; |
} |
// Regular type. |
- return type.name; |
+ return type.simpleName(); |
} |
/** |
* Remove leading indentation to line up with first line. |
*/ |
- unindentCode(SourceSpan span) { |
- final column = getSpanColumn(span); |
- final lines = span.text.split('\n'); |
+ 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); |
@@ -1267,11 +1365,11 @@ class Dartdoc { |
/** |
* Takes a string of Dart code and turns it into sanitized HTML. |
*/ |
- formatCode(SourceSpan span) { |
+ formatCode(Location span) { |
final code = unindentCode(span); |
// Syntax highlight. |
- return classifySource(new SourceFile('', code)); |
+ return classifySource(code); |
} |
/** |
@@ -1279,8 +1377,10 @@ class Dartdoc { |
* brackets. It will try to figure out what the name refers to and link or |
* style it appropriately. |
*/ |
- md.Node resolveNameReference(String name, [Member member = null, |
- Type type = null, Library library = null]) { |
+ md.Node resolveNameReference(String name, |
+ [MemberMirror member = null, |
+ ObjectMirror type = null, |
+ LibraryMirror library = null]) { |
makeLink(String href) { |
final anchor = new md.Element.text('a', name); |
anchor.attributes['href'] = relativePath(href); |
@@ -1288,24 +1388,10 @@ class Dartdoc { |
return anchor; |
} |
- findMember(Type type, String memberName) { |
- final member = type.members[memberName]; |
- if (member == null) return null; |
- |
- // Special case: if the member we've resolved is a property (i.e. it wraps |
- // a getter and/or setter then *that* member itself won't be on the docs, |
- // just the getter or setter will be. So pick one of those to link to. |
- if (member.isProperty) { |
- return member.canGet ? member.getter : member.setter; |
- } |
- |
- return member; |
- } |
- |
// See if it's a parameter of the current method. |
- if (member != null) { |
- for (final parameter in member.parameters) { |
- if (parameter.name == name) { |
+ if (member is MethodMirror) { |
+ for (final parameter in member.parameters()) { |
+ if (parameter.simpleName() == name) { |
final element = new md.Element.text('span', name); |
element.attributes['class'] = 'param'; |
return element; |
@@ -1315,7 +1401,7 @@ class Dartdoc { |
// See if it's another member of the current type. |
if (type != null) { |
- final member = findMember(type, name); |
+ final member = findMirror(type.declaredMembers(), name); |
if (member != null) { |
return makeLink(memberUrl(member)); |
} |
@@ -1326,12 +1412,14 @@ class Dartdoc { |
if (library != null) { |
// See if it's a constructor |
final constructorLink = (() { |
- final match = new RegExp(@'new (\w+)(?:\.(\w+))?').firstMatch(name); |
+ final match = |
+ new RegExp(@'new ([\w$]+)(?:\.([\w$]+))?').firstMatch(name); |
if (match == null) return; |
- final type = library.types[match[1]]; |
+ final type = findMirror(library.types(), match[1]); |
if (type == null) return; |
- final constructor = type.getConstructor( |
- match[2] == null ? '' : match[2]); |
+ final constructor = |
+ findMirror(type.constructors(), |
+ match[2] == null ? '' : match[2]); |
if (constructor == null) return; |
return makeLink(memberUrl(constructor)); |
})(); |
@@ -1339,23 +1427,23 @@ class Dartdoc { |
// See if it's a member of another type |
final foreignMemberLink = (() { |
- final match = new RegExp(@'(\w+)\.(\w+)').firstMatch(name); |
+ final match = new RegExp(@'([\w$]+)\.([\w$]+)').firstMatch(name); |
if (match == null) return; |
- final type = library.types[match[1]]; |
+ final type = findMirror(library.types(), match[1]); |
if (type == null) return; |
- final member = findMember(type, match[2]); |
+ final member = findMirror(type.declaredMembers(), match[2]); |
if (member == null) return; |
return makeLink(memberUrl(member)); |
})(); |
if (foreignMemberLink != null) return foreignMemberLink; |
- final type = library.types[name]; |
+ final type = findMirror(library.types(), name); |
if (type != null) { |
return makeLink(typeUrl(type)); |
} |
// See if it's a top-level member in the current library. |
- final member = findMember(library.topType, name); |
+ final member = findMirror(library.declaredMembers(), name); |
if (member != null) { |
return makeLink(memberUrl(member)); |
} |
@@ -1368,12 +1456,6 @@ class Dartdoc { |
return new md.Element.text('code', name); |
} |
- // TODO(rnystrom): Move into SourceSpan? |
- int getSpanColumn(SourceSpan span) { |
- final line = span.file.getLine(span.start); |
- return span.file.getColumn(line, span.start); |
- } |
- |
generateAppCacheManifest() { |
print('Generating app cache manifest from output $outputDir'); |
startFile('appcache.manifest'); |
@@ -1394,4 +1476,12 @@ class Dartdoc { |
toCache.onDone = (done) => endFile(); |
toCache.list(recursive: true); |
} |
+ |
+ /** |
+ * Returns [:true:] if [type] should be regarded as an exception. |
+ */ |
+ bool isException(TypeMirror type) { |
+ return type.simpleName().endsWith('Exception'); |
+ } |
} |
+ |