OLD | NEW |
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 Loading... |
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 = '<'; | |
563 | |
564 /// Chunk the provided name into individual parts to be resolved. We take a | |
565 /// simplistic approach to chunking, though, we break at " ", ",", "<" | |
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<_bar_>] will produce | |
604 /// <a>resolvedFoo</a><<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<_bar_>] will produce |
| 894 /// <a>resolvedFoo</a><<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 = '<'; |
| 925 |
| 926 /// Chunk the provided name into individual parts to be resolved. We take a |
| 927 /// simplistic approach to chunking, though, we break at " ", ",", "<" |
| 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 Loading... |
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 Loading... |
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 } | |
OLD | NEW |