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

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

Issue 16915007: docgen working with a temporary link hack. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/docgen/bin/docgen.dart ('k') | pkg/docgen/pubspec.yaml » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/docgen/lib/docgen.dart
diff --git a/pkg/docgen/lib/docgen.dart b/pkg/docgen/lib/docgen.dart
new file mode 100644
index 0000000000000000000000000000000000000000..73ba6aa2e104f48ec7be3260e206ea2d988f8e1c
--- /dev/null
+++ b/pkg/docgen/lib/docgen.dart
@@ -0,0 +1,542 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * **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]
+ *
+ * This creates a file called `docs/<library_name>` in your current working
Andrei Mouravski 2013/06/24 22:07:11 Is there an extension?
janicejl 2013/06/25 00:48:42 Done.
+ * directory.
+ */
+library docgen;
+
+import 'dart:io';
+import 'dart:json';
+import 'dart:async';
+
+import 'package:args/args.dart';
+import 'package:logging/logging.dart';
+import 'package:markdown/markdown.dart' as markdown;
+
+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';
+
+/// Logger for Dart Doc Generator.
Andrei Mouravski 2013/06/24 22:07:11 No need for this comment.
janicejl 2013/06/25 00:48:42 Done.
+var logger = new Logger("Docgen");
+
+/// Unique ID, will get incremented everytime an ID is requested.
Andrei Mouravski 2013/06/24 22:07:11 /// Counter used to provide unique IDs for each di
janicejl 2013/06/25 00:48:42 Done.
+int _uid = 0;
Andrei Mouravski 2013/06/24 22:07:11 How about _nextId to match below comment.
janicejl 2013/06/25 00:48:42 Done.
+
+int getID() => _uid++;
Andrei Mouravski 2013/06/24 22:07:11 Just make it: int get nextId => _uid++;
janicejl 2013/06/25 00:48:42 Done.
+
+const String usage = "Usage: dart docgen.dart [OPTIONS] [fooDir/barFile]";
+
+/**
+ * Returns a ArgParser with all the flags and options created.
Andrei Mouravski 2013/06/24 22:07:11 "Creates parser for docgen command line arguments.
janicejl 2013/06/25 00:48:42 Done.
+ */
+ArgParser initArgParser() {
Andrei Mouravski 2013/06/24 22:07:11 This should probably move to bin/dartdoc.dart sinc
janicejl 2013/06/25 00:48:42 Done.
+ var parser = new ArgParser();
+ parser.addFlag("help", abbr: "h",
Andrei Mouravski 2013/06/24 22:07:11 Help should be a command instead.
janicejl 2013/06/25 00:48:42 I talked to Bob about making help a command, and h
+ help: "Prints help and usage information.",
+ negatable: false,
+ callback: (help) {
+ if (help) print(parser.getUsage());
Andrei Mouravski 2013/06/24 22:07:11 Don't print this way. Use the logger to output thi
janicejl 2013/06/25 00:48:42 Done.
+ });
+ parser.addFlag("verbose", abbr: "v",
+ help: "Runs docgen with logging.", negatable: false,
Andrei Mouravski 2013/06/24 22:07:11 It should already have logging. This should say so
janicejl 2013/06/25 00:48:42 Done.
+ callback: (verbose) {
+ if (verbose) logger.onRecord.listen((record) => print(record.message));
+ });
+ parser.addFlag("yaml", abbr: "y",
Andrei Mouravski 2013/06/24 22:07:11 How about: parser.addFlag("output-format", "o", ei
janicejl 2013/06/25 00:48:42 Done.
+ help: "Outputs to YAML.", defaultsTo: true);
+ parser.addFlag("json", abbr: "j",
+ help: "Outputs to JSON.");
+ parser.addFlag("hide-private",
+ help: "Hides private declarations.", negatable: false);
Andrei Mouravski 2013/06/24 22:07:11 Are they hidden, or ignored? That is to say, are
janicejl 2013/06/25 00:48:42 Done.
+ parser.addFlag("sdk",
+ help: "Flag to parse SDK Library files.", defaultsTo: true);
Andrei Mouravski 2013/06/24 22:07:11 "include-sdk" maybe? Probably should default to f
janicejl 2013/06/25 00:48:42 Done.
+
+ return parser;
+}
+
+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;
+}
+
+/**
+ * This class documents a list of libraries.
+ */
+class Docgen {
+
+ /// Libraries to be documented.
+ List<LibraryMirror> _libraries;
+
+ /// Current library being documented to be used for comment links.
+ LibraryMirror _currentLibrary;
+
+ /// Current class being documented to be used for comment links.
+ ClassMirror _currentClass;
+
+ /// Current member being documented to be used for comment links.
+ MemberMirror _currentMember;
+
+ /// Resolves reference links
+ markdown.Resolver linkResolver;
+
+ bool outputToYaml;
+ bool outputToJson;
+ bool hidePrivate;
+ /// State for whether or not the SDK libraries should also be outputted.
+ bool sdk;
+
+ /**
+ * Docgen constructor initializes the link resolver for markdown parsing.
+ * Also initializes the command line arguments.
+ */
+ Docgen(ArgResults argResults) {
+ outputToYaml = argResults["yaml"];
+ outputToJson = argResults["json"];
+ hidePrivate = argResults["hide-private"];
+ sdk = argResults["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() {
+ _libraries.forEach((library) {
+ // Files belonging to the SDK have a uri that begins with "dart:".
+ if (sdk || !library.uri.toString().startsWith("dart:")) {
+ _currentLibrary = library;
+ var result = new Library(library.qualifiedName, _getComment(library),
+ _getVariables(library.variables), _getMethods(library.functions),
+ _getClasses(library.classes), getID());
+ 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.
+ */
+ String _getComment(DeclarationMirror mirror) {
+ String commentText;
+ mirror.metadata.forEach((metadata) {
+ if (metadata is CommentInstanceMirror) {
+ CommentInstanceMirror comment = metadata;
+ if (comment.isDocComment) {
+ if (commentText == null) {
+ commentText = comment.trimmedText;
+ } else {
+ commentText = "$commentText ${comment.trimmedText}";
+ }
+ }
+ }
+ });
+ commentText = commentText == null ? "" :
+ markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver)
+ .replaceAll("\n", "");
+ return commentText;
+ }
+
+ /**
+ * Converts all [_] references in comments to <code>_</code>.
+ */
+ // TODO(tmandel): Create proper links for [_] style markdown based
+ // on scope once layout of viewer is finished.
+ markdown.Node fixReference(String name, LibraryMirror currentLibrary,
+ ClassMirror currentClass, MemberMirror currentMember) {
+ return new markdown.Element.text('code', name);
+ }
+
+ /**
+ * Returns a map of [Variable] objects constructed from inputted mirrors.
+ */
+ Map<String, Variable> _getVariables(Map<String, VariableMirror> mirrorMap) {
+ var data = {};
+ mirrorMap.forEach((String mirrorName, VariableMirror mirror) {
+ if (!hidePrivate || !mirror.isPrivate) {
+ _currentMember = mirror;
+ data[mirrorName] = new Variable(mirrorName, mirror.isFinal,
+ mirror.isStatic, mirror.type.toString(), _getComment(mirror),
+ getID());
+ }
+ });
+ return data;
+ }
+
+ /**
+ * Returns a map of [Method] objects constructed from inputted mirrors.
+ */
+ Map<String, Method> _getMethods(Map<String, MethodMirror> mirrorMap) {
+ var data = {};
+ mirrorMap.forEach((String mirrorName, MethodMirror mirror) {
+ if (!hidePrivate || !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), getID());
+ }
+ });
+ return data;
+ }
+
+ /**
+ * Returns a map of [Class] objects constructed from inputted mirrors.
+ */
+ Map<String, Class> _getClasses(Map<String, ClassMirror> mirrorMap) {
+ var data = {};
+ mirrorMap.forEach((String mirrorName, ClassMirror mirror) {
+ if (!hidePrivate || !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),
+ getID());
+ }
+ });
+ return data;
+ }
+
+ /**
+ * Returns a map of [Parameter] objects constructed from inputted mirrors.
+ */
+ Map<String, Parameter> _getParameters(List<ParameterMirror> mirrorList) {
+ var data = {};
+ mirrorList.forEach((ParameterMirror mirror) {
+ _currentMember = mirror;
+ data[mirror.simpleName] = new Parameter(mirror.simpleName,
+ mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue,
+ mirror.type.toString(), mirror.defaultValue, getID());
+ });
+ return data;
+ }
+}
+
+/**
+ * Transforms the map by calling toMap on each value in it.
+ */
+Map recurseMap(Map inputMap) {
+ var outputMap = {};
+ inputMap.forEach((key, value) {
+ outputMap[key] = value.toMap();
+ });
+ return outputMap;
+}
+
+/**
+ * A class containing contents of a Dart library.
+ */
+class Library {
+
+ /// Unique ID number for resolving links.
+ int id;
+
+ /// Documentation comment with converted markdown.
+ String comment;
+
+ /// Top-level variables in the library.
+ Map<String, Variable> variables;
+
+ /// Top-level functions in the library.
+ Map<String, Method> functions;
+
+ /// Classes defined within the library
+ Map<String, Class> classes;
+
+ String name;
+
+ Library(this.name, this.comment, this.variables,
+ 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);
+ libraryMap["functions"] = recurseMap(functions);
+ libraryMap["classes"] = recurseMap(classes);
+ return libraryMap;
+ }
+}
+
+/**
+ * A class containing contents of a Dart class.
+ */
+// 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;
+
+ /// List of the names of interfaces that this class implements.
+ List<String> interfaces;
+
+ /// Top-level variables in the class.
+ Map<String, Variable> variables;
+
+ /// Methods in the class.
+ Map<String, Method> methods;
+
+ String name;
+ String superclass;
+ bool isAbstract;
+ bool isTypedef;
+
+ Class(this.name, this.superclass, this.isAbstract, this.isTypedef,
+ 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;
+ classMap["abstract"] = isAbstract.toString();
+ classMap["typedef"] = isTypedef.toString();
+ classMap["implements"] = new List.from(interfaces);
+ classMap["variables"] = recurseMap(variables);
+ classMap["methods"] = recurseMap(methods);
+ return classMap;
+ }
+}
+
+/**
+ * A class containing properties of a Dart variable.
+ */
+class Variable {
+
+ /// Unique ID number for resolving links.
+ int id;
+
+ /// Documentation comment with converted markdown.
+ String comment;
+
+ String name;
+ bool isFinal;
+ bool isStatic;
+ String type;
+
+ 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();
+ variableMap["static"] = isStatic.toString();
+ variableMap["type"] = type;
+ return variableMap;
+ }
+}
+
+/**
+ * A class containing properties of a Dart method.
+ */
+class Method {
+
+ /// Unique ID number for resolving links.
+ int id;
+
+ /// Documentation comment with converted markdown.
+ String comment;
+
+ /// Parameters for this method.
+ Map<String, Parameter> parameters;
+
+ String name;
+ bool isSetter;
+ bool isGetter;
+ bool isConstructor;
+ bool isOperator;
+ bool isStatic;
+ String returnType;
+
+ Method(this.name, this.isSetter, this.isGetter, this.isConstructor,
+ this.isOperator, this.isStatic, this.returnType, this.comment,
+ 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" :
+ isOperator ? "operator" : isConstructor ? "constructor" : "method";
+ methodMap["static"] = isStatic.toString();
+ methodMap["return"] = returnType;
+ methodMap["parameters"] = recurseMap(parameters);
+ return methodMap;
+ }
+}
+
+/**
+ * A class containing properties of a Dart method/function parameter.
+ */
+class Parameter {
+
+ /// Unique ID number for resolving links.
+ int id;
+
+ String name;
+ bool isOptional;
+ bool isNamed;
+ bool hasDefaultValue;
+ String type;
+ String defaultValue;
+
+ Parameter(this.name, this.isOptional, this.isNamed, this.hasDefaultValue,
+ 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();
+ parameterMap["default"] = hasDefaultValue.toString();
+ parameterMap["type"] = type;
+ parameterMap["value"] = defaultValue;
+ return parameterMap;
+ }
+}
+
+/**
+ * Writes text to a file in the 'docs' directory.
+ */
+void _writeToFile(String text, String filename) {
+ Directory dir = new Directory('docs');
+ if (!dir.existsSync()) {
+ dir.createSync();
+ }
+ File file = new File('docs/$filename');
+ if (!file.existsSync()) {
+ file.createSync();
+ }
+ file.openSync();
+ file.writeAsString(text);
+}
« no previous file with comments | « pkg/docgen/bin/docgen.dart ('k') | pkg/docgen/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698