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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/docgen/lib/dart2yaml.dart ('k') | pkg/docgen/test/multi_library_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// **docgen** is a tool for creating machine readable representations of Dart 5 /// **docgen** is a tool for creating machine readable representations of Dart
6 /// code metadata, including: classes, members, comments and annotations. 6 /// code metadata, including: classes, members, comments and annotations.
7 /// 7 ///
8 /// docgen is run on a `.dart` file or a directory containing `.dart` files. 8 /// docgen is run on a `.dart` file or a directory containing `.dart` files.
9 /// 9 ///
10 /// $ dart docgen.dart [OPTIONS] [FILE/DIR] 10 /// $ dart docgen.dart [OPTIONS] [FILE/DIR]
(...skipping 16 matching lines...) Expand all
27 import '../../../sdk/lib/_internal/compiler/compiler.dart' as api; 27 import '../../../sdk/lib/_internal/compiler/compiler.dart' as api;
28 import '../../../sdk/lib/_internal/compiler/implementation/filenames.dart'; 28 import '../../../sdk/lib/_internal/compiler/implementation/filenames.dart';
29 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirro r.dart' 29 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirro r.dart'
30 as dart2js; 30 as dart2js;
31 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart' ; 31 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart' ;
32 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util. dart' 32 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util. dart'
33 as dart2js_util; 33 as dart2js_util;
34 import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider. dart'; 34 import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider. dart';
35 import '../../../sdk/lib/_internal/libraries.dart'; 35 import '../../../sdk/lib/_internal/libraries.dart';
36 36
37 var logger = new Logger('Docgen'); 37 const _DEFAULT_OUTPUT_DIRECTORY = 'docs';
38 38
39 const DEFAULT_OUTPUT_DIRECTORY = 'docs'; 39 /// Annotations that we do not display in the viewer.
40 40 const List<String> _SKIPPED_ANNOTATIONS = const [
41 var _outputDirectory;
42
43 const String USAGE = 'Usage: dart docgen.dart [OPTIONS] fooDir/barFile';
44
45
46 List<String> skippedAnnotations = const [
47 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates', 41 'metadata.DocsEditable', '_js_helper.JSName', '_js_helper.Creates',
48 '_js_helper.Returns']; 42 '_js_helper.Returns'];
49 43
50 /// Set of libraries declared in the SDK, so libraries that can be accessed
51 /// when running dart by default.
52 Iterable<LibraryMirror> _sdkLibraries;
53
54 /// The dart:core library, which contains all types that are always available
55 /// without import.
56 LibraryMirror _coreLibrary;
57
58 /// Support for [:foo:]-style code comments to the markdown parser. 44 /// Support for [:foo:]-style code comments to the markdown parser.
59 List<markdown.InlineSyntax> markdownSyntaxes = 45 List<markdown.InlineSyntax> _MARKDOWN_SYNTAXES =
60 [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')]; 46 [new markdown.CodeSyntax(r'\[:\s?((?:.|\n)*?)\s?:\]')];
61 47
48 // TODO(efortuna): The use of this field is odd (this is based on how it was
49 // originally used. Try to cleanup.
62 /// Index of all indexable items. This also ensures that no class is 50 /// Index of all indexable items. This also ensures that no class is
63 /// created more than once. 51 /// created more than once.
64 Map<String, Indexable> entityMap = new Map<String, Indexable>(); 52 Map<String, Indexable> entityMap = new Map<String, Indexable>();
65 53
66 /// This is set from the command line arguments flag --include-private 54 /// Index of all the dart2js mirrors examined to corresponding MirrorBased
67 bool _includePrivate = false; 55 /// docgen objects.
56 ///
57 /// Used for lookup because of the dart2js mirrors exports
58 /// issue. The second level map is indexed by owner docName for faster lookup.
59 /// Why two levels of lookup? Speed, man. Speed.
60 Map<String, Map<String, Set<MirrorBased>>> mirrorToDocgen =
61 new Map<String, Map<String, Set<MirrorBased>>>();
68 62
69 /// This is set from the command line flag --include-dependent-packages 63 /// Given a Dart2jsMirror, find the corresponding Docgen [MirrorBased] object.
70 bool _includeDependentPackages = false; 64 ///
65 /// We have this global lookup function to avoid re-implementing looking up the
66 /// scoping rules for comment resolution here (it is currently done in mirrors).
67 /// If no corresponding MirrorBased object is found, we return a [DummyMirror]
68 /// that simply returns the original mirror's qualifiedName while behaving like
69 /// a MirrorBased object.
70 MirrorBased getDocgenObject(DeclarationMirror mirror, [Indexable owner]) {
71 Map<String, Set<MirrorBased>> docgenObj =
72 mirrorToDocgen[mirror.qualifiedName];
73 if (docgenObj == null) {
74 return new DummyMirror(mirror, owner);
75 }
71 76
72 /// Library names to explicitly exclude. 77 Set<MirrorBased> results = new Set<MirrorBased>();
73 /// 78 var setToExamine = new Set();
74 /// Set from the command line option 79 if (owner != null) {
75 /// --exclude-lib. 80 var firstSet = docgenObj[owner.docName];
76 List<String> _excluded; 81 if (firstSet != null) setToExamine.addAll(firstSet);
82 if (Indexable._coreLibrary != null &&
83 docgenObj[Indexable._coreLibrary.docName] != null) {
84 setToExamine.addAll(docgenObj[Indexable._coreLibrary.docName]);
85 }
86 } else {
87 for (var value in docgenObj.values) {
88 setToExamine.addAll(value);
89 }
90 }
77 91
78 // TODO(janicejl): Make MDN content generic or pluggable. Maybe move 92 for(MirrorBased mirrorBased in setToExamine) {
79 // MDN-specific code to its own library that is imported into the default impl? 93 // This check makes me sad, but Dart doesn't seem to be dynamic enough to
80 /// Map of all the comments for dom elements from MDN. 94 // write this another way. Dart2js and the editor didn't like the Type
81 Map _mdn; 95 // lookup in a map.
96 if (mirrorBased.mirror.qualifiedName == mirror.qualifiedName &&
97 ((mirror is ClassMirror && mirrorBased is Class)
98 || (mirror is LibraryMirror && mirrorBased is Library)
99 || (mirror is MethodMirror && mirrorBased is Method)
100 || (mirror is VariableMirror && mirrorBased is Variable)
101 || (mirror is TypedefMirror && mirrorBased is Typedef)
102 || (mirror is TypeMirror && mirrorBased is Type)
103 || (mirror is InstanceMirror && mirrorBased is Annotation))) {
104 results.add(mirrorBased);
105 }
106 }
107
108 if (results.length > 0) {
109 return results.first; // This might occur if we didn't specify an "owner."
110 }
111 return new DummyMirror(mirror, owner);
112 }
113
114 /// For types that we do not explicitly create or have not yet created in our
115 /// entity map (like core types).
116 class DummyMirror implements MirrorBased {
117 DeclarationMirror mirror;
118 /// The library that contains this element, if any. Used as a hint to help
119 /// determine which object we're referring to when looking up this mirror in
120 /// our map.
121 Indexable owner;
122 DummyMirror(this.mirror, [this.owner]);
123
124 String get docName {
125 if (mirror == null) return '';
126 if (mirror is LibraryMirror) {
127 return mirror.qualifiedName.replaceAll('.','-');
128 }
129 var mirrorOwner = mirror.owner;
130 if (mirrorOwner == null) return mirror.qualifiedName;
131 var simpleName = mirror.simpleName;
132 if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
133 // We name constructors specially -- including the class name again and a
134 // "-" to separate the constructor from its name (if any).
135 simpleName = '${mirrorOwner.simpleName}-$simpleName';
136 }
137 return getDocgenObject(mirrorOwner, owner).docName + '.' + simpleName;
138 }
139 List<Annotation> _createAnnotations(DeclarationMirror mirror,
140 MirrorBased owner) => null;
141
142 bool get isPrivate => mirror == null? false : mirror.isPrivate;
143 }
144
145 abstract class MirrorBased {
146 DeclarationMirror get mirror;
147 MirrorBased owner;
148
149 /// Returns this object's qualified name, but following the conventions
150 /// we're using in Dartdoc, which is that library names with dots in them
151 /// have them replaced with hyphens.
152 String get docName => owner.docName + '.' + mirror.simpleName;
153
154 /// Returns a list of meta annotations assocated with a mirror.
155 List<Annotation> _createAnnotations(DeclarationMirror mirror,
156 MirrorBased owner) {
157 var annotationMirrors = mirror.metadata.where((e) =>
158 e is dart2js.Dart2JsConstructedConstantMirror);
159 var annotations = [];
160 annotationMirrors.forEach((annotation) {
161 var docgenAnnotation = new Annotation(annotation, owner);
162 if (!_SKIPPED_ANNOTATIONS.contains(
163 docgenAnnotation.mirror.qualifiedName)) {
164 annotations.add(docgenAnnotation);
165 }
166 });
167 return annotations;
168 }
169
170 bool get isPrivate => false;
171 }
82 172
83 /// Docgen constructor initializes the link resolver for markdown parsing. 173 /// Docgen constructor initializes the link resolver for markdown parsing.
84 /// Also initializes the command line arguments. 174 /// Also initializes the command line arguments.
85 /// 175 ///
86 /// [packageRoot] is the packages directory of the directory being analyzed. 176 /// [packageRoot] is the packages directory of the directory being analyzed.
87 /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will 177 /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will
88 /// also be documented. 178 /// also be documented.
89 /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented. 179 /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented.
90 /// This option is useful when only the SDK libraries are needed. 180 /// This option is useful when only the SDK libraries are needed.
91 /// 181 ///
92 /// Returned Future completes with true if document generation is successful. 182 /// Returned Future completes with true if document generation is successful.
93 Future<bool> docgen(List<String> files, {String packageRoot, 183 Future<bool> docgen(List<String> files, {String packageRoot,
94 bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false, 184 bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false,
95 bool parseSdk: false, bool append: false, String introduction: '', 185 bool parseSdk: false, bool append: false, String introduction: '',
96 out: DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries, 186 out: _DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries : const [],
97 bool includeDependentPackages}) { 187 bool includeDependentPackages: false}) {
98 _excluded = excludeLibraries; 188 return _Generator.generateDocumentation(files, packageRoot: packageRoot,
99 _includePrivate = includePrivate; 189 outputToYaml: outputToYaml, includePrivate: includePrivate,
100 _outputDirectory = out; 190 includeSdk: includeSdk, parseSdk: parseSdk, append: append,
101 _includeDependentPackages = includeDependentPackages; 191 introduction: introduction, out: out, excludeLibraries: excludeLibraries,
102 if (!append) { 192 includeDependentPackages: includeDependentPackages);
103 var dir = new Directory(_outputDirectory);
104 if (dir.existsSync()) dir.deleteSync(recursive: true);
105 }
106
107 if (packageRoot == null && !parseSdk) {
108 var type = FileSystemEntity.typeSync(files.first);
109 if (type == FileSystemEntityType.DIRECTORY) {
110 packageRoot = _findPackageRoot(files.first);
111 } else if (type == FileSystemEntityType.FILE) {
112 logger.warning('WARNING: No package root defined. If Docgen fails, try '
113 'again by setting the --package-root option.');
114 }
115 }
116 logger.info('Package Root: ${packageRoot}');
117 if (_includeDependentPackages) {
118 files.addAll(allDependentPackageDirs(files.first));
119 }
120 var requestedLibraries = _listLibraries(files);
121 var allLibraries = []..addAll(requestedLibraries);
122 if (includeSdk) {
123 allLibraries.addAll(_listSdk());
124 }
125
126 return getMirrorSystem(allLibraries, packageRoot: packageRoot,
127 parseSdk: parseSdk)
128 .then((MirrorSystem mirrorSystem) {
129 if (mirrorSystem.libraries.isEmpty) {
130 throw new StateError('No library mirrors were created.');
131 }
132 var availableLibraries = mirrorSystem.libraries.values.where(
133 (each) => each.uri.scheme == 'file');
134 _sdkLibraries = mirrorSystem.libraries.values.where(
135 (each) => each.uri.scheme == 'dart');
136 _coreLibrary = _sdkLibraries.singleWhere((lib) =>
137 lib.uri.toString().startsWith('dart:core'));
138 var availableLibrariesByPath = new Map.fromIterables(
139 availableLibraries.map((each) => each.uri),
140 availableLibraries);
141 var librariesToDocument = requestedLibraries.map(
142 (each) => availableLibrariesByPath.putIfAbsent(each,
143 () => throw "Missing library $each")).toList();
144 librariesToDocument.addAll((includeSdk || parseSdk) ? _sdkLibraries : []);
145 librariesToDocument.removeWhere((x) => _excluded.contains(x.simpleName));
146 _documentLibraries(librariesToDocument, includeSdk: includeSdk,
147 outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
148 introduction: introduction);
149 return true;
150 });
151 }
152
153 /// All of the directories for our dependent packages
154 List<String> allDependentPackageDirs(String packageDirectory) {
155 var dependentsJson = Process.runSync('pub', ['list-package-dirs'],
156 workingDirectory: packageDirectory, runInShell: true);
157 if (dependentsJson.exitCode != 0) {
158 print(dependentsJson.stderr);
159 }
160 var dependents = JSON.decode(dependentsJson.stdout)['packages'];
161 return dependents.values.toList();
162 }
163
164 /// For a library's [mirror], determine the name of the package (if any) we
165 /// believe it came from (because of its file URI).
166 ///
167 /// If [library] is specified, we set the packageName field. If no package could
168 /// be determined, we return an empty string.
169 String _findPackage(LibraryMirror mirror, [Library library]) {
170 if (mirror == null) return '';
171 if (library == null) {
172 library = entityMap[docName(mirror)];
173 }
174 if (library != null) {
175 if (library.hasBeenCheckedForPackage) return library.packageName;
176 library.hasBeenCheckedForPackage = true;
177 }
178 if (mirror.uri.scheme != 'file') return '';
179 var filePath = mirror.uri.toFilePath();
180 // We assume that we are documenting only libraries under package/lib
181 var rootdir = path.dirname((path.dirname(filePath)));
182 var pubspec = path.join(rootdir, 'pubspec.yaml');
183 var packageName = _packageName(pubspec);
184 if (library != null) {
185 library.packageName = packageName;
186 // Associate the package readme with all the libraries. This is a bit
187 // wasteful, but easier than trying to figure out which partial match
188 // is best.
189 library.packageIntro = _packageIntro(rootdir);
190 }
191 return packageName;
192 }
193
194 String _packageIntro(packageDir) {
195 var dir = new Directory(packageDir);
196 var files = dir.listSync();
197 var readmes = files.where((FileSystemEntity each) => (each is File &&
198 each.path.substring(packageDir.length + 1, each.path.length)
199 .startsWith('README'))).toList();
200 if (readmes.isEmpty) return '';
201 // If there are multiples, pick the shortest name.
202 readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
203 var readme = readmes.first;
204 var linkResolver = (name) => fixReference(name, null, null, null);
205 var contents = markdown.markdownToHtml(readme
206 .readAsStringSync(), linkResolver: linkResolver,
207 inlineSyntaxes: markdownSyntaxes);
208 return contents;
209 }
210
211 List<Uri> _listLibraries(List<String> args) {
212 var libraries = new List<Uri>();
213 for (var arg in args) {
214 var type = FileSystemEntity.typeSync(arg);
215
216 if (type == FileSystemEntityType.FILE) {
217 if (arg.endsWith('.dart')) {
218 libraries.add(new Uri.file(path.absolute(arg)));
219 logger.info('Added to libraries: ${libraries.last}');
220 }
221 } else {
222 libraries.addAll(_listDartFromDir(arg));
223 }
224 }
225 return libraries;
226 }
227
228 List<Uri> _listDartFromDir(String args) {
229 var libraries = [];
230 // To avoid anaylzing package files twice, only files with paths not
231 // containing '/packages' will be added. The only exception is if the file to
232 // analyze already has a '/package' in its path.
233 var files = listDir(args, recursive: true).where((f) => f.endsWith('.dart') &&
234 (!f.contains('${path.separator}packages') ||
235 args.contains('${path.separator}packages'))).toList();
236
237 files.forEach((String f) {
238 // Only include libraries at the top level of "lib"
239 if (path.basename(path.dirname(f)) == 'lib') {
240 // Only add the file if it does not contain 'part of'
241 // TODO(janicejl): Remove when Issue(12406) is resolved.
242 var contents = new File(f).readAsStringSync();
243 if (!(contents.contains(new RegExp('\npart of ')) ||
244 contents.startsWith(new RegExp('part of ')))) {
245 libraries.add(new Uri.file(path.normalize(path.absolute(f))));
246 logger.info('Added to libraries: $f');
247 }
248 }
249 });
250 return libraries;
251 }
252
253 String _findPackageRoot(String directory) {
254 var files = listDir(directory, recursive: true);
255 // Return '' means that there was no pubspec.yaml and therefor no packageRoot.
256 String packageRoot = files.firstWhere((f) =>
257 f.endsWith('${path.separator}pubspec.yaml'), orElse: () => '');
258 if (packageRoot != '') {
259 packageRoot = path.join(path.dirname(packageRoot), 'packages');
260 }
261 return packageRoot;
262 }
263
264 /// Read a pubspec and return the library name.
265 String _packageName(String pubspecName) {
266 File pubspec = new File(pubspecName);
267 if (!pubspec.existsSync()) return '';
268 var contents = pubspec.readAsStringSync();
269 var spec = loadYaml(contents);
270 return spec["name"];
271 }
272
273 List<Uri> _listSdk() {
274 var sdk = new List<Uri>();
275 LIBRARIES.forEach((String name, LibraryInfo info) {
276 if (info.documented) {
277 sdk.add(Uri.parse('dart:$name'));
278 logger.info('Add to SDK: ${sdk.last}');
279 }
280 });
281 return sdk;
282 } 193 }
283 194
284 /// Analyzes set of libraries by getting a mirror system and triggers the 195 /// Analyzes set of libraries by getting a mirror system and triggers the
285 /// documentation of the libraries. 196 /// documentation of the libraries.
286 Future<MirrorSystem> getMirrorSystem(List<Uri> libraries, 197 Future<MirrorSystem> getMirrorSystem(List<Uri> libraries,
287 {String packageRoot, bool parseSdk: false}) { 198 {String packageRoot, bool parseSdk: false}) {
288 if (libraries.isEmpty) throw new StateError('No Libraries.'); 199 if (libraries.isEmpty) throw new StateError('No Libraries.');
289 // Finds the root of SDK library based off the location of docgen. 200 // Finds the root of SDK library based off the location of docgen.
290 201
291 var root = findRootDirectory(); 202 var root = _Generator._rootDirectory;
292 var sdkRoot = path.normalize(path.absolute(path.join(root, 'sdk'))); 203 var sdkRoot = path.normalize(path.absolute(path.join(root, 'sdk')));
293 logger.info('SDK Root: ${sdkRoot}'); 204 _Generator.logger.info('SDK Root: ${sdkRoot}');
294 return _analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot); 205 return _Generator._analyzeLibraries(libraries, sdkRoot,
206 packageRoot: packageRoot);
295 } 207 }
296 208
297 String findRootDirectory() { 209 class _Generator {
298 var scriptDir = path.absolute(path.dirname(Platform.script.toFilePath())); 210 static var _outputDirectory;
299 var root = scriptDir; 211
300 while(path.basename(root) != 'dart') { 212 /// This is set from the command line arguments flag --include-private
301 root = path.dirname(root); 213 static bool _includePrivate = false;
302 } 214
303 return root; 215 /// Library names to explicitly exclude.
216 ///
217 /// Set from the command line option
218 /// --exclude-lib.
219 static List<String> _excluded;
220
221 static Logger logger = new Logger('Docgen');
222
223 /// Docgen constructor initializes the link resolver for markdown parsing.
224 /// Also initializes the command line arguments.
225 ///
226 /// [packageRoot] is the packages directory of the directory being analyzed.
227 /// If [includeSdk] is `true`, then any SDK libraries explicitly imported will
228 /// also be documented.
229 /// If [parseSdk] is `true`, then all Dart SDK libraries will be documented.
230 /// This option is useful when only the SDK libraries are needed.
231 ///
232 /// Returned Future completes with true if document generation is successful.
233 static Future<bool> generateDocumentation(List<String> files,
234 {String packageRoot, bool outputToYaml: true, bool includePrivate: false,
235 bool includeSdk: false, bool parseSdk: false, bool append: false,
236 String introduction: '', out: _DEFAULT_OUTPUT_DIRECTORY,
237 List<String> excludeLibraries : const [],
238 bool includeDependentPackages: false}) {
239 _excluded = excludeLibraries;
240 _includePrivate = includePrivate;
241 logger.onRecord.listen((record) => print(record.message));
242
243 _ensureOutputDirectory(out, append);
244 var updatedPackageRoot = _obtainPackageRoot(packageRoot, parseSdk, files);
245
246 var requestedLibraries = _findLibrariesToDocument(files,
247 includeDependentPackages);
248
249 var allLibraries = []..addAll(requestedLibraries);
250 if (includeSdk) {
251 allLibraries.addAll(_listSdk());
252 }
253
254 return getMirrorSystem(allLibraries, packageRoot: updatedPackageRoot,
255 parseSdk: parseSdk)
256 .then((MirrorSystem mirrorSystem) {
257 if (mirrorSystem.libraries.isEmpty) {
258 throw new StateError('No library mirrors were created.');
259 }
260 Indexable.initializeTopLevelLibraries(mirrorSystem);
261
262 var availableLibraries = mirrorSystem.libraries.values.where(
263 (each) => each.uri.scheme == 'file');
264 var availableLibrariesByPath = new Map.fromIterables(
265 availableLibraries.map((each) => each.uri),
266 availableLibraries);
267 var librariesToDocument = requestedLibraries.map(
268 (each) => availableLibrariesByPath.putIfAbsent(each,
269 () => throw "Missing library $each")).toList();
270 librariesToDocument.addAll(
271 (includeSdk || parseSdk) ? Indexable._sdkLibraries : []);
272 librariesToDocument.removeWhere(
273 (x) => _excluded.contains(x.simpleName));
274 _documentLibraries(librariesToDocument, includeSdk: includeSdk,
275 outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
276 introduction: introduction);
277 return true;
278 });
279 }
280
281 /// Writes text to a file in the output directory.
282 static void _writeToFile(String text, String filename, {bool append: false}) {
283 if (text == null) return;
284 Directory dir = new Directory(_outputDirectory);
285 if (!dir.existsSync()) {
286 dir.createSync();
287 }
288 if (path.split(filename).length > 1) {
289 var splitList = path.split(filename);
290 for (int i = 0; i < splitList.length; i++) {
291 var level = splitList[i];
292 }
293 for (var level in path.split(filename)) {
294 var subdir = new Directory(path.join(_outputDirectory,
295 path.dirname(filename)));
296 if (!subdir.existsSync()) {
297 subdir.createSync();
298 }
299 }
300 }
301 File file = new File(path.join(_outputDirectory, filename));
302 file.writeAsStringSync(text,
303 mode: append ? FileMode.APPEND : FileMode.WRITE);
304 }
305
306 /// Creates documentation for filtered libraries.
307 static void _documentLibraries(List<LibraryMirror> libs,
308 {bool includeSdk: false, bool outputToYaml: true, bool append: false,
309 bool parseSdk: false, String introduction: ''}) {
310 libs.forEach((lib) {
311 // Files belonging to the SDK have a uri that begins with 'dart:'.
312 if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
313 var library = generateLibrary(lib);
314 entityMap[library.name] = library;
315 }
316 });
317
318 var filteredEntities = entityMap.values.where(_isFullChainVisible);
319
320 /*var filteredEntities2 = new Set<MirrorBased>();
321 for (Map<String, Set<MirrorBased>> firstLevel in mirrorToDocgen.values) {
322 for (Set<MirrorBased> items in firstLevel.values) {
323 for (MirrorBased item in items) {
324 if (_isFullChainVisible(item)) {
325 filteredEntities2.add(item);
326 }
327 }
328 }
329 }*/
330
331 /*print('THHHHHEEE DIFFERENCE IS');
332 var set1 = new Set.from(filteredEntities);
333 var set2 = new Set.from(filteredEntities2);
334 var aResult = set2.difference(set1);
335 for (MirrorBased r in aResult) {
336 print(' a result is $r and ${r.docName}');
337 }*/
338 //print(set1.difference(set2));
339
340 // Outputs a JSON file with all libraries and their preview comments.
341 // This will help the viewer know what libraries are available to read in.
342 var libraryMap;
343 var linkResolver = (name) => Indexable.globalFixReference(name);
344 if (append) {
345 var docsDir = listDir(_outputDirectory);
346 if (!docsDir.contains('$_outputDirectory/library_list.json')) {
347 throw new StateError('No library_list.json');
348 }
349 libraryMap =
350 JSON.decode(new File(
351 '$_outputDirectory/library_list.json').readAsStringSync());
352 libraryMap['libraries'].addAll(filteredEntities
353 .where((e) => e is Library)
354 .map((e) => e.previewMap));
355 if (introduction.isNotEmpty) {
356 var intro = libraryMap['introduction'];
357 if (intro.isNotEmpty) intro += '<br/><br/>';
358 intro += markdown.markdownToHtml(
359 new File(introduction).readAsStringSync(),
360 linkResolver: linkResolver, inlineSyntaxes: _MARKDOWN_SYNTAXES);
361 libraryMap['introduction'] = intro;
362 }
363 outputToYaml = libraryMap['filetype'] == 'yaml';
364 } else {
365 libraryMap = {
366 'libraries' : filteredEntities.where((e) =>
367 e is Library).map((e) => e.previewMap).toList(),
368 'introduction' : introduction == '' ?
369 '' : markdown.markdownToHtml(new File(introduction)
370 .readAsStringSync(), linkResolver: linkResolver,
371 inlineSyntaxes: _MARKDOWN_SYNTAXES),
372 'filetype' : outputToYaml ? 'yaml' : 'json'
373 };
374 }
375 _writeToFile(JSON.encode(libraryMap), 'library_list.json');
376
377 // Output libraries and classes to file after all information is generated.
378 filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
379 _writeIndexableToFile(output, outputToYaml);
380 });
381
382 // Outputs all the qualified names documented with their type.
383 // This will help generate search results.
384 _writeToFile(filteredEntities.map((e) =>
385 '${e.qualifiedName} ${e.typeName}').join('\n') + '\n',
386 'index.txt', append: append);
387 var index = new Map.fromIterables(
388 filteredEntities.map((e) => e.qualifiedName),
389 filteredEntities.map((e) => e.typeName));
390 if (append) {
391 var previousIndex =
392 JSON.decode(new File(
393 '$_outputDirectory/index.json').readAsStringSync());
394 index.addAll(previousIndex);
395 }
396 _writeToFile(JSON.encode(index), 'index.json');
397 }
398
399 static void _writeIndexableToFile(Indexable result, bool outputToYaml) {
400 var outputFile = result.fileName;
401 var output;
402 if (outputToYaml) {
403 output = getYamlString(result.toMap());
404 outputFile = outputFile + '.yaml';
405 } else {
406 output = JSON.encode(result.toMap());
407 outputFile = outputFile + '.json';
408 }
409 _writeToFile(output, outputFile);
410 }
411
412 /// Set the location of the ouput directory, and ensure that the location is
413 /// available on the file system.
414 static void _ensureOutputDirectory(String outputDirectory, bool append) {
415 _outputDirectory = outputDirectory;
416 if (!append) {
417 var dir = new Directory(_outputDirectory);
418 if (dir.existsSync()) dir.deleteSync(recursive: true);
419 }
420 }
421
422
423 static String get _rootDirectory {
424 var scriptDir = path.absolute(path.dirname(Platform.script.toFilePath()));
425 var root = scriptDir;
426 while(path.basename(root) != 'dart') {
427 root = path.dirname(root);
428 }
429 return root;
430 }
431
432 /// Analyzes set of libraries and provides a mirror system which can be used
433 /// for static inspection of the source code.
434 static Future<MirrorSystem> _analyzeLibraries(List<Uri> libraries,
435 String libraryRoot, {String packageRoot}) {
436 SourceFileProvider provider = new CompilerSourceFileProvider();
437 api.DiagnosticHandler diagnosticHandler =
438 (new FormattingDiagnosticHandler(provider)
439 ..showHints = false
440 ..showWarnings = false)
441 .diagnosticHandler;
442 Uri libraryUri = new Uri.file(appendSlash(libraryRoot));
443 Uri packageUri = null;
444 if (packageRoot != null) {
445 packageUri = new Uri.file(appendSlash(packageRoot));
446 }
447 return dart2js.analyze(libraries, libraryUri, packageUri,
448 provider.readStringFromUri, diagnosticHandler,
449 ['--preserve-comments', '--categories=Client,Server'])
450 ..catchError((error) {
451 logger.severe('Error: Failed to create mirror system. ');
452 // TODO(janicejl): Use the stack trace package when bug is resolved.
453 // Currently, a string is thrown when it fails to create a mirror
454 // system, and it is not possible to use the stack trace. BUG(#11622)
455 // To avoid printing the stack trace.
456 exit(1);
457 });
458 }
459
460 /// For this run of docgen, determine the packageRoot value.
461 ///
462 /// If packageRoot is not explicitly passed, we examine the files we're
463 /// documenting to attempt to find a package root.
464 static String _obtainPackageRoot(String packageRoot, bool parseSdk,
465 List<String> files) {
466 if (packageRoot == null && !parseSdk) {
467 // TODO(efortuna): This logic seems not very robust, but it's from the
468 // original version of the code, pre-refactor, so I'm leavingt it for now.
469 // Revisit to make more robust.
470 var type = FileSystemEntity.typeSync(files.first);
471 if (type == FileSystemEntityType.DIRECTORY) {
472 var files2 = listDir(files.first, recursive: true);
473 // Return '' means that there was no pubspec.yaml and therefor no p
474 // ackageRoot.
475 packageRoot = files2.firstWhere((f) =>
476 f.endsWith('${path.separator}pubspec.yaml'), orElse: () => '');
477 if (packageRoot != '') {
478 packageRoot = path.join(path.dirname(packageRoot), 'packages');
479 }
480 } else if (type == FileSystemEntityType.FILE) {
481 logger.warning('WARNING: No package root defined. If Docgen fails, try '
482 'again by setting the --package-root option.');
483 }
484 }
485 logger.info('Package Root: ${packageRoot}');
486 return packageRoot;
487 }
488
489 /// Given the user provided list of items to document, expand all directories
490 /// to document out into specific files and add any dependent packages for
491 /// documentation if desired.
492 static List<Uri> _findLibrariesToDocument(List<String> args,
493 bool includeDependentPackages) {
494 if (includeDependentPackages) {
495 args.addAll(_allDependentPackageDirs(args.first));
496 }
497
498 var libraries = new List<Uri>();
499 for (var arg in args) {
500 if (FileSystemEntity.typeSync(arg) == FileSystemEntityType.FILE) {
501 if (arg.endsWith('.dart')) {
502 libraries.add(new Uri.file(path.absolute(arg)));
503 logger.info('Added to libraries: ${libraries.last}');
504 }
505 } else {
506 libraries.addAll(_findFilesToDocumentInPackage(arg));
507 }
508 }
509 return libraries;
510 }
511
512 /// Given a package name, explore the directory and pull out all top level
513 /// library files in the "lib" directory to document.
514 static List<Uri> _findFilesToDocumentInPackage(String packageName) {
515 var libraries = [];
516 // To avoid anaylzing package files twice, only files with paths not
517 // containing '/packages' will be added. The only exception is if the file
518 // to analyze already has a '/package' in its path.
519 var files = listDir(packageName, recursive: true).where(
520 (f) => f.endsWith('.dart') && (!f.contains('${path.separator}packages')
521 || packageName.contains('${path.separator}packages'))).toList();
522
523 files.forEach((String f) {
524 // Only include libraries at the top level of "lib"
525 if (path.basename(path.dirname(f)) == 'lib') {
526 // Only add the file if it does not contain 'part of'
527 // TODO(janicejl): Remove when Issue(12406) is resolved.
528 var contents = new File(f).readAsStringSync();
529 if (!(contents.contains(new RegExp('\npart of ')) ||
530 contents.startsWith(new RegExp('part of ')))) {
531 libraries.add(new Uri.file(path.normalize(path.absolute(f))));
532 logger.info('Added to libraries: $f');
533 }
534 }
535 });
536 return libraries;
537 }
538
539 /// All of the directories for our dependent packages
540 static List<String> _allDependentPackageDirs(String packageDirectory) {
541 var dependentsJson = Process.runSync('pub', ['list-package-dirs'],
542 workingDirectory: packageDirectory, runInShell: true);
543 if (dependentsJson.exitCode != 0) {
544 print(dependentsJson.stderr);
545 }
546 var dependents = JSON.decode(dependentsJson.stdout)['packages'];
547 return dependents.values.toList();
548 }
549
550 /// For all the libraries, return a list of the libraries that are part of
551 /// the SDK.
552 static List<Uri> _listSdk() {
553 var sdk = new List<Uri>();
554 LIBRARIES.forEach((String name, LibraryInfo info) {
555 if (info.documented) {
556 sdk.add(Uri.parse('dart:$name'));
557 logger.info('Add to SDK: ${sdk.last}');
558 }
559 });
560 return sdk;
561 }
562
563 static bool _isFullChainVisible(MirrorBased item) {
564 // TODO: reconcile with isVisible
565 var result = _includePrivate || (!item.isPrivate && (item.owner != null ?
566 _isFullChainVisible(item.owner) : true));
567 return result;
568 }
569
570 /// Currently left public for testing purposes. :-/
571 static Library generateLibrary(dart2js.Dart2JsLibraryMirror library) {
572 var result = new Library(library);
573 result._findPackage(library);
574 logger.fine('Generated library for ${result.name}');
575 return result;
576 }
304 } 577 }
305 578
306 /// Analyzes set of libraries and provides a mirror system which can be used 579 /// An item that is categorized in our mirrorToDocgen map, as a distinct,
307 /// for static inspection of the source code. 580 /// searchable element.
308 Future<MirrorSystem> _analyzeLibraries(List<Uri> libraries,
309 String libraryRoot, {String packageRoot}) {
310 SourceFileProvider provider = new CompilerSourceFileProvider();
311 api.DiagnosticHandler diagnosticHandler =
312 (new FormattingDiagnosticHandler(provider)
313 ..showHints = false
314 ..showWarnings = false)
315 .diagnosticHandler;
316 Uri libraryUri = new Uri.file(appendSlash(libraryRoot));
317 Uri packageUri = null;
318 if (packageRoot != null) {
319 packageUri = new Uri.file(appendSlash(packageRoot));
320 }
321 return dart2js.analyze(libraries, libraryUri, packageUri,
322 provider.readStringFromUri, diagnosticHandler,
323 ['--preserve-comments', '--categories=Client,Server'])
324 ..catchError((error) {
325 logger.severe('Error: Failed to create mirror system. ');
326 // TODO(janicejl): Use the stack trace package when bug is resolved.
327 // Currently, a string is thrown when it fails to create a mirror
328 // system, and it is not possible to use the stack trace. BUG(#11622)
329 // To avoid printing the stack trace.
330 exit(1);
331 });
332 }
333
334 /// Creates documentation for filtered libraries.
335 void _documentLibraries(List<LibraryMirror> libs, {bool includeSdk: false,
336 bool outputToYaml: true, bool append: false, bool parseSdk: false,
337 String introduction: ''}) {
338 libs.forEach((lib) {
339 // Files belonging to the SDK have a uri that begins with 'dart:'.
340 if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
341 var library = generateLibrary(lib);
342 entityMap[library.name] = library;
343 }
344 });
345 // After everything is created, do a pass through all classes to make sure no
346 // intermediate classes created by mixins are included, all the links to
347 // exported members point to the new library.
348 entityMap.values.where((e) => e is Class).forEach(
349 (c) => c.updateLinksAndRemoveIntermediaryClasses());
350 // Everything is a subclass of Object, therefore empty the list to avoid a
351 // giant list of subclasses to be printed out.
352 if (includeSdk) (entityMap['dart-core.Object'] as Class).subclasses.clear();
353
354 var filteredEntities = entityMap.values.where(_isVisible);
355
356 // Outputs a JSON file with all libraries and their preview comments.
357 // This will help the viewer know what libraries are available to read in.
358 var libraryMap;
359 var linkResolver = (name) => fixReference(name, null, null, null);
360 if (append) {
361 var docsDir = listDir(_outputDirectory);
362 if (!docsDir.contains('$_outputDirectory/library_list.json')) {
363 throw new StateError('No library_list.json');
364 }
365 libraryMap =
366 JSON.decode(new File(
367 '$_outputDirectory/library_list.json').readAsStringSync());
368 libraryMap['libraries'].addAll(filteredEntities
369 .where((e) => e is Library)
370 .map((e) => e.previewMap));
371 if (introduction.isNotEmpty) {
372 var intro = libraryMap['introduction'];
373 if (intro.isNotEmpty) intro += '<br/><br/>';
374 intro += markdown.markdownToHtml(
375 new File(introduction).readAsStringSync(),
376 linkResolver: linkResolver, inlineSyntaxes: markdownSyntaxes);
377 libraryMap['introduction'] = intro;
378 }
379 outputToYaml = libraryMap['filetype'] == 'yaml';
380 } else {
381 libraryMap = {
382 'libraries' : filteredEntities.where((e) =>
383 e is Library).map((e) => e.previewMap).toList(),
384 'introduction' : introduction == '' ?
385 '' : markdown.markdownToHtml(new File(introduction)
386 .readAsStringSync(), linkResolver: linkResolver,
387 inlineSyntaxes: markdownSyntaxes),
388 'filetype' : outputToYaml ? 'yaml' : 'json'
389 };
390 }
391 _writeToFile(JSON.encode(libraryMap), 'library_list.json');
392
393 // Output libraries and classes to file after all information is generated.
394 filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
395 _writeIndexableToFile(output, outputToYaml);
396 });
397
398 // Outputs all the qualified names documented with their type.
399 // This will help generate search results.
400 _writeToFile(filteredEntities.map((e) =>
401 '${e.qualifiedName} ${e.typeName}').join('\n') + '\n',
402 'index.txt', append: append);
403 var index = new Map.fromIterables(
404 filteredEntities.map((e) => e.qualifiedName),
405 filteredEntities.map((e) => e.typeName));
406 if (append) {
407 var previousIndex =
408 JSON.decode(new File('$_outputDirectory/index.json').readAsStringSync()) ;
409 index.addAll(previousIndex);
410 }
411 _writeToFile(JSON.encode(index), 'index.json');
412 }
413
414 Library generateLibrary(dart2js.Dart2JsLibraryMirror library) {
415 var result = new Library(library);
416 _findPackage(library, result);
417 logger.fine('Generated library for ${result.name}');
418 return result;
419 }
420
421 void _writeIndexableToFile(Indexable result, bool outputToYaml) {
422 var outputFile = result.fileName;
423 var output;
424 if (outputToYaml) {
425 output = getYamlString(result.toMap());
426 outputFile = outputFile + '.yaml';
427 } else {
428 output = JSON.encode(result.toMap());
429 outputFile = outputFile + '.json';
430 }
431 _writeToFile(output, outputFile);
432 }
433
434 /// Returns true if a library name starts with an underscore, and false
435 /// otherwise.
436 /// 581 ///
437 /// An example that starts with _ is _js_helper. 582 /// These are items that refer to concrete entities (a Class, for example,
438 /// An example that contains ._ is dart._collection.dev 583 /// but not a Type, which is a "pointer" to a class) that we wish to be
439 // This is because LibraryMirror.isPrivate returns `false` all the time. 584 /// globally resolvable. This includes things such as class methods and
440 bool _isLibraryPrivate(LibraryMirror mirror) { 585 /// variables, but parameters for methods are not "Indexable" as we do not want
441 var sdkLibrary = LIBRARIES[mirror.simpleName]; 586 /// the user to be able to search for a method based on its parameter names!
442 if (sdkLibrary != null) { 587 /// The set of indexable items also includes Typedefs, since the user can refer
443 return !sdkLibrary.documented; 588 /// to them as concrete entities in a particular scope.
444 } else if (mirror.simpleName.startsWith('_') || 589 class Indexable extends MirrorBased {
445 mirror.simpleName.contains('._')) { 590 /// The dart:core library, which contains all types that are always available
446 return true; 591 /// without import.
447 } 592 static Library _coreLibrary;
448 return false; 593
449 } 594 /// Set of libraries declared in the SDK, so libraries that can be accessed
450 595 /// when running dart by default.
451 /// A declaration is private if itself is private, or the owner is private. 596 static Iterable<LibraryMirror> _sdkLibraries;
452 // Issue(12202) - A declaration is public even if it's owner is private. 597
453 bool _isHidden(DeclarationMirror mirror) {
454 if (mirror is LibraryMirror) {
455 return _isLibraryPrivate(mirror);
456 } else if (mirror.owner is LibraryMirror) {
457 return (mirror.isPrivate || _isLibraryPrivate(mirror.owner));
458 } else {
459 return (mirror.isPrivate || _isHidden(mirror.owner));
460 }
461 }
462
463 bool _isVisible(Indexable item) {
464 return _includePrivate || !item.isPrivate;
465 }
466
467 /// Generates MDN comments from database.json.
468 void _mdnComment(Indexable item) {
469 //Check if MDN is loaded.
470 if (_mdn == null) {
471 // Reading in MDN related json file.
472 var root = findRootDirectory();
473 var mdnPath = path.join(root, 'utils/apidoc/mdn/database.json');
474 _mdn = JSON.decode(new File(mdnPath).readAsStringSync());
475 }
476 if (item is Library) return;
477 var domAnnotation = item.annotations.firstWhere(
478 (e) => e.qualifiedName == 'metadata.DomName', orElse: () => null);
479 if (domAnnotation == null) return;
480 var domName = domAnnotation.parameters.single;
481 var parts = domName.split('.');
482 if (parts.length == 2) item.comment = _mdnMemberComment(parts[0], parts[1]);
483 if (parts.length == 1) item.comment = _mdnTypeComment(parts[0]);
484 }
485
486 /// Generates the MDN Comment for variables and method DOM elements.
487 String _mdnMemberComment(String type, String member) {
488 var mdnType = _mdn[type];
489 if (mdnType == null) return '';
490 var mdnMember = mdnType['members'].firstWhere((e) => e['name'] == member,
491 orElse: () => null);
492 if (mdnMember == null) return '';
493 if (mdnMember['help'] == null || mdnMember['help'] == '') return '';
494 if (mdnMember['url'] == null) return '';
495 return _htmlMdn(mdnMember['help'], mdnMember['url']);
496 }
497
498 /// Generates the MDN Comment for class DOM elements.
499 String _mdnTypeComment(String type) {
500 var mdnType = _mdn[type];
501 if (mdnType == null) return '';
502 if (mdnType['summary'] == null || mdnType['summary'] == "") return '';
503 if (mdnType['srcUrl'] == null) return '';
504 return _htmlMdn(mdnType['summary'], mdnType['srcUrl']);
505 }
506
507 String _htmlMdn(String content, String url) {
508 return '<div class="mdn">' + content.trim() + '<p class="mdn-note">'
509 '<a href="' + url.trim() + '">from Mdn</a></p></div>';
510 }
511
512 /// Look for the specified name starting with the current member, and
513 /// progressively working outward to the current library scope.
514 String findElementInScope(String name, LibraryMirror currentLibrary,
515 ClassMirror currentClass, MemberMirror currentMember) {
516 var packagePrefix = _findPackage(currentLibrary);
517 if (packagePrefix != '') packagePrefix += '/';
518
519 determineLookupFunc(name) => name.contains('.') ?
520 dart2js_util.lookupQualifiedInScope :
521 (mirror, name) => mirror.lookupInScope(name);
522 var lookupFunc = determineLookupFunc(name);
523
524 var memberScope = currentMember == null ?
525 null : lookupFunc(currentMember, name);
526 if (memberScope != null) return packagePrefix + docName(memberScope);
527
528 var classScope = currentClass;
529 while (classScope != null) {
530 var classFunc = lookupFunc(currentClass, name);
531 if (classFunc != null) return packagePrefix + docName(classFunc);
532 classScope = classScope.superclass;
533 }
534
535 var libraryScope = currentLibrary == null ?
536 null : lookupFunc(currentLibrary, name);
537 if (libraryScope != null) return packagePrefix + docName(libraryScope);
538
539 // Look in the dart core library scope.
540 var coreScope = _coreLibrary == null? null : lookupFunc(_coreLibrary, name);
541 if (coreScope != null) return packagePrefix + docName(_coreLibrary);
542
543 // If it's a reference that starts with a another library name, then it
544 // looks for a match of that library name in the other sdk libraries.
545 if(name.contains('.')) {
546 var index = name.indexOf('.');
547 var libraryName = name.substring(0, index);
548 var remainingName = name.substring(index + 1);
549 foundLibraryName(library) => library.uri.pathSegments[0] == libraryName;
550
551 if (_sdkLibraries.any(foundLibraryName)) {
552 var library = _sdkLibraries.singleWhere(foundLibraryName);
553 // Look to see if it's a fully qualified library name.
554 var scope = determineLookupFunc(remainingName)(library, remainingName);
555 if (scope != null) return packagePrefix + docName(scope);
556 }
557 }
558 return null;
559 }
560
561 // HTML escaped version of '<' character.
562 final _LESS_THAN = '&lt;';
563
564 /// Chunk the provided name into individual parts to be resolved. We take a
565 /// simplistic approach to chunking, though, we break at " ", ",", "&lt;"
566 /// and ">". All other characters are grouped into the name to be resolved.
567 /// As a result, these characters will all be treated as part of the item to be
568 /// resolved (aka the * is interpreted literally as a *, not as an indicator for
569 /// bold <em>.
570 List<String> _tokenizeComplexReference(String name) {
571 var tokens = [];
572 var append = false;
573 var index = 0;
574 while(index < name.length) {
575 if (name.indexOf(_LESS_THAN, index) == index) {
576 tokens.add(_LESS_THAN);
577 append = false;
578 index += _LESS_THAN.length;
579 } else if (name[index] == ' ' || name[index] == ',' ||
580 name[index] == '>') {
581 tokens.add(name[index]);
582 append = false;
583 index++;
584 } else {
585 if (append) {
586 tokens[tokens.length - 1] = tokens.last + name[index];
587 } else {
588 tokens.add(name[index]);
589 append = true;
590 }
591 index++;
592 }
593 }
594 return tokens;
595 }
596
597 /// This is a more complex reference. Try to break up if its of the form A<B>
598 /// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"),
599 /// or of the form A<B>. Note: unlike other the other markdown-style links, all
600 /// text inside the square brackets is treated as part of the link (aka the * is
601 /// interpreted literally as a *, not as a indicator for bold <em>.
602 ///
603 /// Example: [foo&lt;_bar_>] will produce
604 /// <a>resolvedFoo</a>&lt;<a>resolved_bar_</a>> rather than an italicized
605 /// version of resolvedBar.
606 markdown.Node _fixComplexReference(String name, LibraryMirror currentLibrary,
607 ClassMirror currentClass, MemberMirror currentMember) {
608 // Parse into multiple elements we can try to resolve.
609 var tokens = _tokenizeComplexReference(name);
610
611 // Produce an html representation of our elements. Group unresolved and plain
612 // text are grouped into "link" elements so they display as code.
613 final textElements = [' ', ',', '>', _LESS_THAN];
614 var accumulatedHtml = '';
615
616 for (var token in tokens) {
617 bool added = false;
618 if (!textElements.contains(token)) {
619 String elementName = findElementInScope(token, currentLibrary,
620 currentClass, currentMember);
621 if (elementName != null) {
622 accumulatedHtml += markdown.renderToHtml([new markdown.Element.text(
623 'a', elementName)]);
624 added = true;
625 }
626 }
627 if (!added) {
628 accumulatedHtml += token;
629 }
630 }
631 return new markdown.Text(accumulatedHtml);
632 }
633
634 /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
635 markdown.Node fixReference(String name, LibraryMirror currentLibrary,
636 ClassMirror currentClass, MemberMirror currentMember) {
637 // Attempt the look up the whole name up in the scope.
638 String elementName =
639 findElementInScope(name, currentLibrary, currentClass, currentMember);
640 if (elementName != null) {
641 return new markdown.Element.text('a', elementName);
642 }
643 return _fixComplexReference(name, currentLibrary, currentClass,
644 currentMember);
645 }
646
647 markdown.Node fixReferenceWithScope(String name, DeclarationMirror scope) {
648 if (scope is LibraryMirror) return fixReference(name, scope, null, null);
649 if (scope is ClassMirror)
650 return fixReference(name, scope.library, scope, null);
651 if (scope is MemberMirror) {
652 var owner = scope.owner;
653 if (owner is ClassMirror) {
654 return fixReference(name, owner.library, owner, scope);
655 } else {
656 return fixReference(name, owner, null, scope);
657 }
658 }
659 return null;
660 }
661
662 /// Writes text to a file in the output directory.
663 void _writeToFile(String text, String filename, {bool append: false}) {
664 if (text == null) return;
665 Directory dir = new Directory(_outputDirectory);
666 if (!dir.existsSync()) {
667 dir.createSync();
668 }
669 if (path.split(filename).length > 1) {
670 var splitList = path.split(filename);
671 for (int i = 0; i < splitList.length; i++) {
672 var level = splitList[i];
673 }
674 for (var level in path.split(filename)) {
675 var subdir = new Directory(path.join(_outputDirectory,
676 path.dirname(filename)));
677 if (!subdir.existsSync()) {
678 subdir.createSync();
679 }
680 }
681 }
682 File file = new File(path.join(_outputDirectory, filename));
683 file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE);
684 }
685
686 /// Transforms the map by calling toMap on each value in it.
687 Map recurseMap(Map inputMap) {
688 var outputMap = {};
689 inputMap.forEach((key, value) {
690 if (value is Map) {
691 outputMap[key] = recurseMap(value);
692 } else {
693 outputMap[key] = value.toMap();
694 }
695 });
696 return outputMap;
697 }
698
699 /// A type for the function that generates a comment from a mirror.
700 typedef String CommentGenerator(Mirror m);
701
702 /// A class representing all programming constructs, like library or class.
703 class Indexable {
704 String get qualifiedName => fileName; 598 String get qualifiedName => fileName;
705 bool isPrivate; 599 bool isPrivate;
706 DeclarationMirror mirror; 600 DeclarationMirror mirror;
601 /// The comment text pre-resolution. We keep this around because inherited
602 /// methods need to resolve links differently from the superclass.
603 String _unresolvedComment = '';
604
605 // TODO(janicejl): Make MDN content generic or pluggable. Maybe move
606 // MDN-specific code to its own library that is imported into the default
607 // impl?
608 /// Map of all the comments for dom elements from MDN.
609 static Map _mdn;
707 610
708 Indexable(this.mirror) { 611 Indexable(this.mirror) {
709 this.isPrivate = _isHidden(mirror); 612 this.isPrivate = _isHidden(mirror);
710 } 613
614 var map = mirrorToDocgen[this.mirror.qualifiedName];
615 if (map == null) map = new Map<String, Set<MirrorBased>>();
616
617 var set = map[owner.docName];
618 if (set == null) set = new Set<MirrorBased>();
619 set.add(this);
620 map[owner.docName] = set;
621 mirrorToDocgen[this.mirror.qualifiedName] = map;
622 }
623
624 /** Walk up the owner chain to find the owning library. */
625 Library _getOwningLibrary(Indexable owner) {
626 if (owner is Library) return owner;
627 // TODO: is this needed?
628 if (owner is DummyMirror) return getDocgenObject(owner.mirror.library);
629 return _getOwningLibrary(owner.owner);
630 }
631
632 static initializeTopLevelLibraries(MirrorSystem mirrorSystem) {
633 _sdkLibraries = mirrorSystem.libraries.values.where(
634 (each) => each.uri.scheme == 'dart');
635 _coreLibrary = new Library(_sdkLibraries.singleWhere((lib) =>
636 lib.uri.toString().startsWith('dart:core')));
637 }
638
639 markdown.Node fixReferenceWithScope(String name) => null;
640
641 /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
642 markdown.Node fixReference(String name) {
643 // Attempt the look up the whole name up in the scope.
644 String elementName = findElementInScope(name);
645 if (elementName != null) {
646 return new markdown.Element.text('a', elementName);
647 }
648 return _fixComplexReference(name);
649 }
650
651 /// Look for the specified name starting with the current member, and
652 /// progressively working outward to the current library scope.
653 String findElementInScope(String name) =>
654 _findElementInScope(name, packagePrefix);
655
656 static determineLookupFunc(name) => name.contains('.') ?
657 dart2js_util.lookupQualifiedInScope :
658 (mirror, name) => mirror.lookupInScope(name);
711 659
712 // The qualified name (for URL purposes) and the file name are the same, 660 // The qualified name (for URL purposes) and the file name are the same,
713 // of the form packageName/ClassName or packageName/ClassName.methodName. 661 // of the form packageName/ClassName or packageName/ClassName.methodName.
714 // This defines both the URL and the directory structure. 662 // This defines both the URL and the directory structure.
715 String get fileName => packagePrefix + ownerPrefix + name; 663 String get fileName {
716 664 return packagePrefix + ownerPrefix + name;
717 Indexable get owningEntity => entityMap[owner]; 665 }
718 666
719 String get ownerPrefix => owningEntity == null ? 667 String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
720 (owner == null || owner.isEmpty ? '' : owner + '.') :
721 owningEntity.qualifiedName + '.';
722 668
723 String get packagePrefix => ''; 669 String get packagePrefix => '';
724 670
725 /// Documentation comment with converted markdown. 671 /// Documentation comment with converted markdown.
726 String _comment; 672 String _comment;
727 673
728 String get comment { 674 String get comment {
729 if (_comment != null) return _comment; 675 if (_comment != null) return _comment;
730 _comment = _commentToHtml(mirror); 676
677 _comment = _commentToHtml();
731 if (_comment.isEmpty) { 678 if (_comment.isEmpty) {
732 _mdnComment(this); 679 _comment = _mdnComment();
733 } 680 }
734 return _comment; 681 return _comment;
735 } 682 }
736 683
737 set comment(x) => _comment = x; 684 set comment(x) => _comment = x;
738 685
739 String get name => mirror.simpleName; 686 String get name => mirror.simpleName;
740 687
741 /// Qualified Name of the owner of this Indexable Item. 688 MirrorBased get owner => new DummyMirror(mirror.owner);
742 String get owner => docName(mirror.owner); 689
690 /// Generates MDN comments from database.json.
691 String _mdnComment() {
692 //Check if MDN is loaded.
693 if (_mdn == null) {
694 // Reading in MDN related json file.
695 var root = _Generator._rootDirectory;
696 var mdnPath = path.join(root, 'utils/apidoc/mdn/database.json');
697 _mdn = JSON.decode(new File(mdnPath).readAsStringSync());
698 }
699 // TODO: refactor OOP
700 if (this is Library) return '';
701 var domAnnotation = this.annotations.firstWhere(
702 (e) => e.mirror.qualifiedName == 'metadata.DomName',
703 orElse: () => null);
704 if (domAnnotation == null) return '';
705 var domName = domAnnotation.parameters.single;
706 var parts = domName.split('.');
707 if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
708 if (parts.length == 1) return _mdnTypeComment(parts[0]);
709 }
710
711 /// Generates the MDN Comment for variables and method DOM elements.
712 String _mdnMemberComment(String type, String member) {
713 var mdnType = _mdn[type];
714 if (mdnType == null) return '';
715 var mdnMember = mdnType['members'].firstWhere((e) => e['name'] == member,
716 orElse: () => null);
717 if (mdnMember == null) return '';
718 if (mdnMember['help'] == null || mdnMember['help'] == '') return '';
719 if (mdnMember['url'] == null) return '';
720 return _htmlMdn(mdnMember['help'], mdnMember['url']);
721 }
722
723 /// Generates the MDN Comment for class DOM elements.
724 String _mdnTypeComment(String type) {
725 var mdnType = _mdn[type];
726 if (mdnType == null) return '';
727 if (mdnType['summary'] == null || mdnType['summary'] == "") return '';
728 if (mdnType['srcUrl'] == null) return '';
729 return _htmlMdn(mdnType['summary'], mdnType['srcUrl']);
730 }
731
732 String _htmlMdn(String content, String url) {
733 return '<div class="mdn">' + content.trim() + '<p class="mdn-note">'
734 '<a href="' + url.trim() + '">from Mdn</a></p></div>';
735 }
743 736
744 /// The type of this member to be used in index.txt. 737 /// The type of this member to be used in index.txt.
745 String get typeName => ''; 738 String get typeName => '';
746 739
747 /// Creates a [Map] with this [Indexable]'s name and a preview comment. 740 /// Creates a [Map] with this [Indexable]'s name and a preview comment.
748 Map get previewMap { 741 Map get previewMap {
749 var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName }; 742 var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName };
750 if (comment != '') { 743 if (comment != '') {
751 var index = comment.indexOf('</p>'); 744 var index = comment.indexOf('</p>');
752 finalMap['preview'] = '${comment.substring(0, index)}</p>'; 745 finalMap['preview'] = '${comment.substring(0, index)}</p>';
753 } 746 }
754 return finalMap; 747 return finalMap;
755 } 748 }
756 749
757 /// Returns any documentation comments associated with a mirror with 750 String _getCommentText() {
758 /// simple markdown converted to html.
759 ///
760 /// It's possible to have a comment that comes from one mirror applied to
761 /// another, in the case of an inherited comment.
762 String _commentToHtml(itemToDocument) {
763 String commentText; 751 String commentText;
764 mirror.metadata.forEach((metadata) { 752 mirror.metadata.forEach((metadata) {
765 if (metadata is CommentInstanceMirror) { 753 if (metadata is CommentInstanceMirror) {
766 CommentInstanceMirror comment = metadata; 754 CommentInstanceMirror comment = metadata;
767 if (comment.isDocComment) { 755 if (comment.isDocComment) {
768 if (commentText == null) { 756 if (commentText == null) {
769 commentText = comment.trimmedText; 757 commentText = comment.trimmedText;
770 } else { 758 } else {
771 commentText = '$commentText\n${comment.trimmedText}'; 759 commentText = '$commentText\n${comment.trimmedText}';
772 } 760 }
773 } 761 }
774 } 762 }
775 }); 763 });
776
777 var linkResolver = (name) => fixReferenceWithScope(name, itemToDocument);
778 commentText = commentText == null ? '' :
779 markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver,
780 inlineSyntaxes: markdownSyntaxes);
781 return commentText; 764 return commentText;
782 } 765 }
783 766
767 /// Returns any documentation comments associated with a mirror with
768 /// simple markdown converted to html.
769 ///
770 /// By default we resolve any comment references within our own scope.
771 /// However, if a method is inherited, we want the inherited comments, but
772 /// links to the subclasses's version of the methods.
773 String _commentToHtml([Indexable resolvingScope]) {
774 if (resolvingScope == null) resolvingScope = this;
775 var commentText = _getCommentText();
776 _unresolvedComment = commentText;
777
778 var linkResolver = (name) => resolvingScope.fixReferenceWithScope(name);
779 commentText = commentText == null ? '' :
780 markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver,
781 inlineSyntaxes: _MARKDOWN_SYNTAXES);
782 return commentText;
783 }
784
784 /// Returns a map of [Variable] objects constructed from [mirrorMap]. 785 /// Returns a map of [Variable] objects constructed from [mirrorMap].
785 /// The optional parameter [containingLibrary] is contains data for variables 786 /// The optional parameter [containingLibrary] is contains data for variables
786 /// defined at the top level of a library (potentially for exporting 787 /// defined at the top level of a library (potentially for exporting
787 /// purposes). 788 /// purposes).
788 Map<String, Variable> _createVariables(Map<String, 789 Map<String, Variable> _createVariables(Map<String, VariableMirror> mirrorMap,
789 VariableMirror> mirrorMap, [Library containingLibrary]) { 790 Indexable owner) {
790 var data = {}; 791 var data = {};
791 // TODO(janicejl): When map to map feature is created, replace the below 792 // TODO(janicejl): When map to map feature is created, replace the below
792 // with a filter. Issue(#9590). 793 // with a filter. Issue(#9590).
793 mirrorMap.forEach((String mirrorName, VariableMirror mirror) { 794 mirrorMap.forEach((String mirrorName, VariableMirror mirror) {
794 if (_includePrivate || !_isHidden(mirror)) { 795 if (_Generator._includePrivate || !_isHidden(mirror)) {
795 if (containingLibrary != null && mirror.owner.qualifiedName != 796 var variable = new Variable(mirrorName, mirror, owner);
796 containingLibrary.mirror.qualifiedName) { 797 entityMap[variable.docName] = variable;
797 entityMap[docName(mirror)] = new ExportedVariable(mirrorName, mirror, 798 data[mirrorName] = entityMap[variable.docName];
798 containingLibrary);
799 } else {
800 entityMap[docName(mirror)] = new Variable(mirrorName, mirror);
801 }
802 data[mirrorName] = entityMap[docName(mirror)];
803 } 799 }
804 }); 800 });
805 return data; 801 return data;
806 } 802 }
807 803
808 /// Returns a map of [Method] objects constructed from [mirrorMap]. 804 /// Returns a map of [Method] objects constructed from [mirrorMap].
809 /// The optional parameter [containingLibrary] is contains data for variables 805 /// The optional parameter [containingLibrary] is contains data for variables
810 /// defined at the top level of a library (potentially for exporting 806 /// defined at the top level of a library (potentially for exporting
811 /// purposes). 807 /// purposes).
812 MethodGroup _createMethods(Map<String, MethodMirror> mirrorMap, 808 Map<String, Method> _createMethods(Map<String, MethodMirror> mirrorMap,
813 [Library containingLibrary]) { 809 Indexable owner) {
814 var group = new MethodGroup(); 810 var group = new Map<String, Method>();
815 mirrorMap.forEach((String mirrorName, MethodMirror mirror) { 811 mirrorMap.forEach((String mirrorName, MethodMirror mirror) {
816 if (_includePrivate || !mirror.isPrivate) { 812 if (_Generator._includePrivate || !mirror.isPrivate) {
817 group.addMethod(mirror, containingLibrary); 813 var method = new Method(mirror, owner);
814 entityMap[method.docName] = method;
815 group[mirror.simpleName] = method;
818 } 816 }
819 }); 817 });
820 return group; 818 return group;
821 } 819 }
822 820
823 /// Returns a map of [Parameter] objects constructed from [mirrorList]. 821 /// Returns a map of [Parameter] objects constructed from [mirrorList].
824 Map<String, Parameter> _createParameters(List<ParameterMirror> mirrorList) { 822 Map<String, Parameter> _createParameters(List<ParameterMirror> mirrorList,
823 [Indexable owner]) {
825 var data = {}; 824 var data = {};
826 mirrorList.forEach((ParameterMirror mirror) { 825 mirrorList.forEach((ParameterMirror mirror) {
827 data[mirror.simpleName] = new Parameter(mirror.simpleName, 826 data[mirror.simpleName] = new Parameter(mirror, owner);
828 mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue,
829 _createType(mirror.type), mirror.defaultValue,
830 _createAnnotations(mirror));
831 }); 827 });
832 return data; 828 return data;
833 } 829 }
834 830
835 /// Returns a map of [Generic] objects constructed from the class mirror. 831 /// Returns a map of [Generic] objects constructed from the class mirror.
836 Map<String, Generic> _createGenerics(ClassMirror mirror) { 832 Map<String, Generic> _createGenerics(ClassMirror mirror) {
837 return new Map.fromIterable(mirror.typeVariables, 833 return new Map.fromIterable(mirror.typeVariables,
838 key: (e) => e.toString(), 834 key: (e) => e.toString(),
839 value: (e) => new Generic(e.toString(), e.upperBound.qualifiedName)); 835 value: (e) => new Generic(e));
840 }
841
842 /// Returns a single [Type] object constructed from the Method.returnType
843 /// Type mirror.
844 Type _createType(TypeMirror mirror) {
845 return new Type(docName(mirror), _createTypeGenerics(mirror));
846 }
847
848 /// Returns a list of [Type] objects constructed from TypeMirrors.
849 List<Type> _createTypeGenerics(TypeMirror mirror) {
850 if (mirror is ClassMirror && !mirror.isTypedef) {
851 var innerList = [];
852 mirror.typeArguments.forEach((e) {
853 innerList.add(new Type(docName(e), _createTypeGenerics(e)));
854 });
855 return innerList;
856 }
857 return [];
858 }
859
860 /// Returns a list of meta annotations assocated with a mirror.
861 List<Annotation> _createAnnotations(DeclarationMirror mirror) {
862 var annotationMirrors = mirror.metadata.where((e) =>
863 e is dart2js.Dart2JsConstructedConstantMirror);
864 var annotations = [];
865 annotationMirrors.forEach((annotation) {
866 var parameterList = annotation.type.variables.values
867 .where((e) => e.isFinal)
868 .map((e) => annotation.getField(e.simpleName).reflectee)
869 .where((e) => e != null)
870 .toList();
871 if (!skippedAnnotations.contains(docName(annotation.type))) {
872 annotations.add(new Annotation(docName(annotation.type),
873 parameterList));
874 }
875 });
876 return annotations;
877 } 836 }
878 837
879 /// Return an informative [Object.toString] for debugging. 838 /// Return an informative [Object.toString] for debugging.
880 String toString() => "${super.toString()}(${name.toString()})"; 839 String toString() => "${super.toString()}(${name.toString()})";
881 840
882 /// Return a map representation of this type. 841 /// Return a map representation of this type.
883 Map toMap() {} 842 Map toMap() {}
843
844
845 /// A declaration is private if itself is private, or the owner is private.
846 // Issue(12202) - A declaration is public even if it's owner is private.
847 bool _isHidden(DeclarationMirror mirror) {
848 if (mirror is LibraryMirror) {
849 return _isLibraryPrivate(mirror);
850 } else if (mirror.owner is LibraryMirror) {
851 return (mirror.isPrivate || _isLibraryPrivate(mirror.owner)
852 || mirror.isNameSynthetic);
853 } else {
854 return (mirror.isPrivate || _isHidden(mirror.owner)
855 || owner.mirror.isNameSynthetic);
856 }
857 }
858
859 /// Returns true if a library name starts with an underscore, and false
860 /// otherwise.
861 ///
862 /// An example that starts with _ is _js_helper.
863 /// An example that contains ._ is dart._collection.dev
864 // This is because LibraryMirror.isPrivate returns `false` all the time.
865 bool _isLibraryPrivate(LibraryMirror mirror) {
866 var sdkLibrary = LIBRARIES[mirror.simpleName];
867 if (sdkLibrary != null) {
868 return !sdkLibrary.documented;
869 } else if (mirror.simpleName.startsWith('_') ||
870 mirror.simpleName.contains('._')) {
871 return true;
872 }
873 return false;
874 }
875
876 ////// Top level resolution functions
877 /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
878 static markdown.Node globalFixReference(String name) {
879 // Attempt the look up the whole name up in the scope.
880 String elementName = _findElementInScope(name, '');
881 if (elementName != null) {
882 return new markdown.Element.text('a', elementName);
883 }
884 return _fixComplexReference(name);
885 }
886
887 /// This is a more complex reference. Try to break up if its of the form A<B>
888 /// where A is an alphanumeric string and B is an A, a list of B ("B, B, B"),
889 /// or of the form A<B>. Note: unlike other the other markdown-style links,
890 /// all text inside the square brackets is treated as part of the link (aka
891 /// the * is interpreted literally as a *, not as a indicator for bold <em>.
892 ///
893 /// Example: [foo&lt;_bar_>] will produce
894 /// <a>resolvedFoo</a>&lt;<a>resolved_bar_</a>> rather than an italicized
895 /// version of resolvedBar.
896 static markdown.Node _fixComplexReference(String name) {
897 // Parse into multiple elements we can try to resolve.
898 var tokens = _tokenizeComplexReference(name);
899
900 // Produce an html representation of our elements. Group unresolved and
901 // plain text are grouped into "link" elements so they display as code.
902 final textElements = [' ', ',', '>', _LESS_THAN];
903 var accumulatedHtml = '';
904
905 for (var token in tokens) {
906 bool added = false;
907 if (!textElements.contains(token)) {
908 String elementName = _findElementInScope(token, '');
909 if (elementName != null) {
910 accumulatedHtml += markdown.renderToHtml([new markdown.Element.text(
911 'a', elementName)]);
912 added = true;
913 }
914 }
915 if (!added) {
916 accumulatedHtml += token;
917 }
918 }
919 return new markdown.Text(accumulatedHtml);
920 }
921
922
923 // HTML escaped version of '<' character.
924 static final _LESS_THAN = '&lt;';
925
926 /// Chunk the provided name into individual parts to be resolved. We take a
927 /// simplistic approach to chunking, though, we break at " ", ",", "&lt;"
928 /// and ">". All other characters are grouped into the name to be resolved.
929 /// As a result, these characters will all be treated as part of the item to
930 /// be resolved (aka the * is interpreted literally as a *, not as an
931 /// indicator for bold <em>.
932 static List<String> _tokenizeComplexReference(String name) {
933 var tokens = [];
934 var append = false;
935 var index = 0;
936 while(index < name.length) {
937 if (name.indexOf(_LESS_THAN, index) == index) {
938 tokens.add(_LESS_THAN);
939 append = false;
940 index += _LESS_THAN.length;
941 } else if (name[index] == ' ' || name[index] == ',' ||
942 name[index] == '>') {
943 tokens.add(name[index]);
944 append = false;
945 index++;
946 } else {
947 if (append) {
948 tokens[tokens.length - 1] = tokens.last + name[index];
949 } else {
950 tokens.add(name[index]);
951 append = true;
952 }
953 index++;
954 }
955 }
956 return tokens;
957 }
958
959 static String _findElementInScope(String name, String packagePrefix) {
960 var lookupFunc = determineLookupFunc(name);
961 // Look in the dart core library scope.
962 var coreScope = _coreLibrary == null? null :
963 lookupFunc(_coreLibrary.mirror, name);
964 if (coreScope != null) return packagePrefix + _coreLibrary.docName;
965
966 // If it's a reference that starts with a another library name, then it
967 // looks for a match of that library name in the other sdk libraries.
968 if(name.contains('.')) {
969 var index = name.indexOf('.');
970 var libraryName = name.substring(0, index);
971 var remainingName = name.substring(index + 1);
972 foundLibraryName(library) => library.uri.pathSegments[0] == libraryName;
973
974 if (_sdkLibraries.any(foundLibraryName)) {
975 var library = _sdkLibraries.singleWhere(foundLibraryName);
976 // Look to see if it's a fully qualified library name.
977 var scope = determineLookupFunc(remainingName)(library, remainingName);
978 if (scope != null) {
979 var result = getDocgenObject(scope);
980 if (result is DummyMirror) {
981 return packagePrefix + result.docName;
982 } else {
983 return result.packagePrefix + result.docName;
984 }
985 }
986 }
987 }
988 return null;
989 }
990
991 Map expandMethodMap(Map<String, Method> mapToExpand) => {
992 'setters': recurseMap(_filterMap(new Map(), mapToExpand,
993 (key, val) => val.mirror.isSetter)),
994 'getters': recurseMap(_filterMap(new Map(), mapToExpand,
995 (key, val) => val.mirror.isGetter)),
996 'constructors': recurseMap(_filterMap(new Map(), mapToExpand,
997 (key, val) => val.mirror.isConstructor)),
998 'operators': recurseMap(_filterMap(new Map(), mapToExpand,
999 (key, val) => val.mirror.isOperator)),
1000 'methods': recurseMap(_filterMap(new Map(), mapToExpand,
1001 (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
1002 };
1003
1004 /// Transforms the map by calling toMap on each value in it.
1005 Map recurseMap(Map inputMap) {
1006 var outputMap = {};
1007 inputMap.forEach((key, value) {
1008 if (value is Map) {
1009 outputMap[key] = recurseMap(value);
1010 } else {
1011 outputMap[key] = value.toMap();
1012 }
1013 });
1014 return outputMap;
1015 }
1016
1017 Map _filterMap(exported, map, test) {
1018 map.forEach((key, value) {
1019 if (test(key, value)) exported[key] = value;
1020 });
1021 return exported;
1022 }
1023
1024 bool get _isVisible => _Generator._includePrivate || !isPrivate;
884 } 1025 }
885 1026
886 /// A class containing contents of a Dart library. 1027 /// A class containing contents of a Dart library.
887 class Library extends Indexable { 1028 class Library extends Indexable {
888 1029
889 /// Top-level variables in the library. 1030 /// Top-level variables in the library.
890 Map<String, Variable> variables; 1031 Map<String, Variable> variables;
891 1032
892 /// Top-level functions in the library. 1033 /// Top-level functions in the library.
893 MethodGroup functions; 1034 Map<String, Method> functions;
894 1035
895 /// Classes defined within the library 1036 Map<String, Class> classes = {};
896 ClassGroup classes; 1037 Map<String, Typedef> typedefs = {};
1038 Map<String, Class> errors = {};
897 1039
898 String packageName = ''; 1040 String packageName = '';
899 bool hasBeenCheckedForPackage = false; 1041 bool hasBeenCheckedForPackage = false;
900 String packageIntro; 1042 String packageIntro;
901 1043
902 Map<String, Exported> _exportedMembers; 1044 /// Returns the [Library] for the given [mirror] if it has already been
1045 /// created, else creates it.
1046 factory Library(LibraryMirror mirror) {
1047 var library = getDocgenObject(mirror);
1048 if (library is DummyMirror) {
1049 library = new Library._(mirror);
1050 }
1051 return library;
1052 }
903 1053
904 Library(LibraryMirror libraryMirror) : super(libraryMirror) { 1054 Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
905 var exported = _calcExportedItems(libraryMirror); 1055 var exported = _calcExportedItems(libraryMirror);
906 _createClasses(exported['classes']..addAll(libraryMirror.classes)); 1056 var exportedClasses = exported['classes']..addAll(libraryMirror.classes);
907 this.functions = _createMethods( 1057 _findPackage(mirror);
908 exported['methods']..addAll(libraryMirror.functions), this); 1058 classes = {};
909 this.variables = _createVariables( 1059 typedefs = {};
910 exported['variables']..addAll(libraryMirror.variables), this); 1060 errors = {};
1061 exportedClasses.forEach((String mirrorName, ClassMirror classMirror) {
1062 if (classMirror.isTypedef) {
1063 // This is actually a Dart2jsTypedefMirror, and it does define value,
1064 // but we don't have visibility to that type.
1065 var mirror = classMirror;
1066 if (_Generator._includePrivate || !mirror.isPrivate) {
1067 entityMap[getDocgenObject(mirror).docName] =
1068 new Typedef(mirror, this);
1069 typedefs[mirror.simpleName] =
1070 entityMap[getDocgenObject(mirror).docName];
1071 }
1072 } else {
1073 var clazz = new Class(classMirror, this);
911 1074
912 var exportedVariables = {}; 1075 if (clazz.isError()) {
913 variables.forEach((key, value) { 1076 errors[classMirror.simpleName] = clazz;
914 if (value is ExportedVariable) { 1077 } else if (classMirror.isClass) {
915 exportedVariables[key] = value; 1078 classes[classMirror.simpleName] = clazz;
916 } 1079 } else {
1080 throw new ArgumentError(
1081 '${classMirror.simpleName} - no class type match. ');
1082 }
1083 }
917 }); 1084 });
918 _exportedMembers = new Map.from(this.classes.exported) 1085 this.functions = _createMethods(exported['methods']
919 ..addAll(this.functions.exported) 1086 ..addAll(libraryMirror.functions), this);
920 ..addAll(exportedVariables); 1087 this.variables = _createVariables(exported['variables']
1088 ..addAll(libraryMirror.variables), this);
921 } 1089 }
922 1090
1091 /// Look for the specified name starting with the current member, and
1092 /// progressively working outward to the current library scope.
1093 String findElementInScope(String name) {
1094 var lookupFunc = Indexable.determineLookupFunc(name);
1095 var libraryScope = lookupFunc(mirror, name);
1096 if (libraryScope != null) {
1097 var result = getDocgenObject(libraryScope, this);
1098 if (result is DummyMirror) return packagePrefix + result.docName;
1099 return result.packagePrefix + result.docName;
1100 }
1101 return super.findElementInScope(name);
1102 }
1103
1104 /// For a library's [mirror], determine the name of the package (if any) we
1105 /// believe it came from (because of its file URI).
1106 ///
1107 /// If no package could be determined, we return an empty string.
1108 String _findPackage(LibraryMirror mirror) {
1109 if (mirror == null) return '';
1110 if (hasBeenCheckedForPackage) return packageName;
1111 hasBeenCheckedForPackage = true;
1112 if (mirror.uri.scheme != 'file') return '';
1113 var filePath = mirror.uri.toFilePath();
1114 // We assume that we are documenting only libraries under package/lib
1115 var rootdir = path.dirname((path.dirname(filePath)));
1116 var pubspec = path.join(rootdir, 'pubspec.yaml');
1117 packageName = _packageName(pubspec);
1118 // Associate the package readme with all the libraries. This is a bit
1119 // wasteful, but easier than trying to figure out which partial match
1120 // is best.
1121 packageIntro = _packageIntro(rootdir);
1122 return packageName;
1123 }
1124
1125 String _packageIntro(packageDir) {
1126 var dir = new Directory(packageDir);
1127 var files = dir.listSync();
1128 var readmes = files.where((FileSystemEntity each) => (each is File &&
1129 each.path.substring(packageDir.length + 1, each.path.length)
1130 .startsWith('README'))).toList();
1131 if (readmes.isEmpty) return '';
1132 // If there are multiples, pick the shortest name.
1133 readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
1134 var readme = readmes.first;
1135 var linkResolver = (name) => Indexable.globalFixReference(name);
1136 var contents = markdown.markdownToHtml(readme
1137 .readAsStringSync(), linkResolver: linkResolver,
1138 inlineSyntaxes: _MARKDOWN_SYNTAXES);
1139 return contents;
1140 }
1141
1142 /// Read a pubspec and return the library name.
1143 String _packageName(String pubspecName) {
1144 File pubspec = new File(pubspecName);
1145 if (!pubspec.existsSync()) return '';
1146 var contents = pubspec.readAsStringSync();
1147 var spec = loadYaml(contents);
1148 return spec["name"];
1149 }
1150
1151 markdown.Node fixReferenceWithScope(String name) => fixReference(name);
1152
923 String get packagePrefix => packageName == null || packageName.isEmpty ? 1153 String get packagePrefix => packageName == null || packageName.isEmpty ?
924 '' : '$packageName/'; 1154 '' : '$packageName/';
925 1155
926 Map get previewMap { 1156 Map get previewMap {
927 var basic = super.previewMap; 1157 var basic = super.previewMap;
928 basic['packageName'] = packageName; 1158 basic['packageName'] = packageName;
929 if (packageIntro != null) { 1159 if (packageIntro != null) {
930 basic['packageIntro'] = packageIntro; 1160 basic['packageIntro'] = packageIntro;
931 } 1161 }
932 return basic; 1162 return basic;
933 } 1163 }
934 1164
935 String get owner => ''; 1165 String get name => docName;
936 1166
937 String get name => docName(mirror); 1167 String get docName => mirror.qualifiedName.replaceAll('.','-');
938
939 /// Set our classes field with error, typedef and regular classes.
940 void _createClasses(Map<String, ClassMirror> mirrorMap) {
941 this.classes = new ClassGroup();
942 mirrorMap.forEach((String mirrorName, ClassMirror mirror) {
943 this.classes.addClass(mirror, this);
944 });
945 }
946 1168
947 /// For the given library determine what items (if any) are exported. 1169 /// For the given library determine what items (if any) are exported.
948 /// 1170 ///
949 /// Returns a Map with three keys: "classes", "methods", and "variables" the 1171 /// Returns a Map with three keys: "classes", "methods", and "variables" the
950 /// values of which point to a map of exported name identifiers with values 1172 /// values of which point to a map of exported name identifiers with values
951 /// corresponding to the actual DeclarationMirror. 1173 /// corresponding to the actual DeclarationMirror.
952 Map<String, Map<String, DeclarationMirror>> _calcExportedItems( 1174 Map<String, Map<String, DeclarationMirror>> _calcExportedItems(
953 LibraryMirror library) { 1175 LibraryMirror library) {
954 var exports = {}; 1176 var exports = {};
955 exports['classes'] = {}; 1177 exports['classes'] = {};
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
999 // If there is a show in the export, add only the show items to the 1221 // If there is a show in the export, add only the show items to the
1000 // library. Ex: "export foo show bar" 1222 // library. Ex: "export foo show bar"
1001 // Otherwise, add all items, and then remove the hidden ones. 1223 // Otherwise, add all items, and then remove the hidden ones.
1002 // Ex: "export foo hide bar" 1224 // Ex: "export foo hide bar"
1003 _populateExports(export, 1225 _populateExports(export,
1004 export.combinators.any((combinator) => combinator.isShow)); 1226 export.combinators.any((combinator) => combinator.isShow));
1005 } 1227 }
1006 return exports; 1228 return exports;
1007 } 1229 }
1008 1230
1231 /// Checks if the given name is a key for any of the Class Maps.
1232 bool containsKey(String name) {
1233 return classes.containsKey(name) || errors.containsKey(name);
1234 }
1235
1009 /// Generates a map describing the [Library] object. 1236 /// Generates a map describing the [Library] object.
1010 Map toMap() => { 1237 Map toMap() => {
1011 'name': name, 1238 'name': name,
1012 'qualifiedName': qualifiedName, 1239 'qualifiedName': qualifiedName,
1013 'comment': comment, 1240 'comment': comment,
1014 'variables': recurseMap(variables), 1241 'variables': recurseMap(variables),
1015 'functions': functions.toMap(), 1242 'functions': expandMethodMap(functions),
1016 'classes': classes.toMap(), 1243 'classes': {
1244 'class': classes.values.where((c) => c._isVisible)
1245 .map((e) => e.previewMap).toList(),
1246 'typedef': recurseMap(typedefs),
1247 'error': errors.values.where((e) => e._isVisible)
1248 .map((e) => e.previewMap).toList()
1249 },
1017 'packageName': packageName, 1250 'packageName': packageName,
1018 'packageIntro' : packageIntro 1251 'packageIntro' : packageIntro
1019 }; 1252 };
1020 1253
1021 String get typeName => 'library'; 1254 String get typeName => 'library';
1022 } 1255 }
1023 1256
1024 /// A class containing contents of a Dart class. 1257 /// A class containing contents of a Dart class.
1025 class Class extends Indexable implements Comparable { 1258 class Class extends Indexable implements Comparable {
1026 1259
1027 /// List of the names of interfaces that this class implements. 1260 /// List of the names of interfaces that this class implements.
1028 List<Class> interfaces = []; 1261 List<Class> interfaces = [];
1029 1262
1030 /// Names of classes that extends or implements this class. 1263 /// Names of classes that extends or implements this class.
1031 Set<Class> subclasses = new Set<Class>(); 1264 Set<Class> subclasses = new Set<Class>();
1032 1265
1033 /// Top-level variables in the class. 1266 /// Top-level variables in the class.
1034 Map<String, Variable> variables; 1267 Map<String, Variable> variables;
1035 1268
1036 /// Inherited variables in the class. 1269 /// Inherited variables in the class.
1037 Map<String, Variable> inheritedVariables = {}; 1270 Map<String, Variable> inheritedVariables;
1038 1271
1039 /// Methods in the class. 1272 /// Methods in the class.
1040 MethodGroup methods; 1273 Map<String, Method> methods;
1041 1274
1042 /// Inherited methods in the class. 1275 Map<String, Method> inheritedMethods;
1043 MethodGroup inheritedMethods = new MethodGroup();
1044 1276
1045 /// Generic infomation about the class. 1277 /// Generic infomation about the class.
1046 Map<String, Generic> generics; 1278 Map<String, Generic> generics;
1047 1279
1048 Class superclass; 1280 Class superclass;
1049 bool isAbstract; 1281 bool isAbstract;
1050 1282
1051 /// List of the meta annotations on the class. 1283 /// List of the meta annotations on the class.
1052 List<Annotation> annotations; 1284 List<Annotation> annotations;
1053 1285
1054 /// Make sure that we don't check for inherited comments more than once. 1286 /// Make sure that we don't check for inherited comments more than once.
1055 bool _commentsEnsured = false; 1287 bool _commentsEnsured = false;
1056 1288
1289 Indexable owner;
1290
1057 /// Returns the [Class] for the given [mirror] if it has already been created, 1291 /// Returns the [Class] for the given [mirror] if it has already been created,
1058 /// else creates it. 1292 /// else creates it.
1059 factory Class(ClassMirror mirror) { 1293 factory Class(ClassMirror mirror, Library owner) {
1060 var clazz = entityMap[docName(mirror)]; 1294 var clazz = getDocgenObject(mirror, owner);
1061 if (clazz == null) { 1295 if (clazz is DummyMirror) {
1062 clazz = new Class._(mirror); 1296 clazz = new Class._(mirror, owner);
1063 entityMap[docName(mirror)] = clazz; 1297 entityMap[clazz.docName] = clazz;
1064 } 1298 }
1065 return clazz; 1299 return clazz;
1066 } 1300 }
1067 1301
1068 Class._(ClassMirror classMirror) : super(classMirror) { 1302 /// Called when we are constructing a superclass or interface class, but it
1069 var superclass = classMirror.superclass != null ? 1303 /// is not known if it belongs to the same owner as the original class. In
1070 new Class(classMirror.superclass) : null; 1304 /// this case, we create an object whose owner is what the original mirror
1071 var interfaces = classMirror.superinterfaces.map( 1305 /// says it is.
1072 (interface) => new Class(interface)); 1306 factory Class._possiblyDifferentOwner(ClassMirror mirror,
1307 Library originalOwner) {
1308 if (mirror.owner is LibraryMirror) {
1309 var realOwner = getDocgenObject(mirror.owner);
1310 if (realOwner is Library) {
1311 return new Class(mirror, realOwner);
1312 } else {
1313 return new Class(mirror, originalOwner);
1314 }
1315 } else {
1316 return new Class(mirror, originalOwner);
1317 }
1318 }
1073 1319
1074 this.superclass = superclass; 1320 Class._(ClassMirror classMirror, this.owner) : super(classMirror) {
1075 this.interfaces = interfaces.toList(); 1321 inheritedVariables = {};
1076 this.variables = _createVariables(classMirror.variables);
1077 this.methods = _createMethods(classMirror.methods);
1078 this.annotations = _createAnnotations(classMirror);
1079 this.generics = _createGenerics(classMirror);
1080 this.isAbstract = classMirror.isAbstract;
1081 1322
1082 // Tell all superclasses that you are a subclass. 1323 // The reason we do this madness is the superclass and interface owners may
1083 if (!classMirror.isNameSynthetic && _isVisible(this)) { 1324 // not be this class's owner!! Example: BaseClient in http pkg.
1325 var superinterfaces = classMirror.superinterfaces.map(
1326 (interface) => new Class._possiblyDifferentOwner(interface, owner));
1327 this.superclass = classMirror.superclass == null? null :
1328 new Class._possiblyDifferentOwner(classMirror.superclass, owner);
1329
1330 interfaces = superinterfaces.toList();
1331 variables = _createVariables(classMirror.variables, this);
1332 methods = _createMethods(classMirror.methods, this);
1333 annotations = _createAnnotations(classMirror, this);
1334 generics = _createGenerics(classMirror);
1335 isAbstract = classMirror.isAbstract;
1336 inheritedMethods = new Map<String, Method>();
1337
1338 // Tell all superclasses that you are a subclass, unless you are not
1339 // visible or an intermediary mixin class.
1340 if (!classMirror.isNameSynthetic && _isVisible) {
1084 parentChain().forEach((parentClass) { 1341 parentChain().forEach((parentClass) {
1085 parentClass.addSubclass(this); 1342 parentClass.addSubclass(this);
1086 }); 1343 });
1087 } 1344 }
1088 1345
1089 if (this.superclass != null) addInherited(superclass); 1346 if (this.superclass != null) addInherited(superclass);
1090 interfaces.forEach((interface) => addInherited(interface)); 1347 interfaces.forEach((interface) => addInherited(interface));
1091 } 1348 }
1092 1349
1350 String get packagePrefix => owner.packagePrefix;
1351
1352 String _lookupInClassAndSuperclasses(String name) {
1353 var lookupFunc = Indexable.determineLookupFunc(name);
1354 var classScope = this;
1355 while (classScope != null) {
1356 var classFunc = lookupFunc(classScope.mirror, name);
1357 if (classFunc != null) {
1358 return packagePrefix + getDocgenObject(classFunc, owner).docName;
1359 }
1360 classScope = classScope.superclass;
1361 }
1362 return null;
1363 }
1364
1365 /// Look for the specified name starting with the current member, and
1366 /// progressively working outward to the current library scope.
1367 String findElementInScope(String name) {
1368 var lookupFunc = Indexable.determineLookupFunc(name);
1369 var result = _lookupInClassAndSuperclasses(name);
1370 if (result != null) {
1371 return result;
1372 }
1373 result = owner.findElementInScope(name);
1374 return result == null ? super.findElementInScope(name) : result;
1375 }
1376
1377 markdown.Node fixReferenceWithScope(String name) => fixReference(name);
1378
1093 String get typeName => 'class'; 1379 String get typeName => 'class';
1094 1380
1095 /// Returns a list of all the parent classes. 1381 /// Returns a list of all the parent classes.
1096 List<Class> parentChain() { 1382 List<Class> parentChain() {
1097 var parent = superclass == null ? [] : [superclass]; 1383 var parent = superclass == null ? [] : [superclass];
1098 parent.addAll(interfaces); 1384 parent.addAll(interfaces);
1099 return parent; 1385 return parent;
1100 } 1386 }
1101 1387
1102 /// Add all inherited variables and methods from the provided superclass. 1388 /// Add all inherited variables and methods from the provided superclass.
1103 /// If [_includePrivate] is true, it also adds the variables and methods from 1389 /// If [_includePrivate] is true, it also adds the variables and methods from
1104 /// the superclass. 1390 /// the superclass.
1105 void addInherited(Class superclass) { 1391 void addInherited(Class superclass) {
1106 inheritedVariables.addAll(superclass.inheritedVariables); 1392 inheritedVariables.addAll(superclass.inheritedVariables);
1107 inheritedVariables.addAll(_filterStatics(superclass.variables)); 1393 inheritedVariables.addAll(_allButStatics(superclass.variables));
1108 inheritedMethods.addInherited(superclass); 1394 addInheritedMethod(superclass, this);
1395 }
1396
1397 /** [newParent] refers to the actual class is currently using these methods.
1398 * which may be different because with the mirror system, we only point to the
1399 * original canonical superclasse's method.
1400 */
1401 void addInheritedMethod(Class parent, Class newParent) {
1402 parent.inheritedMethods.forEach((name, method) {
1403 if(!method.isConstructor){
1404 inheritedMethods[name] = new Method(method.mirror, newParent, method);
1405 }}
1406 );
1407 _allButStatics(parent.methods).forEach((name, method) {
1408 if (!method.isConstructor) {
1409 inheritedMethods[name] = new Method(method.mirror, newParent, method);
1410 }}
1411 );
1412 }
1413
1414 /// Remove statics from the map of inherited items before adding them.
1415 Map _allButStatics(Map items) {
1416 var result = {};
1417 items.forEach((name, item) {
1418 if (!item.isStatic) {
1419 result[name] = item;
1420 }
1421 });
1422 return result;
1109 } 1423 }
1110 1424
1111 /// Add the subclass to the class. 1425 /// Add the subclass to the class.
1112 /// 1426 ///
1113 /// If [this] is private, it will add the subclass to the list of subclasses 1427 /// If [this] is private (or an intermediary mixin class), it will add the
1114 /// in the superclasses. 1428 /// subclass to the list of subclasses in the superclasses.
1115 void addSubclass(Class subclass) { 1429 void addSubclass(Class subclass) {
1116 if (!_includePrivate && isPrivate) { 1430 if (docName == 'dart-core.Object') return;
1431
1432 if (!_Generator._includePrivate && isPrivate || mirror.isNameSynthetic) {
1117 if (superclass != null) superclass.addSubclass(subclass); 1433 if (superclass != null) superclass.addSubclass(subclass);
1118 interfaces.forEach((interface) { 1434 interfaces.forEach((interface) {
1119 interface.addSubclass(subclass); 1435 interface.addSubclass(subclass);
1120 }); 1436 });
1121 } else { 1437 } else {
1122 subclasses.add(subclass); 1438 subclasses.add(subclass);
1123 } 1439 }
1124 } 1440 }
1125 1441
1126 /// Check if this [Class] is an error or exception. 1442 /// Check if this [Class] is an error or exception.
1127 bool isError() { 1443 bool isError() {
1128 if (qualifiedName == 'dart-core.Error' || 1444 if (qualifiedName == 'dart-core.Error' ||
1129 qualifiedName == 'dart-core.Exception') 1445 qualifiedName == 'dart-core.Exception')
1130 return true; 1446 return true;
1131 for (var interface in interfaces) { 1447 for (var interface in interfaces) {
1132 if (interface.isError()) return true; 1448 if (interface.isError()) return true;
1133 } 1449 }
1134 if (superclass == null) return false; 1450 if (superclass == null) return false;
1135 return superclass.isError(); 1451 return superclass.isError();
1136 } 1452 }
1137 1453
1138 /// Check that the class exists in the owner library.
1139 ///
1140 /// If it does not exist in the owner library, it is a mixin applciation and
1141 /// should be removed.
1142 void updateLinksAndRemoveIntermediaryClasses() {
1143 var library = entityMap[owner];
1144 if (library != null) {
1145 if (!library.classes.containsKey(name) && mirror.isNameSynthetic) {
1146 // In the mixin case, remove the intermediary classes.
1147 this.isPrivate = true;
1148 // Since we are now making the mixin a private class, make all elements
1149 // with the mixin as an owner private too.
1150 entityMap.values.where((e) => e.owner == qualifiedName).forEach(
1151 (element) => element.isPrivate = true);
1152 // Move the subclass up to the next public superclass
1153 subclasses.forEach((subclass) => addSubclass(subclass));
1154 } else {
1155 // It is an exported item. Loop through each of the exported types,
1156 // and tell them to update their links, given these other exported
1157 // names within the library.
1158 for (Exported member in library._exportedMembers.values) {
1159 member.updateExports(library._exportedMembers);
1160 }
1161 }
1162 }
1163 }
1164
1165 /// Makes sure that all methods with inherited equivalents have comments. 1454 /// Makes sure that all methods with inherited equivalents have comments.
1166 void ensureComments() { 1455 void ensureComments() {
1167 if (_commentsEnsured) return; 1456 if (_commentsEnsured) return;
1168 _commentsEnsured = true; 1457 _commentsEnsured = true;
1458 if (superclass != null) superclass.ensureComments();
1169 inheritedMethods.forEach((qualifiedName, inheritedMethod) { 1459 inheritedMethods.forEach((qualifiedName, inheritedMethod) {
1170 var method = methods[qualifiedName]; 1460 var method = methods[qualifiedName];
1171 if (method != null) method.ensureCommentFor(inheritedMethod); 1461 if (method != null) {
1462 // if we have overwritten this method in this class, we still provide
1463 // the opportunity to inherit the comments.
1464 method.ensureCommentFor(inheritedMethod);
1465 }
1466 });
1467 // we need to populate the comments for all methods. so that the subclasses
1468 // can get for their inherited versions the comments.
1469 methods.forEach((qualifiedName, method) {
1470 if (!method.mirror.isConstructor) method.ensureCommentFor(method);
1172 }); 1471 });
1173 } 1472 }
1174 1473
1175 /// If a class extends a private superclass, find the closest public 1474 /// If a class extends a private superclass, find the closest public
1176 /// superclass of the private superclass. 1475 /// superclass of the private superclass.
1177 String validSuperclass() { 1476 String validSuperclass() {
1178 if (superclass == null) return 'dart.core.Object'; 1477 if (superclass == null) return 'dart.core.Object';
1179 if (_isVisible(superclass)) return superclass.qualifiedName; 1478 if (superclass._isVisible) return superclass.qualifiedName;
1180 return superclass.validSuperclass(); 1479 return superclass.validSuperclass();
1181 } 1480 }
1182 1481
1183 /// Generates a map describing the [Class] object. 1482 /// Generates a map describing the [Class] object.
1184 Map toMap() => { 1483 Map toMap() => {
1185 'name': name, 1484 'name': name,
1186 'qualifiedName': qualifiedName, 1485 'qualifiedName': qualifiedName,
1187 'comment': comment, 1486 'comment': comment,
1188 'isAbstract' : isAbstract, 1487 'isAbstract' : isAbstract,
1189 'superclass': validSuperclass(), 1488 'superclass': validSuperclass(),
1190 'implements': interfaces.where(_isVisible) 1489 'implements': interfaces.where((i) => i._isVisible)
1191 .map((e) => e.qualifiedName).toList(), 1490 .map((e) => e.qualifiedName).toList(),
1192 'subclass': (subclasses.toList()..sort()) 1491 'subclass': (subclasses.toList()..sort())
1193 .map((x) => x.qualifiedName).toList(), 1492 .map((x) => x.qualifiedName).toList(),
1194 'variables': recurseMap(variables), 1493 'variables': recurseMap(variables),
1195 'inheritedVariables': recurseMap(inheritedVariables), 1494 'inheritedVariables': recurseMap(inheritedVariables),
1196 'methods': methods.toMap(), 1495 'methods': expandMethodMap(methods),
1197 'inheritedMethods': inheritedMethods.toMap(), 1496 'inheritedMethods': expandMethodMap(inheritedMethods),
1198 'annotations': annotations.map((a) => a.toMap()).toList(), 1497 'annotations': annotations.map((a) => a.toMap()).toList(),
1199 'generics': recurseMap(generics) 1498 'generics': recurseMap(generics)
1200 }; 1499 };
1201 1500
1202 int compareTo(aClass) => name.compareTo(aClass.name); 1501 int compareTo(aClass) => name.compareTo(aClass.name);
1203 } 1502 }
1204 1503
1205 abstract class Exported {
1206 void updateExports(Map<String, Indexable> libraryExports);
1207 }
1208
1209 Map _filterMap(exported, map, test) {
1210 map.forEach((key, value) {
1211 if (test(value)) exported[key] = value;
1212 });
1213 return exported;
1214 }
1215
1216 class ExportedClass extends Class implements Exported {
1217 Class _originalClass;
1218 Library _exportingLibrary;
1219
1220 ExportedClass(ClassMirror originalClass, Library this._exportingLibrary) :
1221 super._(originalClass) {
1222 _originalClass = new Class(originalClass);
1223 }
1224
1225 // The qualified name (for URL purposes) and the file name are the same,
1226 // of the form packageName/ClassName or packageName/ClassName.methodName.
1227 // This defines both the URL and the directory structure.
1228 String get fileName => path.join(_exportingLibrary.packageName,
1229 _exportingLibrary.mirror.qualifiedName + '.' + _originalClass.name);
1230
1231 void updateExports(Map<String, Indexable> libraryExports) {
1232 // TODO(efortuna): If this class points to another exported class or type
1233 // of some sort, then that reference needs to be updated here.
1234 /* these need to be updated:
1235 'comment': comment,
1236 'superclass': validSuperclass(),
1237 'implements': interfaces.where(_isVisible)
1238 .map((e) => e.qualifiedName).toList(),
1239 'subclass': (subclasses.toList()..sort())
1240 .map((x) => x.qualifiedName).toList(),
1241 'variables': recurseMap(variables),
1242 'inheritedVariables': recurseMap(inheritedVariables),
1243 'methods': methods.toMap(),
1244 'inheritedMethods': inheritedMethods.toMap(),
1245 'annotations': annotations.map((a) => a.toMap()).toList(),
1246 'generics': recurseMap(generics)
1247 */
1248 }
1249 }
1250
1251 /// A container to categorize classes into the following groups: abstract
1252 /// classes, regular classes, typedefs, and errors.
1253 class ClassGroup {
1254 Map<String, Class> classes = {};
1255 Map<String, Typedef> typedefs = {};
1256 Map<String, Class> errors = {};
1257
1258 Map<String, Exported> get exported {
1259 var exported = _filterMap({}, classes, (value) => value is ExportedClass);
1260 // TODO(efortuna): The line below needs updating.
1261 exported = _filterMap(exported, typedefs,
1262 (value) => value is ExportedClass);
1263 exported = _filterMap(exported, errors,
1264 (value) => value is ExportedClass);
1265 return exported;
1266 }
1267
1268 void addClass(ClassMirror classMirror, Library containingLibrary) {
1269 if (classMirror.isTypedef) {
1270 // This is actually a Dart2jsTypedefMirror, and it does define value,
1271 // but we don't have visibility to that type.
1272 var mirror = classMirror;
1273 if (_includePrivate || !mirror.isPrivate) {
1274 entityMap[docName(mirror)] = new Typedef(mirror);
1275 typedefs[mirror.simpleName] = entityMap[docName(mirror)];
1276 }
1277 } else {
1278 var clazz = new Class(classMirror);
1279
1280 if (classMirror.library.qualifiedName !=
1281 containingLibrary.mirror.qualifiedName) {
1282 var exportedClass = new ExportedClass(classMirror, containingLibrary);
1283 entityMap[clazz.fileName] = exportedClass;
1284 clazz = exportedClass;
1285 }
1286
1287 if (clazz.isError()) {
1288 errors[classMirror.simpleName] = clazz;
1289 } else if (classMirror.isClass) {
1290 classes[classMirror.simpleName] = clazz;
1291 } else {
1292 throw new ArgumentError(
1293 '${classMirror.simpleName} - no class type match. ');
1294 }
1295 }
1296 }
1297
1298 /// Checks if the given name is a key for any of the Class Maps.
1299 bool containsKey(String name) {
1300 return classes.containsKey(name) || errors.containsKey(name);
1301 }
1302
1303 Map toMap() => {
1304 'class': classes.values.where(_isVisible)
1305 .map((e) => e.previewMap).toList(),
1306 'typedef': recurseMap(typedefs),
1307 'error': errors.values.where(_isVisible)
1308 .map((e) => e.previewMap).toList()
1309 };
1310 }
1311
1312 class Typedef extends Indexable { 1504 class Typedef extends Indexable {
1313 String returnType; 1505 String returnType;
1314 1506
1315 Map<String, Parameter> parameters; 1507 Map<String, Parameter> parameters;
1316 1508
1317 /// Generic information about the typedef. 1509 /// Generic information about the typedef.
1318 Map<String, Generic> generics; 1510 Map<String, Generic> generics;
1319 1511
1320 /// List of the meta annotations on the typedef. 1512 /// List of the meta annotations on the typedef.
1321 List<Annotation> annotations; 1513 List<Annotation> annotations;
1322 1514
1323 Typedef(mirror) : super(mirror) { 1515 /// Returns the [Library] for the given [mirror] if it has already been
1324 this.returnType = docName(mirror.value.returnType); 1516 /// created, else creates it.
1325 this.generics = _createGenerics(mirror); 1517 factory Typedef(TypedefMirror mirror, Library owningLibrary) {
1326 this.parameters = _createParameters(mirror.value.parameters); 1518 var aTypedef = getDocgenObject(mirror, owningLibrary);
1327 this.annotations = _createAnnotations(mirror); 1519 if (aTypedef is DummyMirror) {
1520 aTypedef = new Typedef._(mirror, owningLibrary);
1521 }
1522 return aTypedef;
1523 }
1524
1525 Typedef._(TypedefMirror mirror, Library owningLibrary) : super(mirror) {
1526 owner = owningLibrary;
1527 returnType = getDocgenObject(mirror.value.returnType).docName;
1528 generics = _createGenerics(mirror);
1529 parameters = _createParameters(mirror.value.parameters);
1530 annotations = _createAnnotations(mirror, this);
1328 } 1531 }
1329 1532
1330 Map toMap() => { 1533 Map toMap() => {
1331 'name': name, 1534 'name': name,
1332 'qualifiedName': qualifiedName, 1535 'qualifiedName': qualifiedName,
1333 'comment': comment, 1536 'comment': comment,
1334 'return': returnType, 1537 'return': returnType,
1335 'parameters': recurseMap(parameters), 1538 'parameters': recurseMap(parameters),
1336 'annotations': annotations.map((a) => a.toMap()).toList(), 1539 'annotations': annotations.map((a) => a.toMap()).toList(),
1337 'generics': recurseMap(generics) 1540 'generics': recurseMap(generics)
1338 }; 1541 };
1339 1542
1340 String get typeName => 'typedef'; 1543 String get typeName => 'typedef';
1341 } 1544 }
1342 1545
1343 /// A class containing properties of a Dart variable. 1546 /// A class containing properties of a Dart variable.
1344 class Variable extends Indexable { 1547 class Variable extends Indexable {
1345 1548
1346 bool isFinal; 1549 bool isFinal;
1347 bool isStatic; 1550 bool isStatic;
1348 bool isConst; 1551 bool isConst;
1349 Type type; 1552 Type type;
1350 String _variableName; 1553 String _variableName;
1554 Indexable owner;
1351 1555
1352 /// List of the meta annotations on the variable. 1556 /// List of the meta annotations on the variable.
1353 List<Annotation> annotations; 1557 List<Annotation> annotations;
1354 1558
1355 Variable(this._variableName, VariableMirror mirror) : super(mirror) { 1559 factory Variable(String variableName, VariableMirror mirror,
1356 this.isFinal = mirror.isFinal; 1560 Indexable owner) {
1357 this.isStatic = mirror.isStatic; 1561 var variable = getDocgenObject(mirror);
1358 this.isConst = mirror.isConst; 1562 if (variable is DummyMirror) {
1359 this.type = _createType(mirror.type); 1563 return new Variable._(variableName, mirror, owner);
1360 this.annotations = _createAnnotations(mirror); 1564 }
1565 return variable;
1566 }
1567
1568 Variable._(this._variableName, VariableMirror mirror, this.owner) :
1569 super(mirror) {
1570 isFinal = mirror.isFinal;
1571 isStatic = mirror.isStatic;
1572 isConst = mirror.isConst;
1573 type = new Type(mirror.type, _getOwningLibrary(owner));
1574 annotations = _createAnnotations(mirror, _getOwningLibrary(owner));
1361 } 1575 }
1362 1576
1363 String get name => _variableName; 1577 String get name => _variableName;
1364 1578
1365 /// Generates a map describing the [Variable] object. 1579 /// Generates a map describing the [Variable] object.
1366 Map toMap() => { 1580 Map toMap() => {
1367 'name': name, 1581 'name': name,
1368 'qualifiedName': qualifiedName, 1582 'qualifiedName': qualifiedName,
1369 'comment': comment, 1583 'comment': comment,
1370 'final': isFinal.toString(), 1584 'final': isFinal.toString(),
1371 'static': isStatic.toString(), 1585 'static': isStatic.toString(),
1372 'constant': isConst.toString(), 1586 'constant': isConst.toString(),
1373 'type': new List.filled(1, type.toMap()), 1587 'type': new List.filled(1, type.toMap()),
1374 'annotations': annotations.map((a) => a.toMap()).toList() 1588 'annotations': annotations.map((a) => a.toMap()).toList()
1375 }; 1589 };
1376 1590
1591 String get packagePrefix => owner.packagePrefix;
1592
1377 String get typeName => 'property'; 1593 String get typeName => 'property';
1378 1594
1379 get comment { 1595 get comment {
1380 if (_comment != null) return _comment; 1596 if (_comment != null) return _comment;
1381 var owningClass = owningEntity; 1597 if (owner is Class) {
1382 if (owningClass is Class) { 1598 (owner as Class).ensureComments();
1383 owningClass.ensureComments();
1384 } 1599 }
1385 return super.comment; 1600 return super.comment;
1386 } 1601 }
1387 }
1388 1602
1389 class ExportedVariable extends Variable implements Exported { 1603 markdown.Node fixReferenceWithScope(String name) => fixReference(name);
1390 Library _exportingLibrary;
1391 1604
1392 ExportedVariable(String variableName, VariableMirror originalVariable, 1605 String findElementInScope(String name) {
1393 Library this._exportingLibrary) : super(variableName, originalVariable); 1606 var lookupFunc = Indexable.determineLookupFunc(name);
1607 var result = lookupFunc(mirror, name);
1608 if (result != null) {
1609 result = getDocgenObject(result);
1610 if (result is DummyMirror) return packagePrefix + result.docName;
1611 return result.packagePrefix + result.docName;
1612 }
1394 1613
1395 String get fileName => path.join(_exportingLibrary.packageName, 1614 if (owner != null) {
1396 _exportingLibrary.mirror.qualifiedName + '.' + super.name); 1615 var result = owner.findElementInScope(name);
1397 1616 if (result != null) {
1398 void updateExports(Map<String, Indexable> libraryExports) { 1617 return result;
1399 // TODO(efortuna): if this class points to another exported class or type 1618 }
1400 // of some sort, then that reference needs to be updated here. 1619 }
1401 /* these need to be updated: 1620 return super.findElementInScope(name);
1402 'comment': comment,
1403 'type': new List.filled(1, type.toMap()),
1404 'annotations': annotations.map((a) => a.toMap()).toList()
1405 */
1406 } 1621 }
1407 } 1622 }
1408 1623
1409 /// A class containing properties of a Dart method. 1624 /// A class containing properties of a Dart method.
1410 class Method extends Indexable { 1625 class Method extends Indexable {
1411 1626
1412 /// Parameters for this method. 1627 /// Parameters for this method.
1413 Map<String, Parameter> parameters; 1628 Map<String, Parameter> parameters;
1414 1629
1415 bool isStatic; 1630 bool isStatic;
1416 bool isAbstract; 1631 bool isAbstract;
1417 bool isConst; 1632 bool isConst;
1418 bool isConstructor; 1633 bool isConstructor;
1419 bool isGetter; 1634 bool isGetter;
1420 bool isSetter; 1635 bool isSetter;
1421 bool isOperator; 1636 bool isOperator;
1422 Type returnType; 1637 Type returnType;
1638 Method methodInheritedFrom;
1423 1639
1424 /// Qualified name to state where the comment is inherited from. 1640 /// Qualified name to state where the comment is inherited from.
1425 String commentInheritedFrom = ""; 1641 String commentInheritedFrom = "";
1426 1642
1427 /// List of the meta annotations on the method. 1643 /// List of the meta annotations on the method.
1428 List<Annotation> annotations; 1644 List<Annotation> annotations;
1429 1645
1430 Method(MethodMirror mirror) : super(mirror) { 1646 Indexable owner;
1647
1648 factory Method(MethodMirror mirror, Indexable owner,
1649 [Method methodInheritedFrom]) {
1650 var method = getDocgenObject(mirror, owner);
1651 if (method is DummyMirror) {
1652 method = new Method._(mirror, owner, methodInheritedFrom);
1653 }
1654 return method;
1655 }
1656
1657 Method._(MethodMirror mirror, this.owner, this.methodInheritedFrom)
1658 : super(mirror) {
1431 this.isStatic = mirror.isStatic; 1659 this.isStatic = mirror.isStatic;
1432 this.isAbstract = mirror.isAbstract; 1660 this.isAbstract = mirror.isAbstract;
1433 this.isConst = mirror.isConstConstructor; 1661 this.isConst = mirror.isConstConstructor;
1434 this.returnType = _createType(mirror.returnType); 1662 this.returnType = new Type(mirror.returnType, _getOwningLibrary(owner));
1435 this.parameters = _createParameters(mirror.parameters); 1663 this.parameters = _createParameters(mirror.parameters, owner);
1436 this.annotations = _createAnnotations(mirror); 1664 this.annotations = _createAnnotations(mirror, _getOwningLibrary(owner));
1437 this.isConstructor = mirror.isConstructor; 1665 this.isConstructor = mirror.isConstructor;
1438 this.isGetter = mirror.isGetter; 1666 this.isGetter = mirror.isGetter;
1439 this.isSetter = mirror.isSetter; 1667 this.isSetter = mirror.isSetter;
1440 this.isOperator = mirror.isOperator; 1668 this.isOperator = mirror.isOperator;
1441 } 1669 }
1442 1670
1671 String get packagePrefix => owner.packagePrefix;
1672
1673 markdown.Node fixReferenceWithScope(String name) => fixReference(name);
1674
1675 /// Look for the specified name starting with the current member, and
1676 /// progressively working outward to the current library scope.
1677 String findElementInScope(String name) {
1678 var lookupFunc = Indexable.determineLookupFunc(name);
1679
1680 var memberScope = lookupFunc(this.mirror, name);
1681 if (memberScope != null) {
1682 // do we check for a dummy mirror returned here and look up with an owner
1683 // higher ooooor in getDocgenObject do we include more things in our
1684 // lookup
1685 var result = getDocgenObject(memberScope, owner);
1686 if (result is DummyMirror && owner.owner != null
1687 && owner.owner is! DummyMirror) {
1688 var aresult = getDocgenObject(memberScope, owner.owner);
1689 if (aresult is! DummyMirror) result = aresult;
1690 }
1691 if (result is DummyMirror) return packagePrefix + result.docName;
1692 return result.packagePrefix + result.docName;
1693 }
1694
1695 if (owner != null) {
1696 var result = owner.findElementInScope(name);
1697 if (result != null) return result;
1698 }
1699 return super.findElementInScope(name);
1700 }
1701
1702 String get docName {
1703 if ((mirror as MethodMirror).isConstructor) {
1704 // We name constructors specially -- including the class name again and a
1705 // "-" to separate the constructor from its name (if any).
1706 return '${mirror.owner.simpleName.replaceAll(".", "_")}.'
1707 '${mirror.owner.simpleName}-${mirror.simpleName}';
1708 }
1709 return super.docName;
1710 }
1711
1443 /// Makes sure that the method with an inherited equivalent have comments. 1712 /// Makes sure that the method with an inherited equivalent have comments.
1444 void ensureCommentFor(Method inheritedMethod) { 1713 void ensureCommentFor(Method inheritedMethod) {
1445 if (comment.isNotEmpty) return; 1714 if (comment.isNotEmpty) return;
1446 comment = inheritedMethod._commentToHtml(mirror); 1715
1716 comment = inheritedMethod._commentToHtml(this);
1717 _unresolvedComment = inheritedMethod._unresolvedComment;
1447 commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ? 1718 commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
1448 inheritedMethod.qualifiedName : inheritedMethod.commentInheritedFrom; 1719 inheritedMethod.qualifiedName : inheritedMethod.commentInheritedFrom;
1449 } 1720 }
1450 1721
1451 /// Generates a map describing the [Method] object. 1722 /// Generates a map describing the [Method] object.
1452 Map toMap() => { 1723 Map toMap() => {
1453 'name': name, 1724 'name': name,
1454 'qualifiedName': qualifiedName, 1725 'qualifiedName': qualifiedName,
1455 'comment': comment, 1726 'comment': comment,
1456 'commentFrom': commentInheritedFrom, 1727 'commentFrom': (methodInheritedFrom != null &&
1728 commentInheritedFrom == methodInheritedFrom.docName ? ''
1729 : commentInheritedFrom),
1730 'inheritedFrom': (methodInheritedFrom == null? '' :
1731 methodInheritedFrom.docName),
1457 'static': isStatic.toString(), 1732 'static': isStatic.toString(),
1458 'abstract': isAbstract.toString(), 1733 'abstract': isAbstract.toString(),
1459 'constant': isConst.toString(), 1734 'constant': isConst.toString(),
1460 'return': new List.filled(1, returnType.toMap()), 1735 'return': new List.filled(1, returnType.toMap()),
1461 'parameters': recurseMap(parameters), 1736 'parameters': recurseMap(parameters),
1462 'annotations': annotations.map((a) => a.toMap()).toList() 1737 'annotations': annotations.map((a) => a.toMap()).toList()
1463 }; 1738 };
1464 1739
1465 String get typeName => isConstructor ? 'constructor' : 1740 String get typeName => isConstructor ? 'constructor' :
1466 isGetter ? 'getter' : isSetter ? 'setter' : 1741 isGetter ? 'getter' : isSetter ? 'setter' :
1467 isOperator ? 'operator' : 'method'; 1742 isOperator ? 'operator' : 'method';
1468 1743
1469 get comment { 1744 get comment {
1470 if (_comment != null) return _comment; 1745 if (_comment != null) return _comment;
1471 var owningClass = owningEntity; 1746 if (owner is Class) {
1472 if (owningClass is Class) { 1747 (owner as Class).ensureComments();
1473 owningClass.ensureComments();
1474 } 1748 }
1475 return super.comment; 1749 var result = super.comment;
1476 } 1750 if (result == '' && methodInheritedFrom != null) {
1477 } 1751 // this should be NOT from the MIRROR, but from the COMMENT
1752 _unresolvedComment = methodInheritedFrom._unresolvedComment;
1478 1753
1479 class ExportedMethod extends Method implements Exported { 1754 var linkResolver = (name) => fixReferenceWithScope(name);
1480 Library _exportingLibrary; 1755 comment = _unresolvedComment == null ? '' :
1481 1756 markdown.markdownToHtml(_unresolvedComment.trim(),
1482 ExportedMethod(MethodMirror originalMethod, Library this._exportingLibrary) : 1757 linkResolver: linkResolver, inlineSyntaxes: _MARKDOWN_SYNTAXES);
1483 super(originalMethod); 1758 commentInheritedFrom = methodInheritedFrom.commentInheritedFrom;
1484 1759 result = comment;
1485 // TODO(efortuna): Refactor this code so the exported items can share this 1760 //print('result was $comment');
1486 // behavior.
1487 String get fileName => path.join(_exportingLibrary.packageName,
1488 _exportingLibrary.mirror.qualifiedName + '.' + super.name);
1489
1490 void updateExports(Map<String, Indexable> libraryExports) {
1491 // TODO(efortuna): if this class points to another exported class or type
1492 // of some sort, then that reference needs to be updated here.
1493 /* these need to be updated:
1494 'qualifiedName': qualifiedName,
1495 'comment': comment,
1496 'commentFrom': commentInheritedFrom,
1497 'return': new List.filled(1, returnType.toMap()),
1498 'parameters': recurseMap(parameters),
1499 'annotations': annotations.map((a) => a.toMap()).toList()
1500 */
1501 }
1502 }
1503
1504
1505
1506 /// A container to categorize methods into the following groups: setters,
1507 /// getters, constructors, operators, regular methods.
1508 class MethodGroup {
1509 Map<String, Method> setters = {};
1510 Map<String, Method> getters = {};
1511 Map<String, Method> constructors = {};
1512 Map<String, Method> operators = {};
1513 Map<String, Method> regularMethods = {};
1514
1515 Map<String, Exported> get exported {
1516 var exported = {};
1517 for (Map<String, Method> group in [setters, getters, constructors,
1518 operators, regularMethods]) {
1519 exported = _filterMap(exported, group,
1520 (value) => value is ExportedMethod);
1521 } 1761 }
1522 return exported; 1762 return result;
1523 }
1524
1525 /// The optional parameter [containingLibrary] is contains data for variables
1526 /// defined at the top level of a library (potentially for exporting
1527 /// purposes).
1528 void addMethod(MethodMirror mirror, [Library containingLibrary]) {
1529 var method;
1530 if (containingLibrary != null && mirror.owner.qualifiedName !=
1531 containingLibrary.mirror.qualifiedName) {
1532 method = new ExportedMethod(mirror, containingLibrary);
1533 } else {
1534 method = new Method(mirror);
1535 }
1536 entityMap[docName(mirror)] = method;
1537 if (mirror.isSetter) {
1538 setters[mirror.simpleName] = method;
1539 } else if (mirror.isGetter) {
1540 getters[mirror.simpleName] = method;
1541 } else if (mirror.isConstructor) {
1542 constructors[mirror.simpleName] = method;
1543 } else if (mirror.isOperator) {
1544 operators[mirror.simpleName] = method;
1545 } else if (mirror.isRegularMethod) {
1546 regularMethods[mirror.simpleName] = method;
1547 } else {
1548 throw new ArgumentError('${mirror.simpleName} - no method type match');
1549 }
1550 }
1551
1552 void addInherited(Class parent) {
1553 setters.addAll(parent.inheritedMethods.setters);
1554 setters.addAll(_filterStatics(parent.methods.setters));
1555 getters.addAll(parent.inheritedMethods.getters);
1556 getters.addAll(_filterStatics(parent.methods.getters));
1557 operators.addAll(parent.inheritedMethods.operators);
1558 operators.addAll(_filterStatics(parent.methods.operators));
1559 regularMethods.addAll(parent.inheritedMethods.regularMethods);
1560 regularMethods.addAll(_filterStatics(parent.methods.regularMethods));
1561 }
1562
1563 Map toMap() => {
1564 'setters': recurseMap(setters),
1565 'getters': recurseMap(getters),
1566 'constructors': recurseMap(constructors),
1567 'operators': recurseMap(operators),
1568 'methods': recurseMap(regularMethods)
1569 };
1570
1571 Method operator [](String qualifiedName) {
1572 if (setters.containsKey(qualifiedName)) return setters[qualifiedName];
1573 if (getters.containsKey(qualifiedName)) return getters[qualifiedName];
1574 if (operators.containsKey(qualifiedName)) return operators[qualifiedName];
1575 if (regularMethods.containsKey(qualifiedName)) {
1576 return regularMethods[qualifiedName];
1577 }
1578 return null;
1579 }
1580
1581 void forEach(void f(String key, Method value)) {
1582 setters.forEach(f);
1583 getters.forEach(f);
1584 operators.forEach(f);
1585 regularMethods.forEach(f);
1586 } 1763 }
1587 } 1764 }
1588 1765
1589 /// A class containing properties of a Dart method/function parameter. 1766 /// A class containing properties of a Dart method/function parameter.
1590 class Parameter { 1767 class Parameter extends MirrorBased {
1591 1768
1769 ParameterMirror mirror;
1592 String name; 1770 String name;
1593 bool isOptional; 1771 bool isOptional;
1594 bool isNamed; 1772 bool isNamed;
1595 bool hasDefaultValue; 1773 bool hasDefaultValue;
1596 Type type; 1774 Type type;
1597 String defaultValue; 1775 String defaultValue;
1598 1776
1599 /// List of the meta annotations on the parameter. 1777 /// List of the meta annotations on the parameter.
1600 List<Annotation> annotations; 1778 List<Annotation> annotations;
1601 1779
1602 Parameter(this.name, this.isOptional, this.isNamed, this.hasDefaultValue, 1780 Parameter(this.mirror, [Indexable owner]) {
1603 this.type, this.defaultValue, this.annotations); 1781 name = mirror.simpleName;
1782 isOptional = mirror.isOptional;
1783 isNamed = mirror.isNamed;
1784 hasDefaultValue = mirror.hasDefaultValue;
1785 defaultValue = mirror.defaultValue;
1786 type = new Type(mirror.type, owner);
1787 annotations = _createAnnotations(mirror, this);
1788 }
1604 1789
1605 /// Generates a map describing the [Parameter] object. 1790 /// Generates a map describing the [Parameter] object.
1606 Map toMap() => { 1791 Map toMap() => {
1607 'name': name, 1792 'name': name,
1608 'optional': isOptional.toString(), 1793 'optional': isOptional.toString(),
1609 'named': isNamed.toString(), 1794 'named': isNamed.toString(),
1610 'default': hasDefaultValue.toString(), 1795 'default': hasDefaultValue.toString(),
1611 'type': new List.filled(1, type.toMap()), 1796 'type': new List.filled(1, type.toMap()),
1612 'value': defaultValue, 1797 'value': defaultValue,
1613 'annotations': annotations.map((a) => a.toMap()).toList() 1798 'annotations': annotations.map((a) => a.toMap()).toList()
1614 }; 1799 };
1615 } 1800 }
1616 1801
1617 /// A class containing properties of a Generic. 1802 /// A Docgen wrapper around the dart2js mirror for a generic type.
1618 class Generic { 1803 class Generic extends MirrorBased {
1619 String name; 1804 TypeVariableMirror mirror;
1620 String type; 1805 Generic(this.mirror);
1621
1622 Generic(this.name, this.type);
1623
1624 Map toMap() => { 1806 Map toMap() => {
1625 'name': name, 1807 'name': mirror.toString(),
1626 'type': type 1808 'type': mirror.upperBound.qualifiedName
1627 }; 1809 };
1628 } 1810 }
1629 1811
1630 /// Holds the name of a return type, and its generic type parameters. 1812 /// Holds the name of a return type, and its generic type parameters.
1631 /// 1813 ///
1632 /// Return types are of a form [outer]<[inner]>. 1814 /// Return types are of a form [outer]<[inner]>.
1633 /// If there is no [inner] part, [inner] will be an empty list. 1815 /// If there is no [inner] part, [inner] will be an empty list.
1634 /// 1816 ///
1635 /// For example: 1817 /// For example:
1636 /// int size() 1818 /// int size()
(...skipping 11 matching lines...) Expand all
1648 /// Map<String, List<int>> 1830 /// Map<String, List<int>>
1649 /// "return" : 1831 /// "return" :
1650 /// - "outer" : "dart-core.Map" 1832 /// - "outer" : "dart-core.Map"
1651 /// "inner" : 1833 /// "inner" :
1652 /// - "outer" : "dart-core.String" 1834 /// - "outer" : "dart-core.String"
1653 /// "inner" : 1835 /// "inner" :
1654 /// - "outer" : "dart-core.List" 1836 /// - "outer" : "dart-core.List"
1655 /// "inner" : 1837 /// "inner" :
1656 /// - "outer" : "dart-core.int" 1838 /// - "outer" : "dart-core.int"
1657 /// "inner" : 1839 /// "inner" :
1658 class Type { 1840 class Type extends MirrorBased {
1659 String outer; 1841 TypeMirror mirror;
1660 List<Type> inner; 1842 MirrorBased owner;
1661 1843
1662 Type(this.outer, this.inner); 1844 factory Type(TypeMirror mirror, [MirrorBased owner]) {
1845 return new Type._(mirror, owner);
1846 }
1663 1847
1664 Map toMap() => { 1848 Type._(this.mirror, this.owner);
1665 'outer': outer, 1849
1666 'inner': inner.map((e) => e.toMap()).toList() 1850 /// Returns a list of [Type] objects constructed from TypeMirrors.
1667 }; 1851 List<Type> _createTypeGenerics(TypeMirror mirror) {
1852 if (mirror is ClassMirror && !mirror.isTypedef) {
1853 var innerList = [];
1854 mirror.typeArguments.forEach((e) {
1855 innerList.add(new Type(e, owner));
1856 });
1857 return innerList;
1858 }
1859 return [];
1860 }
1861
1862 Map toMap() {
1863 // We may encounter types whose corresponding library has not been
1864 // processed yet, so look up the owner at the last moment.
1865 var result = getDocgenObject(mirror, owner);
1866 return {
1867 'outer': result.docName,
1868 'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
1869 };
1870 }
1668 } 1871 }
1669 1872
1670 /// Holds the name of the annotation, and its parameters. 1873 /// Holds the name of the annotation, and its parameters.
1671 class Annotation { 1874 class Annotation extends MirrorBased {
1672 String qualifiedName;
1673 List<String> parameters; 1875 List<String> parameters;
1876 /// The class of this annotation.
1877 ClassMirror mirror;
1674 1878
1675 Annotation(this.qualifiedName, this.parameters); 1879 Annotation(InstanceMirror originalMirror, MirrorBased annotationOwner) {
1880 mirror = originalMirror.type;
1881 parameters = originalMirror.type.variables.values
1882 .where((e) => e.isFinal)
1883 .map((e) => originalMirror.getField(e.simpleName).reflectee)
1884 .where((e) => e != null)
1885 .toList();
1886 owner = annotationOwner;
1887 }
1676 1888
1677 Map toMap() => { 1889 Map toMap() => {
1678 'name': qualifiedName, 1890 'name': getDocgenObject(mirror, owner).docName,
1679 'parameters': parameters 1891 'parameters': parameters
1680 }; 1892 };
1681 } 1893 }
1682
1683 /// Given a mirror, returns its qualified name, but following the conventions
1684 /// we're using in Dartdoc, which is that library names with dots in them
1685 /// have them replaced with hyphens.
1686 String docName(DeclarationMirror m) {
1687 if (m is LibraryMirror) {
1688 return m.qualifiedName.replaceAll('.','-');
1689 }
1690 var owner = m.owner;
1691 if (owner == null) return m.qualifiedName;
1692 var simpleName = m.simpleName;
1693 if (m is MethodMirror && m.isConstructor) {
1694 // We name constructors specially -- including the class name again and a
1695 // "-" to separate the constructor from its name (if any).
1696 simpleName = '${owner.simpleName}-$simpleName';
1697 }
1698 return docName(owner) + '.' + simpleName;
1699 }
1700
1701 /// Remove statics from the map of inherited items before adding them.
1702 Map _filterStatics(Map items) {
1703 var result = {};
1704 items.forEach((name, item) {
1705 if (!item.isStatic) {
1706 result[name] = item;
1707 }
1708 });
1709 return result;
1710 }
OLDNEW
« 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