| Index: frog/samples/doc.dart
|
| diff --git a/frog/samples/doc.dart b/frog/samples/doc.dart
|
| deleted file mode 100644
|
| index 6ddb7a6ee7686d5e100211dbb245570453d8fb86..0000000000000000000000000000000000000000
|
| --- a/frog/samples/doc.dart
|
| +++ /dev/null
|
| @@ -1,542 +0,0 @@
|
| -// Copyright (c) 2011, 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.
|
| -
|
| -// TODO(rnystrom): This is moving from a sample to being a real project. Right
|
| -// now, to try this out:
|
| -// 1. Compile interact.dart to JS:
|
| -// $ ./frogsh --out=docs/interact.js --compile-only docs/interact.dart
|
| -// 2. Run the doc generator:
|
| -// $ ./frogsh samples/doc.dart
|
| -// 3. Look at the results in frog/docs/
|
| -
|
| -/** An awesome documentation generator. */
|
| -#library('doc');
|
| -
|
| -#import('../lang.dart');
|
| -#import('../file_system_node.dart');
|
| -#import('classify.dart');
|
| -
|
| -/** Path to starting library or application. */
|
| -// TODO(rnystrom): Make this a command-line arg.
|
| -final libPath = 'samples/doc.dart';
|
| -
|
| -/** Path to corePath library. */
|
| -final corePath = 'lib';
|
| -
|
| -/** Path to generate html files into. */
|
| -final outdir = './docs';
|
| -
|
| -/** Special comment position used to store the library-level doc comment. */
|
| -final _libraryDoc = -1;
|
| -
|
| -/** The file currently being written to. */
|
| -StringBuffer _file;
|
| -
|
| -/**
|
| - * The cached lookup-table to associate doc comments with spans. The outer map
|
| - * is from filenames to doc comments in that file. The inner map maps from the
|
| - * token positions to doc comments. Each position is the starting offset of the
|
| - * next non-comment token *following* the doc comment. For example, the position
|
| - * for this comment would be the position of the "Map" token below.
|
| - */
|
| -Map<String, Map<int, String>> _comments;
|
| -
|
| -// TODO(jimhug): This generates really ugly output with lots of holes.
|
| -
|
| -/**
|
| - * Run this from the frog/samples directory. Before running, you need
|
| - * to create a docs dir with 'mkdir docs' - since Dart currently doesn't
|
| - * support creating new directories.
|
| - */
|
| -void main() {
|
| - // TODO(rnystrom): Get options and homedir like frog.dart does.
|
| - final files = new NodeFileSystem();
|
| - parseOptions('.', [] /* args */, files);
|
| -
|
| - initializeWorld(files);
|
| -
|
| - world.withTiming('parsed', () {
|
| - world.processScript(libPath);
|
| - });
|
| -
|
| - world.withTiming('resolved', () {
|
| - world.resolveAll();
|
| - });
|
| -
|
| - world.withTiming('generated docs', () {
|
| - _comments = <String, Map<int, String>>{};
|
| -
|
| - for (var library in world.libraries.getValues()) {
|
| - docLibrary(library);
|
| - }
|
| -
|
| - docIndex(world.libraries.getValues());
|
| - });
|
| -}
|
| -
|
| -startFile() {
|
| - _file = new StringBuffer();
|
| -}
|
| -
|
| -write(String s) {
|
| - _file.add(s);
|
| -}
|
| -
|
| -writeln(String s) {
|
| - write(s);
|
| - write('\n');
|
| -}
|
| -
|
| -endFile(String outfile) {
|
| - world.files.writeString(outfile, _file.toString());
|
| - _file = null;
|
| -}
|
| -
|
| -/** Turns a library name into something that's safe to use as a file name. */
|
| -sanitize(String name) => name.replaceAll(':', '_').replaceAll('/', '_');
|
| -
|
| -docIndex(List<Library> libraries) {
|
| - startFile();
|
| - // TODO(rnystrom): Need to figure out what this should look like.
|
| - writeln(
|
| - '''
|
| - <html><head>
|
| - <title>Index</title>
|
| - <link rel="stylesheet" type="text/css" href="styles.css" />
|
| - </head>
|
| - <body>
|
| - <div class="content">
|
| - <ul>
|
| - ''');
|
| -
|
| - var sorted = new List<Library>.from(libraries);
|
| - sorted.sort((a, b) => a.name.compareTo(b.name));
|
| -
|
| - for (var library in sorted) {
|
| - writeln(
|
| - '''
|
| - <li><a href="${sanitize(library.name)}.html">
|
| - Library ${library.name}</a>
|
| - </li>
|
| - ''');
|
| - }
|
| -
|
| - writeln(
|
| - '''
|
| - </ul>
|
| - </div>
|
| - </body></html>
|
| - ''');
|
| -
|
| - endFile('$outdir/index.html');
|
| -}
|
| -
|
| -docLibrary(Library library) {
|
| - startFile();
|
| - writeln(
|
| - '''
|
| - <html>
|
| - <head>
|
| - <title>${library.name}</title>
|
| - <link rel="stylesheet" type="text/css" href="styles.css" />
|
| - <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800" rel="stylesheet" type="text/css">
|
| - <script src="interact.js"></script>
|
| - </head>
|
| - <body>
|
| - <div class="content">
|
| - <h1>Library <strong>${library.name}</strong></h1>
|
| - ''');
|
| -
|
| - bool needsSeparator = false;
|
| -
|
| - // Look for a comment for the entire library.
|
| - final comment = findCommentInFile(library.baseSource, _libraryDoc);
|
| - if (comment != null) {
|
| - writeln('<div class="doc"><p>$comment</p></div>');
|
| - needsSeparator = true;
|
| - }
|
| -
|
| - for (var type in library.types.getValues()) {
|
| - if (needsSeparator) writeln('<hr/>');
|
| - if (docType(type)) needsSeparator = false;
|
| - }
|
| -
|
| - writeln(
|
| - '''
|
| - </div>
|
| - </body></html>
|
| - ''');
|
| -
|
| - endFile('$outdir/${sanitize(library.name)}.html');
|
| -}
|
| -
|
| -/**
|
| - * Documents [Type]. Handles top-level members if given an unnamed Type.
|
| - * Returns [:true:] if it wrote anything.
|
| - */
|
| -bool docType(Type type) {
|
| - bool wroteSomething = false;
|
| -
|
| - if (type.name != null) {
|
| - write(
|
| - '''
|
| - <h2 id="${type.name}">
|
| - ${type.isClass ? "Class" : "Interface"} <strong>${type.name}</strong>
|
| - <a class="anchor-link" href="#${type.name}"
|
| - title="Permalink to ${type.name}">#</a>
|
| - </h2>
|
| - ''');
|
| -
|
| - docInheritance(type);
|
| - docCode(type.span);
|
| - docConstructors(type);
|
| -
|
| - wroteSomething = true;
|
| - }
|
| -
|
| - // Collect the different kinds of members.
|
| - var methods = [];
|
| - var fields = [];
|
| -
|
| - for (var member in orderValuesByKeys(type.members)) {
|
| - if (member.isMethod &&
|
| - (member.definition != null) &&
|
| - !member.name.startsWith('_')) {
|
| - methods.add(member);
|
| - } else if (member.isProperty) {
|
| - if (member.canGet) methods.add(member.getter);
|
| - if (member.canSet) methods.add(member.setter);
|
| - } else if (member.isField && !member.name.startsWith('_')) {
|
| - fields.add(member);
|
| - }
|
| - }
|
| -
|
| - if (methods.length > 0) {
|
| - writeln('<h3>Methods</h3>');
|
| - for (var method in methods) docMethod(type.name, method);
|
| - }
|
| -
|
| - if (fields.length > 0) {
|
| - writeln('<h3>Fields</h3>');
|
| - for (var field in fields) docField(type.name, field);
|
| - }
|
| -
|
| - return wroteSomething || methods.length > 0 || fields.length > 0;
|
| -}
|
| -
|
| -/** Document the superclass and superinterfaces of [Type]. */
|
| -docInheritance(Type type) {
|
| - // Show the superclass and superinterface(s).
|
| - if ((type.parent != null) && (type.parent.isObject) ||
|
| - (type.interfaces != null && type.interfaces.length > 0)) {
|
| - writeln('<p>');
|
| -
|
| - if (type.parent != null) {
|
| - write('Extends ${typeRef(type.parent)}. ');
|
| - }
|
| -
|
| - if (type.interfaces != null) {
|
| - var interfaces = [];
|
| - switch (type.interfaces.length) {
|
| - case 0:
|
| - // Do nothing.
|
| - break;
|
| -
|
| - case 1:
|
| - write('Implements ${typeRef(type.interfaces[0])}.');
|
| - break;
|
| -
|
| - case 2:
|
| - write('''Implements ${typeRef(type.interfaces[0])} and
|
| - ${typeRef(type.interfaces[1])}.''');
|
| - break;
|
| -
|
| - default:
|
| - write('Implements ');
|
| - for (var i = 0; i < type.interfaces.length; i++) {
|
| - write('${typeRef(type.interfaces[i])}');
|
| - if (i < type.interfaces.length - 1) {
|
| - write(', ');
|
| - } else {
|
| - write(' and ');
|
| - }
|
| - }
|
| - write('.');
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -/** Document the constructors for [Type], if any. */
|
| -docConstructors(Type type) {
|
| - if (type.constructors.length > 0) {
|
| - writeln('<h3>Constructors</h3>');
|
| - for (var name in type.constructors.getKeys()) {
|
| - var constructor = type.constructors[name];
|
| - docMethod(type.name, constructor, namedConstructor: name);
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Documents the [method] in a type named [typeName]. Handles all kinds of
|
| - * methods including getters, setters, and constructors.
|
| - */
|
| -docMethod(String typeName, MethodMember method,
|
| - [String namedConstructor = null]) {
|
| - writeln(
|
| - '''
|
| - <div class="method"><h4 id="$typeName.${method.name}">
|
| - <span class="show-code">Code</span>
|
| - ''');
|
| -
|
| - // A null typeName means it's a top-level definition which is implicitly
|
| - // static so doesn't need to annotate it.
|
| - if (method.isStatic && (typeName != null)) {
|
| - write('static ');
|
| - }
|
| -
|
| - if (method.isConstructor) {
|
| - write(method.isConst ? 'const ' : 'new ');
|
| - }
|
| -
|
| - if (namedConstructor == null) {
|
| - write(optionalTypeRef(method.returnType));
|
| - }
|
| -
|
| - // Translate specially-named methods: getters, setters, operators.
|
| - var name = method.name;
|
| - if (name.startsWith('get\$')) {
|
| - // Getter.
|
| - name = 'get ${name.substring(4)}';
|
| - } else if (name.startsWith('set\$')) {
|
| - // Setter.
|
| - name = 'set ${name.substring(4)}';
|
| - } else {
|
| - // See if it's an operator.
|
| - name = TokenKind.rawOperatorFromMethod(name);
|
| - if (name == null) {
|
| - name = method.name;
|
| - } else {
|
| - name = 'operator $name';
|
| - }
|
| - }
|
| -
|
| - write('<strong>$name</strong>');
|
| -
|
| - // Named constructors.
|
| - if (namedConstructor != null && namedConstructor != '') {
|
| - write('.');
|
| - write(namedConstructor);
|
| - }
|
| -
|
| - write('(');
|
| - var paramList = [];
|
| - if (method.parameters == null) print(method.name);
|
| - for (var p in method.parameters) {
|
| - paramList.add('${optionalTypeRef(p.type)}${p.name}');
|
| - }
|
| - write(Strings.join(paramList, ", "));
|
| - write(')');
|
| -
|
| - write(''' <a class="anchor-link" href="#$typeName.${method.name}"
|
| - title="Permalink to $typeName.$name">#</a>''');
|
| - writeln('</h4>');
|
| -
|
| - docCode(method.span, showCode: true);
|
| -
|
| - writeln('</div>');
|
| -}
|
| -
|
| -/** Documents the field [field] in a type named [typeName]. */
|
| -docField(String typeName, FieldMember field) {
|
| - writeln(
|
| - '''
|
| - <div class="field"><h4 id="$typeName.${field.name}">
|
| - <span class="show-code">Code</span>
|
| - ''');
|
| -
|
| - // A null typeName means it's a top-level definition which is implicitly
|
| - // static so doesn't need to annotate it.
|
| - if (field.isStatic && (typeName != null)) {
|
| - write('static ');
|
| - }
|
| -
|
| - if (field.isFinal) {
|
| - write('final ');
|
| - } else if (field.type.name == 'Dynamic') {
|
| - write('var ');
|
| - }
|
| -
|
| - write(optionalTypeRef(field.type));
|
| - write(
|
| - '''
|
| - <strong>${field.name}</strong> <a class="anchor-link"
|
| - href="#$typeName.${field.name}"
|
| - title="Permalink to $typeName.${field.name}">#</a>
|
| - </h4>
|
| - ''');
|
| -
|
| - docCode(field.span, showCode: true);
|
| - writeln('</div>');
|
| -}
|
| -
|
| -/**
|
| - * Writes a type annotation for [type]. Will hyperlink it to that type's
|
| - * documentation if possible.
|
| - */
|
| -typeRef(Type type) {
|
| - if (type.library != null) {
|
| - var library = sanitize(type.library.name);
|
| - return '<a href="${library}.html#${type.name}">${type.name}</a>';
|
| - } else {
|
| - return type.name;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Creates a linked string for an optional type annotation. Returns an empty
|
| - * string if the type is Dynamic.
|
| - */
|
| -optionalTypeRef(Type type) {
|
| - if (type.name == 'Dynamic') {
|
| - return '';
|
| - } else {
|
| - return typeRef(type) + ' ';
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Documents the code contained within [span]. Will include the previous
|
| - * Dartdoc associated with that span if found, and will include the syntax
|
| - * highlighted code itself if desired.
|
| - */
|
| -docCode(SourceSpan span, [bool showCode = false]) {
|
| - if (span == null) return;
|
| -
|
| - writeln('<div class="doc">');
|
| - var comment = findComment(span);
|
| - if (comment != null) {
|
| - writeln('<p>$comment</p>');
|
| - }
|
| -
|
| - if (showCode) {
|
| - writeln('<pre class="source">');
|
| - write(formatCode(span));
|
| - writeln('</pre>');
|
| - }
|
| -
|
| - writeln('</div>');
|
| -}
|
| -
|
| -/** Finds the doc comment preceding the given source span, if there is one. */
|
| -findComment(SourceSpan span) => findCommentInFile(span.file, span.start);
|
| -
|
| -/** Finds the doc comment preceding the given source span, if there is one. */
|
| -findCommentInFile(SourceFile file, int position) {
|
| - // Get the doc comments for this file.
|
| - var fileComments = _comments.putIfAbsent(file.filename,
|
| - () => parseDocComments(file));
|
| -
|
| - return fileComments[position];
|
| -}
|
| -
|
| -parseDocComments(SourceFile file) {
|
| - var comments = <int, String>{};
|
| -
|
| - var tokenizer = new Tokenizer(file, false);
|
| - var lastComment = null;
|
| -
|
| - while (true) {
|
| - var token = tokenizer.next();
|
| - if (token.kind == TokenKind.END_OF_FILE) break;
|
| -
|
| - if (token.kind == TokenKind.COMMENT) {
|
| - var text = token.text;
|
| - if (text.startsWith('/**')) {
|
| - // Remember that we've encountered a doc comment.
|
| - lastComment = stripComment(token.text);
|
| - }
|
| - } else if (token.kind == TokenKind.WHITESPACE) {
|
| - // Ignore whitespace tokens.
|
| - } else if (token.kind == TokenKind.HASH) {
|
| - // Look for #library() to find the library comment.
|
| - var next = tokenizer.next();
|
| - if ((lastComment != null) && (next.kind == TokenKind.LIBRARY)) {
|
| - comments[_libraryDoc] = lastComment;
|
| - lastComment = null;
|
| - }
|
| - } else {
|
| - if (lastComment != null) {
|
| - // We haven't attached the last doc comment to something yet, so stick
|
| - // it to this token.
|
| - comments[token.start] = lastComment;
|
| - lastComment = null;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return comments;
|
| -}
|
| -
|
| -/**
|
| - * Takes a string of Dart code and turns it into sanitized HTML.
|
| - */
|
| -formatCode(SourceSpan span) {
|
| - // Remove leading indentation to line up with first line.
|
| - var column = getSpanColumn(span);
|
| - var lines = span.text.split('\n');
|
| - // TODO(rnystrom): Dirty hack.
|
| - for (int i = 1; i < lines.length; i++) {
|
| - lines[i] = unindent(lines[i], column);
|
| - }
|
| -
|
| - var code = Strings.join(lines, '\n');
|
| -
|
| - // Syntax highlight.
|
| - return classifySource(new SourceFile('', code));
|
| -}
|
| -
|
| -// TODO(rnystrom): Move into SourceSpan?
|
| -int getSpanColumn(SourceSpan span) {
|
| - var line = span.file.getLine(span.start);
|
| - return span.file.getColumn(line, span.start);
|
| -}
|
| -
|
| -/** Removes up to [indentation] leading whitespace characters from [text]. */
|
| -unindent(String text, int indentation) {
|
| - var start;
|
| - for (start = 0; start < Math.min(indentation, text.length); start++) {
|
| - // Stop if we hit a non-whitespace character.
|
| - if (text[start] != ' ') break;
|
| - }
|
| -
|
| - return text.substring(start);
|
| -}
|
| -
|
| -/**
|
| - * Pulls the raw text out of a doc comment (i.e. removes the comment
|
| - * characters.
|
| - */
|
| -// TODO(rnystrom): Should handle [name] and [:code:] in comments. Should also
|
| -// break empty lines into multiple paragraphs. Other formatting?
|
| -// See dart/compiler/java/com/google/dart/compiler/backend/doc for ideas.
|
| -// (/DartDocumentationVisitor.java#180)
|
| -stripComment(comment) {
|
| - StringBuffer buf = new StringBuffer();
|
| -
|
| - for (var line in comment.split('\n')) {
|
| - line = line.trim();
|
| - if (line.startsWith('/**')) line = line.substring(3, line.length);
|
| - if (line.endsWith('*/')) line = line.substring(0, line.length-2);
|
| - line = line.trim();
|
| - while (line.startsWith('*')) line = line.substring(1, line.length);
|
| - line = line.trim();
|
| - buf.add(line);
|
| - buf.add(' ');
|
| - }
|
| -
|
| - return buf.toString();
|
| -}
|
|
|