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 /** | 5 import 'dart:io'; |
6 * The docgen tool takes in a library as input and produces documentation | |
7 * for the library as well as all libraries it imports and uses. The tool can | |
8 * be run by passing in the path to a .dart file like this: | |
9 * | |
10 * ./dart docgen.dart path/to/file.dart | |
11 * | |
12 * This outputs information about all classes, variables, functions, and | |
13 * methods defined in the library and its imported libraries. | |
14 */ | |
15 library docgen; | |
16 | 6 |
17 // TODO(tmandel): Use 'package:' references for imports with relative paths. | 7 import 'package:args/args.dart'; |
18 import 'dart:io'; | 8 import 'package:logging/logging.dart'; |
19 import 'dart:json'; | 9 |
20 import 'dart:async'; | 10 import '../lib/docgen.dart'; |
21 import '../lib/dart2yaml.dart'; | |
22 import '../lib/src/dart2js_mirrors.dart'; | |
23 import 'package:markdown/markdown.dart' as markdown; | |
24 import '../../args/lib/args.dart'; | |
25 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart'
; | 11 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart'
; |
26 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.
dart'; | 12 import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.
dart'; |
27 | 13 |
28 /** | 14 /** |
29 * Entry function to create YAML documentation from Dart files. | 15 * Analyzes Dart files and generates a representation of included libraries, |
| 16 * classes, and members. |
30 */ | 17 */ |
31 void main() { | 18 void main() { |
32 // TODO(tmandel): Use args library once flags are clear. | 19 logger.onRecord.listen((record) => print(record.message)); |
33 Options opts = new Options(); | 20 var results = initArgParser().parse(new Options().arguments); |
34 Docgen docgen = new Docgen(); | 21 if (results["help"]) return; |
35 | 22 new Docgen(results).analyze(listLibraries(results.rest)); |
36 if (opts.arguments.length > 0) { | |
37 List<Path> libraries = [new Path(opts.arguments[0])]; | |
38 Path sdkDirectory = new Path("../../../sdk/"); | |
39 var workingMirrors = analyze(libraries, sdkDirectory, | |
40 options: ['--preserve-comments', '--categories=Client,Server']); | |
41 workingMirrors.then( (MirrorSystem mirrorSystem) { | |
42 var mirrors = mirrorSystem.libraries.values; | |
43 if (mirrors.isEmpty) { | |
44 print("no LibraryMirrors"); | |
45 } else { | |
46 docgen.libraries = mirrors; | |
47 docgen.documentLibraries(); | |
48 } | |
49 }); | |
50 } | |
51 } | 23 } |
52 | 24 |
53 /** | 25 /** |
54 * This class documents a list of libraries. | 26 * Creates parser for docgen command line arguments. |
55 */ | 27 */ |
56 class Docgen { | 28 ArgParser initArgParser() { |
57 | 29 var parser = new ArgParser(); |
58 /// Libraries to be documented. | 30 parser.addFlag("help", abbr: "h", |
59 List<LibraryMirror> _libraries; | 31 help: "Prints help and usage information.", |
| 32 negatable: false, |
| 33 callback: (help) { |
| 34 if (help) { |
| 35 logger.info(parser.getUsage()); |
| 36 logger.info(usage); |
| 37 } |
| 38 }); |
| 39 parser.addFlag("verbose", abbr: "v", |
| 40 help: "Output more logging information.", negatable: false, |
| 41 callback: (verbose) { |
| 42 if (verbose) Logger.root.level = Level.FINEST; |
| 43 }); |
| 44 parser.addOption("output-format", abbr: "o", |
| 45 help: "Sets the output format.", |
| 46 allowed: ["yaml", "json"], |
| 47 allowedHelp: {"yaml" : "Outputs to YAML.", |
| 48 "json" : "Outputs to JSON."}); |
| 49 parser.addFlag("yaml", abbr: "y", |
| 50 help: "Same as output-format=yaml.", negatable: false); |
| 51 parser.addFlag("json", abbr: "j", |
| 52 help: "Same as output-format=json.", negatable: false); |
| 53 parser.addFlag("include-private", |
| 54 help: "Flag to include private declarations.", negatable: false); |
| 55 parser.addFlag("include-sdk", |
| 56 help: "Flag to parse SDK Library files.", negatable: false); |
60 | 57 |
61 /// Saves list of libraries for Docgen object. | 58 return parser; |
62 void set libraries(value) => _libraries = value; | |
63 | |
64 /// Current library being documented to be used for comment links. | |
65 LibraryMirror _currentLibrary; | |
66 | |
67 /// Current class being documented to be used for comment links. | |
68 ClassMirror _currentClass; | |
69 | |
70 /// Current member being documented to be used for comment links. | |
71 MemberMirror _currentMember; | |
72 | |
73 /// Should the output file type be JSON? | |
74 // TODO(tmandel): Add flag to allow for output to JSON. | |
75 bool outputToJson = false; | |
76 | |
77 /// Resolves reference links | |
78 markdown.Resolver linkResolver; | |
79 | |
80 /** | |
81 * Docgen constructor initializes the link resolver for markdown parsing. | |
82 */ | |
83 Docgen() { | |
84 this.linkResolver = (name) => | |
85 fixReference(name, _currentLibrary, _currentClass, _currentMember); | |
86 } | |
87 | |
88 /** | |
89 * Creates documentation for filtered libraries. | |
90 */ | |
91 void documentLibraries() { | |
92 //TODO(tmandel): Filter libraries and determine output type using flags. | |
93 _libraries.forEach((library) { | |
94 _currentLibrary = library; | |
95 var result = new Library(library.qualifiedName, _getComment(library), | |
96 _getVariables(library.variables), _getMethods(library.functions), | |
97 _getClasses(library.classes)); | |
98 if (outputToJson) { | |
99 _writeToFile(stringify(result.toMap()), "${result.name}.json"); | |
100 } else { | |
101 _writeToFile(getYamlString(result.toMap()), "${result.name}.yaml"); | |
102 } | |
103 }); | |
104 } | |
105 | |
106 /** | |
107 * Returns any documentation comments associated with a mirror with | |
108 * simple markdown converted to html. | |
109 */ | |
110 String _getComment(DeclarationMirror mirror) { | |
111 String commentText; | |
112 mirror.metadata.forEach((metadata) { | |
113 if (metadata is CommentInstanceMirror) { | |
114 CommentInstanceMirror comment = metadata; | |
115 if (comment.isDocComment) { | |
116 if (commentText == null) { | |
117 commentText = comment.trimmedText; | |
118 } else { | |
119 commentText = "$commentText ${comment.trimmedText}"; | |
120 } | |
121 } | |
122 } | |
123 }); | |
124 return commentText == null ? "" : | |
125 markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver); | |
126 } | |
127 | |
128 /** | |
129 * Converts all [_] references in comments to <code>_</code>. | |
130 */ | |
131 // TODO(tmandel): Create proper links for [_] style markdown based | |
132 // on scope once layout of viewer is finished. | |
133 markdown.Node fixReference(String name, LibraryMirror currentLibrary, | |
134 ClassMirror currentClass, MemberMirror currentMember) { | |
135 return new markdown.Element.text('code', name); | |
136 } | |
137 | |
138 /** | |
139 * Returns a map of [Variable] objects constructed from inputted mirrors. | |
140 */ | |
141 Map<String, Variable> _getVariables(Map<String, VariableMirror> mirrorMap) { | |
142 var data = {}; | |
143 mirrorMap.forEach((String mirrorName, VariableMirror mirror) { | |
144 _currentMember = mirror; | |
145 data[mirrorName] = new Variable(mirrorName, mirror.isFinal, | |
146 mirror.isStatic, mirror.type.toString(), _getComment(mirror)); | |
147 }); | |
148 return data; | |
149 } | |
150 | |
151 /** | |
152 * Returns a map of [Method] objects constructed from inputted mirrors. | |
153 */ | |
154 Map<String, Method> _getMethods(Map<String, MethodMirror> mirrorMap) { | |
155 var data = {}; | |
156 mirrorMap.forEach((String mirrorName, MethodMirror mirror) { | |
157 _currentMember = mirror; | |
158 data[mirrorName] = new Method(mirrorName, mirror.isSetter, | |
159 mirror.isGetter, mirror.isConstructor, mirror.isOperator, | |
160 mirror.isStatic, mirror.returnType.toString(), _getComment(mirror), | |
161 _getParameters(mirror.parameters)); | |
162 }); | |
163 return data; | |
164 } | |
165 | |
166 /** | |
167 * Returns a map of [Class] objects constructed from inputted mirrors. | |
168 */ | |
169 Map<String, Class> _getClasses(Map<String, ClassMirror> mirrorMap) { | |
170 var data = {}; | |
171 mirrorMap.forEach((String mirrorName, ClassMirror mirror) { | |
172 _currentClass = mirror; | |
173 var superclass; | |
174 if (mirror.superclass != null) { | |
175 superclass = mirror.superclass.qualifiedName; | |
176 } | |
177 var interfaces = | |
178 mirror.superinterfaces.map((interface) => interface.qualifiedName); | |
179 data[mirrorName] = new Class(mirrorName, superclass, mirror.isAbstract, | |
180 mirror.isTypedef, _getComment(mirror), interfaces, | |
181 _getVariables(mirror.variables), _getMethods(mirror.methods)); | |
182 }); | |
183 return data; | |
184 } | |
185 | |
186 /** | |
187 * Returns a map of [Parameter] objects constructed from inputted mirrors. | |
188 */ | |
189 Map<String, Parameter> _getParameters(List<ParameterMirror> mirrorList) { | |
190 var data = {}; | |
191 mirrorList.forEach((ParameterMirror mirror) { | |
192 _currentMember = mirror; | |
193 data[mirror.simpleName] = new Parameter(mirror.simpleName, | |
194 mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue, | |
195 mirror.type.toString(), mirror.defaultValue); | |
196 }); | |
197 return data; | |
198 } | |
199 } | 59 } |
200 | |
201 /** | |
202 * Transforms the map by calling toMap on each value in it. | |
203 */ | |
204 Map recurseMap(Map inputMap) { | |
205 var outputMap = {}; | |
206 inputMap.forEach((key, value) { | |
207 outputMap[key] = value.toMap(); | |
208 }); | |
209 return outputMap; | |
210 } | |
211 | |
212 /** | |
213 * A class containing contents of a Dart library. | |
214 */ | |
215 class Library { | |
216 | |
217 /// Documentation comment with converted markdown. | |
218 String comment; | |
219 | |
220 /// Top-level variables in the library. | |
221 Map<String, Variable> variables; | |
222 | |
223 /// Top-level functions in the library. | |
224 Map<String, Method> functions; | |
225 | |
226 /// Classes defined within the library | |
227 Map<String, Class> classes; | |
228 | |
229 String name; | |
230 | |
231 Library(this.name, this.comment, this.variables, | |
232 this.functions, this.classes); | |
233 | |
234 /// Generates a map describing the [Library] object. | |
235 Map toMap() { | |
236 var libraryMap = {}; | |
237 libraryMap["name"] = name; | |
238 libraryMap["comment"] = comment; | |
239 libraryMap["variables"] = recurseMap(variables); | |
240 libraryMap["functions"] = recurseMap(functions); | |
241 libraryMap["classes"] = recurseMap(classes); | |
242 return libraryMap; | |
243 } | |
244 } | |
245 | |
246 /** | |
247 * A class containing contents of a Dart class. | |
248 */ | |
249 // TODO(tmandel): Figure out how to do typedefs (what is needed) | |
250 class Class { | |
251 | |
252 /// Documentation comment with converted markdown. | |
253 String comment; | |
254 | |
255 /// List of the names of interfaces that this class implements. | |
256 List<String> interfaces; | |
257 | |
258 /// Top-level variables in the class. | |
259 Map<String, Variable> variables; | |
260 | |
261 /// Methods in the class. | |
262 Map<String, Method> methods; | |
263 | |
264 String name; | |
265 String superclass; | |
266 bool isAbstract; | |
267 bool isTypedef; | |
268 | |
269 Class(this.name, this.superclass, this.isAbstract, this.isTypedef, | |
270 this.comment, this.interfaces, this.variables, this.methods); | |
271 | |
272 /// Generates a map describing the [Class] object. | |
273 Map toMap() { | |
274 var classMap = {}; | |
275 classMap["name"] = name; | |
276 classMap["comment"] = comment; | |
277 classMap["superclass"] = superclass; | |
278 classMap["abstract"] = isAbstract.toString(); | |
279 classMap["typedef"] = isTypedef.toString(); | |
280 classMap["implements"] = new List.from(interfaces); | |
281 classMap["variables"] = recurseMap(variables); | |
282 classMap["methods"] = recurseMap(methods); | |
283 return classMap; | |
284 } | |
285 } | |
286 | |
287 /** | |
288 * A class containing properties of a Dart variable. | |
289 */ | |
290 class Variable { | |
291 | |
292 /// Documentation comment with converted markdown. | |
293 String comment; | |
294 | |
295 String name; | |
296 bool isFinal; | |
297 bool isStatic; | |
298 String type; | |
299 | |
300 Variable(this.name, this.isFinal, this.isStatic, this.type, this.comment); | |
301 | |
302 /// Generates a map describing the [Variable] object. | |
303 Map toMap() { | |
304 var variableMap = {}; | |
305 variableMap["name"] = name; | |
306 variableMap["comment"] = comment; | |
307 variableMap["final"] = isFinal.toString(); | |
308 variableMap["static"] = isStatic.toString(); | |
309 variableMap["type"] = type; | |
310 return variableMap; | |
311 } | |
312 } | |
313 | |
314 /** | |
315 * A class containing properties of a Dart method. | |
316 */ | |
317 class Method { | |
318 | |
319 /// Documentation comment with converted markdown. | |
320 String comment; | |
321 | |
322 /// Parameters for this method. | |
323 Map<String, Parameter> parameters; | |
324 | |
325 String name; | |
326 bool isSetter; | |
327 bool isGetter; | |
328 bool isConstructor; | |
329 bool isOperator; | |
330 bool isStatic; | |
331 String returnType; | |
332 | |
333 Method(this.name, this.isSetter, this.isGetter, this.isConstructor, | |
334 this.isOperator, this.isStatic, this.returnType, this.comment, | |
335 this.parameters); | |
336 | |
337 /// Generates a map describing the [Method] object. | |
338 Map toMap() { | |
339 var methodMap = {}; | |
340 methodMap["name"] = name; | |
341 methodMap["comment"] = comment; | |
342 methodMap["type"] = isSetter ? "setter" : isGetter ? "getter" : | |
343 isOperator ? "operator" : isConstructor ? "constructor" : "method"; | |
344 methodMap["static"] = isStatic.toString(); | |
345 methodMap["return"] = returnType; | |
346 methodMap["parameters"] = recurseMap(parameters); | |
347 return methodMap; | |
348 } | |
349 } | |
350 | |
351 /** | |
352 * A class containing properties of a Dart method/function parameter. | |
353 */ | |
354 class Parameter { | |
355 | |
356 String name; | |
357 bool isOptional; | |
358 bool isNamed; | |
359 bool hasDefaultValue; | |
360 String type; | |
361 String defaultValue; | |
362 | |
363 Parameter(this.name, this.isOptional, this.isNamed, this.hasDefaultValue, | |
364 this.type, this.defaultValue); | |
365 | |
366 /// Generates a map describing the [Parameter] object. | |
367 Map toMap() { | |
368 var parameterMap = {}; | |
369 parameterMap["name"] = name; | |
370 parameterMap["optional"] = isOptional.toString(); | |
371 parameterMap["named"] = isNamed.toString(); | |
372 parameterMap["default"] = hasDefaultValue.toString(); | |
373 parameterMap["type"] = type; | |
374 parameterMap["value"] = defaultValue; | |
375 return parameterMap; | |
376 } | |
377 } | |
378 | |
379 /** | |
380 * Writes text to a file in the 'docs' directory. | |
381 */ | |
382 void _writeToFile(String text, String filename) { | |
383 Directory dir = new Directory('docs'); | |
384 if (!dir.existsSync()) { | |
385 dir.createSync(); | |
386 } | |
387 File file = new File('docs/$filename'); | |
388 if (!file.exists()) { | |
389 file.createSync(); | |
390 } | |
391 file.openSync(); | |
392 file.writeAsString(text); | |
393 } | |
OLD | NEW |