Index: pkg/docgen/lib/docgen.dart |
diff --git a/pkg/docgen/bin/docgen.dart b/pkg/docgen/lib/docgen.dart |
similarity index 51% |
copy from pkg/docgen/bin/docgen.dart |
copy to pkg/docgen/lib/docgen.dart |
index 308df289271e71bab2ec5e94b542b2fccffffff0..49d659bf06488434e3a0ea94ada16df71200d2c7 100644 |
--- a/pkg/docgen/bin/docgen.dart |
+++ b/pkg/docgen/lib/docgen.dart |
@@ -3,51 +3,76 @@ |
// BSD-style license that can be found in the LICENSE file. |
/** |
- * The docgen tool takes in a library as input and produces documentation |
- * for the library as well as all libraries it imports and uses. The tool can |
- * be run by passing in the path to a .dart file like this: |
+ * **docgen** is a tool for creating machine readable representations of Dart |
+ * code metadata, including: classes, members, comments and annotations. |
+ * |
+ * docgen is run on a `.dart` file or a directory containing `.dart` files. |
+ * |
+ * $ dart docgen.dart [OPTIONS] [FILE/DIR] |
* |
- * ./dart docgen.dart path/to/file.dart |
- * |
- * This outputs information about all classes, variables, functions, and |
- * methods defined in the library and its imported libraries. |
+ * This creates files called `docs/<library_name>.yaml` in your current |
+ * working directory. |
*/ |
library docgen; |
-// TODO(tmandel): Use 'package:' references for imports with relative paths. |
import 'dart:io'; |
import 'dart:json'; |
import 'dart:async'; |
-import '../lib/dart2yaml.dart'; |
-import '../lib/src/dart2js_mirrors.dart'; |
+ |
+import 'package:args/args.dart'; |
+import 'package:logging/logging.dart'; |
import 'package:markdown/markdown.dart' as markdown; |
-import '../../args/lib/args.dart'; |
+ |
+import 'dart2yaml.dart'; |
+import '../../../sdk/lib/_internal/compiler/compiler.dart' as api; |
+import '../../../sdk/lib/_internal/compiler/implementation/filenames.dart'; |
+import '../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart' |
+ as dart2js; |
import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors.dart'; |
import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'; |
+import '../../../sdk/lib/_internal/compiler/implementation/source_file_provider.dart'; |
-/** |
- * Entry function to create YAML documentation from Dart files. |
- */ |
-void main() { |
- // TODO(tmandel): Use args library once flags are clear. |
- Options opts = new Options(); |
- Docgen docgen = new Docgen(); |
- |
- if (opts.arguments.length > 0) { |
- List<Path> libraries = [new Path(opts.arguments[0])]; |
- Path sdkDirectory = new Path("../../../sdk/"); |
- var workingMirrors = analyze(libraries, sdkDirectory, |
- options: ['--preserve-comments', '--categories=Client,Server']); |
- workingMirrors.then( (MirrorSystem mirrorSystem) { |
- var mirrors = mirrorSystem.libraries.values; |
- if (mirrors.isEmpty) { |
- print("no LibraryMirrors"); |
- } else { |
- docgen.libraries = mirrors; |
- docgen.documentLibraries(); |
- } |
- }); |
+var logger = new Logger("Docgen"); |
+ |
+/// Counter used to provide unique IDs for each distinct item. |
+int _nextID = 0; |
+ |
+int get nextID => _nextID++; |
+ |
+const String usage = "Usage: dart docgen.dart [OPTIONS] [fooDir/barFile]"; |
+ |
+List<Path> listLibraries(List<String> args) { |
+ if (args.length != 1) { |
+ throw new UnsupportedError(usage); |
} |
+ var libraries = new List<Path>(); |
+ var type = FileSystemEntity.typeSync(args[0]); |
+ |
+ if (type == FileSystemEntityType.NOT_FOUND) { |
+ throw new UnsupportedError("File does not exist. $usage"); |
+ } else if (type == FileSystemEntityType.LINK) { |
+ libraries.addAll(listLibrariesFromDir(new Link(args[0]).targetSync())); |
+ } else if (type == FileSystemEntityType.FILE) { |
+ libraries.add(new Path(args[0])); |
+ logger.info("Added to libraries: ${libraries.last.toString()}"); |
+ } else if (type == FileSystemEntityType.DIRECTORY) { |
+ libraries.addAll(listLibrariesFromDir(args[0])); |
+ } |
+ return libraries; |
+} |
+ |
+List<Path> listLibrariesFromDir(String path) { |
+ var libraries = new List<Path>(); |
+ new Directory(path).listSync(recursive: true, |
+ followLinks: true).forEach((file) { |
+ if (new Path(file.path).extension == "dart") { |
+ if (!file.path.contains("/packages/")) { |
+ libraries.add(new Path(file.path)); |
+ logger.info("Added to libraries: ${libraries.last.toString()}"); |
+ } |
+ } |
+ }); |
+ return libraries; |
} |
/** |
@@ -58,9 +83,6 @@ class Docgen { |
/// Libraries to be documented. |
List<LibraryMirror> _libraries; |
- /// Saves list of libraries for Docgen object. |
- void set libraries(value) => _libraries = value; |
- |
/// Current library being documented to be used for comment links. |
LibraryMirror _currentLibrary; |
@@ -70,39 +92,120 @@ class Docgen { |
/// Current member being documented to be used for comment links. |
MemberMirror _currentMember; |
- /// Should the output file type be JSON? |
- // TODO(tmandel): Add flag to allow for output to JSON. |
- bool outputToJson = false; |
- |
/// Resolves reference links |
markdown.Resolver linkResolver; |
+ bool outputToYaml; |
+ bool outputToJson; |
+ bool includePrivate; |
+ /// State for whether or not the SDK libraries should also be outputted. |
+ bool includeSdk; |
+ |
/** |
* Docgen constructor initializes the link resolver for markdown parsing. |
+ * Also initializes the command line arguments. |
*/ |
- Docgen() { |
+ Docgen(ArgResults argResults) { |
+ if (argResults["output-format"] == null) { |
+ outputToYaml = |
+ (argResults["yaml"] == false && argResults["json"] == false) ? |
+ true : argResults["yaml"]; |
+ } else { |
+ if ((argResults["output-format"] == "yaml" && |
+ argResults["json"] == true) || |
+ (argResults["output-format"] == "json" && |
+ argResults["yaml"] == true)) { |
+ throw new UnsupportedError("Cannot have contradictory output flags."); |
+ } |
+ outputToYaml = argResults["output-format"] == "yaml" ? true : false; |
+ } |
+ outputToJson = !outputToYaml; |
+ includePrivate = argResults["include-private"]; |
+ includeSdk = argResults["include-sdk"]; |
+ |
this.linkResolver = (name) => |
fixReference(name, _currentLibrary, _currentClass, _currentMember); |
} |
/** |
+ * Analyzes set of libraries by getting a mirror system and triggers the |
+ * documentation of the libraries. |
+ */ |
+ void analyze(List<Path> libraries) { |
+ // DART_SDK should be set to the root of the SDK library. |
+ var sdkRoot = Platform.environment["DART_SDK"]; |
+ if (sdkRoot != null) { |
+ logger.info("Using DART_SDK to find SDK at $sdkRoot"); |
+ sdkRoot = new Path(sdkRoot); |
+ } else { |
+ // If DART_SDK is not defined in the environment, |
+ // assuming the dart executable is from the Dart SDK folder inside bin. |
+ sdkRoot = new Path(new Options().executable).directoryPath |
+ .directoryPath; |
+ logger.info("SDK Root: ${sdkRoot.toString()}"); |
+ } |
+ |
+ Path packageDir = libraries.last.directoryPath.append("packages"); |
+ logger.info("Package Root: ${packageDir.toString()}"); |
+ getMirrorSystem(libraries, sdkRoot, |
+ packageRoot: packageDir).then((MirrorSystem mirrorSystem) { |
+ if (mirrorSystem.libraries.values.isEmpty) { |
+ throw new UnsupportedError("No Library Mirrors."); |
+ } |
+ this.libraries = mirrorSystem.libraries.values; |
+ documentLibraries(); |
+ }); |
+ } |
+ |
+ /** |
+ * Analyzes set of libraries and provides a mirror system which can be used |
+ * for static inspection of the source code. |
+ */ |
+ Future<MirrorSystem> getMirrorSystem(List<Path> libraries, |
+ Path libraryRoot, {Path packageRoot}) { |
+ SourceFileProvider provider = new SourceFileProvider(); |
+ api.DiagnosticHandler diagnosticHandler = |
+ new FormattingDiagnosticHandler(provider).diagnosticHandler; |
+ Uri libraryUri = currentDirectory.resolve(appendSlash('$libraryRoot')); |
+ Uri packageUri = null; |
+ if (packageRoot != null) { |
+ packageUri = currentDirectory.resolve(appendSlash('$packageRoot')); |
+ } |
+ List<Uri> librariesUri = <Uri>[]; |
+ libraries.forEach((library) { |
+ librariesUri.add(currentDirectory.resolve(library.toString())); |
+ }); |
+ return dart2js.analyze(librariesUri, libraryUri, packageUri, |
+ provider.readStringFromUri, diagnosticHandler, |
+ ['--preserve-comments', '--categories=Client,Server']); |
+ } |
+ |
+ /** |
* Creates documentation for filtered libraries. |
*/ |
void documentLibraries() { |
- //TODO(tmandel): Filter libraries and determine output type using flags. |
_libraries.forEach((library) { |
- _currentLibrary = library; |
- var result = new Library(library.qualifiedName, _getComment(library), |
- _getVariables(library.variables), _getMethods(library.functions), |
- _getClasses(library.classes)); |
- if (outputToJson) { |
- _writeToFile(stringify(result.toMap()), "${result.name}.json"); |
- } else { |
- _writeToFile(getYamlString(result.toMap()), "${result.name}.yaml"); |
- } |
+ // Files belonging to the SDK have a uri that begins with "dart:". |
+ if (includeSdk || !library.uri.toString().startsWith("dart:")) { |
+ _currentLibrary = library; |
+ var result = new Library(library.qualifiedName, _getComment(library), |
+ _getVariables(library.variables), _getMethods(library.functions), |
+ _getClasses(library.classes), nextID); |
+ if (outputToJson) { |
+ _writeToFile(stringify(result.toMap()), "${result.name}.json"); |
+ } |
+ if (outputToYaml) { |
+ _writeToFile(getYamlString(result.toMap()), "${result.name}.yaml"); |
+ } |
+ } |
}); |
} |
+ /// Saves list of libraries for Docgen object. |
+ void set libraries(value){ |
+ _libraries = value; |
+ } |
+ |
/** |
* Returns any documentation comments associated with a mirror with |
* simple markdown converted to html. |
@@ -121,8 +224,10 @@ class Docgen { |
} |
} |
}); |
- return commentText == null ? "" : |
- markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver); |
+ commentText = commentText == null ? "" : |
+ markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver) |
+ .replaceAll("\n", ""); |
+ return commentText; |
} |
/** |
@@ -141,9 +246,12 @@ class Docgen { |
Map<String, Variable> _getVariables(Map<String, VariableMirror> mirrorMap) { |
var data = {}; |
mirrorMap.forEach((String mirrorName, VariableMirror mirror) { |
- _currentMember = mirror; |
- data[mirrorName] = new Variable(mirrorName, mirror.isFinal, |
- mirror.isStatic, mirror.type.toString(), _getComment(mirror)); |
+ if (includePrivate || !mirror.isPrivate) { |
+ _currentMember = mirror; |
+ data[mirrorName] = new Variable(mirrorName, mirror.isFinal, |
+ mirror.isStatic, mirror.type.toString(), _getComment(mirror), |
+ nextID); |
+ } |
}); |
return data; |
} |
@@ -154,11 +262,13 @@ class Docgen { |
Map<String, Method> _getMethods(Map<String, MethodMirror> mirrorMap) { |
var data = {}; |
mirrorMap.forEach((String mirrorName, MethodMirror mirror) { |
- _currentMember = mirror; |
- data[mirrorName] = new Method(mirrorName, mirror.isSetter, |
- mirror.isGetter, mirror.isConstructor, mirror.isOperator, |
- mirror.isStatic, mirror.returnType.toString(), _getComment(mirror), |
- _getParameters(mirror.parameters)); |
+ if (includePrivate || !mirror.isPrivate) { |
+ _currentMember = mirror; |
+ data[mirrorName] = new Method(mirrorName, mirror.isSetter, |
+ mirror.isGetter, mirror.isConstructor, mirror.isOperator, |
+ mirror.isStatic, mirror.returnType.toString(), _getComment(mirror), |
+ _getParameters(mirror.parameters), nextID); |
+ } |
}); |
return data; |
} |
@@ -169,16 +279,17 @@ class Docgen { |
Map<String, Class> _getClasses(Map<String, ClassMirror> mirrorMap) { |
var data = {}; |
mirrorMap.forEach((String mirrorName, ClassMirror mirror) { |
- _currentClass = mirror; |
- var superclass; |
- if (mirror.superclass != null) { |
- superclass = mirror.superclass.qualifiedName; |
+ if (includePrivate || !mirror.isPrivate) { |
+ _currentClass = mirror; |
+ var superclass = (mirror.superclass != null) ? |
+ mirror.superclass.qualifiedName : ""; |
+ var interfaces = |
+ mirror.superinterfaces.map((interface) => interface.qualifiedName); |
+ data[mirrorName] = new Class(mirrorName, superclass, mirror.isAbstract, |
+ mirror.isTypedef, _getComment(mirror), interfaces.toList(), |
+ _getVariables(mirror.variables), _getMethods(mirror.methods), |
+ nextID); |
} |
- var interfaces = |
- mirror.superinterfaces.map((interface) => interface.qualifiedName); |
- data[mirrorName] = new Class(mirrorName, superclass, mirror.isAbstract, |
- mirror.isTypedef, _getComment(mirror), interfaces, |
- _getVariables(mirror.variables), _getMethods(mirror.methods)); |
}); |
return data; |
} |
@@ -192,7 +303,7 @@ class Docgen { |
_currentMember = mirror; |
data[mirror.simpleName] = new Parameter(mirror.simpleName, |
mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue, |
- mirror.type.toString(), mirror.defaultValue); |
+ mirror.type.toString(), mirror.defaultValue, nextID); |
}); |
return data; |
} |
@@ -214,6 +325,9 @@ Map recurseMap(Map inputMap) { |
*/ |
class Library { |
+ /// Unique ID number for resolving links. |
+ int id; |
+ |
/// Documentation comment with converted markdown. |
String comment; |
@@ -229,11 +343,12 @@ class Library { |
String name; |
Library(this.name, this.comment, this.variables, |
- this.functions, this.classes); |
+ this.functions, this.classes, this.id); |
/// Generates a map describing the [Library] object. |
Map toMap() { |
var libraryMap = {}; |
+ libraryMap["id"] = id; |
libraryMap["name"] = name; |
libraryMap["comment"] = comment; |
libraryMap["variables"] = recurseMap(variables); |
@@ -249,6 +364,9 @@ class Library { |
// TODO(tmandel): Figure out how to do typedefs (what is needed) |
class Class { |
+ /// Unique ID number for resolving links. |
+ int id; |
+ |
/// Documentation comment with converted markdown. |
String comment; |
@@ -267,11 +385,12 @@ class Class { |
bool isTypedef; |
Class(this.name, this.superclass, this.isAbstract, this.isTypedef, |
- this.comment, this.interfaces, this.variables, this.methods); |
+ this.comment, this.interfaces, this.variables, this.methods, this.id); |
/// Generates a map describing the [Class] object. |
Map toMap() { |
var classMap = {}; |
+ classMap["id"] = id; |
classMap["name"] = name; |
classMap["comment"] = comment; |
classMap["superclass"] = superclass; |
@@ -289,6 +408,9 @@ class Class { |
*/ |
class Variable { |
+ /// Unique ID number for resolving links. |
+ int id; |
+ |
/// Documentation comment with converted markdown. |
String comment; |
@@ -297,11 +419,13 @@ class Variable { |
bool isStatic; |
String type; |
- Variable(this.name, this.isFinal, this.isStatic, this.type, this.comment); |
+ Variable(this.name, this.isFinal, this.isStatic, this.type, |
+ this.comment, this.id); |
/// Generates a map describing the [Variable] object. |
Map toMap() { |
var variableMap = {}; |
+ variableMap["id"] = id; |
variableMap["name"] = name; |
variableMap["comment"] = comment; |
variableMap["final"] = isFinal.toString(); |
@@ -316,6 +440,9 @@ class Variable { |
*/ |
class Method { |
+ /// Unique ID number for resolving links. |
+ int id; |
+ |
/// Documentation comment with converted markdown. |
String comment; |
@@ -332,11 +459,12 @@ class Method { |
Method(this.name, this.isSetter, this.isGetter, this.isConstructor, |
this.isOperator, this.isStatic, this.returnType, this.comment, |
- this.parameters); |
+ this.parameters, this.id); |
/// Generates a map describing the [Method] object. |
Map toMap() { |
var methodMap = {}; |
+ methodMap["id"] = id; |
methodMap["name"] = name; |
methodMap["comment"] = comment; |
methodMap["type"] = isSetter ? "setter" : isGetter ? "getter" : |
@@ -353,6 +481,9 @@ class Method { |
*/ |
class Parameter { |
+ /// Unique ID number for resolving links. |
+ int id; |
+ |
String name; |
bool isOptional; |
bool isNamed; |
@@ -361,11 +492,12 @@ class Parameter { |
String defaultValue; |
Parameter(this.name, this.isOptional, this.isNamed, this.hasDefaultValue, |
- this.type, this.defaultValue); |
+ this.type, this.defaultValue, this.id); |
/// Generates a map describing the [Parameter] object. |
Map toMap() { |
var parameterMap = {}; |
+ parameterMap["id"] = id; |
parameterMap["name"] = name; |
parameterMap["optional"] = isOptional.toString(); |
parameterMap["named"] = isNamed.toString(); |
@@ -385,7 +517,7 @@ void _writeToFile(String text, String filename) { |
dir.createSync(); |
} |
File file = new File('docs/$filename'); |
- if (!file.exists()) { |
+ if (!file.existsSync()) { |
file.createSync(); |
} |
file.openSync(); |