Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(740)

Unified Diff: pkg/docgen/lib/docgen.dart

Issue 138623002: Enable re-export in docgen. Work in progress. More to come. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/docgen/lib/dart2yaml.dart ('k') | pkg/docgen/test/multi_library_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/docgen/lib/docgen.dart
diff --git a/pkg/docgen/lib/docgen.dart b/pkg/docgen/lib/docgen.dart
index d75b60f6910434507e801f508b942d694a4d4623..febf458a79bcdd70d72a45449b0225144baa7e71 100644
--- a/pkg/docgen/lib/docgen.dart
+++ b/pkg/docgen/lib/docgen.dart
@@ -34,251 +34,162 @@ import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.
import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider.dart';
import '../../../sdk/lib/_internal/libraries.dart';
-var logger = new Logger('Docgen');
+const _DEFAULT_OUTPUT_DIRECTORY = 'docs';
-const DEFAULT_OUTPUT_DIRECTORY = 'docs';
-
-var _outputDirectory;
-
-const String USAGE = 'Usage: dart docgen.dart [OPTIONS] fooDir/barFile';
-
-
-List<String> skippedAnnotations = const [
+/// Annotations that we do not display in the viewer.
+const List<String> _SKIPPED_ANNOTATIONS = const [
'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates',
'_js_helper.Returns'];
-/// Set of libraries declared in the SDK, so libraries that can be accessed
-/// when running dart by default.
-Iterable<LibraryMirror> _sdkLibraries;
-
-/// The dart:core library, which contains all types that are always available
-/// without import.
-LibraryMirror _coreLibrary;
-
/// Support for [:foo:]-style code comments to the markdown parser.
-List<markdown.InlineSyntax> markdownSyntaxes =
+List<markdown.InlineSyntax> _MARKDOWN_SYNTAXES =
[new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')];
+// TODO(efortuna): The use of this field is odd (this is based on how it was
+// originally used. Try to cleanup.
/// Index of all indexable items. This also ensures that no class is
/// created more than once.
Map<String, Indexable> entityMap = new Map<String, Indexable>();
-/// This is set from the command line arguments flag --include-private
-bool _includePrivate = false;
-
-/// This is set from the command line flag --include-dependent-packages
-bool _includeDependentPackages = false;
-
-/// Library names to explicitly exclude.
+/// Index of all the dart2js mirrors examined to corresponding MirrorBased
+/// docgen objects.
///
-/// Set from the command line option
-/// --exclude-lib.
-List<String> _excluded;
+/// Used for lookup because of the dart2js mirrors exports
+/// issue. The second level map is indexed by owner docName for faster lookup.
+/// Why two levels of lookup? Speed, man. Speed.
+Map<String, Map<String, Set<MirrorBased>>> mirrorToDocgen =
+ new Map<String, Map<String, Set<MirrorBased>>>();
-// TODO(janicejl): Make MDN content generic or pluggable. Maybe move
-// MDN-specific code to its own library that is imported into the default impl?
-/// Map of all the comments for dom elements from MDN.
-Map _mdn;
-
-/// Docgen constructor initializes the link resolver for markdown parsing.
-/// Also initializes the command line arguments.
+/// Given a Dart2jsMirror, find the corresponding Docgen [MirrorBased] object.
///
-/// [packageRoot] is the packages directory of the directory being analyzed.
-/// If [includeSdk] is `true`, then any SDK libraries explicitly imported will
-/// also be documented.
-/// If [parseSdk] is `true`, then all Dart SDK libraries will be documented.
-/// This option is useful when only the SDK libraries are needed.
-///
-/// Returned Future completes with true if document generation is successful.
-Future<bool> docgen(List<String> files, {String packageRoot,
- bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false,
- bool parseSdk: false, bool append: false, String introduction: '',
- out: DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries,
- bool includeDependentPackages}) {
- _excluded = excludeLibraries;
- _includePrivate = includePrivate;
- _outputDirectory = out;
- _includeDependentPackages = includeDependentPackages;
- if (!append) {
- var dir = new Directory(_outputDirectory);
- if (dir.existsSync()) dir.deleteSync(recursive: true);
- }
-
- if (packageRoot == null && !parseSdk) {
- var type = FileSystemEntity.typeSync(files.first);
- if (type == FileSystemEntityType.DIRECTORY) {
- packageRoot = _findPackageRoot(files.first);
- } else if (type == FileSystemEntityType.FILE) {
- logger.warning('WARNING: No package root defined. If Docgen fails, try '
- 'again by setting the --package-root option.');
+/// We have this global lookup function to avoid re-implementing looking up the
+/// scoping rules for comment resolution here (it is currently done in mirrors).
+/// If no corresponding MirrorBased object is found, we return a [DummyMirror]
+/// that simply returns the original mirror's qualifiedName while behaving like
+/// a MirrorBased object.
+MirrorBased getDocgenObject(DeclarationMirror mirror, [Indexable owner]) {
+ Map<String, Set<MirrorBased>> docgenObj =
+ mirrorToDocgen[mirror.qualifiedName];
+ if (docgenObj == null) {
+ return new DummyMirror(mirror, owner);
+ }
+
+ Set<MirrorBased> results = new Set<MirrorBased>();
+ var setToExamine = new Set();
+ if (owner != null) {
+ var firstSet = docgenObj[owner.docName];
+ if (firstSet != null) setToExamine.addAll(firstSet);
+ if (Indexable._coreLibrary != null &&
+ docgenObj[Indexable._coreLibrary.docName] != null) {
+ setToExamine.addAll(docgenObj[Indexable._coreLibrary.docName]);
+ }
+ } else {
+ for (var value in docgenObj.values) {
+ setToExamine.addAll(value);
}
- }
- logger.info('Package Root: ${packageRoot}');
- if (_includeDependentPackages) {
- files.addAll(allDependentPackageDirs(files.first));
- }
- var requestedLibraries = _listLibraries(files);
- var allLibraries = []..addAll(requestedLibraries);
- if (includeSdk) {
- allLibraries.addAll(_listSdk());
}
- return getMirrorSystem(allLibraries, packageRoot: packageRoot,
- parseSdk: parseSdk)
- .then((MirrorSystem mirrorSystem) {
- if (mirrorSystem.libraries.isEmpty) {
- throw new StateError('No library mirrors were created.');
- }
- var availableLibraries = mirrorSystem.libraries.values.where(
- (each) => each.uri.scheme == 'file');
- _sdkLibraries = mirrorSystem.libraries.values.where(
- (each) => each.uri.scheme == 'dart');
- _coreLibrary = _sdkLibraries.singleWhere((lib) =>
- lib.uri.toString().startsWith('dart:core'));
- var availableLibrariesByPath = new Map.fromIterables(
- availableLibraries.map((each) => each.uri),
- availableLibraries);
- var librariesToDocument = requestedLibraries.map(
- (each) => availableLibrariesByPath.putIfAbsent(each,
- () => throw "Missing library $each")).toList();
- librariesToDocument.addAll((includeSdk || parseSdk) ? _sdkLibraries : []);
- librariesToDocument.removeWhere((x) => _excluded.contains(x.simpleName));
- _documentLibraries(librariesToDocument, includeSdk: includeSdk,
- outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
- introduction: introduction);
- return true;
- });
-}
+ for(MirrorBased mirrorBased in setToExamine) {
+ // This check makes me sad, but Dart doesn't seem to be dynamic enough to
+ // write this another way. Dart2js and the editor didn't like the Type
+ // lookup in a map.
+ if (mirrorBased.mirror.qualifiedName == mirror.qualifiedName &&
+ ((mirror is ClassMirror && mirrorBased is Class)
+ || (mirror is LibraryMirror && mirrorBased is Library)
+ || (mirror is MethodMirror && mirrorBased is Method)
+ || (mirror is VariableMirror && mirrorBased is Variable)
+ || (mirror is TypedefMirror && mirrorBased is Typedef)
+ || (mirror is TypeMirror && mirrorBased is Type)
+ || (mirror is InstanceMirror && mirrorBased is Annotation))) {
+ results.add(mirrorBased);
+ }
+ }
-/// All of the directories for our dependent packages
-List<String> allDependentPackageDirs(String packageDirectory) {
- var dependentsJson = Process.runSync('pub', ['list-package-dirs'],
- workingDirectory: packageDirectory, runInShell: true);
- if (dependentsJson.exitCode != 0) {
- print(dependentsJson.stderr);
+ if (results.length > 0) {
+ return results.first; // This might occur if we didn't specify an "owner."
}
- var dependents = JSON.decode(dependentsJson.stdout)['packages'];
- return dependents.values.toList();
+ return new DummyMirror(mirror, owner);
}
-/// For a library's [mirror], determine the name of the package (if any) we
-/// believe it came from (because of its file URI).
-///
-/// If [library] is specified, we set the packageName field. If no package could
-/// be determined, we return an empty string.
-String _findPackage(LibraryMirror mirror, [Library library]) {
- if (mirror == null) return '';
- if (library == null) {
- library = entityMap[docName(mirror)];
- }
- if (library != null) {
- if (library.hasBeenCheckedForPackage) return library.packageName;
- library.hasBeenCheckedForPackage = true;
- }
- if (mirror.uri.scheme != 'file') return '';
- var filePath = mirror.uri.toFilePath();
- // We assume that we are documenting only libraries under package/lib
- var rootdir = path.dirname((path.dirname(filePath)));
- var pubspec = path.join(rootdir, 'pubspec.yaml');
- var packageName = _packageName(pubspec);
- if (library != null) {
- library.packageName = packageName;
- // Associate the package readme with all the libraries. This is a bit
- // wasteful, but easier than trying to figure out which partial match
- // is best.
- library.packageIntro = _packageIntro(rootdir);
+/// For types that we do not explicitly create or have not yet created in our
+/// entity map (like core types).
+class DummyMirror implements MirrorBased {
+ DeclarationMirror mirror;
+ /// The library that contains this element, if any. Used as a hint to help
+ /// determine which object we're referring to when looking up this mirror in
+ /// our map.
+ Indexable owner;
+ DummyMirror(this.mirror, [this.owner]);
+
+ String get docName {
+ if (mirror == null) return '';
+ if (mirror is LibraryMirror) {
+ return mirror.qualifiedName.replaceAll('.','-');
+ }
+ var mirrorOwner = mirror.owner;
+ if (mirrorOwner == null) return mirror.qualifiedName;
+ var simpleName = mirror.simpleName;
+ if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
+ // We name constructors specially -- including the class name again and a
+ // "-" to separate the constructor from its name (if any).
+ simpleName = '${mirrorOwner.simpleName}-$simpleName';
+ }
+ return getDocgenObject(mirrorOwner, owner).docName + '.' + simpleName;
}
- return packageName;
-}
+ List<Annotation> _createAnnotations(DeclarationMirror mirror,
+ MirrorBased owner) => null;
-String _packageIntro(packageDir) {
- var dir = new Directory(packageDir);
- var files = dir.listSync();
- var readmes = files.where((FileSystemEntity each) => (each is File &&
- each.path.substring(packageDir.length + 1, each.path.length)
- .startsWith('README'))).toList();
- if (readmes.isEmpty) return '';
- // If there are multiples, pick the shortest name.
- readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
- var readme = readmes.first;
- var linkResolver = (name) => fixReference(name, null, null, null);
- var contents = markdown.markdownToHtml(readme
- .readAsStringSync(), linkResolver: linkResolver,
- inlineSyntaxes: markdownSyntaxes);
- return contents;
+ bool get isPrivate => mirror == null? false : mirror.isPrivate;
}
-List<Uri> _listLibraries(List<String> args) {
- var libraries = new List<Uri>();
- for (var arg in args) {
- var type = FileSystemEntity.typeSync(arg);
+abstract class MirrorBased {
+ DeclarationMirror get mirror;
+ MirrorBased owner;
- if (type == FileSystemEntityType.FILE) {
- if (arg.endsWith('.dart')) {
- libraries.add(new Uri.file(path.absolute(arg)));
- logger.info('Added to libraries: ${libraries.last}');
- }
- } else {
- libraries.addAll(_listDartFromDir(arg));
- }
- }
- return libraries;
-}
+ /// Returns this object's qualified name, but following the conventions
+ /// we're using in Dartdoc, which is that library names with dots in them
+ /// have them replaced with hyphens.
+ String get docName => owner.docName + '.' + mirror.simpleName;
-List<Uri> _listDartFromDir(String args) {
- var libraries = [];
- // To avoid anaylzing package files twice, only files with paths not
- // containing '/packages' will be added. The only exception is if the file to
- // analyze already has a '/package' in its path.
- var files = listDir(args, recursive: true).where((f) => f.endsWith('.dart') &&
- (!f.contains('${path.separator}packages') ||
- args.contains('${path.separator}packages'))).toList();
-
- files.forEach((String f) {
- // Only include libraries at the top level of "lib"
- if (path.basename(path.dirname(f)) == 'lib') {
- // Only add the file if it does not contain 'part of'
- // TODO(janicejl): Remove when Issue(12406) is resolved.
- var contents = new File(f).readAsStringSync();
- if (!(contents.contains(new RegExp('\npart of ')) ||
- contents.startsWith(new RegExp('part of ')))) {
- libraries.add(new Uri.file(path.normalize(path.absolute(f))));
- logger.info('Added to libraries: $f');
+ /// Returns a list of meta annotations assocated with a mirror.
+ List<Annotation> _createAnnotations(DeclarationMirror mirror,
+ MirrorBased owner) {
+ var annotationMirrors = mirror.metadata.where((e) =>
+ e is dart2js.Dart2JsConstructedConstantMirror);
+ var annotations = [];
+ annotationMirrors.forEach((annotation) {
+ var docgenAnnotation = new Annotation(annotation, owner);
+ if (!_SKIPPED_ANNOTATIONS.contains(
+ docgenAnnotation.mirror.qualifiedName)) {
+ annotations.add(docgenAnnotation);
}
- }
- });
- return libraries;
-}
-
-String _findPackageRoot(String directory) {
- var files = listDir(directory, recursive: true);
- // Return '' means that there was no pubspec.yaml and therefor no packageRoot.
- String packageRoot = files.firstWhere((f) =>
- f.endsWith('${path.separator}pubspec.yaml'), orElse: () => '');
- if (packageRoot != '') {
- packageRoot = path.join(path.dirname(packageRoot), 'packages');
+ });
+ return annotations;
}
- return packageRoot;
-}
-/// Read a pubspec and return the library name.
-String _packageName(String pubspecName) {
- File pubspec = new File(pubspecName);
- if (!pubspec.existsSync()) return '';
- var contents = pubspec.readAsStringSync();
- var spec = loadYaml(contents);
- return spec["name"];
+ bool get isPrivate => false;
}
-List<Uri> _listSdk() {
- var sdk = new List<Uri>();
- LIBRARIES.forEach((String name, LibraryInfo info) {
- if (info.documented) {
- sdk.add(Uri.parse('dart:$name'));
- logger.info('Add to SDK: ${sdk.last}');
- }
- });
- return sdk;
+/// Docgen constructor initializes the link resolver for markdown parsing.
+/// Also initializes the command line arguments.
+///
+/// [packageRoot] is the packages directory of the directory being analyzed.
+/// If [includeSdk] is `true`, then any SDK libraries explicitly imported will
+/// also be documented.
+/// If [parseSdk] is `true`, then all Dart SDK libraries will be documented.
+/// This option is useful when only the SDK libraries are needed.
+///
+/// Returned Future completes with true if document generation is successful.
+Future<bool> docgen(List<String> files, {String packageRoot,
+ bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false,
+ bool parseSdk: false, bool append: false, String introduction: '',
+ out: _DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries : const [],
+ bool includeDependentPackages: false}) {
+ return _Generator.generateDocumentation(files, packageRoot: packageRoot,
+ outputToYaml: outputToYaml, includePrivate: includePrivate,
+ includeSdk: includeSdk, parseSdk: parseSdk, append: append,
+ introduction: introduction, out: out, excludeLibraries: excludeLibraries,
+ includeDependentPackages: includeDependentPackages);
}
/// Analyzes set of libraries by getting a mirror system and triggers the
@@ -288,437 +199,472 @@ Future<MirrorSystem> getMirrorSystem(List<Uri> libraries,
if (libraries.isEmpty) throw new StateError('No Libraries.');
// Finds the root of SDK library based off the location of docgen.
- var root = findRootDirectory();
+ var root = _Generator._rootDirectory;
var sdkRoot = path.normalize(path.absolute(path.join(root, 'sdk')));
- logger.info('SDK Root: ${sdkRoot}');
- return _analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot);
+ _Generator.logger.info('SDK Root: ${sdkRoot}');
+ return _Generator._analyzeLibraries(libraries, sdkRoot,
+ packageRoot: packageRoot);
}
-String findRootDirectory() {
- var scriptDir = path.absolute(path.dirname(Platform.script.toFilePath()));
- var root = scriptDir;
- while(path.basename(root) != 'dart') {
- root = path.dirname(root);
- }
- return root;
-}
+class _Generator {
+ static var _outputDirectory;
-/// Analyzes set of libraries and provides a mirror system which can be used
-/// for static inspection of the source code.
-Future<MirrorSystem> _analyzeLibraries(List<Uri> libraries,
- String libraryRoot, {String packageRoot}) {
- SourceFileProvider provider = new CompilerSourceFileProvider();
- api.DiagnosticHandler diagnosticHandler =
- (new FormattingDiagnosticHandler(provider)
- ..showHints = false
- ..showWarnings = false)
- .diagnosticHandler;
- Uri libraryUri = new Uri.file(appendSlash(libraryRoot));
- Uri packageUri = null;
- if (packageRoot != null) {
- packageUri = new Uri.file(appendSlash(packageRoot));
- }
- return dart2js.analyze(libraries, libraryUri, packageUri,
- provider.readStringFromUri, diagnosticHandler,
- ['--preserve-comments', '--categories=Client,Server'])
- ..catchError((error) {
- logger.severe('Error: Failed to create mirror system. ');
- // TODO(janicejl): Use the stack trace package when bug is resolved.
- // Currently, a string is thrown when it fails to create a mirror
- // system, and it is not possible to use the stack trace. BUG(#11622)
- // To avoid printing the stack trace.
- exit(1);
- });
-}
+ /// This is set from the command line arguments flag --include-private
+ static bool _includePrivate = false;
-/// Creates documentation for filtered libraries.
-void _documentLibraries(List<LibraryMirror> libs, {bool includeSdk: false,
- bool outputToYaml: true, bool append: false, bool parseSdk: false,
- String introduction: ''}) {
- libs.forEach((lib) {
- // Files belonging to the SDK have a uri that begins with 'dart:'.
- if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
- var library = generateLibrary(lib);
- entityMap[library.name] = library;
- }
- });
- // After everything is created, do a pass through all classes to make sure no
- // intermediate classes created by mixins are included, all the links to
- // exported members point to the new library.
- entityMap.values.where((e) => e is Class).forEach(
- (c) => c.updateLinksAndRemoveIntermediaryClasses());
- // Everything is a subclass of Object, therefore empty the list to avoid a
- // giant list of subclasses to be printed out.
- if (includeSdk) (entityMap['dart-core.Object'] as Class).subclasses.clear();
-
- var filteredEntities = entityMap.values.where(_isVisible);
-
- // Outputs a JSON file with all libraries and their preview comments.
- // This will help the viewer know what libraries are available to read in.
- var libraryMap;
- var linkResolver = (name) => fixReference(name, null, null, null);
- if (append) {
- var docsDir = listDir(_outputDirectory);
- if (!docsDir.contains('$_outputDirectory/library_list.json')) {
- throw new StateError('No library_list.json');
- }
- libraryMap =
- JSON.decode(new File(
- '$_outputDirectory/library_list.json').readAsStringSync());
- libraryMap['libraries'].addAll(filteredEntities
- .where((e) => e is Library)
- .map((e) => e.previewMap));
- if (introduction.isNotEmpty) {
- var intro = libraryMap['introduction'];
- if (intro.isNotEmpty) intro += '<br/><br/>';
- intro += markdown.markdownToHtml(
- new File(introduction).readAsStringSync(),
- linkResolver: linkResolver, inlineSyntaxes: markdownSyntaxes);
- libraryMap['introduction'] = intro;
- }
- outputToYaml = libraryMap['filetype'] == 'yaml';
- } else {
- libraryMap = {
- 'libraries' : filteredEntities.where((e) =>
- e is Library).map((e) => e.previewMap).toList(),
- 'introduction' : introduction == '' ?
- '' : markdown.markdownToHtml(new File(introduction)
- .readAsStringSync(), linkResolver: linkResolver,
- inlineSyntaxes: markdownSyntaxes),
- 'filetype' : outputToYaml ? 'yaml' : 'json'
- };
- }
- _writeToFile(JSON.encode(libraryMap), 'library_list.json');
-
- // Output libraries and classes to file after all information is generated.
- filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
- _writeIndexableToFile(output, outputToYaml);
- });
-
- // Outputs all the qualified names documented with their type.
- // This will help generate search results.
- _writeToFile(filteredEntities.map((e) =>
- '${e.qualifiedName} ${e.typeName}').join('\n') + '\n',
- 'index.txt', append: append);
- var index = new Map.fromIterables(
- filteredEntities.map((e) => e.qualifiedName),
- filteredEntities.map((e) => e.typeName));
- if (append) {
- var previousIndex =
- JSON.decode(new File('$_outputDirectory/index.json').readAsStringSync());
- index.addAll(previousIndex);
- }
- _writeToFile(JSON.encode(index), 'index.json');
-}
+ /// Library names to explicitly exclude.
+ ///
+ /// Set from the command line option
+ /// --exclude-lib.
+ static List<String> _excluded;
-Library generateLibrary(dart2js.Dart2JsLibraryMirror library) {
- var result = new Library(library);
- _findPackage(library, result);
- logger.fine('Generated library for ${result.name}');
- return result;
-}
+ static Logger logger = new Logger('Docgen');
-void _writeIndexableToFile(Indexable result, bool outputToYaml) {
- var outputFile = result.fileName;
- var output;
- if (outputToYaml) {
- output = getYamlString(result.toMap());
- outputFile = outputFile + '.yaml';
- } else {
- output = JSON.encode(result.toMap());
- outputFile = outputFile + '.json';
- }
- _writeToFile(output, outputFile);
-}
-
-/// Returns true if a library name starts with an underscore, and false
-/// otherwise.
-///
-/// An example that starts with _ is _js_helper.
-/// An example that contains ._ is dart._collection.dev
-// This is because LibraryMirror.isPrivate returns `false` all the time.
-bool _isLibraryPrivate(LibraryMirror mirror) {
- var sdkLibrary = LIBRARIES[mirror.simpleName];
- if (sdkLibrary != null) {
- return !sdkLibrary.documented;
- } else if (mirror.simpleName.startsWith('_') ||
- mirror.simpleName.contains('._')) {
- return true;
- }
- return false;
-}
+ /// Docgen constructor initializes the link resolver for markdown parsing.
+ /// Also initializes the command line arguments.
+ ///
+ /// [packageRoot] is the packages directory of the directory being analyzed.
+ /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will
+ /// also be documented.
+ /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented.
+ /// This option is useful when only the SDK libraries are needed.
+ ///
+ /// Returned Future completes with true if document generation is successful.
+ static Future<bool> generateDocumentation(List<String> files,
+ {String packageRoot, bool outputToYaml: true, bool includePrivate: false,
+ bool includeSdk: false, bool parseSdk: false, bool append: false,
+ String introduction: '', out: _DEFAULT_OUTPUT_DIRECTORY,
+ List<String> excludeLibraries : const [],
+ bool includeDependentPackages: false}) {
+ _excluded = excludeLibraries;
+ _includePrivate = includePrivate;
+ logger.onRecord.listen((record) => print(record.message));
+
+ _ensureOutputDirectory(out, append);
+ var updatedPackageRoot = _obtainPackageRoot(packageRoot, parseSdk, files);
+
+ var requestedLibraries = _findLibrariesToDocument(files,
+ includeDependentPackages);
+
+ var allLibraries = []..addAll(requestedLibraries);
+ if (includeSdk) {
+ allLibraries.addAll(_listSdk());
+ }
-/// A declaration is private if itself is private, or the owner is private.
-// Issue(12202) - A declaration is public even if it's owner is private.
-bool _isHidden(DeclarationMirror mirror) {
- if (mirror is LibraryMirror) {
- return _isLibraryPrivate(mirror);
- } else if (mirror.owner is LibraryMirror) {
- return (mirror.isPrivate || _isLibraryPrivate(mirror.owner));
- } else {
- return (mirror.isPrivate || _isHidden(mirror.owner));
+ return getMirrorSystem(allLibraries, packageRoot: updatedPackageRoot,
+ parseSdk: parseSdk)
+ .then((MirrorSystem mirrorSystem) {
+ if (mirrorSystem.libraries.isEmpty) {
+ throw new StateError('No library mirrors were created.');
+ }
+ Indexable.initializeTopLevelLibraries(mirrorSystem);
+
+ var availableLibraries = mirrorSystem.libraries.values.where(
+ (each) => each.uri.scheme == 'file');
+ var availableLibrariesByPath = new Map.fromIterables(
+ availableLibraries.map((each) => each.uri),
+ availableLibraries);
+ var librariesToDocument = requestedLibraries.map(
+ (each) => availableLibrariesByPath.putIfAbsent(each,
+ () => throw "Missing library $each")).toList();
+ librariesToDocument.addAll(
+ (includeSdk || parseSdk) ? Indexable._sdkLibraries : []);
+ librariesToDocument.removeWhere(
+ (x) => _excluded.contains(x.simpleName));
+ _documentLibraries(librariesToDocument, includeSdk: includeSdk,
+ outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
+ introduction: introduction);
+ return true;
+ });
}
-}
-
-bool _isVisible(Indexable item) {
- return _includePrivate || !item.isPrivate;
-}
-
-/// Generates MDN comments from database.json.
-void _mdnComment(Indexable item) {
- //Check if MDN is loaded.
- if (_mdn == null) {
- // Reading in MDN related json file.
- var root = findRootDirectory();
- var mdnPath = path.join(root, 'utils/apidoc/mdn/database.json');
- _mdn = JSON.decode(new File(mdnPath).readAsStringSync());
- }
- if (item is Library) return;
- var domAnnotation = item.annotations.firstWhere(
- (e) => e.qualifiedName == 'metadata.DomName', orElse: () => null);
- if (domAnnotation == null) return;
- var domName = domAnnotation.parameters.single;
- var parts = domName.split('.');
- if (parts.length == 2) item.comment = _mdnMemberComment(parts[0], parts[1]);
- if (parts.length == 1) item.comment = _mdnTypeComment(parts[0]);
-}
-/// Generates the MDN Comment for variables and method DOM elements.
-String _mdnMemberComment(String type, String member) {
- var mdnType = _mdn[type];
- if (mdnType == null) return '';
- var mdnMember = mdnType['members'].firstWhere((e) => e['name'] == member,
- orElse: () => null);
- if (mdnMember == null) return '';
- if (mdnMember['help'] == null || mdnMember['help'] == '') return '';
- if (mdnMember['url'] == null) return '';
- return _htmlMdn(mdnMember['help'], mdnMember['url']);
-}
+ /// Writes text to a file in the output directory.
+ static void _writeToFile(String text, String filename, {bool append: false}) {
+ if (text == null) return;
+ Directory dir = new Directory(_outputDirectory);
+ if (!dir.existsSync()) {
+ dir.createSync();
+ }
+ if (path.split(filename).length > 1) {
+ var splitList = path.split(filename);
+ for (int i = 0; i < splitList.length; i++) {
+ var level = splitList[i];
+ }
+ for (var level in path.split(filename)) {
+ var subdir = new Directory(path.join(_outputDirectory,
+ path.dirname(filename)));
+ if (!subdir.existsSync()) {
+ subdir.createSync();
+ }
+ }
+ }
+ File file = new File(path.join(_outputDirectory, filename));
+ file.writeAsStringSync(text,
+ mode: append ? FileMode.APPEND : FileMode.WRITE);
+ }
+
+ /// Creates documentation for filtered libraries.
+ static void _documentLibraries(List<LibraryMirror> libs,
+ {bool includeSdk: false, bool outputToYaml: true, bool append: false,
+ bool parseSdk: false, String introduction: ''}) {
+ libs.forEach((lib) {
+ // Files belonging to the SDK have a uri that begins with 'dart:'.
+ if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
+ var library = generateLibrary(lib);
+ entityMap[library.name] = library;
+ }
+ });
-/// Generates the MDN Comment for class DOM elements.
-String _mdnTypeComment(String type) {
- var mdnType = _mdn[type];
- if (mdnType == null) return '';
- if (mdnType['summary'] == null || mdnType['summary'] == "") return '';
- if (mdnType['srcUrl'] == null) return '';
- return _htmlMdn(mdnType['summary'], mdnType['srcUrl']);
-}
+ var filteredEntities = entityMap.values.where(_isFullChainVisible);
-String _htmlMdn(String content, String url) {
- return '<div class="mdn">' + content.trim() + '<p class="mdn-note">'
- '<a href="' + url.trim() + '">from Mdn</a></p></div>';
-}
+ /*var filteredEntities2 = new Set<MirrorBased>();
+ for (Map<String, Set<MirrorBased>> firstLevel in mirrorToDocgen.values) {
+ for (Set<MirrorBased> items in firstLevel.values) {
+ for (MirrorBased item in items) {
+ if (_isFullChainVisible(item)) {
+ filteredEntities2.add(item);
+ }
+ }
+ }
+ }*/
+
+ /*print('THHHHHEEE DIFFERENCE IS');
+ var set1 = new Set.from(filteredEntities);
+ var set2 = new Set.from(filteredEntities2);
+ var aResult = set2.difference(set1);
+ for (MirrorBased r in aResult) {
+ print(' a result is $r and ${r.docName}');
+ }*/
+ //print(set1.difference(set2));
+
+ // Outputs a JSON file with all libraries and their preview comments.
+ // This will help the viewer know what libraries are available to read in.
+ var libraryMap;
+ var linkResolver = (name) => Indexable.globalFixReference(name);
+ if (append) {
+ var docsDir = listDir(_outputDirectory);
+ if (!docsDir.contains('$_outputDirectory/library_list.json')) {
+ throw new StateError('No library_list.json');
+ }
+ libraryMap =
+ JSON.decode(new File(
+ '$_outputDirectory/library_list.json').readAsStringSync());
+ libraryMap['libraries'].addAll(filteredEntities
+ .where((e) => e is Library)
+ .map((e) => e.previewMap));
+ if (introduction.isNotEmpty) {
+ var intro = libraryMap['introduction'];
+ if (intro.isNotEmpty) intro += '<br/><br/>';
+ intro += markdown.markdownToHtml(
+ new File(introduction).readAsStringSync(),
+ linkResolver: linkResolver, inlineSyntaxes: _MARKDOWN_SYNTAXES);
+ libraryMap['introduction'] = intro;
+ }
+ outputToYaml = libraryMap['filetype'] == 'yaml';
+ } else {
+ libraryMap = {
+ 'libraries' : filteredEntities.where((e) =>
+ e is Library).map((e) => e.previewMap).toList(),
+ 'introduction' : introduction == '' ?
+ '' : markdown.markdownToHtml(new File(introduction)
+ .readAsStringSync(), linkResolver: linkResolver,
+ inlineSyntaxes: _MARKDOWN_SYNTAXES),
+ 'filetype' : outputToYaml ? 'yaml' : 'json'
+ };
+ }
+ _writeToFile(JSON.encode(libraryMap), 'library_list.json');
-/// Look for the specified name starting with the current member, and
-/// progressively working outward to the current library scope.
-String findElementInScope(String name, LibraryMirror currentLibrary,
- ClassMirror currentClass, MemberMirror currentMember) {
- var packagePrefix = _findPackage(currentLibrary);
- if (packagePrefix != '') packagePrefix += '/';
+ // Output libraries and classes to file after all information is generated.
+ filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
+ _writeIndexableToFile(output, outputToYaml);
+ });
- determineLookupFunc(name) => name.contains('.') ?
- dart2js_util.lookupQualifiedInScope :
- (mirror, name) => mirror.lookupInScope(name);
- var lookupFunc = determineLookupFunc(name);
-
- var memberScope = currentMember == null ?
- null : lookupFunc(currentMember, name);
- if (memberScope != null) return packagePrefix + docName(memberScope);
-
- var classScope = currentClass;
- while (classScope != null) {
- var classFunc = lookupFunc(currentClass, name);
- if (classFunc != null) return packagePrefix + docName(classFunc);
- classScope = classScope.superclass;
- }
-
- var libraryScope = currentLibrary == null ?
- null : lookupFunc(currentLibrary, name);
- if (libraryScope != null) return packagePrefix + docName(libraryScope);
-
- // Look in the dart core library scope.
- var coreScope = _coreLibrary == null? null : lookupFunc(_coreLibrary, name);
- if (coreScope != null) return packagePrefix + docName(_coreLibrary);
-
- // If it's a reference that starts with a another library name, then it
- // looks for a match of that library name in the other sdk libraries.
- if(name.contains('.')) {
- var index = name.indexOf('.');
- var libraryName = name.substring(0, index);
- var remainingName = name.substring(index + 1);
- foundLibraryName(library) => library.uri.pathSegments[0] == libraryName;
-
- if (_sdkLibraries.any(foundLibraryName)) {
- var library = _sdkLibraries.singleWhere(foundLibraryName);
- // Look to see if it's a fully qualified library name.
- var scope = determineLookupFunc(remainingName)(library, remainingName);
- if (scope != null) return packagePrefix + docName(scope);
+ // Outputs all the qualified names documented with their type.
+ // This will help generate search results.
+ _writeToFile(filteredEntities.map((e) =>
+ '${e.qualifiedName} ${e.typeName}').join('\n') + '\n',
+ 'index.txt', append: append);
+ var index = new Map.fromIterables(
+ filteredEntities.map((e) => e.qualifiedName),
+ filteredEntities.map((e) => e.typeName));
+ if (append) {
+ var previousIndex =
+ JSON.decode(new File(
+ '$_outputDirectory/index.json').readAsStringSync());
+ index.addAll(previousIndex);
}
+ _writeToFile(JSON.encode(index), 'index.json');
}
- return null;
-}
-// HTML escaped version of '<' character.
-final _LESS_THAN = '&lt;';
-
-/// Chunk the provided name into individual parts to be resolved. We take a
-/// simplistic approach to chunking, though, we break at " ", ",", "&lt;"
-/// and ">". All other characters are grouped into the name to be resolved.
-/// As a result, these characters will all be treated as part of the item to be
-/// resolved (aka the * is interpreted literally as a *, not as an indicator for
-/// bold <em>.
-List<String> _tokenizeComplexReference(String name) {
- var tokens = [];
- var append = false;
- var index = 0;
- while(index < name.length) {
- if (name.indexOf(_LESS_THAN, index) == index) {
- tokens.add(_LESS_THAN);
- append = false;
- index += _LESS_THAN.length;
- } else if (name[index] == ' ' || name[index] == ',' ||
- name[index] == '>') {
- tokens.add(name[index]);
- append = false;
- index++;
+ static void _writeIndexableToFile(Indexable result, bool outputToYaml) {
+ var outputFile = result.fileName;
+ var output;
+ if (outputToYaml) {
+ output = getYamlString(result.toMap());
+ outputFile = outputFile + '.yaml';
} else {
- if (append) {
- tokens[tokens.length - 1] = tokens.last + name[index];
- } else {
- tokens.add(name[index]);
- append = true;
- }
- index++;
+ output = JSON.encode(result.toMap());
+ outputFile = outputFile + '.json';
}
+ _writeToFile(output, outputFile);
}
- return tokens;
-}
-/// This is a more complex reference. Try to break up if its of the form A<B>
-/// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"),
-/// or of the form A<B>. Note: unlike other the other markdown-style links, all
-/// text inside the square brackets is treated as part of the link (aka the * is
-/// interpreted literally as a *, not as a indicator for bold <em>.
-///
-/// Example: [foo&lt;_bar_>] will produce
-/// <a>resolvedFoo</a>&lt;<a>resolved_bar_</a>> rather than an italicized
-/// version of resolvedBar.
-markdown.Node _fixComplexReference(String name, LibraryMirror currentLibrary,
- ClassMirror currentClass, MemberMirror currentMember) {
- // Parse into multiple elements we can try to resolve.
- var tokens = _tokenizeComplexReference(name);
-
- // Produce an html representation of our elements. Group unresolved and plain
- // text are grouped into "link" elements so they display as code.
- final textElements = [' ', ',', '>', _LESS_THAN];
- var accumulatedHtml = '';
-
- for (var token in tokens) {
- bool added = false;
- if (!textElements.contains(token)) {
- String elementName = findElementInScope(token, currentLibrary,
- currentClass, currentMember);
- if (elementName != null) {
- accumulatedHtml += markdown.renderToHtml([new markdown.Element.text(
- 'a', elementName)]);
- added = true;
- }
- }
- if (!added) {
- accumulatedHtml += token;
- }
+ /// Set the location of the ouput directory, and ensure that the location is
+ /// available on the file system.
+ static void _ensureOutputDirectory(String outputDirectory, bool append) {
+ _outputDirectory = outputDirectory;
+ if (!append) {
+ var dir = new Directory(_outputDirectory);
+ if (dir.existsSync()) dir.deleteSync(recursive: true);
+ }
}
- return new markdown.Text(accumulatedHtml);
-}
-/// Converts all [foo] references in comments to <a>libraryName.foo</a>.
-markdown.Node fixReference(String name, LibraryMirror currentLibrary,
- ClassMirror currentClass, MemberMirror currentMember) {
- // Attempt the look up the whole name up in the scope.
- String elementName =
- findElementInScope(name, currentLibrary, currentClass, currentMember);
- if (elementName != null) {
- return new markdown.Element.text('a', elementName);
- }
- return _fixComplexReference(name, currentLibrary, currentClass,
- currentMember);
-}
-markdown.Node fixReferenceWithScope(String name, DeclarationMirror scope) {
- if (scope is LibraryMirror) return fixReference(name, scope, null, null);
- if (scope is ClassMirror)
- return fixReference(name, scope.library, scope, null);
- if (scope is MemberMirror) {
- var owner = scope.owner;
- if (owner is ClassMirror) {
- return fixReference(name, owner.library, owner, scope);
- } else {
- return fixReference(name, owner, null, scope);
+ static String get _rootDirectory {
+ var scriptDir = path.absolute(path.dirname(Platform.script.toFilePath()));
+ var root = scriptDir;
+ while(path.basename(root) != 'dart') {
+ root = path.dirname(root);
}
- }
- return null;
-}
-
-/// Writes text to a file in the output directory.
-void _writeToFile(String text, String filename, {bool append: false}) {
- if (text == null) return;
- Directory dir = new Directory(_outputDirectory);
- if (!dir.existsSync()) {
- dir.createSync();
- }
- if (path.split(filename).length > 1) {
- var splitList = path.split(filename);
- for (int i = 0; i < splitList.length; i++) {
- var level = splitList[i];
+ return root;
+ }
+
+ /// Analyzes set of libraries and provides a mirror system which can be used
+ /// for static inspection of the source code.
+ static Future<MirrorSystem> _analyzeLibraries(List<Uri> libraries,
+ String libraryRoot, {String packageRoot}) {
+ SourceFileProvider provider = new CompilerSourceFileProvider();
+ api.DiagnosticHandler diagnosticHandler =
+ (new FormattingDiagnosticHandler(provider)
+ ..showHints = false
+ ..showWarnings = false)
+ .diagnosticHandler;
+ Uri libraryUri = new Uri.file(appendSlash(libraryRoot));
+ Uri packageUri = null;
+ if (packageRoot != null) {
+ packageUri = new Uri.file(appendSlash(packageRoot));
}
- for (var level in path.split(filename)) {
- var subdir = new Directory(path.join(_outputDirectory,
- path.dirname(filename)));
- if (!subdir.existsSync()) {
- subdir.createSync();
+ return dart2js.analyze(libraries, libraryUri, packageUri,
+ provider.readStringFromUri, diagnosticHandler,
+ ['--preserve-comments', '--categories=Client,Server'])
+ ..catchError((error) {
+ logger.severe('Error: Failed to create mirror system. ');
+ // TODO(janicejl): Use the stack trace package when bug is resolved.
+ // Currently, a string is thrown when it fails to create a mirror
+ // system, and it is not possible to use the stack trace. BUG(#11622)
+ // To avoid printing the stack trace.
+ exit(1);
+ });
+ }
+
+ /// For this run of docgen, determine the packageRoot value.
+ ///
+ /// If packageRoot is not explicitly passed, we examine the files we're
+ /// documenting to attempt to find a package root.
+ static String _obtainPackageRoot(String packageRoot, bool parseSdk,
+ List<String> files) {
+ if (packageRoot == null && !parseSdk) {
+ // TODO(efortuna): This logic seems not very robust, but it's from the
+ // original version of the code, pre-refactor, so I'm leavingt it for now.
+ // Revisit to make more robust.
+ var type = FileSystemEntity.typeSync(files.first);
+ if (type == FileSystemEntityType.DIRECTORY) {
+ var files2 = listDir(files.first, recursive: true);
+ // Return '' means that there was no pubspec.yaml and therefor no p
+ // ackageRoot.
+ packageRoot = files2.firstWhere((f) =>
+ f.endsWith('${path.separator}pubspec.yaml'), orElse: () => '');
+ if (packageRoot != '') {
+ packageRoot = path.join(path.dirname(packageRoot), 'packages');
+ }
+ } else if (type == FileSystemEntityType.FILE) {
+ logger.warning('WARNING: No package root defined. If Docgen fails, try '
+ 'again by setting the --package-root option.');
}
+ }
+ logger.info('Package Root: ${packageRoot}');
+ return packageRoot;
+ }
+
+ /// Given the user provided list of items to document, expand all directories
+ /// to document out into specific files and add any dependent packages for
+ /// documentation if desired.
+ static List<Uri> _findLibrariesToDocument(List<String> args,
+ bool includeDependentPackages) {
+ if (includeDependentPackages) {
+ args.addAll(_allDependentPackageDirs(args.first));
}
- }
- File file = new File(path.join(_outputDirectory, filename));
- file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE);
-}
-/// Transforms the map by calling toMap on each value in it.
-Map recurseMap(Map inputMap) {
- var outputMap = {};
- inputMap.forEach((key, value) {
- if (value is Map) {
- outputMap[key] = recurseMap(value);
- } else {
- outputMap[key] = value.toMap();
+ var libraries = new List<Uri>();
+ for (var arg in args) {
+ if (FileSystemEntity.typeSync(arg) == FileSystemEntityType.FILE) {
+ if (arg.endsWith('.dart')) {
+ libraries.add(new Uri.file(path.absolute(arg)));
+ logger.info('Added to libraries: ${libraries.last}');
+ }
+ } else {
+ libraries.addAll(_findFilesToDocumentInPackage(arg));
+ }
}
- });
- return outputMap;
+ return libraries;
+ }
+
+ /// Given a package name, explore the directory and pull out all top level
+ /// library files in the "lib" directory to document.
+ static List<Uri> _findFilesToDocumentInPackage(String packageName) {
+ var libraries = [];
+ // To avoid anaylzing package files twice, only files with paths not
+ // containing '/packages' will be added. The only exception is if the file
+ // to analyze already has a '/package' in its path.
+ var files = listDir(packageName, recursive: true).where(
+ (f) => f.endsWith('.dart') && (!f.contains('${path.separator}packages')
+ || packageName.contains('${path.separator}packages'))).toList();
+
+ files.forEach((String f) {
+ // Only include libraries at the top level of "lib"
+ if (path.basename(path.dirname(f)) == 'lib') {
+ // Only add the file if it does not contain 'part of'
+ // TODO(janicejl): Remove when Issue(12406) is resolved.
+ var contents = new File(f).readAsStringSync();
+ if (!(contents.contains(new RegExp('\npart of ')) ||
+ contents.startsWith(new RegExp('part of ')))) {
+ libraries.add(new Uri.file(path.normalize(path.absolute(f))));
+ logger.info('Added to libraries: $f');
+ }
+ }
+ });
+ return libraries;
+ }
+
+ /// All of the directories for our dependent packages
+ static List<String> _allDependentPackageDirs(String packageDirectory) {
+ var dependentsJson = Process.runSync('pub', ['list-package-dirs'],
+ workingDirectory: packageDirectory, runInShell: true);
+ if (dependentsJson.exitCode != 0) {
+ print(dependentsJson.stderr);
+ }
+ var dependents = JSON.decode(dependentsJson.stdout)['packages'];
+ return dependents.values.toList();
+ }
+
+ /// For all the libraries, return a list of the libraries that are part of
+ /// the SDK.
+ static List<Uri> _listSdk() {
+ var sdk = new List<Uri>();
+ LIBRARIES.forEach((String name, LibraryInfo info) {
+ if (info.documented) {
+ sdk.add(Uri.parse('dart:$name'));
+ logger.info('Add to SDK: ${sdk.last}');
+ }
+ });
+ return sdk;
+ }
+
+ static bool _isFullChainVisible(MirrorBased item) {
+ // TODO: reconcile with isVisible
+ var result = _includePrivate || (!item.isPrivate && (item.owner != null ?
+ _isFullChainVisible(item.owner) : true));
+ return result;
+ }
+
+ /// Currently left public for testing purposes. :-/
+ static Library generateLibrary(dart2js.Dart2JsLibraryMirror library) {
+ var result = new Library(library);
+ result._findPackage(library);
+ logger.fine('Generated library for ${result.name}');
+ return result;
+ }
}
-/// A type for the function that generates a comment from a mirror.
-typedef String CommentGenerator(Mirror m);
+/// An item that is categorized in our mirrorToDocgen map, as a distinct,
+/// searchable element.
+///
+/// These are items that refer to concrete entities (a Class, for example,
+/// but not a Type, which is a "pointer" to a class) that we wish to be
+/// globally resolvable. This includes things such as class methods and
+/// variables, but parameters for methods are not "Indexable" as we do not want
+/// the user to be able to search for a method based on its parameter names!
+/// The set of indexable items also includes Typedefs, since the user can refer
+/// to them as concrete entities in a particular scope.
+class Indexable extends MirrorBased {
+ /// The dart:core library, which contains all types that are always available
+ /// without import.
+ static Library _coreLibrary;
+
+ /// Set of libraries declared in the SDK, so libraries that can be accessed
+ /// when running dart by default.
+ static Iterable<LibraryMirror> _sdkLibraries;
-/// A class representing all programming constructs, like library or class.
-class Indexable {
String get qualifiedName => fileName;
bool isPrivate;
DeclarationMirror mirror;
+ /// The comment text pre-resolution. We keep this around because inherited
+ /// methods need to resolve links differently from the superclass.
+ String _unresolvedComment = '';
+
+ // TODO(janicejl): Make MDN content generic or pluggable. Maybe move
+ // MDN-specific code to its own library that is imported into the default
+ // impl?
+ /// Map of all the comments for dom elements from MDN.
+ static Map _mdn;
Indexable(this.mirror) {
this.isPrivate = _isHidden(mirror);
+
+ var map = mirrorToDocgen[this.mirror.qualifiedName];
+ if (map == null) map = new Map<String, Set<MirrorBased>>();
+
+ var set = map[owner.docName];
+ if (set == null) set = new Set<MirrorBased>();
+ set.add(this);
+ map[owner.docName] = set;
+ mirrorToDocgen[this.mirror.qualifiedName] = map;
+ }
+
+ /** Walk up the owner chain to find the owning library. */
+ Library _getOwningLibrary(Indexable owner) {
+ if (owner is Library) return owner;
+ // TODO: is this needed?
+ if (owner is DummyMirror) return getDocgenObject(owner.mirror.library);
+ return _getOwningLibrary(owner.owner);
+ }
+
+ static initializeTopLevelLibraries(MirrorSystem mirrorSystem) {
+ _sdkLibraries = mirrorSystem.libraries.values.where(
+ (each) => each.uri.scheme == 'dart');
+ _coreLibrary = new Library(_sdkLibraries.singleWhere((lib) =>
+ lib.uri.toString().startsWith('dart:core')));
+ }
+
+ markdown.Node fixReferenceWithScope(String name) => null;
+
+ /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
+ markdown.Node fixReference(String name) {
+ // Attempt the look up the whole name up in the scope.
+ String elementName = findElementInScope(name);
+ if (elementName != null) {
+ return new markdown.Element.text('a', elementName);
+ }
+ return _fixComplexReference(name);
}
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) =>
+ _findElementInScope(name, packagePrefix);
+
+ static determineLookupFunc(name) => name.contains('.') ?
+ dart2js_util.lookupQualifiedInScope :
+ (mirror, name) => mirror.lookupInScope(name);
+
// The qualified name (for URL purposes) and the file name are the same,
// of the form packageName/ClassName or packageName/ClassName.methodName.
// This defines both the URL and the directory structure.
- String get fileName => packagePrefix + ownerPrefix + name;
-
- Indexable get owningEntity => entityMap[owner];
+ String get fileName {
+ return packagePrefix + ownerPrefix + name;
+ }
- String get ownerPrefix => owningEntity == null ?
- (owner == null || owner.isEmpty ? '' : owner + '.') :
- owningEntity.qualifiedName + '.';
+ String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
String get packagePrefix => '';
@@ -727,9 +673,10 @@ class Indexable {
String get comment {
if (_comment != null) return _comment;
- _comment = _commentToHtml(mirror);
+
+ _comment = _commentToHtml();
if (_comment.isEmpty) {
- _mdnComment(this);
+ _comment = _mdnComment();
}
return _comment;
}
@@ -738,8 +685,54 @@ class Indexable {
String get name => mirror.simpleName;
- /// Qualified Name of the owner of this Indexable Item.
- String get owner => docName(mirror.owner);
+ MirrorBased get owner => new DummyMirror(mirror.owner);
+
+ /// Generates MDN comments from database.json.
+ String _mdnComment() {
+ //Check if MDN is loaded.
+ if (_mdn == null) {
+ // Reading in MDN related json file.
+ var root = _Generator._rootDirectory;
+ var mdnPath = path.join(root, 'utils/apidoc/mdn/database.json');
+ _mdn = JSON.decode(new File(mdnPath).readAsStringSync());
+ }
+ // TODO: refactor OOP
+ if (this is Library) return '';
+ var domAnnotation = this.annotations.firstWhere(
+ (e) => e.mirror.qualifiedName == 'metadata.DomName',
+ orElse: () => null);
+ if (domAnnotation == null) return '';
+ var domName = domAnnotation.parameters.single;
+ var parts = domName.split('.');
+ if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
+ if (parts.length == 1) return _mdnTypeComment(parts[0]);
+ }
+
+ /// Generates the MDN Comment for variables and method DOM elements.
+ String _mdnMemberComment(String type, String member) {
+ var mdnType = _mdn[type];
+ if (mdnType == null) return '';
+ var mdnMember = mdnType['members'].firstWhere((e) => e['name'] == member,
+ orElse: () => null);
+ if (mdnMember == null) return '';
+ if (mdnMember['help'] == null || mdnMember['help'] == '') return '';
+ if (mdnMember['url'] == null) return '';
+ return _htmlMdn(mdnMember['help'], mdnMember['url']);
+ }
+
+ /// Generates the MDN Comment for class DOM elements.
+ String _mdnTypeComment(String type) {
+ var mdnType = _mdn[type];
+ if (mdnType == null) return '';
+ if (mdnType['summary'] == null || mdnType['summary'] == "") return '';
+ if (mdnType['srcUrl'] == null) return '';
+ return _htmlMdn(mdnType['summary'], mdnType['srcUrl']);
+ }
+
+ String _htmlMdn(String content, String url) {
+ return '<div class="mdn">' + content.trim() + '<p class="mdn-note">'
+ '<a href="' + url.trim() + '">from Mdn</a></p></div>';
+ }
/// The type of this member to be used in index.txt.
String get typeName => '';
@@ -754,12 +747,7 @@ class Indexable {
return finalMap;
}
- /// Returns any documentation comments associated with a mirror with
- /// simple markdown converted to html.
- ///
- /// It's possible to have a comment that comes from one mirror applied to
- /// another, in the case of an inherited comment.
- String _commentToHtml(itemToDocument) {
+ String _getCommentText() {
String commentText;
mirror.metadata.forEach((metadata) {
if (metadata is CommentInstanceMirror) {
@@ -773,11 +761,24 @@ class Indexable {
}
}
});
+ return commentText;
+ }
- var linkResolver = (name) => fixReferenceWithScope(name, itemToDocument);
+ /// Returns any documentation comments associated with a mirror with
+ /// simple markdown converted to html.
+ ///
+ /// By default we resolve any comment references within our own scope.
+ /// However, if a method is inherited, we want the inherited comments, but
+ /// links to the subclasses's version of the methods.
+ String _commentToHtml([Indexable resolvingScope]) {
+ if (resolvingScope == null) resolvingScope = this;
+ var commentText = _getCommentText();
+ _unresolvedComment = commentText;
+
+ var linkResolver = (name) => resolvingScope.fixReferenceWithScope(name);
commentText = commentText == null ? '' :
markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver,
- inlineSyntaxes: markdownSyntaxes);
+ inlineSyntaxes: _MARKDOWN_SYNTAXES);
return commentText;
}
@@ -785,21 +786,16 @@ class Indexable {
/// The optional parameter [containingLibrary] is contains data for variables
/// defined at the top level of a library (potentially for exporting
/// purposes).
- Map<String, Variable> _createVariables(Map<String,
- VariableMirror> mirrorMap, [Library containingLibrary]) {
+ Map<String, Variable> _createVariables(Map<String, VariableMirror> mirrorMap,
+ Indexable owner) {
var data = {};
// TODO(janicejl): When map to map feature is created, replace the below
// with a filter. Issue(#9590).
mirrorMap.forEach((String mirrorName, VariableMirror mirror) {
- if (_includePrivate || !_isHidden(mirror)) {
- if (containingLibrary != null && mirror.owner.qualifiedName !=
- containingLibrary.mirror.qualifiedName) {
- entityMap[docName(mirror)] = new ExportedVariable(mirrorName, mirror,
- containingLibrary);
- } else {
- entityMap[docName(mirror)] = new Variable(mirrorName, mirror);
- }
- data[mirrorName] = entityMap[docName(mirror)];
+ if (_Generator._includePrivate || !_isHidden(mirror)) {
+ var variable = new Variable(mirrorName, mirror, owner);
+ entityMap[variable.docName] = variable;
+ data[mirrorName] = entityMap[variable.docName];
}
});
return data;
@@ -809,25 +805,25 @@ class Indexable {
/// The optional parameter [containingLibrary] is contains data for variables
/// defined at the top level of a library (potentially for exporting
/// purposes).
- MethodGroup _createMethods(Map<String, MethodMirror> mirrorMap,
- [Library containingLibrary]) {
- var group = new MethodGroup();
+ Map<String, Method> _createMethods(Map<String, MethodMirror> mirrorMap,
+ Indexable owner) {
+ var group = new Map<String, Method>();
mirrorMap.forEach((String mirrorName, MethodMirror mirror) {
- if (_includePrivate || !mirror.isPrivate) {
- group.addMethod(mirror, containingLibrary);
+ if (_Generator._includePrivate || !mirror.isPrivate) {
+ var method = new Method(mirror, owner);
+ entityMap[method.docName] = method;
+ group[mirror.simpleName] = method;
}
});
return group;
}
/// Returns a map of [Parameter] objects constructed from [mirrorList].
- Map<String, Parameter> _createParameters(List<ParameterMirror> mirrorList) {
+ Map<String, Parameter> _createParameters(List<ParameterMirror> mirrorList,
+ [Indexable owner]) {
var data = {};
mirrorList.forEach((ParameterMirror mirror) {
- data[mirror.simpleName] = new Parameter(mirror.simpleName,
- mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue,
- _createType(mirror.type), mirror.defaultValue,
- _createAnnotations(mirror));
+ data[mirror.simpleName] = new Parameter(mirror, owner);
});
return data;
}
@@ -836,51 +832,196 @@ class Indexable {
Map<String, Generic> _createGenerics(ClassMirror mirror) {
return new Map.fromIterable(mirror.typeVariables,
key: (e) => e.toString(),
- value: (e) => new Generic(e.toString(), e.upperBound.qualifiedName));
+ value: (e) => new Generic(e));
}
- /// Returns a single [Type] object constructed from the Method.returnType
- /// Type mirror.
- Type _createType(TypeMirror mirror) {
- return new Type(docName(mirror), _createTypeGenerics(mirror));
+ /// Return an informative [Object.toString] for debugging.
+ String toString() => "${super.toString()}(${name.toString()})";
+
+ /// Return a map representation of this type.
+ Map toMap() {}
+
+
+ /// A declaration is private if itself is private, or the owner is private.
+ // Issue(12202) - A declaration is public even if it's owner is private.
+ bool _isHidden(DeclarationMirror mirror) {
+ if (mirror is LibraryMirror) {
+ return _isLibraryPrivate(mirror);
+ } else if (mirror.owner is LibraryMirror) {
+ return (mirror.isPrivate || _isLibraryPrivate(mirror.owner)
+ || mirror.isNameSynthetic);
+ } else {
+ return (mirror.isPrivate || _isHidden(mirror.owner)
+ || owner.mirror.isNameSynthetic);
+ }
}
- /// Returns a list of [Type] objects constructed from TypeMirrors.
- List<Type> _createTypeGenerics(TypeMirror mirror) {
- if (mirror is ClassMirror && !mirror.isTypedef) {
- var innerList = [];
- mirror.typeArguments.forEach((e) {
- innerList.add(new Type(docName(e), _createTypeGenerics(e)));
- });
- return innerList;
+ /// Returns true if a library name starts with an underscore, and false
+ /// otherwise.
+ ///
+ /// An example that starts with _ is _js_helper.
+ /// An example that contains ._ is dart._collection.dev
+ // This is because LibraryMirror.isPrivate returns `false` all the time.
+ bool _isLibraryPrivate(LibraryMirror mirror) {
+ var sdkLibrary = LIBRARIES[mirror.simpleName];
+ if (sdkLibrary != null) {
+ return !sdkLibrary.documented;
+ } else if (mirror.simpleName.startsWith('_') ||
+ mirror.simpleName.contains('._')) {
+ return true;
}
- return [];
+ return false;
}
- /// Returns a list of meta annotations assocated with a mirror.
- List<Annotation> _createAnnotations(DeclarationMirror mirror) {
- var annotationMirrors = mirror.metadata.where((e) =>
- e is dart2js.Dart2JsConstructedConstantMirror);
- var annotations = [];
- annotationMirrors.forEach((annotation) {
- var parameterList = annotation.type.variables.values
- .where((e) => e.isFinal)
- .map((e) => annotation.getField(e.simpleName).reflectee)
- .where((e) => e != null)
- .toList();
- if (!skippedAnnotations.contains(docName(annotation.type))) {
- annotations.add(new Annotation(docName(annotation.type),
- parameterList));
+ ////// Top level resolution functions
+ /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
+ static markdown.Node globalFixReference(String name) {
+ // Attempt the look up the whole name up in the scope.
+ String elementName = _findElementInScope(name, '');
+ if (elementName != null) {
+ return new markdown.Element.text('a', elementName);
+ }
+ return _fixComplexReference(name);
+ }
+
+ /// This is a more complex reference. Try to break up if its of the form A<B>
+ /// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"),
+ /// or of the form A<B>. Note: unlike other the other markdown-style links,
+ /// all text inside the square brackets is treated as part of the link (aka
+ /// the * is interpreted literally as a *, not as a indicator for bold <em>.
+ ///
+ /// Example: [foo&lt;_bar_>] will produce
+ /// <a>resolvedFoo</a>&lt;<a>resolved_bar_</a>> rather than an italicized
+ /// version of resolvedBar.
+ static markdown.Node _fixComplexReference(String name) {
+ // Parse into multiple elements we can try to resolve.
+ var tokens = _tokenizeComplexReference(name);
+
+ // Produce an html representation of our elements. Group unresolved and
+ // plain text are grouped into "link" elements so they display as code.
+ final textElements = [' ', ',', '>', _LESS_THAN];
+ var accumulatedHtml = '';
+
+ for (var token in tokens) {
+ bool added = false;
+ if (!textElements.contains(token)) {
+ String elementName = _findElementInScope(token, '');
+ if (elementName != null) {
+ accumulatedHtml += markdown.renderToHtml([new markdown.Element.text(
+ 'a', elementName)]);
+ added = true;
+ }
+ }
+ if (!added) {
+ accumulatedHtml += token;
+ }
+ }
+ return new markdown.Text(accumulatedHtml);
+ }
+
+
+ // HTML escaped version of '<' character.
+ static final _LESS_THAN = '&lt;';
+
+ /// Chunk the provided name into individual parts to be resolved. We take a
+ /// simplistic approach to chunking, though, we break at " ", ",", "&lt;"
+ /// and ">". All other characters are grouped into the name to be resolved.
+ /// As a result, these characters will all be treated as part of the item to
+ /// be resolved (aka the * is interpreted literally as a *, not as an
+ /// indicator for bold <em>.
+ static List<String> _tokenizeComplexReference(String name) {
+ var tokens = [];
+ var append = false;
+ var index = 0;
+ while(index < name.length) {
+ if (name.indexOf(_LESS_THAN, index) == index) {
+ tokens.add(_LESS_THAN);
+ append = false;
+ index += _LESS_THAN.length;
+ } else if (name[index] == ' ' || name[index] == ',' ||
+ name[index] == '>') {
+ tokens.add(name[index]);
+ append = false;
+ index++;
+ } else {
+ if (append) {
+ tokens[tokens.length - 1] = tokens.last + name[index];
+ } else {
+ tokens.add(name[index]);
+ append = true;
+ }
+ index++;
+ }
+ }
+ return tokens;
+ }
+
+ static String _findElementInScope(String name, String packagePrefix) {
+ var lookupFunc = determineLookupFunc(name);
+ // Look in the dart core library scope.
+ var coreScope = _coreLibrary == null? null :
+ lookupFunc(_coreLibrary.mirror, name);
+ if (coreScope != null) return packagePrefix + _coreLibrary.docName;
+
+ // If it's a reference that starts with a another library name, then it
+ // looks for a match of that library name in the other sdk libraries.
+ if(name.contains('.')) {
+ var index = name.indexOf('.');
+ var libraryName = name.substring(0, index);
+ var remainingName = name.substring(index + 1);
+ foundLibraryName(library) => library.uri.pathSegments[0] == libraryName;
+
+ if (_sdkLibraries.any(foundLibraryName)) {
+ var library = _sdkLibraries.singleWhere(foundLibraryName);
+ // Look to see if it's a fully qualified library name.
+ var scope = determineLookupFunc(remainingName)(library, remainingName);
+ if (scope != null) {
+ var result = getDocgenObject(scope);
+ if (result is DummyMirror) {
+ return packagePrefix + result.docName;
+ } else {
+ return result.packagePrefix + result.docName;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ Map expandMethodMap(Map<String, Method> mapToExpand) => {
+ 'setters': recurseMap(_filterMap(new Map(), mapToExpand,
+ (key, val) => val.mirror.isSetter)),
+ 'getters': recurseMap(_filterMap(new Map(), mapToExpand,
+ (key, val) => val.mirror.isGetter)),
+ 'constructors': recurseMap(_filterMap(new Map(), mapToExpand,
+ (key, val) => val.mirror.isConstructor)),
+ 'operators': recurseMap(_filterMap(new Map(), mapToExpand,
+ (key, val) => val.mirror.isOperator)),
+ 'methods': recurseMap(_filterMap(new Map(), mapToExpand,
+ (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
+ };
+
+ /// Transforms the map by calling toMap on each value in it.
+ Map recurseMap(Map inputMap) {
+ var outputMap = {};
+ inputMap.forEach((key, value) {
+ if (value is Map) {
+ outputMap[key] = recurseMap(value);
+ } else {
+ outputMap[key] = value.toMap();
}
});
- return annotations;
+ return outputMap;
}
- /// Return an informative [Object.toString] for debugging.
- String toString() => "${super.toString()}(${name.toString()})";
+ Map _filterMap(exported, map, test) {
+ map.forEach((key, value) {
+ if (test(key, value)) exported[key] = value;
+ });
+ return exported;
+ }
- /// Return a map representation of this type.
- Map toMap() {}
+ bool get _isVisible => _Generator._includePrivate || !isPrivate;
}
/// A class containing contents of a Dart library.
@@ -890,36 +1031,125 @@ class Library extends Indexable {
Map<String, Variable> variables;
/// Top-level functions in the library.
- MethodGroup functions;
+ Map<String, Method> functions;
- /// Classes defined within the library
- ClassGroup classes;
+ Map<String, Class> classes = {};
+ Map<String, Typedef> typedefs = {};
+ Map<String, Class> errors = {};
String packageName = '';
bool hasBeenCheckedForPackage = false;
String packageIntro;
- Map<String, Exported> _exportedMembers;
+ /// Returns the [Library] for the given [mirror] if it has already been
+ /// created, else creates it.
+ factory Library(LibraryMirror mirror) {
+ var library = getDocgenObject(mirror);
+ if (library is DummyMirror) {
+ library = new Library._(mirror);
+ }
+ return library;
+ }
- Library(LibraryMirror libraryMirror) : super(libraryMirror) {
+ Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
var exported = _calcExportedItems(libraryMirror);
- _createClasses(exported['classes']..addAll(libraryMirror.classes));
- this.functions = _createMethods(
- exported['methods']..addAll(libraryMirror.functions), this);
- this.variables = _createVariables(
- exported['variables']..addAll(libraryMirror.variables), this);
-
- var exportedVariables = {};
- variables.forEach((key, value) {
- if (value is ExportedVariable) {
- exportedVariables[key] = value;
- }
+ var exportedClasses = exported['classes']..addAll(libraryMirror.classes);
+ _findPackage(mirror);
+ classes = {};
+ typedefs = {};
+ errors = {};
+ exportedClasses.forEach((String mirrorName, ClassMirror classMirror) {
+ if (classMirror.isTypedef) {
+ // This is actually a Dart2jsTypedefMirror, and it does define value,
+ // but we don't have visibility to that type.
+ var mirror = classMirror;
+ if (_Generator._includePrivate || !mirror.isPrivate) {
+ entityMap[getDocgenObject(mirror).docName] =
+ new Typedef(mirror, this);
+ typedefs[mirror.simpleName] =
+ entityMap[getDocgenObject(mirror).docName];
+ }
+ } else {
+ var clazz = new Class(classMirror, this);
+
+ if (clazz.isError()) {
+ errors[classMirror.simpleName] = clazz;
+ } else if (classMirror.isClass) {
+ classes[classMirror.simpleName] = clazz;
+ } else {
+ throw new ArgumentError(
+ '${classMirror.simpleName} - no class type match. ');
+ }
+ }
});
- _exportedMembers = new Map.from(this.classes.exported)
- ..addAll(this.functions.exported)
- ..addAll(exportedVariables);
+ this.functions = _createMethods(exported['methods']
+ ..addAll(libraryMirror.functions), this);
+ this.variables = _createVariables(exported['variables']
+ ..addAll(libraryMirror.variables), this);
+ }
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = Indexable.determineLookupFunc(name);
+ var libraryScope = lookupFunc(mirror, name);
+ if (libraryScope != null) {
+ var result = getDocgenObject(libraryScope, this);
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
+ return super.findElementInScope(name);
}
+ /// For a library's [mirror], determine the name of the package (if any) we
+ /// believe it came from (because of its file URI).
+ ///
+ /// If no package could be determined, we return an empty string.
+ String _findPackage(LibraryMirror mirror) {
+ if (mirror == null) return '';
+ if (hasBeenCheckedForPackage) return packageName;
+ hasBeenCheckedForPackage = true;
+ if (mirror.uri.scheme != 'file') return '';
+ var filePath = mirror.uri.toFilePath();
+ // We assume that we are documenting only libraries under package/lib
+ var rootdir = path.dirname((path.dirname(filePath)));
+ var pubspec = path.join(rootdir, 'pubspec.yaml');
+ packageName = _packageName(pubspec);
+ // Associate the package readme with all the libraries. This is a bit
+ // wasteful, but easier than trying to figure out which partial match
+ // is best.
+ packageIntro = _packageIntro(rootdir);
+ return packageName;
+ }
+
+ String _packageIntro(packageDir) {
+ var dir = new Directory(packageDir);
+ var files = dir.listSync();
+ var readmes = files.where((FileSystemEntity each) => (each is File &&
+ each.path.substring(packageDir.length + 1, each.path.length)
+ .startsWith('README'))).toList();
+ if (readmes.isEmpty) return '';
+ // If there are multiples, pick the shortest name.
+ readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
+ var readme = readmes.first;
+ var linkResolver = (name) => Indexable.globalFixReference(name);
+ var contents = markdown.markdownToHtml(readme
+ .readAsStringSync(), linkResolver: linkResolver,
+ inlineSyntaxes: _MARKDOWN_SYNTAXES);
+ return contents;
+ }
+
+ /// Read a pubspec and return the library name.
+ String _packageName(String pubspecName) {
+ File pubspec = new File(pubspecName);
+ if (!pubspec.existsSync()) return '';
+ var contents = pubspec.readAsStringSync();
+ var spec = loadYaml(contents);
+ return spec["name"];
+ }
+
+ markdown.Node fixReferenceWithScope(String name) => fixReference(name);
+
String get packagePrefix => packageName == null || packageName.isEmpty ?
'' : '$packageName/';
@@ -932,17 +1162,9 @@ class Library extends Indexable {
return basic;
}
- String get owner => '';
-
- String get name => docName(mirror);
+ String get name => docName;
- /// Set our classes field with error, typedef and regular classes.
- void _createClasses(Map<String, ClassMirror> mirrorMap) {
- this.classes = new ClassGroup();
- mirrorMap.forEach((String mirrorName, ClassMirror mirror) {
- this.classes.addClass(mirror, this);
- });
- }
+ String get docName => mirror.qualifiedName.replaceAll('.','-');
/// For the given library determine what items (if any) are exported.
///
@@ -1006,14 +1228,25 @@ class Library extends Indexable {
return exports;
}
+ /// Checks if the given name is a key for any of the Class Maps.
+ bool containsKey(String name) {
+ return classes.containsKey(name) || errors.containsKey(name);
+ }
+
/// Generates a map describing the [Library] object.
Map toMap() => {
'name': name,
'qualifiedName': qualifiedName,
'comment': comment,
'variables': recurseMap(variables),
- 'functions': functions.toMap(),
- 'classes': classes.toMap(),
+ 'functions': expandMethodMap(functions),
+ 'classes': {
+ 'class': classes.values.where((c) => c._isVisible)
+ .map((e) => e.previewMap).toList(),
+ 'typedef': recurseMap(typedefs),
+ 'error': errors.values.where((e) => e._isVisible)
+ .map((e) => e.previewMap).toList()
+ },
'packageName': packageName,
'packageIntro' : packageIntro
};
@@ -1034,13 +1267,12 @@ class Class extends Indexable implements Comparable {
Map<String, Variable> variables;
/// Inherited variables in the class.
- Map<String, Variable> inheritedVariables = {};
+ Map<String, Variable> inheritedVariables;
/// Methods in the class.
- MethodGroup methods;
+ Map<String, Method> methods;
- /// Inherited methods in the class.
- MethodGroup inheritedMethods = new MethodGroup();
+ Map<String, Method> inheritedMethods;
/// Generic infomation about the class.
Map<String, Generic> generics;
@@ -1054,33 +1286,58 @@ class Class extends Indexable implements Comparable {
/// Make sure that we don't check for inherited comments more than once.
bool _commentsEnsured = false;
+ Indexable owner;
+
/// Returns the [Class] for the given [mirror] if it has already been created,
/// else creates it.
- factory Class(ClassMirror mirror) {
- var clazz = entityMap[docName(mirror)];
- if (clazz == null) {
- clazz = new Class._(mirror);
- entityMap[docName(mirror)] = clazz;
+ factory Class(ClassMirror mirror, Library owner) {
+ var clazz = getDocgenObject(mirror, owner);
+ if (clazz is DummyMirror) {
+ clazz = new Class._(mirror, owner);
+ entityMap[clazz.docName] = clazz;
}
return clazz;
}
- Class._(ClassMirror classMirror) : super(classMirror) {
- var superclass = classMirror.superclass != null ?
- new Class(classMirror.superclass) : null;
- var interfaces = classMirror.superinterfaces.map(
- (interface) => new Class(interface));
+ /// Called when we are constructing a superclass or interface class, but it
+ /// is not known if it belongs to the same owner as the original class. In
+ /// this case, we create an object whose owner is what the original mirror
+ /// says it is.
+ factory Class._possiblyDifferentOwner(ClassMirror mirror,
+ Library originalOwner) {
+ if (mirror.owner is LibraryMirror) {
+ var realOwner = getDocgenObject(mirror.owner);
+ if (realOwner is Library) {
+ return new Class(mirror, realOwner);
+ } else {
+ return new Class(mirror, originalOwner);
+ }
+ } else {
+ return new Class(mirror, originalOwner);
+ }
+ }
+
+ Class._(ClassMirror classMirror, this.owner) : super(classMirror) {
+ inheritedVariables = {};
- this.superclass = superclass;
- this.interfaces = interfaces.toList();
- this.variables = _createVariables(classMirror.variables);
- this.methods = _createMethods(classMirror.methods);
- this.annotations = _createAnnotations(classMirror);
- this.generics = _createGenerics(classMirror);
- this.isAbstract = classMirror.isAbstract;
+ // The reason we do this madness is the superclass and interface owners may
+ // not be this class's owner!! Example: BaseClient in http pkg.
+ var superinterfaces = classMirror.superinterfaces.map(
+ (interface) => new Class._possiblyDifferentOwner(interface, owner));
+ this.superclass = classMirror.superclass == null? null :
+ new Class._possiblyDifferentOwner(classMirror.superclass, owner);
- // Tell all superclasses that you are a subclass.
- if (!classMirror.isNameSynthetic && _isVisible(this)) {
+ interfaces = superinterfaces.toList();
+ variables = _createVariables(classMirror.variables, this);
+ methods = _createMethods(classMirror.methods, this);
+ annotations = _createAnnotations(classMirror, this);
+ generics = _createGenerics(classMirror);
+ isAbstract = classMirror.isAbstract;
+ inheritedMethods = new Map<String, Method>();
+
+ // Tell all superclasses that you are a subclass, unless you are not
+ // visible or an intermediary mixin class.
+ if (!classMirror.isNameSynthetic && _isVisible) {
parentChain().forEach((parentClass) {
parentClass.addSubclass(this);
});
@@ -1090,6 +1347,35 @@ class Class extends Indexable implements Comparable {
interfaces.forEach((interface) => addInherited(interface));
}
+ String get packagePrefix => owner.packagePrefix;
+
+ String _lookupInClassAndSuperclasses(String name) {
+ var lookupFunc = Indexable.determineLookupFunc(name);
+ var classScope = this;
+ while (classScope != null) {
+ var classFunc = lookupFunc(classScope.mirror, name);
+ if (classFunc != null) {
+ return packagePrefix + getDocgenObject(classFunc, owner).docName;
+ }
+ classScope = classScope.superclass;
+ }
+ return null;
+ }
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = Indexable.determineLookupFunc(name);
+ var result = _lookupInClassAndSuperclasses(name);
+ if (result != null) {
+ return result;
+ }
+ result = owner.findElementInScope(name);
+ return result == null ? super.findElementInScope(name) : result;
+ }
+
+ markdown.Node fixReferenceWithScope(String name) => fixReference(name);
+
String get typeName => 'class';
/// Returns a list of all the parent classes.
@@ -1104,16 +1390,46 @@ class Class extends Indexable implements Comparable {
/// the superclass.
void addInherited(Class superclass) {
inheritedVariables.addAll(superclass.inheritedVariables);
- inheritedVariables.addAll(_filterStatics(superclass.variables));
- inheritedMethods.addInherited(superclass);
+ inheritedVariables.addAll(_allButStatics(superclass.variables));
+ addInheritedMethod(superclass, this);
+ }
+
+ /** [newParent] refers to the actual class is currently using these methods.
+ * which may be different because with the mirror system, we only point to the
+ * original canonical superclasse's method.
+ */
+ void addInheritedMethod(Class parent, Class newParent) {
+ parent.inheritedMethods.forEach((name, method) {
+ if(!method.isConstructor){
+ inheritedMethods[name] = new Method(method.mirror, newParent, method);
+ }}
+ );
+ _allButStatics(parent.methods).forEach((name, method) {
+ if (!method.isConstructor) {
+ inheritedMethods[name] = new Method(method.mirror, newParent, method);
+ }}
+ );
+ }
+
+ /// Remove statics from the map of inherited items before adding them.
+ Map _allButStatics(Map items) {
+ var result = {};
+ items.forEach((name, item) {
+ if (!item.isStatic) {
+ result[name] = item;
+ }
+ });
+ return result;
}
/// Add the subclass to the class.
///
- /// If [this] is private, it will add the subclass to the list of subclasses
- /// in the superclasses.
+ /// If [this] is private (or an intermediary mixin class), it will add the
+ /// subclass to the list of subclasses in the superclasses.
void addSubclass(Class subclass) {
- if (!_includePrivate && isPrivate) {
+ if (docName == 'dart-core.Object') return;
+
+ if (!_Generator._includePrivate && isPrivate || mirror.isNameSynthetic) {
if (superclass != null) superclass.addSubclass(subclass);
interfaces.forEach((interface) {
interface.addSubclass(subclass);
@@ -1135,40 +1451,23 @@ class Class extends Indexable implements Comparable {
return superclass.isError();
}
- /// Check that the class exists in the owner library.
- ///
- /// If it does not exist in the owner library, it is a mixin applciation and
- /// should be removed.
- void updateLinksAndRemoveIntermediaryClasses() {
- var library = entityMap[owner];
- if (library != null) {
- if (!library.classes.containsKey(name) && mirror.isNameSynthetic) {
- // In the mixin case, remove the intermediary classes.
- this.isPrivate = true;
- // Since we are now making the mixin a private class, make all elements
- // with the mixin as an owner private too.
- entityMap.values.where((e) => e.owner == qualifiedName).forEach(
- (element) => element.isPrivate = true);
- // Move the subclass up to the next public superclass
- subclasses.forEach((subclass) => addSubclass(subclass));
- } else {
- // It is an exported item. Loop through each of the exported types,
- // and tell them to update their links, given these other exported
- // names within the library.
- for (Exported member in library._exportedMembers.values) {
- member.updateExports(library._exportedMembers);
- }
- }
- }
- }
-
/// Makes sure that all methods with inherited equivalents have comments.
void ensureComments() {
if (_commentsEnsured) return;
_commentsEnsured = true;
+ if (superclass != null) superclass.ensureComments();
inheritedMethods.forEach((qualifiedName, inheritedMethod) {
var method = methods[qualifiedName];
- if (method != null) method.ensureCommentFor(inheritedMethod);
+ if (method != null) {
+ // if we have overwritten this method in this class, we still provide
+ // the opportunity to inherit the comments.
+ method.ensureCommentFor(inheritedMethod);
+ }
+ });
+ // we need to populate the comments for all methods. so that the subclasses
+ // can get for their inherited versions the comments.
+ methods.forEach((qualifiedName, method) {
+ if (!method.mirror.isConstructor) method.ensureCommentFor(method);
});
}
@@ -1176,7 +1475,7 @@ class Class extends Indexable implements Comparable {
/// superclass of the private superclass.
String validSuperclass() {
if (superclass == null) return 'dart.core.Object';
- if (_isVisible(superclass)) return superclass.qualifiedName;
+ if (superclass._isVisible) return superclass.qualifiedName;
return superclass.validSuperclass();
}
@@ -1187,14 +1486,14 @@ class Class extends Indexable implements Comparable {
'comment': comment,
'isAbstract' : isAbstract,
'superclass': validSuperclass(),
- 'implements': interfaces.where(_isVisible)
+ 'implements': interfaces.where((i) => i._isVisible)
.map((e) => e.qualifiedName).toList(),
'subclass': (subclasses.toList()..sort())
.map((x) => x.qualifiedName).toList(),
'variables': recurseMap(variables),
'inheritedVariables': recurseMap(inheritedVariables),
- 'methods': methods.toMap(),
- 'inheritedMethods': inheritedMethods.toMap(),
+ 'methods': expandMethodMap(methods),
+ 'inheritedMethods': expandMethodMap(inheritedMethods),
'annotations': annotations.map((a) => a.toMap()).toList(),
'generics': recurseMap(generics)
};
@@ -1202,113 +1501,6 @@ class Class extends Indexable implements Comparable {
int compareTo(aClass) => name.compareTo(aClass.name);
}
-abstract class Exported {
- void updateExports(Map<String, Indexable> libraryExports);
-}
-
-Map _filterMap(exported, map, test) {
- map.forEach((key, value) {
- if (test(value)) exported[key] = value;
- });
- return exported;
-}
-
-class ExportedClass extends Class implements Exported {
- Class _originalClass;
- Library _exportingLibrary;
-
- ExportedClass(ClassMirror originalClass, Library this._exportingLibrary) :
- super._(originalClass) {
- _originalClass = new Class(originalClass);
- }
-
- // The qualified name (for URL purposes) and the file name are the same,
- // of the form packageName/ClassName or packageName/ClassName.methodName.
- // This defines both the URL and the directory structure.
- String get fileName => path.join(_exportingLibrary.packageName,
- _exportingLibrary.mirror.qualifiedName + '.' + _originalClass.name);
-
- void updateExports(Map<String, Indexable> libraryExports) {
- // TODO(efortuna): If this class points to another exported class or type
- // of some sort, then that reference needs to be updated here.
- /* these need to be updated:
- 'comment': comment,
- 'superclass': validSuperclass(),
- 'implements': interfaces.where(_isVisible)
- .map((e) => e.qualifiedName).toList(),
- 'subclass': (subclasses.toList()..sort())
- .map((x) => x.qualifiedName).toList(),
- 'variables': recurseMap(variables),
- 'inheritedVariables': recurseMap(inheritedVariables),
- 'methods': methods.toMap(),
- 'inheritedMethods': inheritedMethods.toMap(),
- 'annotations': annotations.map((a) => a.toMap()).toList(),
- 'generics': recurseMap(generics)
- */
- }
-}
-
-/// A container to categorize classes into the following groups: abstract
-/// classes, regular classes, typedefs, and errors.
-class ClassGroup {
- Map<String, Class> classes = {};
- Map<String, Typedef> typedefs = {};
- Map<String, Class> errors = {};
-
- Map<String, Exported> get exported {
- var exported = _filterMap({}, classes, (value) => value is ExportedClass);
- // TODO(efortuna): The line below needs updating.
- exported = _filterMap(exported, typedefs,
- (value) => value is ExportedClass);
- exported = _filterMap(exported, errors,
- (value) => value is ExportedClass);
- return exported;
- }
-
- void addClass(ClassMirror classMirror, Library containingLibrary) {
- if (classMirror.isTypedef) {
- // This is actually a Dart2jsTypedefMirror, and it does define value,
- // but we don't have visibility to that type.
- var mirror = classMirror;
- if (_includePrivate || !mirror.isPrivate) {
- entityMap[docName(mirror)] = new Typedef(mirror);
- typedefs[mirror.simpleName] = entityMap[docName(mirror)];
- }
- } else {
- var clazz = new Class(classMirror);
-
- if (classMirror.library.qualifiedName !=
- containingLibrary.mirror.qualifiedName) {
- var exportedClass = new ExportedClass(classMirror, containingLibrary);
- entityMap[clazz.fileName] = exportedClass;
- clazz = exportedClass;
- }
-
- if (clazz.isError()) {
- errors[classMirror.simpleName] = clazz;
- } else if (classMirror.isClass) {
- classes[classMirror.simpleName] = clazz;
- } else {
- throw new ArgumentError(
- '${classMirror.simpleName} - no class type match. ');
- }
- }
- }
-
- /// Checks if the given name is a key for any of the Class Maps.
- bool containsKey(String name) {
- return classes.containsKey(name) || errors.containsKey(name);
- }
-
- Map toMap() => {
- 'class': classes.values.where(_isVisible)
- .map((e) => e.previewMap).toList(),
- 'typedef': recurseMap(typedefs),
- 'error': errors.values.where(_isVisible)
- .map((e) => e.previewMap).toList()
- };
-}
-
class Typedef extends Indexable {
String returnType;
@@ -1320,11 +1512,22 @@ class Typedef extends Indexable {
/// List of the meta annotations on the typedef.
List<Annotation> annotations;
- Typedef(mirror) : super(mirror) {
- this.returnType = docName(mirror.value.returnType);
- this.generics = _createGenerics(mirror);
- this.parameters = _createParameters(mirror.value.parameters);
- this.annotations = _createAnnotations(mirror);
+ /// Returns the [Library] for the given [mirror] if it has already been
+ /// created, else creates it.
+ factory Typedef(TypedefMirror mirror, Library owningLibrary) {
+ var aTypedef = getDocgenObject(mirror, owningLibrary);
+ if (aTypedef is DummyMirror) {
+ aTypedef = new Typedef._(mirror, owningLibrary);
+ }
+ return aTypedef;
+ }
+
+ Typedef._(TypedefMirror mirror, Library owningLibrary) : super(mirror) {
+ owner = owningLibrary;
+ returnType = getDocgenObject(mirror.value.returnType).docName;
+ generics = _createGenerics(mirror);
+ parameters = _createParameters(mirror.value.parameters);
+ annotations = _createAnnotations(mirror, this);
}
Map toMap() => {
@@ -1348,16 +1551,27 @@ class Variable extends Indexable {
bool isConst;
Type type;
String _variableName;
+ Indexable owner;
/// List of the meta annotations on the variable.
List<Annotation> annotations;
- Variable(this._variableName, VariableMirror mirror) : super(mirror) {
- this.isFinal = mirror.isFinal;
- this.isStatic = mirror.isStatic;
- this.isConst = mirror.isConst;
- this.type = _createType(mirror.type);
- this.annotations = _createAnnotations(mirror);
+ factory Variable(String variableName, VariableMirror mirror,
+ Indexable owner) {
+ var variable = getDocgenObject(mirror);
+ if (variable is DummyMirror) {
+ return new Variable._(variableName, mirror, owner);
+ }
+ return variable;
+ }
+
+ Variable._(this._variableName, VariableMirror mirror, this.owner) :
+ super(mirror) {
+ isFinal = mirror.isFinal;
+ isStatic = mirror.isStatic;
+ isConst = mirror.isConst;
+ type = new Type(mirror.type, _getOwningLibrary(owner));
+ annotations = _createAnnotations(mirror, _getOwningLibrary(owner));
}
String get name => _variableName;
@@ -1374,35 +1588,36 @@ class Variable extends Indexable {
'annotations': annotations.map((a) => a.toMap()).toList()
};
+ String get packagePrefix => owner.packagePrefix;
+
String get typeName => 'property';
get comment {
if (_comment != null) return _comment;
- var owningClass = owningEntity;
- if (owningClass is Class) {
- owningClass.ensureComments();
+ if (owner is Class) {
+ (owner as Class).ensureComments();
}
return super.comment;
}
-}
-class ExportedVariable extends Variable implements Exported {
- Library _exportingLibrary;
+ markdown.Node fixReferenceWithScope(String name) => fixReference(name);
- ExportedVariable(String variableName, VariableMirror originalVariable,
- Library this._exportingLibrary) : super(variableName, originalVariable);
-
- String get fileName => path.join(_exportingLibrary.packageName,
- _exportingLibrary.mirror.qualifiedName + '.' + super.name);
+ String findElementInScope(String name) {
+ var lookupFunc = Indexable.determineLookupFunc(name);
+ var result = lookupFunc(mirror, name);
+ if (result != null) {
+ result = getDocgenObject(result);
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
- void updateExports(Map<String, Indexable> libraryExports) {
- // TODO(efortuna): if this class points to another exported class or type
- // of some sort, then that reference needs to be updated here.
- /* these need to be updated:
- 'comment': comment,
- 'type': new List.filled(1, type.toMap()),
- 'annotations': annotations.map((a) => a.toMap()).toList()
- */
+ if (owner != null) {
+ var result = owner.findElementInScope(name);
+ if (result != null) {
+ return result;
+ }
+ }
+ return super.findElementInScope(name);
}
}
@@ -1420,6 +1635,7 @@ class Method extends Indexable {
bool isSetter;
bool isOperator;
Type returnType;
+ Method methodInheritedFrom;
/// Qualified name to state where the comment is inherited from.
String commentInheritedFrom = "";
@@ -1427,23 +1643,78 @@ class Method extends Indexable {
/// List of the meta annotations on the method.
List<Annotation> annotations;
- Method(MethodMirror mirror) : super(mirror) {
+ Indexable owner;
+
+ factory Method(MethodMirror mirror, Indexable owner,
+ [Method methodInheritedFrom]) {
+ var method = getDocgenObject(mirror, owner);
+ if (method is DummyMirror) {
+ method = new Method._(mirror, owner, methodInheritedFrom);
+ }
+ return method;
+ }
+
+ Method._(MethodMirror mirror, this.owner, this.methodInheritedFrom)
+ : super(mirror) {
this.isStatic = mirror.isStatic;
this.isAbstract = mirror.isAbstract;
this.isConst = mirror.isConstConstructor;
- this.returnType = _createType(mirror.returnType);
- this.parameters = _createParameters(mirror.parameters);
- this.annotations = _createAnnotations(mirror);
+ this.returnType = new Type(mirror.returnType, _getOwningLibrary(owner));
+ this.parameters = _createParameters(mirror.parameters, owner);
+ this.annotations = _createAnnotations(mirror, _getOwningLibrary(owner));
this.isConstructor = mirror.isConstructor;
this.isGetter = mirror.isGetter;
this.isSetter = mirror.isSetter;
this.isOperator = mirror.isOperator;
}
+ String get packagePrefix => owner.packagePrefix;
+
+ markdown.Node fixReferenceWithScope(String name) => fixReference(name);
+
+ /// Look for the specified name starting with the current member, and
+ /// progressively working outward to the current library scope.
+ String findElementInScope(String name) {
+ var lookupFunc = Indexable.determineLookupFunc(name);
+
+ var memberScope = lookupFunc(this.mirror, name);
+ if (memberScope != null) {
+ // do we check for a dummy mirror returned here and look up with an owner
+ // higher ooooor in getDocgenObject do we include more things in our
+ // lookup
+ var result = getDocgenObject(memberScope, owner);
+ if (result is DummyMirror && owner.owner != null
+ && owner.owner is! DummyMirror) {
+ var aresult = getDocgenObject(memberScope, owner.owner);
+ if (aresult is! DummyMirror) result = aresult;
+ }
+ if (result is DummyMirror) return packagePrefix + result.docName;
+ return result.packagePrefix + result.docName;
+ }
+
+ if (owner != null) {
+ var result = owner.findElementInScope(name);
+ if (result != null) return result;
+ }
+ return super.findElementInScope(name);
+ }
+
+ String get docName {
+ if ((mirror as MethodMirror).isConstructor) {
+ // We name constructors specially -- including the class name again and a
+ // "-" to separate the constructor from its name (if any).
+ return '${mirror.owner.simpleName.replaceAll(".", "_")}.'
+ '${mirror.owner.simpleName}-${mirror.simpleName}';
+ }
+ return super.docName;
+ }
+
/// Makes sure that the method with an inherited equivalent have comments.
void ensureCommentFor(Method inheritedMethod) {
if (comment.isNotEmpty) return;
- comment = inheritedMethod._commentToHtml(mirror);
+
+ comment = inheritedMethod._commentToHtml(this);
+ _unresolvedComment = inheritedMethod._unresolvedComment;
commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
inheritedMethod.qualifiedName : inheritedMethod.commentInheritedFrom;
}
@@ -1453,7 +1724,11 @@ class Method extends Indexable {
'name': name,
'qualifiedName': qualifiedName,
'comment': comment,
- 'commentFrom': commentInheritedFrom,
+ 'commentFrom': (methodInheritedFrom != null &&
+ commentInheritedFrom == methodInheritedFrom.docName ? ''
+ : commentInheritedFrom),
+ 'inheritedFrom': (methodInheritedFrom == null? '' :
+ methodInheritedFrom.docName),
'static': isStatic.toString(),
'abstract': isAbstract.toString(),
'constant': isConst.toString(),
@@ -1468,127 +1743,30 @@ class Method extends Indexable {
get comment {
if (_comment != null) return _comment;
- var owningClass = owningEntity;
- if (owningClass is Class) {
- owningClass.ensureComments();
+ if (owner is Class) {
+ (owner as Class).ensureComments();
}
- return super.comment;
- }
-}
-
-class ExportedMethod extends Method implements Exported {
- Library _exportingLibrary;
-
- ExportedMethod(MethodMirror originalMethod, Library this._exportingLibrary) :
- super(originalMethod);
-
- // TODO(efortuna): Refactor this code so the exported items can share this
- // behavior.
- String get fileName => path.join(_exportingLibrary.packageName,
- _exportingLibrary.mirror.qualifiedName + '.' + super.name);
-
- void updateExports(Map<String, Indexable> libraryExports) {
- // TODO(efortuna): if this class points to another exported class or type
- // of some sort, then that reference needs to be updated here.
- /* these need to be updated:
- 'qualifiedName': qualifiedName,
- 'comment': comment,
- 'commentFrom': commentInheritedFrom,
- 'return': new List.filled(1, returnType.toMap()),
- 'parameters': recurseMap(parameters),
- 'annotations': annotations.map((a) => a.toMap()).toList()
- */
- }
-}
-
-
-
-/// A container to categorize methods into the following groups: setters,
-/// getters, constructors, operators, regular methods.
-class MethodGroup {
- Map<String, Method> setters = {};
- Map<String, Method> getters = {};
- Map<String, Method> constructors = {};
- Map<String, Method> operators = {};
- Map<String, Method> regularMethods = {};
-
- Map<String, Exported> get exported {
- var exported = {};
- for (Map<String, Method> group in [setters, getters, constructors,
- operators, regularMethods]) {
- exported = _filterMap(exported, group,
- (value) => value is ExportedMethod);
- }
- return exported;
- }
-
- /// The optional parameter [containingLibrary] is contains data for variables
- /// defined at the top level of a library (potentially for exporting
- /// purposes).
- void addMethod(MethodMirror mirror, [Library containingLibrary]) {
- var method;
- if (containingLibrary != null && mirror.owner.qualifiedName !=
- containingLibrary.mirror.qualifiedName) {
- method = new ExportedMethod(mirror, containingLibrary);
- } else {
- method = new Method(mirror);
- }
- entityMap[docName(mirror)] = method;
- if (mirror.isSetter) {
- setters[mirror.simpleName] = method;
- } else if (mirror.isGetter) {
- getters[mirror.simpleName] = method;
- } else if (mirror.isConstructor) {
- constructors[mirror.simpleName] = method;
- } else if (mirror.isOperator) {
- operators[mirror.simpleName] = method;
- } else if (mirror.isRegularMethod) {
- regularMethods[mirror.simpleName] = method;
- } else {
- throw new ArgumentError('${mirror.simpleName} - no method type match');
+ var result = super.comment;
+ if (result == '' && methodInheritedFrom != null) {
+ // this should be NOT from the MIRROR, but from the COMMENT
+ _unresolvedComment = methodInheritedFrom._unresolvedComment;
+
+ var linkResolver = (name) => fixReferenceWithScope(name);
+ comment = _unresolvedComment == null ? '' :
+ markdown.markdownToHtml(_unresolvedComment.trim(),
+ linkResolver: linkResolver, inlineSyntaxes: _MARKDOWN_SYNTAXES);
+ commentInheritedFrom = methodInheritedFrom.commentInheritedFrom;
+ result = comment;
+ //print('result was $comment');
}
- }
-
- void addInherited(Class parent) {
- setters.addAll(parent.inheritedMethods.setters);
- setters.addAll(_filterStatics(parent.methods.setters));
- getters.addAll(parent.inheritedMethods.getters);
- getters.addAll(_filterStatics(parent.methods.getters));
- operators.addAll(parent.inheritedMethods.operators);
- operators.addAll(_filterStatics(parent.methods.operators));
- regularMethods.addAll(parent.inheritedMethods.regularMethods);
- regularMethods.addAll(_filterStatics(parent.methods.regularMethods));
- }
-
- Map toMap() => {
- 'setters': recurseMap(setters),
- 'getters': recurseMap(getters),
- 'constructors': recurseMap(constructors),
- 'operators': recurseMap(operators),
- 'methods': recurseMap(regularMethods)
- };
-
- Method operator [](String qualifiedName) {
- if (setters.containsKey(qualifiedName)) return setters[qualifiedName];
- if (getters.containsKey(qualifiedName)) return getters[qualifiedName];
- if (operators.containsKey(qualifiedName)) return operators[qualifiedName];
- if (regularMethods.containsKey(qualifiedName)) {
- return regularMethods[qualifiedName];
- }
- return null;
- }
-
- void forEach(void f(String key, Method value)) {
- setters.forEach(f);
- getters.forEach(f);
- operators.forEach(f);
- regularMethods.forEach(f);
+ return result;
}
}
/// A class containing properties of a Dart method/function parameter.
-class Parameter {
+class Parameter extends MirrorBased {
+ ParameterMirror mirror;
String name;
bool isOptional;
bool isNamed;
@@ -1599,8 +1777,15 @@ class Parameter {
/// List of the meta annotations on the parameter.
List<Annotation> annotations;
- Parameter(this.name, this.isOptional, this.isNamed, this.hasDefaultValue,
- this.type, this.defaultValue, this.annotations);
+ Parameter(this.mirror, [Indexable owner]) {
+ name = mirror.simpleName;
+ isOptional = mirror.isOptional;
+ isNamed = mirror.isNamed;
+ hasDefaultValue = mirror.hasDefaultValue;
+ defaultValue = mirror.defaultValue;
+ type = new Type(mirror.type, owner);
+ annotations = _createAnnotations(mirror, this);
+ }
/// Generates a map describing the [Parameter] object.
Map toMap() => {
@@ -1614,16 +1799,13 @@ class Parameter {
};
}
-/// A class containing properties of a Generic.
-class Generic {
- String name;
- String type;
-
- Generic(this.name, this.type);
-
+/// A Docgen wrapper around the dart2js mirror for a generic type.
+class Generic extends MirrorBased {
+ TypeVariableMirror mirror;
+ Generic(this.mirror);
Map toMap() => {
- 'name': name,
- 'type': type
+ 'name': mirror.toString(),
+ 'type': mirror.upperBound.qualifiedName
};
}
@@ -1655,56 +1837,57 @@ class Generic {
/// "inner" :
/// - "outer" : "dart-core.int"
/// "inner" :
-class Type {
- String outer;
- List<Type> inner;
+class Type extends MirrorBased {
+ TypeMirror mirror;
+ MirrorBased owner;
- Type(this.outer, this.inner);
+ factory Type(TypeMirror mirror, [MirrorBased owner]) {
+ return new Type._(mirror, owner);
+ }
- Map toMap() => {
- 'outer': outer,
- 'inner': inner.map((e) => e.toMap()).toList()
- };
+ Type._(this.mirror, this.owner);
+
+ /// Returns a list of [Type] objects constructed from TypeMirrors.
+ List<Type> _createTypeGenerics(TypeMirror mirror) {
+ if (mirror is ClassMirror && !mirror.isTypedef) {
+ var innerList = [];
+ mirror.typeArguments.forEach((e) {
+ innerList.add(new Type(e, owner));
+ });
+ return innerList;
+ }
+ return [];
+ }
+
+ Map toMap() {
+ // We may encounter types whose corresponding library has not been
+ // processed yet, so look up the owner at the last moment.
+ var result = getDocgenObject(mirror, owner);
+ return {
+ 'outer': result.docName,
+ 'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
+ };
+ }
}
/// Holds the name of the annotation, and its parameters.
-class Annotation {
- String qualifiedName;
+class Annotation extends MirrorBased {
List<String> parameters;
+ /// The class of this annotation.
+ ClassMirror mirror;
- Annotation(this.qualifiedName, this.parameters);
+ Annotation(InstanceMirror originalMirror, MirrorBased annotationOwner) {
+ mirror = originalMirror.type;
+ parameters = originalMirror.type.variables.values
+ .where((e) => e.isFinal)
+ .map((e) => originalMirror.getField(e.simpleName).reflectee)
+ .where((e) => e != null)
+ .toList();
+ owner = annotationOwner;
+ }
Map toMap() => {
- 'name': qualifiedName,
+ 'name': getDocgenObject(mirror, owner).docName,
'parameters': parameters
};
}
-
-/// Given a mirror, returns its qualified name, but following the conventions
-/// we're using in Dartdoc, which is that library names with dots in them
-/// have them replaced with hyphens.
-String docName(DeclarationMirror m) {
- if (m is LibraryMirror) {
- return m.qualifiedName.replaceAll('.','-');
- }
- var owner = m.owner;
- if (owner == null) return m.qualifiedName;
- var simpleName = m.simpleName;
- if (m is MethodMirror && m.isConstructor) {
- // We name constructors specially -- including the class name again and a
- // "-" to separate the constructor from its name (if any).
- simpleName = '${owner.simpleName}-$simpleName';
- }
- return docName(owner) + '.' + simpleName;
-}
-
-/// Remove statics from the map of inherited items before adding them.
-Map _filterStatics(Map items) {
- var result = {};
- items.forEach((name, item) {
- if (!item.isStatic) {
- result[name] = item;
- }
- });
- return result;
-}
« no previous file with comments | « pkg/docgen/lib/dart2yaml.dart ('k') | pkg/docgen/test/multi_library_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698