Remove dartdoc.

R=alanknight@google.com, efortuna@google.com

Review URL: https://codereview.chromium.org//140303009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@32388 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
dgrove@google.com 2014-02-06 22:47:26 +00:00
parent e19eef550e
commit 7bed1bc4e9
44 changed files with 3 additions and 8494 deletions

View file

@ -1,6 +0,0 @@
# Generated output.
/docs/
# Compiled .js output.
static/client-static.js
static/client-live-nav.js

View file

@ -1,84 +0,0 @@
Dartdoc generates static HTML documentation from Dart code.
To use it, from this directory, run:
$ dartdoc <path to .dart file>
This will create a "docs" directory with the docs for your libraries.
How docs are generated
----------------------
To make beautiful docs from your library, dartdoc parses it and every library it
imports (recursively). From each library, it parses all classes and members,
finds the associated doc comments and builds crosslinked docs from them.
"Doc comments" can be in one of a few forms:
/**
* JavaDoc style block comments.
*/
/** Which can also be single line. */
/// Triple-slash line comments.
/// Which can be multiple lines.
The body of a doc comment will be parsed as markdown which means you can apply
most of the formatting and structuring you want while still having docs that
look nice in plain text. For example:
/// This is a doc comment. This is the first paragraph in the comment. It
/// can span multiple lines.
///
/// A blank line starts a new paragraph like this one.
///
/// * Unordered lists start with `*` or `-` or `+`.
/// * And can have multiple items.
/// 1. You can nest lists.
/// 2. Like this numbered one.
///
/// ---
///
/// Three dashes, underscores, or tildes on a line by themselves create a
/// horizontal rule.
///
/// to.get(a.block + of.code) {
/// indent(it, 4.spaces);
/// like(this);
/// }
///
/// There are a few inline styles you can apply: *emphasis*, **strong**,
/// and `inline code`. You can also use underscores for _emphasis_ and
/// __strong__.
///
/// An H1 header using equals on the next line
/// ==========================================
///
/// And an H2 in that style using hyphens
/// -------------------------------------
///
/// # Or an H1 - H6 using leading hashes
/// ## H2
/// ### H3
/// #### H4 you can also have hashes at then end: ###
/// ##### H5
/// ###### H6
There is also an extension to markdown specific to dartdoc: A name inside
square brackets that is not a markdown link (i.e. doesn't have square brackets
or parentheses following it) like:
Calls [someMethod], passing in [arg].
is understood to be the name of some member or type that's in the scope of the
member where that comment appears. Dartdoc will automatically figure out what
the name refers to and generate an approriate link to that member or type.
Attribution
-----------
dartdoc uses the delightful Silk icon set by Mark James.
http://www.famfamfam.com/lab/icons/silk/

View file

@ -1,285 +0,0 @@
// Copyright (c) 2012, 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.
/**
* To generate docs for a library, run this script with the path to an
* entrypoint .dart file, like:
*
* $ dart dartdoc.dart foo.dart
*
* This will create a "docs" directory with the docs for your libraries. To
* create these beautiful docs, dartdoc parses your library and every library
* it imports (recursively). From each library, it parses all classes and
* members, finds the associated doc comments and builds crosslinked docs from
* them.
*/
library dartdoc;
import 'dart:async';
import 'dart:io';
import '../lib/dartdoc.dart';
import '../lib/src/dartdoc/utils.dart';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
/**
* Run this from the `lib/_internal/dartdoc` directory.
*/
main(List<String> arguments) {
// Need this because ArgParser.getUsage doesn't show command invocation.
final USAGE = 'Usage dartdoc [options] <entrypoint(s)>\n[options] include:';
final dartdoc = new Dartdoc();
final argParser = new ArgParser();
String libPath = path.join(scriptDir, '..', '..', '..', '..');
String packageRoot;
argParser.addFlag('no-code',
help: 'Do not include source code in the documentation.',
defaultsTo: false, negatable: false,
callback: (noCode) => dartdoc.includeSource = !noCode);
argParser.addOption('mode', abbr: 'm',
help: 'Define how HTML pages are generated.',
allowed: ['static', 'live-nav'], allowedHelp: {
'static': 'Generates completely static HTML containing\n'
'everything you need to browse the docs. The only\n'
'client side behavior is trivial stuff like syntax\n'
'highlighting code, and the find-as-you-type search\n'
'box.',
'live-nav': '(Default) Generated docs do not included baked HTML\n'
'navigation. Instead a single `nav.json` file is\n'
'created and the appropriate navigation is generated\n'
'client-side by parsing that and building HTML.\n'
'\tThis dramatically reduces the generated size of\n'
'the HTML since a large fraction of each static page\n'
'is just redundant navigation links.\n'
'\tIn this mode, the browser will do a XHR for\n'
'nav.json which means that to preview docs locallly,\n'
'you will need to enable requesting file:// links in\n'
'your browser or run a little local server like\n'
'`python -m SimpleHTTPServer`.'},
defaultsTo: 'live-nav',
callback: (genMode) {
dartdoc.mode = (genMode == 'static' ? MODE_STATIC : MODE_LIVE_NAV);
});
argParser.addFlag('generate-app-cache',
help: 'Generates the App Cache manifest file, enabling\n'
'offline doc viewing.',
defaultsTo: false, negatable: false,
callback: (generate) => dartdoc.generateAppCache = generate);
argParser.addFlag('omit-generation-time',
help: 'Omits generation timestamp from output.',
defaultsTo: false, negatable: false,
callback: (genTimestamp) => dartdoc.omitGenerationTime = genTimestamp);
argParser.addFlag('verbose', abbr: 'v',
help: 'Print verbose information during generation.',
defaultsTo: false, negatable: false,
callback: (verb) => dartdoc.verbose = verb);
argParser.addFlag('include-api',
help: 'Include the used API libraries in the generated\n'
'documentation. If the --link-api option is used,\n'
'this option is ignored.',
defaultsTo: false, negatable: false,
callback: (incApi) => dartdoc.includeApi = incApi);
argParser.addFlag('link-api',
help: 'Link to the online language API in the generated\n'
'documentation. The option overrides inclusion\n'
'through --include-api or --include-lib.',
defaultsTo: false, negatable: false,
callback: (linkApi) => dartdoc.linkToApi = linkApi);
argParser.addFlag('show-private',
help: 'Document private types and members.',
defaultsTo: false,
callback: (showPrivate) => dartdoc.showPrivate = showPrivate);
argParser.addFlag('inherit-from-object',
help: 'Show members inherited from Object.',
defaultsTo: false, negatable: false,
callback: (inherit) => dartdoc.inheritFromObject = inherit);
argParser.addFlag('enable-diagnostic-colors', negatable: false);
argParser.addOption('out',
help: 'Generates files into directory specified. If\n'
'omitted the files are generated into ./docs/',
callback: (outDir) {
if(outDir != null) {
dartdoc.outputDir = outDir;
}
});
argParser.addOption('include-lib',
help: 'Use this option to explicitly specify which\n'
'libraries to include in the documentation. If\n'
'omitted, all used libraries are included by\n'
'default. Specify a comma-separated list of\n'
'library names, or call this option multiple times.',
callback: (incLibs) {
if(!incLibs.isEmpty) {
List<String> allLibs = new List<String>();
for(final lst in incLibs) {
var someLibs = lst.split(',');
for(final lib in someLibs) {
allLibs.add(lib);
}
}
dartdoc.includedLibraries = allLibs;
}
}, allowMultiple: true);
argParser.addOption('exclude-lib',
help: 'Use this option to explicitly specify which\n'
'libraries to exclude from the documentation. If\n'
'omitted, no libraries are excluded. Specify a\n'
'comma-separated list of library names, or call\n'
'this option multiple times.',
callback: (excLibs) {
if(!excLibs.isEmpty) {
List<String> allLibs = new List<String>();
for(final lst in excLibs) {
var someLibs = lst.split(',');
for(final lib in someLibs) {
allLibs.add(lib);
}
}
dartdoc.excludedLibraries = allLibs;
}
}, allowMultiple: true);
argParser.addOption('package-root',
help: 'Sets the package directory to the specified directory.\n'
'If omitted the package directory is the closest packages directory to'
' the entrypoint.',
callback: (packageDir) {
if(packageDir != null) {
packageRoot = packageDir;
}
});
argParser.addOption('library-root',
help: 'Sets the library root directory to the specified directory.',
callback: (libraryRoot) {
if (libraryRoot != null) {
libPath = libraryRoot;
}
});
// TODO(amouravski): This method is deprecated. Remove on April 22.
argParser.addOption('pkg',
help: 'Deprecated: same as --package-root.',
callback: (packageDir) {
if(packageDir != null) {
packageRoot = packageDir;
}
});
dartdoc.dartdocPath = path.join(libPath, 'lib', '_internal', 'dartdoc');
if (arguments.isEmpty) {
print('No arguments provided.');
print(USAGE);
print(argParser.getUsage());
exit(1);
}
final entrypoints = <Uri>[];
try {
final option = argParser.parse(arguments, allowTrailingOptions: true);
// This checks to see if the root of all entrypoints is the same.
// If it is not, then we display a warning, as package imports might fail.
var entrypointRoot;
for (final entrypoint in option.rest) {
var uri = Uri.parse(entrypoint);
// If it looks like it was a file path (no scheme, or a one letter scheme
// which is likely a drive letter on Windows), turn it into a file URL.
if (uri.scheme == '' || uri.scheme.length == 1) {
uri = path.toUri(entrypoint);
}
entrypoints.add(uri);
if (uri.scheme != 'file') continue;
if (entrypointRoot == null) {
entrypointRoot = path.dirname(entrypoint);
} else if (entrypointRoot != path.dirname(entrypoint)) {
print('Warning: entrypoints are at different directories. "package:"'
' imports may fail.');
}
}
} on FormatException catch (e) {
print(e.message);
print(USAGE);
print(argParser.getUsage());
exit(1);
}
if (entrypoints.isEmpty) {
print('No entrypoints provided.');
print(argParser.getUsage());
exit(1);
}
if (packageRoot == null) packageRoot = _getPackageRoot(entrypoints);
cleanOutputDirectory(dartdoc.outputDir);
// Start the analysis and documentation.
dartdoc.documentLibraries(entrypoints, libPath, packageRoot)
// Prepare the dart2js script code and copy static resources.
// TODO(amouravski): move compileScript out and pre-generate the client
// scripts. This takes a long time and the js hardly ever changes.
.then((_) => compileScript(dartdoc.mode, dartdoc.outputDir, libPath,
dartdoc.tmpPath))
.then((_) => copyDirectory(
path.join(libPath, 'lib', '_internal', 'dartdoc', 'static'),
dartdoc.outputDir))
.then((_) {
print(dartdoc.status);
if (dartdoc.totals == 0) {
exit(1);
}
})
.catchError((e, trace) {
print('Error: generation failed: ${e}');
if (trace != null) print("StackTrace: $trace");
dartdoc.cleanup();
exit(1);
})
.whenComplete(() => dartdoc.cleanup());
}
String _getPackageRoot(List<Uri> entrypoints) {
// Check if there's a `packages` directory in the entry point directory.
var fileEntrypoint = entrypoints.firstWhere(
(entrypoint) => entrypoint.scheme == 'file',
orElse: () => null);
if (fileEntrypoint == null) return;
var script = path.normalize(path.absolute(path.fromUri(fileEntrypoint)));
var dir = path.join(path.dirname(script), 'packages/');
if (new Directory(dir).existsSync()) return dir;
// If there is not, then check if the entrypoint is somewhere in a `lib`
// directory.
var parts = path.split(path.dirname(script));
var libDir = parts.lastIndexOf('lib');
if (libDir > 0) {
return path.join(path.joinAll(parts.take(libDir)), 'packages');
} else {
return null;
}
}

View file

@ -1,15 +0,0 @@
# 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.
test/markdown_test: Pass
test/dartdoc_test: Pass, Slow # Issue 16311
test/dartdoc_search_test: Pass, Skip
# Dartdoc only runs on the VM, so just rule out all compilers.
[ $compiler == dart2js || $compiler == dart2dart ]
*: Skip
# Dartdoc only runs on the standalone VM, not in dartium.
[ $runtime == drt || $runtime == dartium ]
*: Skip

View file

@ -1,206 +0,0 @@
// Copyright (c) 2012, 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.
library classify;
import '../../compiler/implementation/scanner/scannerlib.dart';
// TODO(rnystrom): Use "package:" URL (#4968).
import 'markdown.dart' as md;
/**
* Kinds of tokens that we care to highlight differently. The values of the
* fields here will be used as CSS class names for the generated spans.
*/
class Classification {
static const NONE = null;
static const ERROR = "e";
static const COMMENT = "c";
static const IDENTIFIER = "i";
static const KEYWORD = "k";
static const OPERATOR = "o";
static const STRING = "s";
static const NUMBER = "n";
static const PUNCTUATION = "p";
// A few things that are nice to make different:
static const TYPE_IDENTIFIER = "t";
// Between a keyword and an identifier
static const SPECIAL_IDENTIFIER = "r";
static const ARROW_OPERATOR = "a";
static const STRING_INTERPOLATION = 'si';
}
/// Returns a marked up HTML string. If the code does not appear to be valid
/// Dart code, returns the original [text].
String classifySource(String text) {
try {
var html = new StringBuffer();
var tokenizer = new StringScanner.fromString(text, includeComments: true);
var whitespaceOffset = 0;
var token = tokenizer.tokenize();
var inString = false;
while (token.kind != EOF_TOKEN) {
html.write(text.substring(whitespaceOffset, token.charOffset));
whitespaceOffset = token.charOffset + token.slowCharCount;
// Track whether or not we're in a string.
switch (token.kind) {
case STRING_TOKEN:
case STRING_INTERPOLATION_TOKEN:
inString = true;
break;
}
final kind = classify(token);
final escapedText = md.escapeHtml(token.value);
if (kind != null) {
// Add a secondary class to tokens appearing within a string so that
// we can highlight tokens in an interpolation specially.
var stringClass = inString ? Classification.STRING_INTERPOLATION : '';
html.write('<span class="$kind $stringClass">$escapedText</span>');
} else {
html.write(escapedText);
}
// Track whether or not we're in a string.
if (token.kind == STRING_TOKEN) {
inString = false;
}
token = token.next;
}
return html.toString();
} catch (e) {
return text;
}
}
bool _looksLikeType(String name) {
// If the name looks like an UppercaseName, assume it's a type.
return _looksLikePublicType(name) || _looksLikePrivateType(name);
}
bool _looksLikePublicType(String name) {
// If the name looks like an UppercaseName, assume it's a type.
return name.length >= 2 && isUpper(name[0]) && isLower(name[1]);
}
bool _looksLikePrivateType(String name) {
// If the name looks like an _UppercaseName, assume it's a type.
return (name.length >= 3 && name[0] == '_' && isUpper(name[1])
&& isLower(name[2]));
}
// These ensure that they don't return "true" if the string only has symbols.
bool isUpper(String s) => s.toLowerCase() != s;
bool isLower(String s) => s.toUpperCase() != s;
String classify(Token token) {
switch (token.kind) {
case IDENTIFIER_TOKEN:
// Special case for names that look like types.
final text = token.value;
if (_looksLikeType(text)
|| text == 'num'
|| text == 'bool'
|| text == 'int'
|| text == 'double') {
return Classification.TYPE_IDENTIFIER;
}
return Classification.IDENTIFIER;
case STRING_TOKEN:
case STRING_INTERPOLATION_TOKEN:
return Classification.STRING;
case INT_TOKEN:
case HEXADECIMAL_TOKEN:
case DOUBLE_TOKEN:
return Classification.NUMBER;
case COMMENT_TOKEN:
return Classification.COMMENT;
// => is so awesome it is in a class of its own.
case FUNCTION_TOKEN:
return Classification.ARROW_OPERATOR;
case OPEN_PAREN_TOKEN:
case CLOSE_PAREN_TOKEN:
case OPEN_SQUARE_BRACKET_TOKEN:
case CLOSE_SQUARE_BRACKET_TOKEN:
case OPEN_CURLY_BRACKET_TOKEN:
case CLOSE_CURLY_BRACKET_TOKEN:
case COLON_TOKEN:
case SEMICOLON_TOKEN:
case COMMA_TOKEN:
case PERIOD_TOKEN:
case PERIOD_PERIOD_TOKEN:
return Classification.PUNCTUATION;
case PLUS_PLUS_TOKEN:
case MINUS_MINUS_TOKEN:
case TILDE_TOKEN:
case BANG_TOKEN:
case EQ_TOKEN:
case BAR_EQ_TOKEN:
case CARET_EQ_TOKEN:
case AMPERSAND_EQ_TOKEN:
case LT_LT_EQ_TOKEN:
case GT_GT_EQ_TOKEN:
case PLUS_EQ_TOKEN:
case MINUS_EQ_TOKEN:
case STAR_EQ_TOKEN:
case SLASH_EQ_TOKEN:
case TILDE_SLASH_EQ_TOKEN:
case PERCENT_EQ_TOKEN:
case QUESTION_TOKEN:
case BAR_BAR_TOKEN:
case AMPERSAND_AMPERSAND_TOKEN:
case BAR_TOKEN:
case CARET_TOKEN:
case AMPERSAND_TOKEN:
case LT_LT_TOKEN:
case GT_GT_TOKEN:
case PLUS_TOKEN:
case MINUS_TOKEN:
case STAR_TOKEN:
case SLASH_TOKEN:
case TILDE_SLASH_TOKEN:
case PERCENT_TOKEN:
case EQ_EQ_TOKEN:
case BANG_EQ_TOKEN:
case EQ_EQ_EQ_TOKEN:
case BANG_EQ_EQ_TOKEN:
case LT_TOKEN:
case GT_TOKEN:
case LT_EQ_TOKEN:
case GT_EQ_TOKEN:
case INDEX_TOKEN:
case INDEX_EQ_TOKEN:
return Classification.OPERATOR;
// Color keyword token. Most are colored as keywords.
case HASH_TOKEN:
case KEYWORD_TOKEN:
if (token.stringValue == 'void') {
// Color "void" as a type.
return Classification.TYPE_IDENTIFIER;
}
if (token.stringValue == 'this' || token.stringValue == 'super') {
// Color "this" and "super" as identifiers.
return Classification.SPECIAL_IDENTIFIER;
}
return Classification.KEYWORD;
case EOF_TOKEN:
return Classification.NONE;
default:
return Classification.NONE;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,115 +0,0 @@
// Copyright (c) 2012, 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.
/// Parses text in a markdown-like format and renders to HTML.
library markdown;
// TODO(rnystrom): Use "package:" URL (#4968).
part 'src/markdown/ast.dart';
part 'src/markdown/block_parser.dart';
part 'src/markdown/html_renderer.dart';
part 'src/markdown/inline_parser.dart';
typedef Node Resolver(String name);
/// Converts the given string of markdown to HTML.
String markdownToHtml(String markdown, {inlineSyntaxes, linkResolver}) {
final document = new Document(inlineSyntaxes: inlineSyntaxes,
linkResolver: linkResolver);
// Replace windows line endings with unix line endings, and split.
final lines = markdown.replaceAll('\r\n','\n').split('\n');
document.parseRefLinks(lines);
final blocks = document.parseLines(lines);
return renderToHtml(blocks);
}
/// Replaces `<`, `&`, and `>`, with their HTML entity equivalents.
String escapeHtml(String html) {
return html.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;');
}
/// Maintains the context needed to parse a markdown document.
class Document {
final Map<String, Link> refLinks;
List<InlineSyntax> inlineSyntaxes;
Resolver linkResolver;
Document({this.inlineSyntaxes, this.linkResolver})
: refLinks = <String, Link>{};
parseRefLinks(List<String> lines) {
// This is a hideous regex. It matches:
// [id]: http:foo.com "some title"
// Where there may whitespace in there, and where the title may be in
// single quotes, double quotes, or parentheses.
final indent = r'^[ ]{0,3}'; // Leading indentation.
final id = r'\[([^\]]+)\]'; // Reference id in [brackets].
final quote = r'"[^"]+"'; // Title in "double quotes".
final apos = r"'[^']+'"; // Title in 'single quotes'.
final paren = r"\([^)]+\)"; // Title in (parentheses).
final pattern = new RegExp(
'$indent$id:\\s+(\\S+)\\s*($quote|$apos|$paren|)\\s*\$');
for (int i = 0; i < lines.length; i++) {
final match = pattern.firstMatch(lines[i]);
if (match != null) {
// Parse the link.
var id = match[1];
var url = match[2];
var title = match[3];
if (title == '') {
// No title.
title = null;
} else {
// Remove "", '', or ().
title = title.substring(1, title.length - 1);
}
// References are case-insensitive.
id = id.toLowerCase();
refLinks[id] = new Link(id, url, title);
// Remove it from the output. We replace it with a blank line which will
// get consumed by later processing.
lines[i] = '';
}
}
}
/// Parse the given [lines] of markdown to a series of AST nodes.
List<Node> parseLines(List<String> lines) {
final parser = new BlockParser(lines, this);
final blocks = [];
while (!parser.isDone) {
for (final syntax in BlockSyntax.syntaxes) {
if (syntax.canParse(parser)) {
final block = syntax.parse(parser);
if (block != null) blocks.add(block);
break;
}
}
}
return blocks;
}
/// Takes a string of raw text and processes all inline markdown tags,
/// returning a list of AST nodes. For example, given ``"*this **is** a*
/// `markdown`"``, returns:
/// `<em>this <strong>is</strong> a</em> <code>markdown</code>`.
List<Node> parseInline(String text) => new InlineParser(text, this).parse();
}
class Link {
final String id;
final String url;
final String title;
Link(this.id, this.url, this.title);
}

View file

@ -1,97 +0,0 @@
// Copyright (c) 2012, 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.
/** Provides client-side behavior for generated docs. */
library client;
import 'dart:html';
import 'dart:convert';
// TODO(rnystrom): Use "package:" URL (#4968).
import '../../classify.dart';
import '../../markdown.dart' as md;
import '../dartdoc/nav.dart';
import 'dropdown.dart';
import 'search.dart';
import 'client-shared.dart';
main() {
setup();
// Request the navigation data so we can build the HTML for it.
HttpRequest.getString('${prefix}nav.json').then((text) {
var json = JSON.decode(text);
buildNavigation(json);
setupSearch(json);
});
}
/**
* Takes [libraries], a JSON array representing a set of libraries and builds
* the appropriate navigation DOM for it relative to the current library and
* type.
*/
buildNavigation(List libraries) {
final html = new StringBuffer();
for (Map libraryInfo in libraries) {
String libraryName = libraryInfo[NAME];
html.write('<h2><div class="icon-library"></div>');
if (currentLibrary == libraryName && currentType == null) {
html.write('<strong>${md.escapeHtml(libraryName)}</strong>');
} else {
final url = getLibraryUrl(libraryName);
html.write('<a href="$url">${md.escapeHtml(libraryName)}</a>');
}
html.write('</h2>');
// Only list the types for the current library.
if (currentLibrary == libraryName && libraryInfo.containsKey(TYPES)) {
buildLibraryNavigation(html, libraryInfo);
}
}
// Insert it into the DOM.
final navElement = document.query('.nav');
navElement.innerHtml = html.toString();
}
/** Writes the navigation for the types contained by [library] to [html]. */
buildLibraryNavigation(StringBuffer html, Map libraryInfo) {
// Show the exception types separately.
final types = [];
final exceptions = [];
for (Map typeInfo in libraryInfo[TYPES]) {
var name = typeInfo[NAME];
if (name.endsWith('Exception') || name.endsWith('Error')) {
exceptions.add(typeInfo);
} else {
types.add(typeInfo);
}
}
if (types.length == 0 && exceptions.length == 0) return;
writeType(String icon, Map typeInfo) {
html.write('<li>');
if (currentType == typeInfo[NAME]) {
html.write(
'<div class="icon-$icon"></div><strong>${getTypeName(typeInfo)}</strong>');
} else {
html.write(
'''
<a href="${getTypeUrl(currentLibrary, typeInfo)}">
<div class="icon-$icon"></div>${getTypeName(typeInfo)}
</a>
''');
}
html.write('</li>');
}
html.write('<ul class="icon">');
types.forEach((typeInfo) =>
writeType(kindToString(typeInfo[KIND]), typeInfo));
exceptions.forEach((typeInfo) => writeType('exception', typeInfo));
html.write('</ul>');
}

View file

@ -1,114 +0,0 @@
// Copyright (c) 2012, 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.
library client_shared;
import 'dart:html';
import 'dropdown.dart';
import '../../classify.dart';
import '../dartdoc/nav.dart';
// Code shared between the different client-side libraries.
// The names of the library and type that this page documents.
String currentLibrary = null;
String currentType = null;
// What we need to prefix relative URLs with to get them to work.
String prefix = '';
void setup() {
setupLocation();
setupShortcuts();
enableCodeBlocks();
enableShowHideInherited();
}
void setupLocation() {
// Figure out where we are.
final body = document.query('body');
currentLibrary = body.dataset['library'];
currentType = body.dataset['type'];
prefix = (currentType != null) ? '../' : '';
}
/**
* Finds all code blocks and makes them toggleable. Syntax highlights each
* code block the first time it's shown.
*/
enableCodeBlocks() {
for (var elem in document.queryAll('.method, .field')) {
var showCode = elem.query('.show-code');
// Skip it if we don't have a code link. Will happen if source code is
// disabled.
if (showCode == null) continue;
var preList = elem.queryAll('pre.source');
showCode.onClick.listen((e) {
for (final pre in preList) {
if (pre.classes.contains('expanded')) {
pre.classes.remove('expanded');
} else {
// Syntax highlight.
if (!pre.classes.contains('formatted')) {
pre.innerHtml = classifySource(pre.text);
pre.classes.add('formatted');
};
pre.classes.add('expanded');
}
}
});
}
}
/**
* Enables show/hide functionality for inherited members and comments.
*/
void enableShowHideInherited() {
var showInherited = document.query('#show-inherited');
if (showInherited == null) return;
showInherited.dataset.putIfAbsent('show-inherited', () => 'block');
showInherited.onClick.listen((e) {
String display = showInherited.dataset['show-inherited'];
if (display == 'block') {
display = 'none';
showInherited.innerHtml = 'Show inherited';
} else {
display = 'block';
showInherited.innerHtml = 'Hide inherited';
}
showInherited.dataset['show-inherited'] = display;
for (var elem in document.queryAll('.inherited')) {
elem.style.display = display;
}
});
}
/** Turns [name] into something that's safe to use as a file name. */
String sanitize(String name) => name.replaceAll(':', '_').replaceAll('/', '_');
String getTypeName(Map typeInfo) =>
typeInfo.containsKey('args')
? '${typeInfo[NAME]}&lt;${typeInfo[NAME]}&gt;'
: typeInfo[NAME];
String getLibraryUrl(String libraryName) =>
'$prefix${sanitize(libraryName)}.html';
String getTypeUrl(String libraryName, Map typeInfo) =>
'$prefix${sanitize(libraryName)}/${sanitize(typeInfo[NAME])}.html';
String getLibraryMemberUrl(String libraryName, Map memberInfo) =>
'$prefix${sanitize(libraryName)}.html#${getMemberAnchor(memberInfo)}';
String getTypeMemberUrl(String libraryName, String typeName, Map memberInfo) =>
'$prefix${sanitize(libraryName)}/${sanitize(typeName)}.html#'
'${getMemberAnchor(memberInfo)}';
String getMemberAnchor(Map memberInfo) => memberInfo.containsKey(LINK_NAME)
? memberInfo[LINK_NAME] : memberInfo[NAME];

View file

@ -1,363 +0,0 @@
// Copyright (c) 2012, 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.
library dropdown;
import 'dart:html';
import 'search.dart';
import 'client-shared.dart';
import '../dartdoc/nav.dart';
List libraryList;
InputElement searchInput;
DivElement dropdown;
/**
* Update the search drop down based on the current search text.
*/
updateDropDown(Event event) {
if (libraryList == null) return;
if (searchInput == null) return;
if (dropdown == null) return;
var results = <Result>[];
String text = searchInput.value;
if (text == currentSearchText) {
return;
}
if (text.isEmpty) {
updateResults(text, results);
hideDropDown();
return;
}
if (text.contains('.')) {
// Search type members.
String typeText = text.substring(0, text.indexOf('.'));
String memberText = text.substring(text.indexOf('.') + 1);
if (typeText.isEmpty && memberText.isEmpty) {
// Don't search on '.'.
} else if (typeText.isEmpty) {
// Search text is of the form '.id' => Look up members.
matchAllMembers(results, memberText);
} else if (memberText.isEmpty) {
// Search text is of the form 'Type.' => Look up members in 'Type'.
matchAllMembersInType(results, typeText, memberText);
} else {
// Search text is of the form 'Type.id' => Look up member 'id' in 'Type'.
matchMembersInType(results, text, typeText, memberText);
}
} else {
// Search all entities.
var searchText = new SearchText(text);
for (Map<String,dynamic> library in libraryList) {
matchLibrary(results, searchText, library);
matchLibraryMembers(results, searchText, library);
matchTypes(results, searchText, library);
}
}
var elements = <Element>[];
var table = new TableElement();
table.classes.add('drop-down-table');
elements.add(table);
if (results.isEmpty) {
var row = table.insertRow(0);
row.innerHtml = "<tr><td>No matches found for '$text'.</td></tr>";
} else {
results.sort(resultComparator);
var count = 0;
for (Result result in results) {
result.addRow(table);
if (++count >= 10) {
break;
}
}
if (results.length >= 10) {
var row = table.insertRow(table.rows.length);
row.innerHtml = '<tr><td>+ ${results.length-10} more.</td></tr>';
results = results.sublist(0, 10);
}
}
dropdown.children = elements;
updateResults(text, results);
showDropDown();
}
void matchAllMembers(List<Result> results, String memberText) {
var searchText = new SearchText(memberText);
for (Map<String,dynamic> library in libraryList) {
String libraryName = library[NAME];
if (library.containsKey(TYPES)) {
for (Map<String,dynamic> type in library[TYPES]) {
String typeName = type[NAME];
if (type.containsKey(MEMBERS)) {
for (Map<String,dynamic> member in type[MEMBERS]) {
StringMatch memberMatch = obtainMatch(searchText, member[NAME]);
if (memberMatch != null) {
results.add(new Result(memberMatch, member[KIND],
getTypeMemberUrl(libraryName, typeName, member),
library: libraryName, type: typeName, args: type[ARGS],
noargs: member[NO_PARAMS]));
}
}
}
}
}
}
}
void matchAllMembersInType(List<Result> results,
String typeText, String memberText) {
var searchText = new SearchText(typeText);
var emptyText = new SearchText(memberText);
for (Map<String,dynamic> library in libraryList) {
String libraryName = library[NAME];
if (library.containsKey(TYPES)) {
for (Map<String,dynamic> type in library[TYPES]) {
String typeName = type[NAME];
StringMatch typeMatch = obtainMatch(searchText, typeName);
if (typeMatch != null) {
if (type.containsKey(MEMBERS)) {
for (Map<String,dynamic> member in type[MEMBERS]) {
StringMatch memberMatch = obtainMatch(emptyText,
member[NAME]);
results.add(new Result(memberMatch, member[KIND],
getTypeMemberUrl(libraryName, typeName, member),
library: libraryName, prefix: typeMatch,
noargs: member[NO_PARAMS]));
}
}
}
}
}
}
}
void matchMembersInType(List<Result> results,
String text, String typeText, String memberText) {
var searchText = new SearchText(text);
var typeSearchText = new SearchText(typeText);
var memberSearchText = new SearchText(memberText);
for (Map<String,dynamic> library in libraryList) {
String libraryName = library[NAME];
if (library.containsKey(TYPES)) {
for (Map<String,dynamic> type in library[TYPES]) {
String typeName = type[NAME];
StringMatch typeMatch = obtainMatch(typeSearchText, typeName);
if (typeMatch != null) {
if (type.containsKey(MEMBERS)) {
for (Map<String,dynamic> member in type[MEMBERS]) {
// Check for constructor match.
StringMatch constructorMatch = obtainMatch(searchText,
member[NAME]);
if (constructorMatch != null) {
results.add(new Result(constructorMatch, member[KIND],
getTypeMemberUrl(libraryName, typeName, member),
library: libraryName, noargs: member[NO_PARAMS]));
} else {
// Try member match.
StringMatch memberMatch = obtainMatch(memberSearchText,
member[NAME]);
if (memberMatch != null) {
results.add(new Result(memberMatch, member[KIND],
getTypeMemberUrl(libraryName, typeName, member),
library: libraryName, prefix: typeMatch,
args: type[ARGS], noargs: member[NO_PARAMS]));
}
}
}
}
}
}
}
}
}
void matchLibrary(List<Result> results, SearchText searchText, Map library) {
String libraryName = library[NAME];
StringMatch libraryMatch = obtainMatch(searchText, libraryName);
if (libraryMatch != null) {
results.add(new Result(libraryMatch, LIBRARY,
getLibraryUrl(libraryName)));
}
}
void matchLibraryMembers(List<Result> results, SearchText searchText,
Map library) {
if (library.containsKey(MEMBERS)) {
String libraryName = library[NAME];
for (Map<String,dynamic> member in library[MEMBERS]) {
StringMatch memberMatch = obtainMatch(searchText, member[NAME]);
if (memberMatch != null) {
results.add(new Result(memberMatch, member[KIND],
getLibraryMemberUrl(libraryName, member),
library: libraryName, noargs: member[NO_PARAMS]));
}
}
}
}
void matchTypes(List<Result> results, SearchText searchText,
Map library) {
if (library.containsKey(TYPES)) {
String libraryName = library[NAME];
for (Map<String,dynamic> type in library[TYPES]) {
String typeName = type[NAME];
matchType(results, searchText, libraryName, type);
matchTypeMembers(results, searchText, libraryName, type);
}
}
}
void matchType(List<Result> results, SearchText searchText,
String libraryName, Map type) {
String typeName = type[NAME];
StringMatch typeMatch = obtainMatch(searchText, typeName);
if (typeMatch != null) {
results.add(new Result(typeMatch, type[KIND],
getTypeUrl(libraryName, type),
library: libraryName, args: type[ARGS]));
}
}
void matchTypeMembers(List<Result> results, SearchText searchText,
String libraryName, Map type) {
if (type.containsKey(MEMBERS)) {
String typeName = type[NAME];
for (Map<String,dynamic> member in type[MEMBERS]) {
StringMatch memberMatch = obtainMatch(searchText, member[NAME]);
if (memberMatch != null) {
results.add(new Result(memberMatch, member[KIND],
getTypeMemberUrl(libraryName, typeName, member),
library: libraryName, type: typeName, args: type[ARGS],
noargs: member[NO_PARAMS]));
}
}
}
}
String currentSearchText;
Result _currentResult;
List<Result> currentResults = const <Result>[];
void updateResults(String searchText, List<Result> results) {
currentSearchText = searchText;
currentResults = results;
if (currentResults.isEmpty) {
_currentResultIndex = -1;
currentResult = null;
} else {
_currentResultIndex = 0;
currentResult = currentResults[0];
}
}
int _currentResultIndex;
void set currentResultIndex(int index) {
if (index < -1) {
return;
}
if (index >= currentResults.length) {
return;
}
if (index != _currentResultIndex) {
_currentResultIndex = index;
if (index >= 0) {
currentResult = currentResults[_currentResultIndex];
} else {
currentResult = null;
}
}
}
int get currentResultIndex => _currentResultIndex;
void set currentResult(Result result) {
if (_currentResult != result) {
if (_currentResult != null) {
_currentResult.row.classes.remove('drop-down-link-select');
}
_currentResult = result;
if (_currentResult != null) {
_currentResult.row.classes.add('drop-down-link-select');
}
}
}
Result get currentResult => _currentResult;
/**
* Navigate the search drop down using up/down inside the search field. Follow
* the result link on enter.
*/
void handleUpDown(KeyboardEvent event) {
if (event.keyCode == KeyCode.UP) {
currentResultIndex--;
event.preventDefault();
} else if (event.keyCode == KeyCode.DOWN) {
currentResultIndex++;
event.preventDefault();
} else if (event.keyCode == KeyCode.ENTER) {
if (currentResult != null) {
window.location.href = currentResult.url;
event.preventDefault();
hideDropDown();
}
}
}
/** Show the search drop down unless there are no current results. */
void showDropDown() {
if (currentResults.isEmpty) {
hideDropDown();
} else {
dropdown.style.visibility = 'visible';
}
}
/** Used to prevent hiding the drop down when it is clicked. */
bool hideDropDownSuspend = false;
/** Hide the search drop down unless suspended. */
void hideDropDown() {
if (hideDropDownSuspend) return;
dropdown.style.visibility = 'hidden';
}
/** Activate search on Ctrl+3 and S. */
void shortcutHandler(KeyboardEvent event) {
if (event.keyCode == KeyCode.THREE && event.ctrlKey) {
searchInput.focus();
event.preventDefault();
} else if (event.target != searchInput && event.keyCode == KeyCode.S) {
// Allow writing 's' in the search input.
searchInput.focus();
event.preventDefault();
}
}
/**
* Setup window shortcuts.
*/
void setupShortcuts() {
window.onKeyDown.listen(shortcutHandler);
}
/** Setup search hooks. */
void setupSearch(var libraries) {
libraryList = libraries;
searchInput = query('#q');
dropdown = query('#drop-down');
searchInput.onKeyDown.listen(handleUpDown);
searchInput.onKeyUp.listen(updateDropDown);
searchInput.onChange.listen(updateDropDown);
searchInput.onReset.listen(updateDropDown);
searchInput.onFocus.listen((event) => showDropDown());
searchInput.onBlur.listen((event) => hideDropDown());
}

View file

@ -1,230 +0,0 @@
// Copyright (c) 2012, 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.
library search;
import 'dart:html';
import 'dropdown.dart';
import '../dartdoc/nav.dart';
/**
* [SearchText] represent the search field text. The text is viewed in three
* ways: [text] holds the original search text, used for performing
* case-sensitive matches, [lowerCase] holds the lower-case search text, used
* for performing case-insenstive matches, [camelCase] holds a camel-case
* interpretation of the search text, used to order matches in camel-case.
*/
class SearchText {
final String text;
final String lowerCase;
final String camelCase;
SearchText(String searchText)
: text = searchText,
lowerCase = searchText.toLowerCase(),
camelCase = searchText.isEmpty ? ''
: '${searchText.substring(0, 1).toUpperCase()}'
'${searchText.substring(1)}';
int get length => text.length;
bool get isEmpty => length == 0;
}
/**
* [StringMatch] represents the case-insensitive matching of [searchText] as a
* substring within a [text].
*/
class StringMatch {
final SearchText searchText;
final String text;
final int matchOffset;
final int matchEnd;
StringMatch(this.searchText,
this.text, this.matchOffset, this.matchEnd);
/**
* Returns the HTML representation of the match.
*/
String toHtml() {
return '${text.substring(0, matchOffset)}'
'<span class="drop-down-link-highlight">$matchText</span>'
'${text.substring(matchEnd)}';
}
String get matchText =>
text.substring(matchOffset, matchEnd);
/**
* Is [:true:] iff [searchText] matches the full [text] case-sensitively.
*/
bool get isFullMatch => text == searchText.text;
/**
* Is [:true:] iff [searchText] matches a substring of [text]
* case-sensitively.
*/
bool get isExactMatch => matchText == searchText.text;
/**
* Is [:true:] iff [searchText] matches a substring of [text] when
* [searchText] is interpreted as camel case.
*/
bool get isCamelCaseMatch => matchText == searchText.camelCase;
}
/**
* [Result] represents a match of the search text on a library, type or member.
*/
class Result {
final StringMatch prefix;
final StringMatch match;
final String library;
final String type;
final String args;
final String kind;
final String url;
final bool noargs;
TableRowElement row;
Result(this.match, this.kind, this.url,
{this.library: null, this.type: null, String args: null,
this.prefix: null, this.noargs: false})
: this.args = args != null ? '&lt;$args&gt;' : '';
bool get isTopLevel => prefix == null && type == null;
void addRow(TableElement table) {
if (row != null) return;
clickHandler(Event event) {
window.location.href = url;
hideDropDown();
}
row = table.insertRow(table.rows.length);
row.classes.add('drop-down-link-tr');
row.onMouseDown.listen((event) => hideDropDownSuspend = true);
row.onClick.listen(clickHandler);
row.onMouseUp.listen((event) => hideDropDownSuspend = false);
var sb = new StringBuffer();
sb.write('<td class="drop-down-link-td">');
sb.write('<table class="drop-down-table"><tr><td colspan="2">');
if (kind == GETTER) {
sb.write('get ');
} else if (kind == SETTER) {
sb.write('set ');
}
sb.write(match.toHtml());
if (kind == CLASS || kind == TYPEDEF) {
sb.write(args);
} else if (kind == CONSTRUCTOR || kind == METHOD) {
if (noargs) {
sb.write("()");
} else {
sb.write('(...)');
}
}
sb.write('</td></tr><tr><td class="drop-down-link-kind">');
sb.write(kindToString(kind));
if (prefix != null) {
sb.write(' in ');
sb.write(prefix.toHtml());
sb.write(args);
} else if (type != null) {
sb.write(' in ');
sb.write(type);
sb.write(args);
}
sb.write('</td><td class="drop-down-link-library">');
if (library != null) {
sb.write('library $library');
}
sb.write('</td></tr></table></td>');
row.innerHtml = sb.toString();
}
}
/**
* Creates a [StringMatch] object for [text] if a substring matches
* [searchText], or returns [: null :] if no match is found.
*/
StringMatch obtainMatch(SearchText searchText, String text) {
if (searchText.isEmpty) {
return new StringMatch(searchText, text, 0, 0);
}
int offset = text.toLowerCase().indexOf(searchText.lowerCase);
if (offset != -1) {
return new StringMatch(searchText, text,
offset, offset + searchText.length);
}
return null;
}
/**
* Compares [a] and [b], regarding [:true:] smaller than [:false:].
*
* [:null:]-values are not handled.
*/
int compareBools(bool a, bool b) {
if (a == b) return 0;
return a ? -1 : 1;
}
/**
* Used to sort the search results heuristically to show the more relevant match
* in the top of the dropdown.
*/
int resultComparator(Result a, Result b) {
// Favor top level entities.
int result = compareBools(a.isTopLevel, b.isTopLevel);
if (result != 0) return result;
if (a.prefix != null && b.prefix != null) {
// Favor full prefix matches.
result = compareBools(a.prefix.isFullMatch, b.prefix.isFullMatch);
if (result != 0) return result;
}
// Favor matches in the start.
result = compareBools(a.match.matchOffset == 0,
b.match.matchOffset == 0);
if (result != 0) return result;
// Favor matches to the end. For example, prefer 'cancel' over 'cancelable'
result = compareBools(a.match.matchEnd == a.match.text.length,
b.match.matchEnd == b.match.text.length);
if (result != 0) return result;
// Favor exact case-sensitive matches.
result = compareBools(a.match.isExactMatch, b.match.isExactMatch);
if (result != 0) return result;
// Favor matches that do not break camel-case.
result = compareBools(a.match.isCamelCaseMatch, b.match.isCamelCaseMatch);
if (result != 0) return result;
// Favor matches close to the begining.
result = a.match.matchOffset.compareTo(b.match.matchOffset);
if (result != 0) return result;
if (a.type != null && b.type != null) {
// Favor short type names over long.
result = a.type.length.compareTo(b.type.length);
if (result != 0) return result;
// Sort type alphabetically.
// TODO(4805): Use [:type.compareToIgnoreCase] when supported.
result = a.type.toLowerCase().compareTo(b.type.toLowerCase());
if (result != 0) return result;
}
// Sort match alphabetically.
// TODO(4805): Use [:text.compareToIgnoreCase] when supported.
return a.match.text.toLowerCase().compareTo(b.match.text.toLowerCase());
}

View file

@ -1,72 +0,0 @@
// Copyright (c) 2012, 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.
library dart2js_util;
import 'dart:async' show Future;
import '../../../compiler/compiler.dart' as api;
import '../../../compiler/implementation/mirrors/analyze.dart' as dart2js;
import '../../../compiler/implementation/mirrors/source_mirrors.dart'
show MirrorSystem;
import '../../../compiler/implementation/source_file_provider.dart'
show FormattingDiagnosticHandler, SourceFileProvider,
CompilerSourceFileProvider;
import '../../../compiler/implementation/filenames.dart'
show appendSlash, currentDirectory;
// TODO(johnniwinther): Support client configurable providers.
/**
* Returns a future that completes to a non-null String when [script]
* has been successfully compiled.
*/
// TODO(amouravski): Remove this method and call dart2js via a process instead.
Future<String> compile(String script,
String libraryRoot,
{String packageRoot,
List<String> options: const <String>[],
api.DiagnosticHandler diagnosticHandler}) {
SourceFileProvider provider = new CompilerSourceFileProvider();
if (diagnosticHandler == null) {
diagnosticHandler =
new FormattingDiagnosticHandler(provider).diagnosticHandler;
}
Uri scriptUri = currentDirectory.resolve(script.toString());
Uri libraryUri = currentDirectory.resolve(appendSlash('$libraryRoot'));
Uri packageUri = null;
if (packageRoot != null) {
packageUri = currentDirectory.resolve(appendSlash('$packageRoot'));
}
return api.compile(scriptUri, libraryUri, packageUri,
provider.readStringFromUri, diagnosticHandler, options);
}
/**
* Analyzes set of libraries and provides a mirror system which can be used for
* static inspection of the source code.
*/
Future<MirrorSystem> analyze(List<String> libraries,
String libraryRoot,
{String packageRoot,
List<String> options: const <String>[],
api.DiagnosticHandler diagnosticHandler}) {
SourceFileProvider provider = new CompilerSourceFileProvider();
if (diagnosticHandler == null) {
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>[];
for (String library in libraries) {
librariesUri.add(currentDirectory.resolve(library));
}
return dart2js.analyze(librariesUri, libraryUri, packageUri,
provider.readStringFromUri, diagnosticHandler,
options);
}

View file

@ -1,74 +0,0 @@
// Copyright (c) 2012, 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.
library dartdoc_nav;
/*
* Constant values used for encoding navigation info.
*
* The generated JSON data is a list of LibraryInfo maps, defined as follows:
*
* LibraryInfo = {
* String NAME, // Library name.
* List<TypeInfo> TYPES, // Library types.
* List<MemberInfo> MEMBERS, // Library functions and variables.
* };
* TypeInfo = {
* String NAME, // Type name.
* String ARGS, // Type variables, e.g. "<K,V>". Optional.
* String KIND, // One of CLASS or TYPEDEF.
* List<MemberInfo> MEMBERS, // Type fields and methods.
* };
* MemberInfo = {
* String NAME, // Member name.
* String KIND, // One of FIELD, CONSTRUCTOR, METHOD, GETTER, or SETTER.
* String LINK_NAME, // Anchor name for the member if different from
* // NAME.
* bool NO_PARAMS, // Is true if member takes no arguments?
* };
*
*
* TODO(johnniwinther): Shorten the string values to reduce JSON output size.
*/
const String LIBRARY = 'library';
const String CLASS = 'class';
const String TYPEDEF = 'typedef';
const String MEMBERS = 'members';
const String TYPES = 'types';
const String ARGS = 'args';
const String NAME = 'name';
const String KIND = 'kind';
const String FIELD = 'field';
const String CONSTRUCTOR = 'constructor';
const String METHOD = 'method';
const String NO_PARAMS = 'noparams';
const String GETTER = 'getter';
const String SETTER = 'setter';
const String LINK_NAME = 'link_name';
/**
* Translation of const values to strings. Used to facilitate shortening of
* constant value strings.
*/
String kindToString(String kind) {
if (kind == LIBRARY) {
return 'library';
} else if (kind == CLASS) {
return 'class';
} else if (kind == TYPEDEF) {
return 'typedef';
} else if (kind == FIELD) {
return 'field';
} else if (kind == CONSTRUCTOR) {
return 'constructor';
} else if (kind == METHOD) {
return 'method';
} else if (kind == GETTER) {
return 'getter';
} else if (kind == SETTER) {
return 'setter';
}
return '';
}

View file

@ -1,142 +0,0 @@
// 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.
// Generic utility functions.
library utils;
import 'dart:io';
import 'dart:math' as math;
import 'package:path/path.dart' as pathos;
import '../../../../compiler/implementation/mirrors/source_mirrors.dart';
import '../export_map.dart';
/** Turns [name] into something that's safe to use as a file name. */
String sanitize(String name) => name.replaceAll(':', '_').replaceAll('/', '_');
/** Returns the number of times [search] occurs in [text]. */
int countOccurrences(String text, String search) {
int start = 0;
int count = 0;
while (true) {
start = text.indexOf(search, start);
if (start == -1) break;
count++;
// Offsetting by search length means overlapping results are not counted.
start += search.length;
}
return count;
}
/** Repeats [text] [count] times, separated by [separator] if given. */
String repeat(String text, int count, {String separator}) {
// TODO(rnystrom): Should be in corelib.
final buffer = new StringBuffer();
for (int i = 0; i < count; i++) {
buffer.write(text);
if ((i < count - 1) && (separator != null)) buffer.write(separator);
}
return buffer.toString();
}
/** Removes up to [indentation] leading whitespace characters from [text]. */
String 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);
}
/** Sorts the map by the key, doing a case-insensitive comparison. */
List<Mirror> orderByName(Iterable<DeclarationMirror> list) {
final elements = new List<Mirror>.from(list);
elements.sort((a,b) {
String aName = MirrorSystem.getName(a.simpleName).toLowerCase();
String bName = MirrorSystem.getName(b.simpleName).toLowerCase();
bool doma = aName.startsWith(r"$dom");
bool domb = bName.startsWith(r"$dom");
return doma == domb ? aName.compareTo(bName) : doma ? 1 : -1;
});
return elements;
}
/**
* Joins [items] into a single, comma-separated string using [conjunction].
* E.g. `['A', 'B', 'C']` becomes `"A, B, and C"`.
*/
String joinWithCommas(List<String> items, [String conjunction = 'and']) {
if (items.length == 1) return items[0];
if (items.length == 2) return "${items[0]} $conjunction ${items[1]}";
return '${items.take(items.length - 1).join(', ')}'
', $conjunction ${items[items.length - 1]}';
}
void writeString(File file, String text) {
var randomAccessFile = file.openSync(mode: FileMode.WRITE);
randomAccessFile.writeStringSync(text);
randomAccessFile.closeSync();
}
/**
* Converts [uri], which should come from a Dart import or export, to a local
* filesystem path. [basePath] is the base directory to use when converting
* relative URIs; without it, relative URIs will not be converted. [packageRoot]
* is the `packages` directory to use when converting `package:` URIs; without
* it, `package:` URIs will not be converted.
*
* If a URI cannot be converted, this will return `null`.
*/
String importUriToPath(Uri uri, {String basePath, String packageRoot}) {
if (uri.scheme == 'file') return pathos.fromUri(uri);
if (basePath != null && uri.scheme == '') {
return pathos.normalize(pathos.absolute(pathos.join(basePath, uri.path)));
}
if (packageRoot != null && uri.scheme == 'package') {
return pathos.normalize(pathos.absolute(
pathos.join(packageRoot, uri.path)));
}
// Ignore unsupported schemes.
return null;
}
/**
* If [map] contains an [Export] under [key], this merges that with [export].
* Otherwise, it sets [key] to [export].
*/
void addOrMergeExport(Map<LibraryMirror, Export> map,
LibraryMirror key, Export export) {
if (map.containsKey(key)) {
map[key] = map[key].merge(export);
} else {
map[key] = export;
}
}
/// A pair of values.
class Pair<E, F> {
E first;
F last;
Pair(this.first, this.last);
String toString() => '($first, $last)';
bool operator==(other) {
if (other is! Pair) return false;
return other.first == first && other.last == last;
}
int get hashCode => first.hashCode ^ last.hashCode;
}

View file

@ -1,233 +0,0 @@
// 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.
/// This library uses the Dart analyzer to find the exports for a set of
/// libraries. It stores these exports in an [ExportMap]. This is used to
/// display exported members as part of the exporting library, since dart2js
/// doesn't provide this information itself.
library export_map;
import '../../../compiler/implementation/mirrors/source_mirrors.dart';
import '../../../compiler/implementation/mirrors/mirrors_util.dart';
/// A class that tracks which libraries export which other libraries.
class ExportMap {
/// A map from libraries to their [Export]s.
///
/// Each key is a library and each value is a list of [Export]s for that
/// library. There's guaranteed to be only one [Export] of a given library
/// in a given list.
final Map<LibraryMirror, List<Export>> exports = {};
/// A cache of the transitive exports for each library. The values are maps
/// from the exported libraries to the [Export] objects, to make it easier to
/// merge multiple exports of the same library.
Map<LibraryMirror, Map<LibraryMirror, Export>> _transitiveExports = {};
ExportMap(MirrorSystem mirrors) {
mirrors.libraries.values.where((lib) => !_isDartLibrary(lib))
.forEach(_computeExports);
}
bool _isDartLibrary(LibraryMirror lib) => lib.uri.scheme == 'dart';
/// Compute all non-dart: exports in [library].
void _computeExports(LibrarySourceMirror library) {
var exportMap = {};
library.libraryDependencies
.where((mirror) =>
mirror.isExport && !_isDartLibrary(mirror.targetLibrary))
.map((mirror) => new Export.fromMirror(mirror))
.forEach((export) {
var target = export.exported;
if (exportMap.containsKey(target)) {
exportMap[target] = exportMap[target].merge(export);
} else {
exportMap[target] = export;
}
});
exports[library] = exportMap.values.toList();
}
/// Returns a list of all exports that [library] transitively exports. This
/// means that if [library] exports another library that in turn exports a
/// third, the third library will be included in the returned list.
///
/// This will automatically handle nested `hide` and `show` directives on the
/// exports, as well as merging multiple exports of the same library.
List<Export> transitiveExports(LibraryMirror library) {
Map<LibraryMirror, Export> _getTransitiveExports(LibraryMirror library) {
if (_transitiveExports.containsKey(library)) {
return _transitiveExports[library];
}
var exportsByPath = <LibraryMirror, Export>{};
_transitiveExports[library] = exportsByPath;
if (exports[library] == null) return exportsByPath;
for (var export in exports[library]) {
exportsByPath[export.exported] = export;
}
for (var export in exports[library]) {
for (var subExport in _getTransitiveExports(export.exported).values) {
subExport = export.compose(subExport);
if (exportsByPath.containsKey(subExport.exported)) {
subExport = subExport.merge(exportsByPath[subExport.exported]);
}
exportsByPath[subExport.exported] = subExport;
}
}
return exportsByPath;
}
return _getTransitiveExports(library).values.toList();
}
}
/// A class that represents one library exporting another.
class Export {
/// The library that contains this export.
final LibraryMirror exporter;
/// The library being exported.
final LibraryMirror exported;
/// The set of identifiers that are explicitly being exported. If this is
/// non-empty, no identifiers other than these will be visible.
///
/// One or both of [show] and [hide] will always be empty.
Set<String> get show => _show;
Set<String> _show;
/// The set of identifiers that are not exported.
///
/// One or both of [show] and [hide] will always be empty.
Set<String> get hide => _hide;
Set<String> _hide;
/// Whether or not members exported are hidden by default.
bool get _hideByDefault => !show.isEmpty;
/// Creates a new export.
///
/// This will normalize [show] and [hide] so that if both are non-empty, only
/// [show] will be set.
factory Export.fromMirror(LibraryDependencyMirror mirror) {
var show = <String>[];
var hide = <String>[];
for (var combinator in mirror.combinators) {
if (combinator.isShow) {
show.addAll(combinator.identifiers);
}
if (combinator.isHide) {
hide.addAll(combinator.identifiers);
}
}
return new Export(
mirror.sourceLibrary, mirror.targetLibrary, show: show, hide: hide);
}
Export(this.exporter, this.exported,
{Iterable<String> show, Iterable<String> hide}) {
_show = new Set<String>.from(show == null ? [] : show);
_hide = new Set<String>.from(hide == null ? [] : hide);
if (!_show.isEmpty) {
_show.removeAll(_hide);
_hide = new Set<String>();
}
}
/// Returns a new [Export] that represents [this] composed with [nested], as
/// though [this] was used to export a library that in turn exported [nested].
Export compose(Export nested) {
var show = new Set<String>();
var hide = new Set<String>();
if (this._hideByDefault) {
show.addAll(this.show);
if (nested._hideByDefault) {
show.retainAll(nested.show);
} else {
show.removeAll(nested.hide);
}
} else if (nested._hideByDefault) {
show.addAll(nested.show);
show.removeAll(this.hide);
} else {
hide.addAll(this.hide);
hide.addAll(nested.hide);
}
return new Export(this.exporter, nested.exported, show: show, hide: hide);
}
/// Returns a new [Export] that merges [this] with [nested], as though both
/// exports were included in the same library.
///
/// [this] and [other] must have the same values for [exporter] and [path].
Export merge(Export other) {
if (this.exported != other.exported) {
throw new ArgumentError("Can't merge two Exports with different paths: "
"export '$exported' from '$exporter' and export '${other.exported}' "
"from '${other.exporter}'.");
} if (this.exporter != other.exporter) {
throw new ArgumentError("Can't merge two Exports with different "
"exporters: export '$exported' from '$exporter' and export "
"'${other.exported}' from '${other.exporter}'.");
}
var show = new Set<String>();
var hide = new Set<String>();
if (this._hideByDefault) {
if (other._hideByDefault) {
show.addAll(this.show);
show.addAll(other.show);
} else {
hide.addAll(other.hide);
hide.removeAll(this.show);
}
} else {
hide.addAll(this.hide);
if (other._hideByDefault) {
hide.removeAll(other.show);
} else {
hide.retainAll(other.hide);
}
}
return new Export(exporter, exported, show: show, hide: hide);
}
/// Returns whether or not a member named [name] is visible through this
/// import, as goverend by [show] and [hide].
bool isMemberVisible(String name) =>
_hideByDefault ? show.contains(name) : !hide.contains(name);
bool operator==(other) => other is Export && other.exporter == exporter &&
other.exported == exported && show.containsAll(other.show) &&
other.show.containsAll(show) && hide.containsAll(other.hide) &&
other.hide.containsAll(hide);
int get hashCode {
var hashCode = exporter.hashCode ^ exported.hashCode;
combineHashCode(name) => hashCode ^= name.hashCode;
show.forEach(combineHashCode);
hide.forEach(combineHashCode);
return hashCode;
}
String toString() {
var combinator = '';
if (!show.isEmpty) {
combinator = ' show ${show.join(', ')}';
} else if (!hide.isEmpty) {
combinator = ' hide ${hide.join(', ')}';
}
return "export '${displayName(exported)}'"
"$combinator (from ${displayName(exporter)})";
}
}

View file

@ -1,257 +0,0 @@
// Copyright (c) 2012, 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.
/**
* Simple library to serialize acyclic Dart types to JSON.
* This library is not intended for broad consumption and should be replaced
* with a more generic Dart serialization library when one is available.
*/
library json_serializer;
import 'dart:async';
import 'dart:convert';
import 'dart:mirrors';
String serialize(Object o) {
var printer = new JsonPrinter();
_serialize(null, o, printer);
return printer.toString();
}
/// Serialize the object with pretty printing.
String prettySerialize(Object o) {
var printer = new JsonPrinter(prettyPrint: true);
_serialize(null, o, printer);
return printer.toString();
}
void _serialize(String name, Object o, JsonPrinter printer) {
if (o == null) return;
if (o is List) {
_serializeList(name, o, printer);
} else if (o is Map) {
_serializeMap(name, o, printer);
} else if (o is String) {
printer.addString(name, o);
} else if (o is bool) {
printer.addBool(name, o);
} else {
_serializeObject(name, o, printer);
}
}
void _serializeObject(String name, Object o, JsonPrinter printer) {
printer.startObject(name);
var mirror = reflect(o);
var classMirror = mirror.type;
var members = <String>[];
determineAllMembers(classMirror, members);
// TODO(jacobr): this code works only because futures for mirrors return
// immediately.
for(String memberName in members) {
var result = mirror.getField(new Symbol(memberName));
_serialize(memberName, result.reflectee, printer);
}
printer.endObject();
}
void determineAllMembers(ClassMirror classMirror,
List<String> members) {
for (var mirror in classMirror.declarations.values) {
if (mirror is VariableMirror ||
(mirror is MethodMirror && mirror.isGetter)) {
if (!members.contains(MirrorSystem.getName(mirror.simpleName))) {
members.add(MirrorSystem.getName(mirror.simpleName));
}
}
}
if (classMirror.superclass != null &&
// TODO(ahe): What is this test for? Consider removing it,
// dart2js will issue an error if there is a cycle in superclass
// hierarchy.
classMirror.superclass.qualifiedName != classMirror.qualifiedName &&
MirrorSystem.getName(classMirror.superclass.qualifiedName) !=
'dart.core.Object') {
determineAllMembers(classMirror.superclass, members);
}
}
void _serializeList(String name, List l, JsonPrinter printer) {
printer.startList(name);
for(var o in l) {
_serialize(null, o, printer);
}
printer.endList();
}
void _serializeMap(String name, Map m, JsonPrinter printer) {
printer.startObject(name);
m.forEach((key, value) =>
_serialize(key, value, printer));
printer.endObject();
}
class JsonPrinter {
static const int BACKSPACE = 8;
static const int TAB = 9;
static const int NEW_LINE = 10;
static const int FORM_FEED = 12;
static const int CARRIAGE_RETURN = 13;
static const int QUOTE = 34;
static const int BACKSLASH = 92;
static const int CHAR_B = 98;
static const int CHAR_F = 102;
static const int CHAR_N = 110;
static const int CHAR_R = 114;
static const int CHAR_T = 116;
static const int CHAR_U = 117;
StringBuffer _sb;
int _indent = 0;
bool _inSet = false;
bool prettyPrint;
JsonPrinter({this.prettyPrint: false}) {
_sb = new StringBuffer();
}
void startObject(String name) {
_start(name);
_sb.write('{');
_indent += 1;
_inSet = false;
}
void endObject() {
_indent -= 1;
if (_inSet) {
_newline();
}
_sb.write('}');
_inSet = true;
}
void startList(String name) {
_start(name);
_inSet = false;
_sb.write('[');
_indent += 1;
}
void endList() {
_indent -= 1;
if (_inSet) {
_newline();
}
_sb.write(']');
_inSet = true;
}
void addString(String name, String value) {
_start(name);
_sb.write('"');
_escape(_sb, value);
_sb.write('"');
_inSet = true;
}
void addBool(String name, bool value) {
_start(name);
_sb.write(value.toString());
_inSet = true;
}
void addNum(String name, num value) {
_start(name);
_sb.write(value.toString());
_inSet = true;
}
void _start(String name) {
if (_inSet) {
_sb.write(',');
}
// Do not print a newline at the beginning of the file.
if (!_sb.isEmpty) {
_newline();
}
if (name != null) {
_sb.write('"$name": ');
}
}
void _newline([int indent = 0]) {
_sb.write('\n');
_indent += indent;
for (var i = 0; i < _indent; ++i) {
_sb.write(' ');
}
}
String toString() {
if (prettyPrint) {
return _sb.toString();
} else {
// Convenient hack to remove the pretty printing this serializer adds by
// default.
return JSON.encode(JSON.decode(_sb.toString()));
}
}
static int _hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
static void _escape(StringBuffer sb, String s) {
final int length = s.length;
bool needsEscape = false;
final codeUnits = new List<int>();
for (int i = 0; i < length; i++) {
int codeUnit = s.codeUnitAt(i);
if (codeUnit < 32) {
needsEscape = true;
codeUnits.add(JsonPrinter.BACKSLASH);
switch (codeUnit) {
case JsonPrinter.BACKSPACE:
codeUnits.add(JsonPrinter.CHAR_B);
break;
case JsonPrinter.TAB:
codeUnits.add(JsonPrinter.CHAR_T);
break;
case JsonPrinter.NEW_LINE:
codeUnits.add(JsonPrinter.CHAR_N);
break;
case JsonPrinter.FORM_FEED:
codeUnits.add(JsonPrinter.CHAR_F);
break;
case JsonPrinter.CARRIAGE_RETURN:
codeUnits.add(JsonPrinter.CHAR_R);
break;
default:
codeUnits.add(JsonPrinter.CHAR_U);
codeUnits.add(_hexDigit((codeUnit >> 12) & 0xf));
codeUnits.add(_hexDigit((codeUnit >> 8) & 0xf));
codeUnits.add(_hexDigit((codeUnit >> 4) & 0xf));
codeUnits.add(_hexDigit(codeUnit & 0xf));
break;
}
} else if (codeUnit == JsonPrinter.QUOTE ||
codeUnit == JsonPrinter.BACKSLASH) {
needsEscape = true;
codeUnits.add(JsonPrinter.BACKSLASH);
codeUnits.add(codeUnit);
} else {
codeUnits.add(codeUnit);
}
}
sb.write(needsEscape ? new String.fromCharCodes(codeUnits) : s);
}
}

View file

@ -1,65 +0,0 @@
// Copyright (c) 2012, 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.
part of markdown;
/// Base class for any AST item. Roughly corresponds to Node in the DOM. Will
/// be either an Element or Text.
abstract class Node {
void accept(NodeVisitor visitor);
}
/// A named tag that can contain other nodes.
class Element implements Node {
final String tag;
final List<Node> children;
final Map<String, String> attributes;
Element(this.tag, this.children)
: attributes = <String, String>{};
Element.empty(this.tag)
: children = null,
attributes = <String, String>{};
Element.withTag(this.tag)
: children = [],
attributes = <String, String>{};
Element.text(this.tag, String text)
: children = [new Text(text)],
attributes = <String, String>{};
bool get isEmpty => children == null;
void accept(NodeVisitor visitor) {
if (visitor.visitElementBefore(this)) {
for (final child in children) child.accept(visitor);
visitor.visitElementAfter(this);
}
}
}
/// A plain text element.
class Text implements Node {
final String text;
Text(this.text);
void accept(NodeVisitor visitor) => visitor.visitText(this);
}
/// Visitor pattern for the AST. Renderers or other AST transformers should
/// implement this.
abstract class NodeVisitor {
/// Called when a Text node has been reached.
void visitText(Text text);
/// Called when an Element has been reached, before its children have been
/// visited. Return `false` to skip its children.
bool visitElementBefore(Element element);
/// Called when an Element has been reached, after its children have been
/// visited. Will not be called if [visitElementBefore] returns `false`.
void visitElementAfter(Element element);
}

View file

@ -1,463 +0,0 @@
// Copyright (c) 2012, 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.
part of markdown;
/// The line contains only whitespace or is empty.
final _RE_EMPTY = new RegExp(r'^([ \t]*)$');
/// A series of `=` or `-` (on the next line) define setext-style headers.
final _RE_SETEXT = new RegExp(r'^((=+)|(-+))$');
/// Leading (and trailing) `#` define atx-style headers.
final _RE_HEADER = new RegExp(r'^(#{1,6})(.*?)#*$');
/// The line starts with `>` with one optional space after.
final _RE_BLOCKQUOTE = new RegExp(r'^[ ]{0,3}>[ ]?(.*)$');
/// A line indented four spaces. Used for code blocks and lists.
final _RE_INDENT = new RegExp(r'^(?: |\t)(.*)$');
/// Three or more hyphens, asterisks or underscores by themselves. Note that
/// a line like `----` is valid as both HR and SETEXT. In case of a tie,
/// SETEXT should win.
final _RE_HR = new RegExp(r'^[ ]{0,3}((-+[ ]{0,2}){3,}|'
r'(_+[ ]{0,2}){3,}|'
r'(\*+[ ]{0,2}){3,})$');
/// Really hacky way to detect block-level embedded HTML. Just looks for
/// "<somename".
final _RE_HTML = new RegExp(r'^<[ ]*\w+[ >]');
/// A line starting with one of these markers: `-`, `*`, `+`. May have up to
/// three leading spaces before the marker and any number of spaces or tabs
/// after.
final _RE_UL = new RegExp(r'^[ ]{0,3}[*+-][ \t]+(.*)$');
/// A line starting with a number like `123.`. May have up to three leading
/// spaces before the marker and any number of spaces or tabs after.
final _RE_OL = new RegExp(r'^[ ]{0,3}\d+\.[ \t]+(.*)$');
/// Maintains the internal state needed to parse a series of lines into blocks
/// of markdown suitable for further inline parsing.
class BlockParser {
final List<String> lines;
/// The markdown document this parser is parsing.
final Document document;
/// Index of the current line.
int pos;
BlockParser(this.lines, this.document)
: pos = 0;
/// Gets the current line.
String get current => lines[pos];
/// Gets the line after the current one or `null` if there is none.
String get next {
// Don't read past the end.
if (pos >= lines.length - 1) return null;
return lines[pos + 1];
}
void advance() {
pos++;
}
bool get isDone => pos >= lines.length;
/// Gets whether or not the current line matches the given pattern.
bool matches(RegExp regex) {
if (isDone) return false;
return regex.firstMatch(current) != null;
}
/// Gets whether or not the current line matches the given pattern.
bool matchesNext(RegExp regex) {
if (next == null) return false;
return regex.firstMatch(next) != null;
}
}
abstract class BlockSyntax {
/// Gets the collection of built-in block parsers. To turn a series of lines
/// into blocks, each of these will be tried in turn. Order matters here.
static List<BlockSyntax> get syntaxes {
// Lazy initialize.
if (_syntaxes == null) {
_syntaxes = [
new EmptyBlockSyntax(),
new BlockHtmlSyntax(),
new SetextHeaderSyntax(),
new HeaderSyntax(),
new CodeBlockSyntax(),
new BlockquoteSyntax(),
new HorizontalRuleSyntax(),
new UnorderedListSyntax(),
new OrderedListSyntax(),
new ParagraphSyntax()
];
}
return _syntaxes;
}
static List<BlockSyntax> _syntaxes;
/// Gets the regex used to identify the beginning of this block, if any.
RegExp get pattern => null;
bool get canEndBlock => true;
bool canParse(BlockParser parser) {
return pattern.firstMatch(parser.current) != null;
}
Node parse(BlockParser parser);
List<String> parseChildLines(BlockParser parser) {
// Grab all of the lines that form the blockquote, stripping off the ">".
final childLines = <String>[];
while (!parser.isDone) {
final match = pattern.firstMatch(parser.current);
if (match == null) break;
childLines.add(match[1]);
parser.advance();
}
return childLines;
}
/// Gets whether or not [parser]'s current line should end the previous block.
static bool isAtBlockEnd(BlockParser parser) {
if (parser.isDone) return true;
return syntaxes.any((s) => s.canParse(parser) && s.canEndBlock);
}
}
class EmptyBlockSyntax extends BlockSyntax {
RegExp get pattern => _RE_EMPTY;
Node parse(BlockParser parser) {
parser.advance();
// Don't actually emit anything.
return null;
}
}
/// Parses setext-style headers.
class SetextHeaderSyntax extends BlockSyntax {
bool canParse(BlockParser parser) {
// Note: matches *next* line, not the current one. We're looking for the
// underlining after this line.
return parser.matchesNext(_RE_SETEXT);
}
Node parse(BlockParser parser) {
final match = _RE_SETEXT.firstMatch(parser.next);
final tag = (match[1][0] == '=') ? 'h1' : 'h2';
final contents = parser.document.parseInline(parser.current);
parser.advance();
parser.advance();
return new Element(tag, contents);
}
}
/// Parses atx-style headers: `## Header ##`.
class HeaderSyntax extends BlockSyntax {
RegExp get pattern => _RE_HEADER;
Node parse(BlockParser parser) {
final match = pattern.firstMatch(parser.current);
parser.advance();
final level = match[1].length;
final contents = parser.document.parseInline(match[2].trim());
return new Element('h$level', contents);
}
}
/// Parses email-style blockquotes: `> quote`.
class BlockquoteSyntax extends BlockSyntax {
RegExp get pattern => _RE_BLOCKQUOTE;
Node parse(BlockParser parser) {
final childLines = parseChildLines(parser);
// Recursively parse the contents of the blockquote.
final children = parser.document.parseLines(childLines);
return new Element('blockquote', children);
}
}
/// Parses preformatted code blocks that are indented four spaces.
class CodeBlockSyntax extends BlockSyntax {
RegExp get pattern => _RE_INDENT;
List<String> parseChildLines(BlockParser parser) {
final childLines = <String>[];
while (!parser.isDone) {
var match = pattern.firstMatch(parser.current);
if (match != null) {
childLines.add(match[1]);
parser.advance();
} else {
// If there's a codeblock, then a newline, then a codeblock, keep the
// code blocks together.
var nextMatch = parser.next != null ?
pattern.firstMatch(parser.next) : null;
if (parser.current.trim() == '' && nextMatch != null) {
childLines.add('');
childLines.add(nextMatch[1]);
parser.advance();
parser.advance();
} else {
break;
}
}
}
return childLines;
}
Node parse(BlockParser parser) {
final childLines = parseChildLines(parser);
// The Markdown tests expect a trailing newline.
childLines.add('');
// Escape the code.
final escaped = escapeHtml(childLines.join('\n'));
return new Element('pre', [new Element.text('code', escaped)]);
}
}
/// Parses horizontal rules like `---`, `_ _ _`, `* * *`, etc.
class HorizontalRuleSyntax extends BlockSyntax {
RegExp get pattern => _RE_HR;
Node parse(BlockParser parser) {
final match = pattern.firstMatch(parser.current);
parser.advance();
return new Element.empty('hr');
}
}
/// Parses inline HTML at the block level. This differs from other markdown
/// implementations in several ways:
///
/// 1. This one is way way WAY simpler.
/// 2. All HTML tags at the block level will be treated as blocks. If you
/// start a paragraph with `<em>`, it will not wrap it in a `<p>` for you.
/// As soon as it sees something like HTML, it stops mucking with it until
/// it hits the next block.
/// 3. Absolutely no HTML parsing or validation is done. We're a markdown
/// parser not an HTML parser!
class BlockHtmlSyntax extends BlockSyntax {
RegExp get pattern => _RE_HTML;
bool get canEndBlock => false;
Node parse(BlockParser parser) {
final childLines = [];
// Eat until we hit a blank line.
while (!parser.isDone && !parser.matches(_RE_EMPTY)) {
childLines.add(parser.current);
parser.advance();
}
return new Text(childLines.join('\n'));
}
}
class ListItem {
bool forceBlock = false;
final List<String> lines;
ListItem(this.lines);
}
/// Base class for both ordered and unordered lists.
abstract class ListSyntax extends BlockSyntax {
bool get canEndBlock => false;
String get listTag;
Node parse(BlockParser parser) {
final items = <ListItem>[];
var childLines = <String>[];
endItem() {
if (childLines.length > 0) {
items.add(new ListItem(childLines));
childLines = <String>[];
}
}
var match;
tryMatch(RegExp pattern) {
match = pattern.firstMatch(parser.current);
return match != null;
}
bool afterEmpty = false;
while (!parser.isDone) {
if (tryMatch(_RE_EMPTY)) {
// Add a blank line to the current list item.
childLines.add('');
} else if (tryMatch(_RE_UL) || tryMatch(_RE_OL)) {
// End the current list item and start a new one.
endItem();
childLines.add(match[1]);
} else if (tryMatch(_RE_INDENT)) {
// Strip off indent and add to current item.
childLines.add(match[1]);
} else if (BlockSyntax.isAtBlockEnd(parser)) {
// Done with the list.
break;
} else {
// Anything else is paragraph text or other stuff that can be in a list
// item. However, if the previous item is a blank line, this means we're
// done with the list and are starting a new top-level paragraph.
if ((childLines.length > 0) && (childLines.last == '')) break;
childLines.add(parser.current);
}
parser.advance();
}
endItem();
// Markdown, because it hates us, specifies two kinds of list items. If you
// have a list like:
//
// * one
// * two
//
// Then it will insert the conents of the lines directly in the <li>, like:
// <ul>
// <li>one</li>
// <li>two</li>
// <ul>
//
// If, however, there are blank lines between the items, each is wrapped in
// paragraphs:
//
// * one
//
// * two
//
// <ul>
// <li><p>one</p></li>
// <li><p>two</p></li>
// <ul>
//
// In other words, sometimes we parse the contents of a list item like a
// block, and sometimes line an inline. The rules our parser implements are:
//
// - If it has more than one line, it's a block.
// - If the line matches any block parser (BLOCKQUOTE, HEADER, HR, INDENT,
// UL, OL) it's a block. (This is for cases like "* > quote".)
// - If there was a blank line between this item and the previous one, it's
// a block.
// - If there was a blank line between this item and the next one, it's a
// block.
// - Otherwise, parse it as an inline.
// Remove any trailing empty lines and note which items are separated by
// empty lines. Do this before seeing which items are single-line so that
// trailing empty lines on the last item don't force it into being a block.
for (int i = 0; i < items.length; i++) {
for (int j = items[i].lines.length - 1; j > 0; j--) {
if (_RE_EMPTY.firstMatch(items[i].lines[j]) != null) {
// Found an empty line. Item and one after it are blocks.
if (i < items.length - 1) {
items[i].forceBlock = true;
items[i + 1].forceBlock = true;
}
items[i].lines.removeLast();
} else {
break;
}
}
}
// Convert the list items to Nodes.
final itemNodes = <Node>[];
for (final item in items) {
bool blockItem = item.forceBlock || (item.lines.length > 1);
// See if it matches some block parser.
final blocksInList = [
_RE_BLOCKQUOTE,
_RE_HEADER,
_RE_HR,
_RE_INDENT,
_RE_UL,
_RE_OL
];
if (!blockItem) {
for (final pattern in blocksInList) {
if (pattern.firstMatch(item.lines[0]) != null) {
blockItem = true;
break;
}
}
}
// Parse the item as a block or inline.
if (blockItem) {
// Block list item.
final children = parser.document.parseLines(item.lines);
itemNodes.add(new Element('li', children));
} else {
// Raw list item.
final contents = parser.document.parseInline(item.lines[0]);
itemNodes.add(new Element('li', contents));
}
}
return new Element(listTag, itemNodes);
}
}
/// Parses unordered lists.
class UnorderedListSyntax extends ListSyntax {
RegExp get pattern => _RE_UL;
String get listTag => 'ul';
}
/// Parses ordered lists.
class OrderedListSyntax extends ListSyntax {
RegExp get pattern => _RE_OL;
String get listTag => 'ol';
}
/// Parses paragraphs of regular text.
class ParagraphSyntax extends BlockSyntax {
bool get canEndBlock => false;
bool canParse(BlockParser parser) => true;
Node parse(BlockParser parser) {
final childLines = [];
// Eat until we hit something that ends a paragraph.
while (!BlockSyntax.isAtBlockEnd(parser)) {
childLines.add(parser.current);
parser.advance();
}
final contents = parser.document.parseInline(childLines.join('\n'));
return new Element('p', contents);
}
}

View file

@ -1,61 +0,0 @@
// Copyright (c) 2012, 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.
part of markdown;
String renderToHtml(List<Node> nodes) => new HtmlRenderer().render(nodes);
/// Translates a parsed AST to HTML.
class HtmlRenderer implements NodeVisitor {
static final _BLOCK_TAGS = new RegExp(
'blockquote|h1|h2|h3|h4|h5|h6|hr|p|pre');
StringBuffer buffer;
HtmlRenderer();
String render(List<Node> nodes) {
buffer = new StringBuffer();
for (final node in nodes) node.accept(this);
return buffer.toString();
}
void visitText(Text text) {
buffer.write(text.text);
}
bool visitElementBefore(Element element) {
// Hackish. Separate block-level elements with newlines.
if (!buffer.isEmpty &&
_BLOCK_TAGS.firstMatch(element.tag) != null) {
buffer.write('\n');
}
buffer.write('<${element.tag}');
// Sort the keys so that we generate stable output.
// TODO(rnystrom): This assumes keys returns a fresh mutable
// collection.
final attributeNames = element.attributes.keys.toList();
attributeNames.sort((a, b) => a.compareTo(b));
for (final name in attributeNames) {
buffer.write(' $name="${element.attributes[name]}"');
}
if (element.isEmpty) {
// Empty element like <hr/>.
buffer.write(' />');
return false;
} else {
buffer.write('>');
return true;
}
}
void visitElementAfter(Element element) {
buffer.write('</${element.tag}>');
}
}

View file

@ -1,419 +0,0 @@
// Copyright (c) 2012, 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.
part of markdown;
/// Maintains the internal state needed to parse inline span elements in
/// markdown.
class InlineParser {
static List<InlineSyntax> defaultSyntaxes = <InlineSyntax>[
// This first regexp matches plain text to accelerate parsing. It must
// be written so that it does not match any prefix of any following
// syntax. Most markdown is plain text, so it is faster to match one
// regexp per 'word' rather than fail to match all the following regexps
// at each non-syntax character position. It is much more important
// that the regexp is fast than complete (for example, adding grouping
// is likely to slow the regexp down enough to negate its benefit).
// Since it is purely for optimization, it can be removed for debugging.
// TODO(amouravski): this regex will glom up any custom syntaxes unless
// they're at the beginning.
new TextSyntax(r'\s*[A-Za-z0-9]+'),
// The real syntaxes.
new AutolinkSyntax(),
new LinkSyntax(),
// "*" surrounded by spaces is left alone.
new TextSyntax(r' \* '),
// "_" surrounded by spaces is left alone.
new TextSyntax(r' _ '),
// Leave already-encoded HTML entities alone. Ensures we don't turn
// "&amp;" into "&amp;amp;"
new TextSyntax(r'&[#a-zA-Z0-9]*;'),
// Encode "&".
new TextSyntax(r'&', sub: '&amp;'),
// Encode "<". (Why not encode ">" too? Gruber is toying with us.)
new TextSyntax(r'<', sub: '&lt;'),
// Parse "**strong**" tags.
new TagSyntax(r'\*\*', tag: 'strong'),
// Parse "__strong__" tags.
new TagSyntax(r'__', tag: 'strong'),
// Parse "*emphasis*" tags.
new TagSyntax(r'\*', tag: 'em'),
// Parse "_emphasis_" tags.
// TODO(rnystrom): Underscores in the middle of a word should not be
// parsed as emphasis like_in_this.
new TagSyntax(r'_', tag: 'em'),
// Parse inline code within double backticks: "``code``".
new CodeSyntax(r'``\s?((?:.|\n)*?)\s?``'),
// Parse inline code within backticks: "`code`".
new CodeSyntax(r'`([^`]*)`')
// We will add the LinkSyntax once we know about the specific link resolver.
];
/// The string of markdown being parsed.
final String source;
/// The markdown document this parser is parsing.
final Document document;
List<InlineSyntax> syntaxes;
/// The current read position.
int pos = 0;
/// Starting position of the last unconsumed text.
int start = 0;
final List<TagState> _stack;
InlineParser(this.source, this.document)
: _stack = <TagState>[] {
/// User specified syntaxes will be the first syntaxes to be evaluated.
if (document.inlineSyntaxes != null) {
syntaxes = [];
syntaxes.addAll(document.inlineSyntaxes);
syntaxes.addAll(defaultSyntaxes);
} else {
syntaxes = defaultSyntaxes;
}
// Custom link resolver goes after the generic text syntax.
syntaxes.insert(1, new LinkSyntax(linkResolver: document.linkResolver));
}
List<Node> parse() {
// Make a fake top tag to hold the results.
_stack.add(new TagState(0, 0, null));
while (!isDone) {
bool matched = false;
// See if any of the current tags on the stack match. We don't allow tags
// of the same kind to nest, so this takes priority over other possible // matches.
for (int i = _stack.length - 1; i > 0; i--) {
if (_stack[i].tryMatch(this)) {
matched = true;
break;
}
}
if (matched) continue;
// See if the current text matches any defined markdown syntax.
for (final syntax in syntaxes) {
if (syntax.tryMatch(this)) {
matched = true;
break;
}
}
if (matched) continue;
// If we got here, it's just text.
advanceBy(1);
}
// Unwind any unmatched tags and get the results.
return _stack[0].close(this, null);
}
writeText() {
writeTextRange(start, pos);
start = pos;
}
writeTextRange(int start, int end) {
if (end > start) {
final text = source.substring(start, end);
final nodes = _stack.last.children;
// If the previous node is text too, just append.
if ((nodes.length > 0) && (nodes.last is Text)) {
final newNode = new Text('${nodes.last.text}$text');
nodes[nodes.length - 1] = newNode;
} else {
nodes.add(new Text(text));
}
}
}
addNode(Node node) {
_stack.last.children.add(node);
}
// TODO(rnystrom): Only need this because RegExp doesn't let you start
// searching from a given offset.
String get currentSource => source.substring(pos, source.length);
bool get isDone => pos == source.length;
void advanceBy(int length) {
pos += length;
}
void consume(int length) {
pos += length;
start = pos;
}
}
/// Represents one kind of markdown tag that can be parsed.
abstract class InlineSyntax {
final RegExp pattern;
InlineSyntax(String pattern)
: pattern = new RegExp(pattern, multiLine: true);
bool tryMatch(InlineParser parser) {
final startMatch = pattern.firstMatch(parser.currentSource);
if ((startMatch != null) && (startMatch.start == 0)) {
// Write any existing plain text up to this point.
parser.writeText();
if (onMatch(parser, startMatch)) {
parser.consume(startMatch[0].length);
}
return true;
}
return false;
}
bool onMatch(InlineParser parser, Match match);
}
/// Matches stuff that should just be passed through as straight text.
class TextSyntax extends InlineSyntax {
String substitute;
TextSyntax(String pattern, {String sub})
: super(pattern),
substitute = sub;
bool onMatch(InlineParser parser, Match match) {
if (substitute == null) {
// Just use the original matched text.
parser.advanceBy(match[0].length);
return false;
}
// Insert the substitution.
parser.addNode(new Text(substitute));
return true;
}
}
/// Matches autolinks like `<http://foo.com>`.
class AutolinkSyntax extends InlineSyntax {
AutolinkSyntax()
: super(r'<((http|https|ftp)://[^>]*)>');
// TODO(rnystrom): Make case insensitive.
bool onMatch(InlineParser parser, Match match) {
final url = match[1];
final anchor = new Element.text('a', escapeHtml(url));
anchor.attributes['href'] = url;
parser.addNode(anchor);
return true;
}
}
/// Matches syntax that has a pair of tags and becomes an element, like `*` for
/// `<em>`. Allows nested tags.
class TagSyntax extends InlineSyntax {
final RegExp endPattern;
final String tag;
TagSyntax(String pattern, {String tag, String end})
: super(pattern),
endPattern = new RegExp((end != null) ? end : pattern, multiLine: true),
tag = tag;
// TODO(rnystrom): Doing this.field doesn't seem to work with named args.
bool onMatch(InlineParser parser, Match match) {
parser._stack.add(new TagState(parser.pos,
parser.pos + match[0].length, this));
return true;
}
bool onMatchEnd(InlineParser parser, Match match, TagState state) {
parser.addNode(new Element(tag, state.children));
return true;
}
}
/// Matches inline links like `[blah] [id]` and `[blah] (url)`.
class LinkSyntax extends TagSyntax {
Resolver linkResolver;
/// The regex for the end of a link needs to handle both reference style and
/// inline styles as well as optional titles for inline links. To make that
/// a bit more palatable, this breaks it into pieces.
static get linkPattern {
final refLink = r'\s?\[([^\]]*)\]'; // "[id]" reflink id.
final title = r'(?:[ ]*"([^"]+)"|)'; // Optional title in quotes.
final inlineLink = '\\s?\\(([^ )]+)$title\\)'; // "(url "title")" link.
return '\](?:($refLink|$inlineLink)|)';
// The groups matched by this are:
// 1: Will be non-empty if it's either a ref or inline link. Will be empty
// if it's just a bare pair of square brackets with nothing after them.
// 2: Contains the id inside [] for a reference-style link.
// 3: Contains the URL for an inline link.
// 4: Contains the title, if present, for an inline link.
}
LinkSyntax({this.linkResolver})
: super(r'\[', end: linkPattern);
bool onMatchEnd(InlineParser parser, Match match, TagState state) {
var url;
var title;
// If we didn't match refLink or inlineLink, then it means there was
// nothing after the first square bracket, so it isn't a normal markdown
// link at all. Instead, we allow users of the library to specify a special
// resolver function ([linkResolver]) that may choose to handle
// this. Otherwise, it's just treated as plain text.
if ((match[1] == null) || (match[1] == '')) {
if (linkResolver == null) return false;
// Only allow implicit links if the content is just text.
// TODO(rnystrom): Do we want to relax this?
if (state.children.length != 1) return false;
if (state.children[0] is! Text) return false;
Text link = state.children[0];
// See if we have a resolver that will generate a link for us.
final node = linkResolver(link.text);
if (node == null) return false;
parser.addNode(node);
return true;
}
if ((match[3] != null) && (match[3] != '')) {
// Inline link like [foo](url).
url = match[3];
title = match[4];
// For whatever reason, markdown allows angle-bracketed URLs here.
if (url.startsWith('<') && url.endsWith('>')) {
url = url.substring(1, url.length - 1);
}
} else {
// Reference link like [foo] [bar].
var id = match[2];
if (id == '') {
// The id is empty ("[]") so infer it from the contents.
id = parser.source.substring(state.startPos + 1, parser.pos);
}
// References are case-insensitive.
id = id.toLowerCase();
// Look up the link.
final link = parser.document.refLinks[id];
// If it's an unknown link just emit plaintext.
if (link == null) return false;
url = link.url;
title = link.title;
}
final anchor = new Element('a', state.children);
anchor.attributes['href'] = escapeHtml(url);
if ((title != null) && (title != '')) {
anchor.attributes['title'] = escapeHtml(title);
}
parser.addNode(anchor);
return true;
}
}
/// Matches backtick-enclosed inline code blocks.
class CodeSyntax extends InlineSyntax {
CodeSyntax(String pattern)
: super(pattern);
bool onMatch(InlineParser parser, Match match) {
parser.addNode(new Element.text('code', escapeHtml(match[1])));
return true;
}
}
/// Keeps track of a currently open tag while it is being parsed. The parser
/// maintains a stack of these so it can handle nested tags.
class TagState {
/// The point in the original source where this tag started.
int startPos;
/// The point in the original source where open tag ended.
int endPos;
/// The syntax that created this node.
final TagSyntax syntax;
/// The children of this node. Will be `null` for text nodes.
final List<Node> children;
TagState(this.startPos, this.endPos, this.syntax)
: children = <Node>[];
/// Attempts to close this tag by matching the current text against its end
/// pattern.
bool tryMatch(InlineParser parser) {
Match endMatch = syntax.endPattern.firstMatch(parser.currentSource);
if ((endMatch != null) && (endMatch.start == 0)) {
// Close the tag.
close(parser, endMatch);
return true;
}
return false;
}
/// Pops this tag off the stack, completes it, and adds it to the output.
/// Will discard any unmatched tags that happen to be above it on the stack.
/// If this is the last node in the stack, returns its children.
List<Node> close(InlineParser parser, Match endMatch) {
// If there are unclosed tags on top of this one when it's closed, that
// means they are mismatched. Mismatched tags are treated as plain text in
// markdown. So for each tag above this one, we write its start tag as text
// and then adds its children to this one's children.
int index = parser._stack.indexOf(this);
// Remove the unmatched children.
final unmatchedTags = parser._stack.sublist(index + 1);
parser._stack.removeRange(index + 1, parser._stack.length);
// Flatten them out onto this tag.
for (final unmatched in unmatchedTags) {
// Write the start tag as text.
parser.writeTextRange(unmatched.startPos, unmatched.endPos);
// Bequeath its children unto this tag.
children.addAll(unmatched.children);
}
// Pop this off the stack.
parser.writeText();
parser._stack.removeLast();
// If the stack is empty now, this is the special "results" node.
if (parser._stack.length == 0) return children;
// We are still parsing, so add this to its parent's children.
if (syntax.onMatchEnd(parser, endMatch, this)) {
parser.consume(endMatch[0].length);
} else {
// Didn't close correctly so revert to text.
parser.start = startPos;
parser.advanceBy(endMatch[0].length);
}
return null;
}
}

View file

@ -1,541 +0,0 @@
// Copyright (c) 2012, 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.
/**
* This library serializes the Dart2Js AST into a compact and easy to use
* [Element] tree useful for code exploration tools such as DartDoc.
*/
library universe_serializer;
import 'dartdoc.dart';
// TODO(rnystrom): Use "package:" URL (#4968).
import 'package:path/path.dart' as path;
import '../../compiler/implementation/mirrors/source_mirrors.dart';
import '../../compiler/implementation/mirrors/mirrors_util.dart';
String _stripUri(String uri) {
String prefix = "/dart/";
int start = uri.indexOf(prefix);
if (start != -1) {
return uri.substring(start + prefix.length);
} else {
return uri;
}
}
String _escapeId(String id) {
return id.replaceAll(new RegExp('[/]'), '#slash');
}
/**
* Base class for all elements in the AST.
*/
class Element {
/** Human readable type name for the node. */
final String kind;
/** Human readable name for the element. */
final String name;
/** Id for the node that is unique within its parent's children. */
final String id;
/** Raw text of the comment associated with the Element if any. */
final String comment;
/** Raw html comment for the Element from MDN. */
String mdnCommentHtml;
/**
* The URL to the page on MDN that content was pulled from for the current
* type being documented. Will be `null` if the type doesn't use any MDN
* content.
*/
String mdnUrl;
/** Children of the node. */
List<Element> children;
/** Whether the element is private. */
final bool isPrivate;
/**
* Uri containing the definition of the element.
*/
String uri;
/**
* Line in the original source file that starts the definition of the element.
*/
String line;
// TODO(jacobr): refactor the code so that lookupMdnComment does not need to
// be passed to every Element constructor.
Element(DeclarationMirror mirror, this.kind, this.name,
String id, this.comment,
MdnComment lookupMdnComment(Mirror))
: line = mirror.location.line.toString(),
id = _escapeId(id),
isPrivate = _optionalBool(mirror.isPrivate),
uri = _stripUri(mirror.location.sourceUri.toString()) {
if (lookupMdnComment != null) {
var mdnComment = lookupMdnComment(mirror);
if (mdnComment != null) {
mdnCommentHtml = mdnComment.mdnComment;
mdnUrl = mdnComment.mdnUrl;
}
}
}
void addChild(Element child) {
if (children == null) {
children = <Element>[];
}
children.add(child);
}
/**
* Remove all URIs that exactly match the parent node's URI.
* This reduces output file size by about 20%.
*/
void stripDuplicateUris(String parentUri, parentLine) {
if (children != null) {
for (var child in children) {
child.stripDuplicateUris(uri, line);
}
}
if (parentUri == uri) {
uri = null;
}
if (line == parentLine) {
line = null;
}
}
}
/**
* Converts false to null. Useful as the serialization scheme we use
* omits null values.
*/
bool _optionalBool(bool value) => value == true ? true : null;
Reference _optionalReference(DeclarationMirror mirror) {
return (mirror != null && nameOf(mirror) != "Dynamic_" &&
nameOf(mirror) != "dynamic") ?
new Reference(mirror) : null;
}
/**
* Helper class to track what members of a library should be included.
*/
class LibrarySubset {
final LibraryMirror library;
Set<String> includedChildren;
LibrarySubset(this.library) : includedChildren = new Set<String>();
}
/**
* [Element] describing a Dart library.
*/
class LibraryElement extends Element {
/**
* Partial versions of LibraryElements containing classes that are extended
* or implemented by classes in this library.
*/
List<LibraryElement> dependencies;
/**
* Construct a LibraryElement from a [mirror].
*
* If [includedChildren] is specified, only elements matching names in
* [includedChildren] are included and no dependencies are included.
* [lookupMdnComment] is an optional function that returns the MDN
* documentation for elements. [dependencies] is an optional map
* tracking all classes dependend on by this [ClassElement].
*/
LibraryElement(LibraryMirror mirror,
{MdnComment lookupMdnComment(Mirror), Set<String> includedChildren})
: super(mirror, 'library', _libraryName(mirror), nameOf(mirror),
computeComment(mirror), lookupMdnComment) {
var requiredDependencies;
// We don't need to track our required dependencies when generating a
// filtered version of this library which will be used as a dependency for
// another library.
if (includedChildren == null)
requiredDependencies = new Map<String, LibrarySubset>();
methodsOf(mirror.declarations).forEach((childMirror) {
var childName = nameOf(childMirror);
if (includedChildren == null || includedChildren.contains(childName))
addChild(new MethodElement(childName, childMirror, lookupMdnComment));
});
gettersOf(mirror.declarations).forEach((childMirror) {
var childName = nameOf(childMirror);
if (includedChildren == null || includedChildren.contains(childName))
addChild(new GetterElement(childName, childMirror, lookupMdnComment));
});
variablesOf(mirror.declarations).forEach((childMirror) {
var childName = nameOf(childMirror);
if (includedChildren == null || includedChildren.contains(childName))
addChild(new VariableElement(childName, childMirror, lookupMdnComment));
});
typesOf(mirror.declarations).forEach((classMirror) {
var className = nameOf(classMirror);
if (includedChildren == null || includedChildren.contains(className)) {
if (classMirror is TypedefMirror) {
addChild(new TypedefElement(className, classMirror));
} else {
addChild(new ClassElement(classMirror,
dependencies: requiredDependencies,
lookupMdnComment: lookupMdnComment));
}
}
});
if (requiredDependencies != null && !requiredDependencies.isEmpty) {
dependencies = requiredDependencies.values.map((librarySubset) =>
new LibraryElement(
librarySubset.library,
lookupMdnComment: lookupMdnComment,
includedChildren: librarySubset.includedChildren)).toList();
}
}
static String _libraryName(LibraryMirror mirror) {
if (mirror.uri.scheme == 'file') {
// TODO(jacobr): this is a hack. Remove once these libraries are removed
// from the sdk.
var uri = mirror.uri;
var uriPath = uri.path;
var parts = path.split(uriPath);
// Find either pkg/ or packages/
var pkgDir = parts.lastIndexOf('pkg');
var packageDir = parts.lastIndexOf('packages');
if (pkgDir >= 0) {
packageDir = pkgDir;
}
var libDir = parts.lastIndexOf('lib');
var rest = parts.sublist(libDir + 1);
// If there's no lib, we can't find the package.
if (libDir < 0 || libDir < packageDir) {
// TODO(jacobr): this is a lousy fallback.
print("Unable to determine package for $uriPath.");
return mirror.uri.toString();
} else if (packageDir >= 0 && rest.length >= 1) {
// For URI: foo/bar/packages/widget/lib/sprocket.dart will return:
// 'package:widget/sprocket.dart'
return 'package:${parts[packageDir + 1]}/${rest.join('/')}';
}
} else {
return mirror.uri.toString();
}
}
void stripDuplicateUris(String parentUri, parentLine) {
super.stripDuplicateUris(parentUri, parentLine);
if (dependencies != null) {
for (var child in dependencies) {
child.stripDuplicateUris(null, null);
}
}
}
}
/**
* Returns whether the class implements or extends [Error] or [Exception].
*/
bool _isThrowable(ClassMirror mirror) {
if (getLibrary(mirror).uri.toString() == 'dart:core' &&
nameOf(mirror) == 'Error' || nameOf(mirror) == 'Exception')
return true;
if (mirror.superclass != null && _isThrowable(mirror.superclass))
return true;
return mirror.superinterfaces.any(_isThrowable);
}
/**
* [Element] describing a Dart class.
*/
class ClassElement extends Element {
/** Base class.*/
final Reference superclass;
/** Whether the class is abstract. */
final bool isAbstract;
/** Interfaces the class implements. */
List<Reference> interfaces;
/** Whether the class implements or extends [Error] or [Exception]. */
bool isThrowable;
/**
* Constructs a [ClassElement] from a [ClassMirror].
*
* [dependencies] is an optional map updated as a side effect of running
* this constructor that tracks what classes from other libraries are
* dependencies of classes in this library. A class is considered a
* dependency if it implements or extends another class.
* [lookupMdnComment] is an optional function that returns the MDN
* documentation for elements. [dependencies] is an optional map
* tracking all classes dependend on by this [ClassElement].
*/
ClassElement(ClassSourceMirror mirror,
{Map<String, LibrarySubset> dependencies,
MdnComment lookupMdnComment(Mirror)})
: super(mirror, 'class', nameOf(mirror),
nameOf(mirror), computeComment(mirror),
lookupMdnComment),
superclass = _optionalReference(mirror.superclass),
isAbstract = _optionalBool(mirror.isAbstract),
isThrowable = _optionalBool(_isThrowable(mirror)){
addCrossLibraryDependencies(clazz) {
if (clazz == null) return;
var clazzLibrary = getLibrary(clazz);
if (getLibrary(mirror) != clazzLibrary) {
String name = nameOf(clazz);
var libraryStub = dependencies.putIfAbsent(name,
() => new LibrarySubset(clazzLibrary));
libraryStub.includedChildren.add(name);
}
for (var interface in clazz.superinterfaces) {
addCrossLibraryDependencies(interface);
}
addCrossLibraryDependencies(clazz.superclass);
}
if (dependencies != null) {
addCrossLibraryDependencies(mirror);
}
for (var interface in mirror.superinterfaces) {
if (this.interfaces == null) {
this.interfaces = <Reference>[];
}
this.interfaces.add(_optionalReference(interface));
}
methodsOf(mirror.declarations).forEach((childMirror) {
String childName = nameOf(childMirror);
addChild(new MethodElement(childName, childMirror, lookupMdnComment));
});
gettersOf(mirror.declarations).forEach((childMirror) {
String childName = nameOf(childMirror);
addChild(new GetterElement(childName, childMirror, lookupMdnComment));
});
variablesOf(mirror.declarations).forEach((childMirror) {
String childName = nameOf(childMirror);
addChild(new VariableElement(childName, childMirror,
lookupMdnComment));
});
constructorsOf(mirror.declarations).forEach((methodMirror) {
String constructorName = nameOf(methodMirror);
addChild(new MethodElement(constructorName, methodMirror,
lookupMdnComment, 'constructor'));
});
for (var typeVariable in mirror.originalDeclaration.typeVariables) {
addChild(new TypeParameterElement(typeVariable));
}
}
}
/**
* [Element] describing a getter.
*/
class GetterElement extends Element {
/** Type of the getter. */
final Reference ref;
final bool isStatic;
GetterElement(String name, MethodMirror mirror,
MdnComment lookupMdnComment(Mirror))
: super(mirror, 'property', nameOf(mirror),
name, computeComment(mirror), lookupMdnComment),
ref = _optionalReference(mirror.returnType),
isStatic = _optionalBool(mirror.isStatic);
}
/**
* [Element] describing a method which may be a regular method, a setter, or an
* operator.
*/
class MethodElement extends Element {
final Reference returnType;
final bool isSetter;
final bool isOperator;
final bool isStatic;
MethodElement(String name, MethodMirror mirror,
MdnComment lookupMdnComment(Mirror), [String kind = 'method'])
: super(mirror, kind, name, '$name${mirror.parameters.length}()',
computeComment(mirror), lookupMdnComment),
returnType = _optionalReference(mirror.returnType),
isSetter = _optionalBool(mirror.isSetter),
isOperator = _optionalBool(mirror.isOperator),
isStatic = _optionalBool(mirror.isStatic) {
for (var param in mirror.parameters) {
addChild(new ParameterElement(param));
}
}
}
/**
* Element describing a parameter.
*/
class ParameterElement extends Element {
/** Type of the parameter. */
final Reference ref;
/**
* Returns the default value for this parameter.
*/
final String defaultValue;
/**
* Is this parameter optional?
*/
final bool isOptional;
/**
* Is this parameter named?
*/
final bool isNamed;
/**
* Returns the initialized field, if this parameter is an initializing formal.
*/
final Reference initializedField;
ParameterElement(ParameterSourceMirror mirror)
: super(mirror, 'param', nameOf(mirror),
nameOf(mirror), null, null),
ref = _optionalReference(mirror.type),
isOptional = _optionalBool(mirror.isOptional),
defaultValue = '${mirror.defaultValue}',
isNamed = _optionalBool(mirror.isNamed),
initializedField = _optionalReference(mirror.initializedField) {
if (mirror.type is FunctionTypeMirror) {
addChild(new FunctionTypeElement(mirror.type));
}
}
}
class FunctionTypeElement extends Element {
final Reference returnType;
FunctionTypeElement(FunctionTypeMirror mirror)
: super(mirror, 'functiontype', nameOf(mirror),
nameOf(mirror), null, null),
returnType = _optionalReference(mirror.returnType) {
for (var param in mirror.parameters) {
addChild(new ParameterElement(param));
}
// TODO(jacobr): can a FunctionTypeElement really have type variables?
for (var typeVariable in mirror.originalDeclaration.typeVariables) {
addChild(new TypeParameterElement(typeVariable));
}
}
}
/**
* Element describing a generic type parameter.
*/
class TypeParameterElement extends Element {
/**
* Upper bound for the parameter.
*
* In the following code sample, [:Bar:] is an upper bound:
* [: class Bar<T extends Foo> { } :]
*/
final Reference upperBound;
TypeParameterElement(TypeVariableMirror mirror)
: super(mirror, 'typeparam', nameOf(mirror),
nameOf(mirror), null, null),
upperBound = mirror.upperBound != null && !isObject(mirror.upperBound) ?
new Reference(mirror.upperBound) : null;
}
/**
* Element describing a variable.
*/
class VariableElement extends Element {
/** Type of the variable. */
final Reference ref;
/** Whether the variable is static. */
final bool isStatic;
/** Whether the variable is final. */
final bool isFinal;
VariableElement(String name, VariableMirror mirror,
MdnComment lookupMdnComment(Mirror))
: super(mirror, 'variable', nameOf(mirror), name,
computeComment(mirror), lookupMdnComment),
ref = _optionalReference(mirror.type),
isStatic = _optionalBool(mirror.isStatic),
isFinal = _optionalBool(mirror.isFinal);
}
/**
* Element describing a typedef.
*/
class TypedefElement extends Element {
/** Return type of the typedef. */
final Reference returnType;
TypedefElement(String name, TypedefMirror mirror)
: super(mirror, 'typedef', nameOf(mirror), name,
computeComment(mirror), null),
returnType = _optionalReference(mirror.referent.returnType) {
for (var param in mirror.referent.parameters) {
addChild(new ParameterElement(param));
}
for (var typeVariable in mirror.originalDeclaration.typeVariables) {
addChild(new TypeParameterElement(typeVariable));
}
}
}
/**
* Reference to an Element with type argument if the reference is parameterized.
*/
class Reference {
final String name;
final String refId;
List<Reference> arguments;
Reference(DeclarationMirror mirror)
: name = displayName(mirror),
refId = getId(mirror) {
if (mirror is ClassMirror) {
if (mirror is !TypedefMirror && !mirror.typeArguments.isEmpty) {
arguments = <Reference>[];
for (var typeArg in mirror.typeArguments) {
arguments.add(_optionalReference(typeArg));
}
}
}
}
// TODO(jacobr): compute the referenceId correctly for the general case so
// that this method can work with all element types not just LibraryElements.
Reference.fromElement(LibraryElement e) : name = e.name, refId = e.id;
static String getId(DeclarationMirror mirror) {
String id = _escapeId(nameOf(mirror));
if (mirror.owner != null) {
id = '${getId(mirror.owner)}/$id';
}
return id;
}
}

View file

@ -1,10 +0,0 @@
name: dartdoc
description: >
Libraries for generating documentation from Dart source code.
dependencies:
args: ">=0.4.2 <1.0.0"
path: ">=0.4.2 <1.0.0"
dev_dependencies:
unittest: ">=0.4.2 <1.0.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,487 +0,0 @@
/* Reset */
body, h1, h2, h3, h4, li, ol, p, pre, section, ul {
margin: 0;
padding: 0;
}
body {
font-family: Georgia, serif;
background: #e8e8e8;
color: #333;
background-image: url('body-bg.png');
background-repeat: repeat;
}
h2 {
font: 400 28px/44px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
h3 {
font: 600 14px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
color: #999;
margin: 22px 0 0 0;
}
h4 {
font: 600 16px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
p {
font-size: 16px;
line-height: 22px;
margin: 0 0 22px 0;
}
h3 + p {
/* Text immediately following a subheader should be snug with it. */
margin-top: 0;
}
strong {
font-weight: 700;
}
pre, code {
font: 14px/22px Menlo, Monaco, Consolas, Courier, monospace;
color: hsl(220, 20%, 30%);
background: hsl(220, 20%, 95%);
margin: 22px 0;
padding: 0 4px;
border-radius: 4px;
overflow-x:auto;
overflow-y:hidden;
}
pre > code {
padding: 0;
}
a {
color: #15c;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: #15c;
}
li {
font-size: 16px;
line-height: 22px;
}
/* First paragraph in an li is snug, but others are spaced out. */
li p:first-child {
margin: 0 0 0 0;
}
li > p {
margin: 22px 0 0 0;
}
ol, ul {
padding-left: 22px;
}
hr {
border: none;
height: 1px;
background: #ccc;
margin: 22px 0 21px 0;
}
hr + h2 {
margin-top: 21px; /* To compensate for the thickness of the hr. */
}
.page {
max-width: 1000px;
background: #fff;
margin: 0 auto 22px auto;
border: solid 1px #ccc;
border-top: none;
box-shadow: 0 0 50px #888;
background-image: url('content-bg.png');
background-repeat: repeat-y;
position: relative;
}
.header {
background: #333;
background-image: url('header-bg.png');
height: 44px;
color: hsl(0, 0%, 50%);
font: 400 15px/44px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
.header .logo {
background-image: url('dart-logo-small.png');
width: 66px;
height: 44px;
float: left;
}
.header a {
color: hsl(0, 0%, 80%);
}
.header a:hover {
color: hsl(0, 0%, 100%);
}
.header #search-box {
display: inline;
float: right;
margin-right: 11px;
}
.search-input, .drop-down {
font: 400 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
width: 300px;
}
.drop-down {
visibility: hidden;
z-index: 1000;
position: absolute;
right: 10px;
top: 36px;
border: 1px #CCC solid;
background-color: white;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.drop-down-table {
width: 100%;
}
.drop-down-link-tr {
padding: 4px 0px;
cursor: pointer;
}
.drop-down-link-td {
border-bottom: 1px solid #EEE;
}
.drop-down-link-tr:hover {
background: #EEE;
color: #333;
}
.drop-down-link-select {
background: #15C;
color: white;
}
.drop-down-link-select:hover {
background: #2AF;
color: white;
}
.drop-down-link-kind, .drop-down-link-library {
font: 400 10px/10px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
.drop-down-link-library {
text-align: right;
}
.drop-down-link-highlight {
font-weight:bold;
}
.nav {
float: left;
width: 263px; /* 12 x 22px - 1 for border */
padding: 0 22px;
overflow: hidden;
background: #f4f4f4;
border-right: solid 1px #ccc;
}
.nav h2 {
font: 400 16px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
margin: 0 -21px;
padding: 11px 22px;
/* Using http://www.colorzilla.com/gradient-editor/ */
background: -moz-linear-gradient(top, hsla(0,0%,0%,0.05) 0%, hsla(0,0%,0%,0) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,hsla(0,0%,0%,0.05)), color-stop(100%,hsla(0,0%,0%,0)));
background: -webkit-linear-gradient(top, hsla(0,0%,0%,0.05) 0%,hsla(0,0%,0%,0) 100%);
background: -o-linear-gradient(top, hsla(0,0%,0%,0.05) 0%,hsla(0,0%,0%,0) 100%);
background: -ms-linear-gradient(top, hsla(0,0%,0%,0.05) 0%,hsla(0,0%,0%,0) 100%);
background: linear-gradient(top, hsla(0,0%,0%,0.05) 0%,hsla(0,0%,0%,0) 100%);
}
ul.icon {
margin: 0 0 22px 0;
padding: 0;
}
ul.icon li {
font: 600 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
list-style-type: none;
white-space: nowrap;
}
/* Indent nested lists. */
ul.icon ul {
margin-left: 22px;
}
.icon-class,
.icon-exception,
.icon-interface {
display: inline-block;
width: 14px;
height: 14px;
margin: 5px 10px 0 2px;
vertical-align: top;
}
.icon-class { background: url('class.png'); }
.icon-exception { background: url('exception.png'); }
.icon-interface { background: url('interface.png'); }
.icon-library {
background: url('library.png');
width: 16px;
height: 14px;
display: inline-block;
margin: 4px 8px 0 0;
vertical-align: top;
}
.type-box {
display: inline-block;
font: 600 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
background: #f4f4f4;
padding: 0 6px 0 3px;
border-radius: 4px;
}
.type-box .icon-class,
.type-box .icon-exception,
.type-box .icon-interface {
/* Make a little snugger with the text. */
margin-right: 5px;
}
.content {
margin-left: 308px; /* 14 x 22 */
padding: 22px 22px;
}
.clear {
clear: both;
}
.footer {
max-width: 956px; /* 1000 - 22 - 22 */
text-align: center;
margin: 22px auto;
color: #888;
}
.footer p {
font: 400 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
.inherited {
}
.inherited-from {
font: 400 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
text-align: right;
opacity: 0.7;
}
.inherited-from, .docs-inherited-from {
font: 400 13px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
text-align: right;
opacity: 0.7;
}
.docs-inherited-from {
margin-top: -22px;
}
.method .doc,
.field .doc {
padding-left: 44px;
/* Ensure there is some space between members with no documentation. */
margin-bottom: 22px;
}
.param {
font: 600 14px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
.crossref {
font: 600 15px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
}
.doc h1 {
font: 700 17px/22px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
color: #666;
}
.doc h2 {
font: 600 16px/22px 'Open Sans', 'Lucida Sans Unicode',
'Lucida Grande', sans-serif;
color: #666;
}
/* Style external links in docs differently. */
.doc a[href^="http:"]:after,
.doc a[href^="https:"]:after {
content: url('external-link.png');
}
/* Highlight members on hover so you can see which docs are for which member. */
.method:hover,
.field:hover {
margin: 0 -22px;
border: solid 4px hsl(40, 100%, 85%);
padding: 0 18px;
border-top: none;
border-bottom: none;
}
/* Only show the "code" link for the highlighted member. */
.show-code, .show-inherited {
float: right;
font: 600 11px 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',
sans-serif;
padding: 0 0 0 6px; /* In case it gets too close to the member. */
border: none;
background: transparent;
margin: 0;
}
.method:hover .show-code,
.field:hover .show-code {
display: inline;
}
/* Only show permalinks when hovered over. */
.anchor-link {
display: none;
}
h2:hover > .anchor-link,
h4:hover > .anchor-link {
display: inline;
}
/* Only show code when expanded. */
pre.source {
display: none;
}
pre.source.expanded {
display: block;
}
/* Links that don't cause a page refresh. */
.anchor-link, .anchor-link:visited,
.show-code, .show-code:visited,
.show-inherited, .show-inherited:visited {
color: hsl(40, 100%, 40%);
cursor: pointer;
}
.anchor-link, .anchor-link:visited,
.show-code, .show-code:visited {
display: none;
}
.anchor-link:hover,
.show-code:hover,
.show-inherited:hover {
color: hsl(40, 100%, 60%);
}
/* Syntax highlighting. */
/* Note: these correspond to the constants in classify.dart. */
.doc pre .e, pre.source .e { color: hsl( 0, 100%, 70%); } /* Error */
.doc pre .c, pre.source .c { color: hsl(220, 20%, 65%); } /* Comment */
.doc pre .i, pre.source .i { color: hsl(220, 20%, 20%); } /* Identifier */
.doc pre .k, pre.source .k { color: hsl(220, 100%, 50%); } /* Keyword */
.doc pre .p, pre.source .p { color: hsl(220, 20%, 50%); } /* Punctuation */
.doc pre .o, pre.source .o { color: hsl(220, 40%, 50%); } /* Operator */
.doc pre .s, pre.source .s { color: hsl( 40, 90%, 40%); } /* String */
.doc pre .n, pre.source .n { color: hsl( 30, 70%, 50%); } /* Number */
.doc pre .t, pre.source .t { color: hsl(180, 40%, 40%); } /* Type Name */
.doc pre .r, pre.source .r { color: hsl(200, 100%, 50%); } /* Special Identifier */
.doc pre .a, pre.source .a { color: hsl(220, 100%, 45%); } /* Arrow */
/* Interpolated expressions within strings. */
.doc pre .si, pre.source .si {
background: hsl(40, 100%, 90%);
}
.doc pre .s.si, pre.source .s.si { background: hsl(40, 80%, 95%); }
.doc pre .i.si, pre.source .i.si { color: hsl(40, 30%, 30%); }
.doc pre .p.si, pre.source .p.si { color: hsl(40, 60%, 40%); }
/* Enable these to debug the grid: */
/*
body {
background: -webkit-linear-gradient(top, #eee 0, #fff 10%, #fff 90%, #eee 100%);
background-size: 22px 22px;
background-repeat: repeat;
}
.page {
background: none;
}
h1 {
border-left: solid 4px green;
}
h2 {
border-left: solid 4px blue;
}
h3 {
border-left: solid 4px red;
}
h4 {
border-left: solid 4px orange;
}
p {
border-left: solid 4px purple;
}
section {
border-left: solid 4px gray;
}
*/

View file

@ -1,53 +0,0 @@
// Copyright (c) 2012, 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.
library dartdoc_search_test;
// TODO(rnystrom): Use "package:" URL (#4968).
import '../../../../../pkg/expect/lib/expect.dart';
import '../lib/src/dartdoc/nav.dart';
import '../lib/src/client/search.dart';
const String URL = 'dummy-url';
testTopLevelVsMembers() {
var search = new SearchText('timer');
var match = obtainMatch(search, 'timer');
// Matching a top-level field 'timer';
var topLevelResult = new Result(match, FIELD, URL);
// Matching a member field 'timer' in 'Foo';
var memberResult = new Result(match, FIELD, URL, type: 'Foo');
Expect.equals(-1, resultComparator(topLevelResult, memberResult),
"Top level fields should be preferred to member fields");
}
testTopLevelFullVsPrefix() {
var search = new SearchText('cancel');
var fullMatch = obtainMatch(search, 'cancel');
var prefixMatch = obtainMatch(search, 'cancelable');
// Matching a top-level method 'cancel';
var fullResult = new Result(fullMatch, METHOD, URL);
// Matching a top-level method 'cancelable';
var prefixResult = new Result(prefixMatch, METHOD, URL);
Expect.equals(-1, resultComparator(fullResult, prefixResult),
"Full matches should be preferred to prefix matches");
}
testMemberFullVsPrefix() {
var search = new SearchText('cancel');
var fullMatch = obtainMatch(search, 'cancel');
var prefixMatch = obtainMatch(search, 'cancelable');
// Matching a member method 'cancel' in 'Isolate';
var fullResult = new Result(fullMatch, METHOD, URL, type: 'Isolate');
// Matching a member field 'cancelable' in 'Event';
var prefixResult = new Result(prefixMatch, FIELD, URL, type: 'Event');
Expect.equals(-1, resultComparator(fullResult, prefixResult),
"Full matches should be preferred to prefix matches");
}
void main() {
testTopLevelVsMembers();
testTopLevelFullVsPrefix();
testMemberFullVsPrefix();
}

View file

@ -1,284 +0,0 @@
// Copyright (c) 2012, 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.
/// Unit tests for doc.
library dartdocTests;
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:unittest/unittest.dart';
// TODO(rnystrom): Use "package:" URL (#4968).
import '../lib/dartdoc.dart' as dd;
import '../lib/markdown.dart';
import 'markdown_test.dart';
main() {
// Some tests take more than the default 20 second unittest timeout.
unittestConfiguration.timeout = null;
group('isAbsolute', () {
final doc = new dd.Dartdoc();
test('returns false if there is no scheme', () {
expect(doc.isAbsolute('index.html'), isFalse);
expect(doc.isAbsolute('foo/index.html'), isFalse);
expect(doc.isAbsolute('foo/bar/index.html'), isFalse);
});
test('returns true if there is a scheme', () {
expect(doc.isAbsolute('http://google.com'), isTrue);
expect(doc.isAbsolute('hTtPs://google.com'), isTrue);
expect(doc.isAbsolute('mailto:fake@email.com'), isTrue);
});
});
group('relativePath', () {
final doc = new dd.Dartdoc();
test('absolute path is unchanged', () {
doc.startFile('dir/sub/file.html');
expect(doc.relativePath('http://foo.com'), equals('http://foo.com'));
});
test('from root to root', () {
doc.startFile('root.html');
expect(doc.relativePath('other.html'), equals('other.html'));
});
test('from root to directory', () {
doc.startFile('root.html');
expect(doc.relativePath('dir/file.html'), equals('dir/file.html'));
});
test('from root to nested', () {
doc.startFile('root.html');
expect(doc.relativePath('dir/sub/file.html'), equals(
'dir/sub/file.html'));
});
test('from directory to root', () {
doc.startFile('dir/file.html');
expect(doc.relativePath('root.html'), equals('../root.html'));
});
test('from nested to root', () {
doc.startFile('dir/sub/file.html');
expect(doc.relativePath('root.html'), equals('../../root.html'));
});
test('from dir to dir with different path', () {
doc.startFile('dir/file.html');
expect(doc.relativePath('other/file.html'), equals('../other/file.html'));
});
test('from nested to nested with different path', () {
doc.startFile('dir/sub/file.html');
expect(doc.relativePath('other/sub/file.html'), equals(
'../../other/sub/file.html'));
});
test('from nested to directory with different path', () {
doc.startFile('dir/sub/file.html');
expect(doc.relativePath('other/file.html'), equals(
'../../other/file.html'));
});
});
group('dartdoc markdown', () {
group('[::] blocks', () {
validateDartdocMarkdown('simple case', '''
before [:source:] after
''', '''
<p>before <code>source</code> after</p>
''');
validateDartdocMarkdown('unmatched [:', '''
before [: after
''', '''
<p>before [: after</p>
''');
validateDartdocMarkdown('multiple spans in one text', '''
a [:one:] b [:two:] c
''', '''
<p>a <code>one</code> b <code>two</code> c</p>
''');
validateDartdocMarkdown('multi-line', '''
before [:first
second:] after
''', '''
<p>before <code>first
second</code> after</p>
''');
validateDartdocMarkdown('contain backticks', '''
before [:can `contain` backticks:] after
''', '''
<p>before <code>can `contain` backticks</code> after</p>
''');
validateDartdocMarkdown('contain double backticks', '''
before [:can ``contain`` backticks:] after
''', '''
<p>before <code>can ``contain`` backticks</code> after</p>
''');
validateDartdocMarkdown('contain backticks with spaces', '''
before [: `tick` :] after
''', '''
<p>before <code>`tick`</code> after</p>
''');
validateDartdocMarkdown('multiline with spaces', '''
before [:in `tick`
another:] after
''', '''
<p>before <code>in `tick`
another</code> after</p>
''');
validateDartdocMarkdown('ignore markup inside code', '''
before [:*b* _c_:] after
''', '''
<p>before <code>*b* _c_</code> after</p>
''');
validateDartdocMarkdown('escape HTML characters', '''
[:<&>:]
''', '''
<p><code>&lt;&amp;&gt;</code></p>
''');
validateDartdocMarkdown('escape HTML tags', '''
'*' [:<em>:]
''', '''
<p>'*' <code>&lt;em&gt;</code></p>
''');
});
});
group('integration tests', () {
test('no entrypoints', () {
_testRunDartDoc([], (result) {
expect(result.exitCode, 1);
});
});
test('entrypoint in lib', () {
_testRunDartDoc(['test_files/lib/no_package_test_file.dart'], (result) {
expect(result.exitCode, 0);
_expectDocumented(result.stdout, libCount: 1, typeCount: 1, memberCount: 0);
});
});
test('entrypoint somewhere with packages locally', () {
_testRunDartDoc(['test_files/package_test_file.dart'], (result) {
expect(result.exitCode, 0);
_expectDocumented(result.stdout, libCount: 1, typeCount: 1, memberCount: 0);
});
});
test('file does not exist', () {
_testRunDartDoc(['test_files/this_file_does_not_exist.dart'], (result) {
expect(result.exitCode, 1);
});
});
});
}
void _testRunDartDoc(List<String> libraryPaths, void eval(ProcessResult)) {
expect(_runDartdoc(libraryPaths).then(eval), completes);
}
/// The path to the root directory of the dartdoc entrypoint.
String get _dartdocDir {
var dir = path.absolute(Platform.script.toFilePath());
while (path.basename(dir) != 'dartdoc') {
if (!path.absolute(dir).contains('dartdoc') || dir == path.dirname(dir)) {
fail('Unable to find root dartdoc directory.');
}
dir = path.dirname(dir);
}
return path.absolute(dir);
}
/// The path to use for the package root for subprocesses.
String get _packageRoot {
var sdkVersionPath = path.join(_dartdocDir, '..', '..', '..', 'version');
if (new File(sdkVersionPath).existsSync()) {
// It looks like dartdoc is being run from the SDK, so we should set the
// package root to the SDK's packages directory.
return path.absolute(path.join(_dartdocDir, '..', '..', '..', 'packages'));
}
// It looks like Dartdoc is being run from the Dart repo, so the package root
// is in the build output directory. We can find that directory relative to
// the Dart executable, but that could be in one of two places: in
// "$BUILD/dart" or "$BUILD/dart-sdk/bin/dart".
var executableDir = path.dirname(Platform.executable);
if (new Directory(path.join(executableDir, 'dart-sdk')).existsSync()) {
// The executable is in "$BUILD/dart".
return path.absolute(path.join(executableDir, 'packages'));
} else {
// The executable is in "$BUILD/dart-sdk/bin/dart".
return path.absolute(path.join(executableDir, '..', '..', 'packages'));
}
}
/// Runs dartdoc with the libraryPaths provided, and completes to dartdoc's
/// ProcessResult.
Future<ProcessResult> _runDartdoc(List<String> libraryPaths) {
var dartBin = Platform.executable;
var dartdoc = path.join(_dartdocDir, 'bin/dartdoc.dart');
final runArgs = ['--package-root=$_packageRoot/', dartdoc];
// Turn relative libraryPaths to absolute ones.
runArgs.addAll(libraryPaths
.map((e) => path.join(path.absolute(dd.scriptDir), e)));
return Process.run(dartBin, runArgs);
}
final _dartdocCompletionRegExp =
new RegExp(r'Documentation complete -- documented (\d+) libraries, (\d+) types, and (\d+) members\.');
void _expectDocumented(String output, { int libCount, int typeCount,
int memberCount}) {
final completionMatches = _dartdocCompletionRegExp.allMatches(output)
.toList();
expect(completionMatches, hasLength(1),
reason: 'dartdoc output should contain one summary');
final completionMatch = completionMatches.single;
if(libCount != null) {
expect(int.parse(completionMatch[1]), libCount,
reason: 'expected library count');
}
if(typeCount != null) {
expect(int.parse(completionMatch[2]), typeCount,
reason: 'expected type count');
}
if(memberCount != null) {
expect(int.parse(completionMatch[3]), memberCount,
reason: 'expected member count');
}
}
validateDartdocMarkdown(String description, String markdown,
String html) {
var dartdoc = new dd.Dartdoc();
validate(description, markdown, html, linkResolver: dartdoc.dartdocResolver,
inlineSyntaxes: dartdoc.dartdocSyntaxes);
}

View file

@ -1,467 +0,0 @@
// 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.
import 'package:unittest/unittest.dart';
import 'dart:async' show Future;
import 'dart:io' show Platform;
import '../lib/src/export_map.dart';
import '../../../../../tests/compiler/dart2js/memory_source_file_helper.dart'
show MemorySourceFileProvider;
import '../../compiler/implementation/mirrors/source_mirrors.dart'
show MirrorSystem, LibraryMirror;
import '../../compiler/implementation/mirrors/analyze.dart'
as source_mirrors;
Future<MirrorSystem> mirrorSystemFor(Map<String, String> memorySourceFiles) {
var provider = new MemorySourceFileProvider(memorySourceFiles);
handler(Uri uri, int begin, int end, String message, kind) {}
var script = Uri.base.resolveUri(Platform.script);
var libraryRoot = script.resolve('../../../../');
// Read packages from 'memory:packages/'.
var packageRoot = Uri.parse('memory:packages/');
var libraries = <Uri>[];
memorySourceFiles.forEach((String path, _) {
if (path.startsWith('packages/')) {
// Analyze files from 'packages/' as packages.
libraries.add(new Uri(scheme: 'package', path: path.substring(9)));
} else {
libraries.add(new Uri(scheme: 'memory', path: path));
}
});
libraries.add(new Uri(scheme: 'dart', path: 'async'));
return source_mirrors.analyze(
libraries, libraryRoot, packageRoot, provider, handler);
}
Future<ExportMap> testExports(Map<String, String> sourceFiles) {
return mirrorSystemFor(sourceFiles).then((mirrors) {
libMirror = (uri) => mirrors.libraries[Uri.parse(uri)];
return new ExportMap(mirrors);
});
}
Function libMirror;
main() {
group('ExportMap', () {
test('with an empty library', () {
return testExports({'lib.dart': ''}).then((map) {
var lib = libMirror('memory:lib.dart');
var nonexistent = libMirror('memory:nonexistent.dart');
var expectedExports = {};
expectedExports[lib] = [];
expect(map.exports, equals(expectedExports));
expect(map.transitiveExports(lib), isEmpty);
expect(map.transitiveExports(nonexistent), isEmpty);
});
});
test('with one library with one export', () {
return testExports({
'a.dart': 'export "b.dart";',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b)
]));
expect(map.exports[b], isEmpty);
expect(map.transitiveExports(b), isEmpty);
});
});
test('with one library with multiple exports', () {
return testExports({
'a.dart': 'export "b.dart";\nexport "c.dart";',
'b.dart': '', 'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
new Export(a, c)
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c)
]));
expect(map.exports[b], isEmpty);
expect(map.transitiveExports(b), isEmpty);
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with two libraries each with one export', () {
return testExports({
'a.dart': 'export "a_export.dart";',
'b.dart': 'export "b_export.dart";',
'a_export.dart': '',
'b_export.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var a_export = libMirror('memory:a_export.dart');
var b_export = libMirror('memory:b_export.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, a_export),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, a_export),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, b_export),
]));
expect(map.exports[b], unorderedEquals([
new Export(b, b_export)
]));
expect(map.exports[a_export], isEmpty);
expect(map.transitiveExports(a_export), isEmpty);
expect(map.exports[b_export], isEmpty);
expect(map.transitiveExports(b_export), isEmpty);
});
});
test('with a transitive export', () {
return testExports({
'a.dart': 'export "b.dart";',
'b.dart': 'export "c.dart";',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c),
]));
expect(map.exports[b], unorderedEquals([
new Export(b, c),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, c),
]));
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with an export through an import', () {
return testExports({
'a.dart': 'import "b.dart";',
'b.dart': 'export "c.dart";',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.exports[b], unorderedEquals([
new Export(b, c),
]));
expect(map.transitiveExports(b), unorderedEquals([
new Export(b, c),
]));
expect(map.exports[a], isEmpty);
expect(map.exports[c], isEmpty);
expect(map.transitiveExports(a), isEmpty);
expect(map.transitiveExports(c), isEmpty);
});
});
test('with an export with a show combinator', () {
return testExports({
'a.dart': 'export "b.dart" show x, y;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, show: ['x', 'y'])
]));
});
});
test('with an export with a hide combinator', () {
return testExports({
'a.dart': 'export "b.dart" hide x, y;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, hide: ['x', 'y'])
]));
});
});
test('with an export with a show and a hide combinator', () {
return testExports({
'a.dart': 'export "b.dart" show x, y hide y, z;',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, show: ['x'])
]));
});
});
test('composes transitive exports', () {
return testExports({
'a.dart': 'export "b.dart" hide x;',
'b.dart': 'export "c.dart" hide y;',
'c.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b, hide: ['x']),
new Export(a, c, hide: ['x', 'y'])
]));
});
});
test('merges adjacent exports', () {
return testExports({
'a.dart': '''
export "b.dart" show x, y;
export "b.dart" hide y, z;
''',
'b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b, hide: ['z']),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b, hide: ['z']),
]));
});
});
test('merges adjacent exports transitively', () {
return testExports({
'a.dart': 'export "b.dart";\nexport "c.dart";',
'b.dart': 'export "d.dart" show x, y;',
'c.dart': 'export "d.dart" hide y, z;',
'd.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('memory:b.dart');
var c = libMirror('memory:c.dart');
var d = libMirror('memory:d.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b),
new Export(a, c),
]));
expect(map.transitiveExports(a), unorderedEquals([
new Export(a, b),
new Export(a, c),
new Export(a, d, hide: ['z']),
]));
});
});
test('resolves package: exports', () {
return testExports({
'a.dart': 'export "package:b/b.dart";',
'packages/b/b.dart': ''
}).then((map) {
var a = libMirror('memory:a.dart');
var b = libMirror('package:b/b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
});
});
test('ignores dart: exports', () {
return testExports({'a.dart': 'export "dart:async";'}).then((map) {
var a = libMirror('memory:a.dart');
expect(map.exports[a], isEmpty);
});
});
test('.parse() resolves package: imports', () {
return testExports({
'packages/a/a.dart': 'export "package:b/b.dart";',
'packages/b/b.dart': ''
}).then((map) {
var a = libMirror('package:a/a.dart');
var b = libMirror('package:b/b.dart');
expect(map.exports[a], unorderedEquals([
new Export(a, b)
]));
});
});
test('.parse() ignores dart: imports', () {
return testExports({}).then((map) {
expect(map.exports, isEmpty);
});
});
});
group('Export', () {
test('normalizes hide and show', () {
expect(new Export(null, null, show: ['x', 'y'], hide: ['y', 'z']),
equals(new Export(null, null, show: ['x'])));
});
test("doesn't care about the order of show or hide", () {
expect(new Export(null, null, show: ['x', 'y']),
equals(new Export(null, null, show: ['y', 'x'])));
expect(new Export(null, null, hide: ['x', 'y']),
equals(new Export(null, null, hide: ['y', 'x'])));
});
test('with no combinators considers anything visible', () {
var export = new Export(null, null);
expect(export.isMemberVisible('x'), isTrue);
expect(export.isMemberVisible('y'), isTrue);
expect(export.isMemberVisible('z'), isTrue);
});
test('with hide combinators considers anything not hidden visible', () {
var export = new Export(null, null, hide: ['x', 'y']);
expect(export.isMemberVisible('x'), isFalse);
expect(export.isMemberVisible('y'), isFalse);
expect(export.isMemberVisible('z'), isTrue);
});
test('with show combinators considers anything not shown invisible', () {
var export = new Export(null, null, show: ['x', 'y']);
expect(export.isMemberVisible('x'), isTrue);
expect(export.isMemberVisible('y'), isTrue);
expect(export.isMemberVisible('z'), isFalse);
});
test('composing uses the parent exporter and child path', () {
return testExports({
'exporter1.dart': '',
'exporter2.dart': '',
'path1.dart': '',
'path2.dart': ''
}).then((map) {
var exporter1 = libMirror('memory:exporter1.dart');
var exporter2 = libMirror('memory:exporter2.dart');
var path1 = libMirror('memory:path1.dart');
var path2 = libMirror('memory:path2.dart');
expect(new Export(exporter1, path1)
.compose(new Export(exporter2, path2)),
equals(new Export(exporter1, path2)));
});
});
test('composing show . show takes the intersection', () {
expect(new Export(null, null, show: ['x', 'y'])
.compose(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['y'])));
});
test('composing show . hide takes the difference', () {
expect(new Export(null, null, show: ['x', 'y'])
.compose(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, show: ['x'])));
});
test('composing hide . show takes the reverse difference', () {
expect(new Export(null, null, hide: ['x', 'y'])
.compose(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['z'])));
});
test('composing hide . hide takes the union', () {
expect(new Export(null, null, hide: ['x', 'y'])
.compose(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['x', 'y', 'z'])));
});
test('merging requires identical exporters and paths', () {
return testExports({
'exporter1.dart': '',
'exporter2.dart': '',
'path1.dart': '',
'path2.dart': ''
}).then((map) {
var exporter1 = libMirror('memory:exporter1.dart');
var exporter2 = libMirror('memory:exporter2.dart');
var path1 = libMirror('memory:path1.dart');
var path2 = libMirror('memory:path2.dart');
expect(() => new Export(exporter1, null)
.merge(new Export(exporter2, null)),
throwsA(isArgumentError));
expect(() => new Export(null, path1)
.merge(new Export(null, path2)),
throwsA(isArgumentError));
expect(new Export(null, null)
.merge(new Export(null, null)),
equals(new Export(null, null)));
});
});
test('merging show + show takes the union', () {
expect(new Export(null, null, show: ['x', 'y'])
.merge(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, show: ['x', 'y', 'z'])));
});
test('merging show + hide takes the difference', () {
expect(new Export(null, null, show: ['x', 'y'])
.merge(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['z'])));
});
test('merging hide + show takes the difference', () {
expect(new Export(null, null, hide: ['x', 'y'])
.merge(new Export(null, null, show: ['y', 'z'])),
equals(new Export(null, null, hide: ['x'])));
});
test('merging hide + hide takes the intersection', () {
expect(new Export(null, null, hide: ['x', 'y'])
.merge(new Export(null, null, hide: ['y', 'z'])),
equals(new Export(null, null, hide: ['y'])));
});
});
}

View file

@ -1,912 +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.
/// Unit tests for markdown.
library markdownTests;
import 'package:unittest/unittest.dart';
// TODO(rnystrom): Use "package:" URL (#4968).
import '../lib/markdown.dart';
/// Most of these tests are based on observing how showdown behaves:
/// http://softwaremaniacs.org/playground/showdown-highlight/
void main() {
group('Paragraphs', () {
validate('consecutive lines form a single paragraph', '''
This is the first line.
This is the second line.
''', '''
<p>This is the first line.
This is the second line.</p>
''');
// TODO(rnystrom): The rules here for what happens to lines following a
// paragraph appear to be completely arbitrary in markdown. If it makes the
// code significantly cleaner, we should consider ourselves free to change
// these tests.
validate('are terminated by a header', '''
para
# header
''', '''
<p>para</p>
<h1>header</h1>
''');
validate('are terminated by a setext header', '''
para
header
==
''', '''
<p>para</p>
<h1>header</h1>
''');
validate('are terminated by a hr', '''
para
___
''', '''
<p>para</p>
<hr />
''');
validate('consume an unordered list', '''
para
* list
''', '''
<p>para
* list</p>
''');
validate('consume an ordered list', '''
para
1. list
''', '''
<p>para
1. list</p>
''');
// Windows line endings have a \r\n format
// instead of the unix \n format.
validate('take account of windows line endings', '''
line1\r\n\r\n line2\r\n
''', '''
<p>line1</p>
<p>line2</p>
''');
});
group('Setext headers', () {
validate('h1', '''
text
===
''', '''
<h1>text</h1>
''');
validate('h2', '''
text
---
''', '''
<h2>text</h2>
''');
validate('h1 on first line becomes text', '''
===
''', '''
<p>===</p>
''');
validate('h2 on first line becomes text', '''
-
''', '''
<p>-</p>
''');
validate('h1 turns preceding list into text', '''
- list
===
''', '''
<h1>- list</h1>
''');
validate('h2 turns preceding list into text', '''
- list
===
''', '''
<h1>- list</h1>
''');
validate('h1 turns preceding blockquote into text', '''
> quote
===
''', '''
<h1>> quote</h1>
''');
validate('h2 turns preceding blockquote into text', '''
> quote
===
''', '''
<h1>> quote</h1>
''');
});
group('Headers', () {
validate('h1', '''
# header
''', '''
<h1>header</h1>
''');
validate('h2', '''
## header
''', '''
<h2>header</h2>
''');
validate('h3', '''
### header
''', '''
<h3>header</h3>
''');
validate('h4', '''
#### header
''', '''
<h4>header</h4>
''');
validate('h5', '''
##### header
''', '''
<h5>header</h5>
''');
validate('h6', '''
###### header
''', '''
<h6>header</h6>
''');
validate('trailing "#" are removed', '''
# header ######
''', '''
<h1>header</h1>
''');
});
group('Unordered lists', () {
validate('asterisk, plus and hyphen', '''
* star
- dash
+ plus
''', '''
<ul>
<li>star</li>
<li>dash</li>
<li>plus</li>
</ul>
''');
validate('allow numbered lines after first', '''
* a
1. b
''', '''
<ul>
<li>a</li>
<li>b</li>
</ul>
''');
validate('allow a tab after the marker', '''
*\ta
+\tb
-\tc
1.\td
''', '''
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul>
''');
validate('wrap items in paragraphs if blank lines separate', '''
* one
* two
''', '''
<ul>
<li><p>one</p></li>
<li><p>two</p></li>
</ul>
''');
validate('force paragraph on item before and after blank lines', '''
* one
* two
* three
''', '''
<ul>
<li>one</li>
<li>
<p>two</p>
</li>
<li>
<p>three</p>
</li>
</ul>
''');
validate('do not force paragraph if item is already block', '''
* > quote
* # header
''', '''
<ul>
<li><blockquote><p>quote</p></blockquote></li>
<li><h1>header</h1></li>
</ul>
''');
validate('can contain multiple paragraphs', '''
* one
two
* three
''', '''
<ul>
<li>
<p>one</p>
<p>two</p>
</li>
<li>
<p>three</p>
</li>
</ul>
''');
validate('can span newlines', '''
* one
two
* three
''', '''
<ul>
<li>
<p>one
two</p>
</li>
<li>
three
</li>
</ul>
''');
// TODO(rnystrom): This is how most other markdown parsers handle
// this but that seems like a nasty special case. For now, let's not
// worry about it.
/*
validate('can nest using indentation', '''
* parent
* child
''', '''
<ul>
<li>parent
<ul><li>child</li></ul></li>
</ul>
''');
*/
});
group('Ordered lists', () {
validate('start with numbers', '''
1. one
45. two
12345. three
''', '''
<ol>
<li>one</li>
<li>two</li>
<li>three</li>
</ol>
''');
validate('allow unordered lines after first', '''
1. a
* b
''', '''
<ol>
<li>a</li>
<li>b</li>
</ol>
''');
});
group('Blockquotes', () {
validate('single line', '''
> blah
''', '''
<blockquote>
<p>blah</p>
</blockquote>
''');
validate('with two paragraphs', '''
> first
>
> second
''', '''
<blockquote>
<p>first</p>
<p>second</p>
</blockquote>
''');
validate('nested', '''
> one
>> two
> > > three
''', '''
<blockquote>
<p>one</p>
<blockquote>
<p>two</p>
<blockquote>
<p>three</p>
</blockquote>
</blockquote>
</blockquote>
''');
});
group('Code blocks', () {
validate('single line', '''
code
''', '''
<pre><code>code</code></pre>
''');
validate('include leading whitespace after indentation', '''
zero
one
two
three
''', '''
<pre><code>zero
one
two
three</code></pre>
''');
validate('code blocks separated by newlines form one block', '''
zero
one
two
three
''', '''
<pre><code>zero
one
two
three</code></pre>
''');
validate('code blocks separated by two newlines form multiple blocks', '''
zero
one
two
three
''', '''
<pre><code>zero
one</code></pre>
<pre><code>two</code></pre>
<pre><code>three</code></pre>
''');
validate('escape HTML characters', '''
<&>
''', '''
<pre><code>&lt;&amp;&gt;</code></pre>
''');
});
group('Horizontal rules', () {
validate('from dashes', '''
---
''', '''
<hr />
''');
validate('from asterisks', '''
***
''', '''
<hr />
''');
validate('from underscores', '''
___
''', '''
<hr />
''');
validate('can include up to two spaces', '''
_ _ _
''', '''
<hr />
''');
});
group('Block-level HTML', () {
validate('single line', '''
<table></table>
''', '''
<table></table>
''');
validate('multi-line', '''
<table>
blah
</table>
''', '''
<table>
blah
</table>
''');
validate('blank line ends block', '''
<table>
blah
</table>
para
''', '''
<table>
blah
</table>
<p>para</p>
''');
validate('HTML can be bogus', '''
<bogus>
blah
</weird>
para
''', '''
<bogus>
blah
</weird>
<p>para</p>
''');
});
group('Strong', () {
validate('using asterisks', '''
before **strong** after
''', '''
<p>before <strong>strong</strong> after</p>
''');
validate('using underscores', '''
before __strong__ after
''', '''
<p>before <strong>strong</strong> after</p>
''');
validate('unmatched asterisks', '''
before ** after
''', '''
<p>before ** after</p>
''');
validate('unmatched underscores', '''
before __ after
''', '''
<p>before __ after</p>
''');
validate('multiple spans in one text', '''
a **one** b __two__ c
''', '''
<p>a <strong>one</strong> b <strong>two</strong> c</p>
''');
validate('multi-line', '''
before **first
second** after
''', '''
<p>before <strong>first
second</strong> after</p>
''');
});
group('Emphasis and strong', () {
validate('single asterisks', '''
before *em* after
''', '''
<p>before <em>em</em> after</p>
''');
validate('single underscores', '''
before _em_ after
''', '''
<p>before <em>em</em> after</p>
''');
validate('double asterisks', '''
before **strong** after
''', '''
<p>before <strong>strong</strong> after</p>
''');
validate('double underscores', '''
before __strong__ after
''', '''
<p>before <strong>strong</strong> after</p>
''');
validate('unmatched asterisk', '''
before *after
''', '''
<p>before *after</p>
''');
validate('unmatched underscore', '''
before _after
''', '''
<p>before _after</p>
''');
validate('multiple spans in one text', '''
a *one* b _two_ c
''', '''
<p>a <em>one</em> b <em>two</em> c</p>
''');
validate('multi-line', '''
before *first
second* after
''', '''
<p>before <em>first
second</em> after</p>
''');
validate('not processed when surrounded by spaces', '''
a * b * c _ d _ e
''', '''
<p>a * b * c _ d _ e</p>
''');
validate('strong then emphasis', '''
**strong***em*
''', '''
<p><strong>strong</strong><em>em</em></p>
''');
validate('emphasis then strong', '''
*em***strong**
''', '''
<p><em>em</em><strong>strong</strong></p>
''');
validate('emphasis inside strong', '''
**strong *em***
''', '''
<p><strong>strong <em>em</em></strong></p>
''');
validate('mismatched in nested', '''
*a _b* c_
''', '''
<p><em>a _b</em> c_</p>
''');
validate('cannot nest tags of same type', '''
*a _b *c* d_ e*
''', '''
<p><em>a _b </em>c<em> d_ e</em></p>
''');
});
group('Inline code', () {
validate('simple case', '''
before `source` after
''', '''
<p>before <code>source</code> after</p>
''');
validate('unmatched backtick', '''
before ` after
''', '''
<p>before ` after</p>
''');
validate('multiple spans in one text', '''
a `one` b `two` c
''', '''
<p>a <code>one</code> b <code>two</code> c</p>
''');
validate('multi-line', '''
before `first
second` after
''', '''
<p>before <code>first
second</code> after</p>
''');
validate('simple double backticks', '''
before ``source`` after
''', '''
<p>before <code>source</code> after</p>
''');
validate('double backticks', '''
before ``can `contain` backticks`` after
''', '''
<p>before <code>can `contain` backticks</code> after</p>
''');
validate('double backticks with spaces', '''
before `` `tick` `` after
''', '''
<p>before <code>`tick`</code> after</p>
''');
validate('multiline double backticks with spaces', '''
before ``in `tick`
another`` after
''', '''
<p>before <code>in `tick`
another</code> after</p>
''');
validate('ignore markup inside code', '''
before `*b* _c_` after
''', '''
<p>before <code>*b* _c_</code> after</p>
''');
validate('escape HTML characters', '''
`<&>`
''', '''
<p><code>&lt;&amp;&gt;</code></p>
''');
validate('escape HTML tags', '''
'*' `<em>`
''', '''
<p>'*' <code>&lt;em&gt;</code></p>
''');
});
group('HTML encoding', () {
validate('less than and ampersand are escaped', '''
< &
''', '''
<p>&lt; &amp;</p>
''');
validate('greater than is not escaped', '''
not you >
''', '''
<p>not you ></p>
''');
validate('existing entities are untouched', '''
&amp;
''', '''
<p>&amp;</p>
''');
});
group('Autolinks', () {
validate('basic link', '''
before <http://foo.com/> after
''', '''
<p>before <a href="http://foo.com/">http://foo.com/</a> after</p>
''');
validate('handles ampersand in url', '''
<http://foo.com/?a=1&b=2>
''', '''
<p><a href="http://foo.com/?a=1&b=2">http://foo.com/?a=1&amp;b=2</a></p>
''');
});
group('Reference links', () {
validate('double quotes for title', '''
links [are] [a] awesome
[a]: http://foo.com "woo"
''', '''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate('single quoted title', """
links [are] [a] awesome
[a]: http://foo.com 'woo'
""", '''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate('parentheses for title', '''
links [are] [a] awesome
[a]: http://foo.com (woo)
''', '''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate('no title', '''
links [are] [a] awesome
[a]: http://foo.com
''', '''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate('unknown link becomes plaintext', '''
[not] [known]
''', '''
<p>[not] [known]</p>
''');
validate('can style link contents', '''
links [*are*] [a] awesome
[a]: http://foo.com
''', '''
<p>links <a href="http://foo.com"><em>are</em></a> awesome</p>
''');
validate('inline styles after a bad link are processed', '''
[bad] `code`
''', '''
<p>[bad] <code>code</code></p>
''');
validate('empty reference uses text from link', '''
links [are][] awesome
[are]: http://foo.com
''', '''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate('references are case-insensitive', '''
links [ARE][] awesome
[are]: http://foo.com
''', '''
<p>links <a href="http://foo.com">ARE</a> awesome</p>
''');
});
group('Inline links', () {
validate('double quotes for title', '''
links [are](http://foo.com "woo") awesome
''', '''
<p>links <a href="http://foo.com" title="woo">are</a> awesome</p>
''');
validate('no title', '''
links [are] (http://foo.com) awesome
''', '''
<p>links <a href="http://foo.com">are</a> awesome</p>
''');
validate('can style link contents', '''
links [*are*](http://foo.com) awesome
''', '''
<p>links <a href="http://foo.com"><em>are</em></a> awesome</p>
''');
});
group('Resolver', () {
var nyanResolver = (text) => new Text('~=[,,_${text}_,,]:3');
validate('simple resolver', '''
resolve [this] thing
''', '''
<p>resolve ~=[,,_this_,,]:3 thing</p>
''', linkResolver: nyanResolver);
});
group('Custom inline syntax', () {
List<InlineSyntax> nyanSyntax =
[new TextSyntax('nyan', sub: '~=[,,_,,]:3')];
validate('simple inline syntax', '''
nyan
''', '''
<p>~=[,,_,,]:3</p>
''', inlineSyntaxes: nyanSyntax);
// TODO(amouravski): need more tests here for custom syntaxes, as some
// things are not quite working properly. The regexps are sometime a little
// too greedy, I think.
});
}
/**
* Removes eight spaces of leading indentation from a multiline string.
*
* Note that this is very sensitive to how the literals are styled. They should
* be:
* '''
* Text starts on own line. Lines up with subsequent lines.
* Lines are indented exactly 8 characters from the left margin.'''
*
* This does nothing if text is only a single line.
*/
// TODO(nweiz): Make this auto-detect the indentation level from the first
// non-whitespace line.
String cleanUpLiteral(String text) {
var lines = text.split('\n');
if (lines.length <= 1) return text;
for (var j = 0; j < lines.length; j++) {
if (lines[j].length > 8) {
lines[j] = lines[j].substring(8, lines[j].length);
} else {
lines[j] = '';
}
}
return lines.join('\n');
}
validate(String description, String markdown, String html,
{bool verbose: false, inlineSyntaxes, linkResolver}) {
test(description, () {
markdown = cleanUpLiteral(markdown);
html = cleanUpLiteral(html);
var result = markdownToHtml(markdown, inlineSyntaxes: inlineSyntaxes,
linkResolver: linkResolver);
var passed = compareOutput(html, result);
if (!passed) {
// Remove trailing newline.
html = html.substring(0, html.length - 1);
print('FAIL: $description');
print(' expect: ${html.replaceAll("\n", "\n ")}');
print(' actual: ${result.replaceAll("\n", "\n ")}');
print('');
}
expect(passed, isTrue, verbose: verbose);
});
}
/// Does a loose comparison of the two strings of HTML. Ignores differences in
/// newlines and indentation.
compareOutput(String a, String b) {
int i = 0;
int j = 0;
skipIgnored(String s, int i) {
// Ignore newlines.
while ((i < s.length) && (s[i] == '\n')) {
i++;
// Ignore indentation.
while ((i < s.length) && (s[i] == ' ')) i++;
}
return i;
}
while (true) {
i = skipIgnored(a, i);
j = skipIgnored(b, j);
// If one string runs out of non-ignored strings, the other must too.
if (i == a.length) return j == b.length;
if (j == b.length) return i == a.length;
if (a[i] != b[j]) return false;
i++;
j++;
}
}

View file

@ -1,5 +0,0 @@
library no_package_test;
class NoPackageTestFile {
}

View file

@ -1,5 +0,0 @@
library package_test;
class PackageTestFile {
}

View file

@ -1,45 +0,0 @@
// Copyright (c) 2012, 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.
import 'package:unittest/unittest.dart';
import '../lib/src/dartdoc/utils.dart';
void main() {
group('countOccurrences', () {
test('empty text returns 0', () {
expect(countOccurrences('', 'needle'), equals(0));
});
test('one occurrence', () {
expect(countOccurrences('bananarama', 'nara'), equals(1));
});
test('multiple occurrences', () {
expect(countOccurrences('bananarama', 'a'), equals(5));
});
test('overlapping matches do not count', () {
expect(countOccurrences('bananarama', 'ana'), equals(1));
});
});
group('repeat', () {
test('zero times returns an empty string', () {
expect(repeat('ba', 0), isEmpty);
});
test('one time returns the string', () {
expect(repeat('ba', 1), equals('ba'));
});
test('multiple times', () {
expect(repeat('ba', 3), equals('bababa'));
});
test('multiple times with a separator', () {
expect(repeat('ba', 3, separator: ' '), equals('ba ba ba'));
});
});
}

View file

@ -15,13 +15,6 @@ lib/indexed_db/dart2js/indexed_db_dart2js: CompileTimeError # Issue 16524
lib/indexed_db/dartium/indexed_db_dartium: CompileTimeError # Issue 16524
lib/_internal/compiler/samples/compile_loop/compile_loop: CompileTimeError # Issue 16524
lib/_internal/compiler/samples/darttags/darttags: CompileTimeError # Issue 16524
lib/_internal/dartdoc/bin/dartdoc: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/dartdoc: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/src/client/client-live-nav: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/src/client/client-shared: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/src/client/dropdown: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/src/client/search: CompileTimeError # Issue 16523
lib/_internal/dartdoc/lib/universe_serializer: CompileTimeError # Issue 16523
lib/js/dart2js/js_dart2js: CompileTimeError # Issue 16524
lib/js/dartium/js_dartium: CompileTimeError # Issue 16524
lib/svg/dart2js/svg_dart2js: CompileTimeError # Issue 16524
@ -37,7 +30,6 @@ lib/web_sql/dartium/web_sql_dartium: CompileTimeError # Issue 16524
lib/_internal/compiler/samples/jsonify/jsonify: CompileTimeError # issue 16466
lib/_internal/compiler/implementation/mirrors/analyze: CompileTimeError # issue 16466
lib/_internal/compiler/implementation/mirrors/dart2js_mirrors: CompileTimeError # issue 16466
lib/_internal/dartdoc/lib/src/dart2js_mirrors: CompileTimeError # issue 16466
# Pass necessary, since CompileTimeError is valid for everything in that
# directory (not only for src/command.dart)

View file

@ -105,7 +105,7 @@ def CopyShellScript(src_file, dest_dir):
def CopyDartScripts(home, sdk_root):
for executable in ['dart2js', 'dartanalyzer', 'dartdoc', 'docgen', 'pub']:
for executable in ['dart2js', 'dartanalyzer', 'docgen', 'pub']:
CopyShellScript(os.path.join(home, 'sdk', 'bin', executable),
os.path.join(sdk_root, 'bin'))
@ -194,7 +194,6 @@ def Main(argv):
for library in [join('_chrome', 'dart2js'), join('_chrome', 'dartium'),
join('_internal', 'compiler'),
join('_internal', 'dartdoc'),
join('_internal', 'lib'),
'async', 'collection', 'convert', 'core',
'crypto', 'internal', 'io', 'isolate',
@ -236,7 +235,7 @@ def Main(argv):
join(RESOURCE, '7zip'),
ignore=ignore_patterns('.svn'))
# Copy dart2js/dartdoc/pub.
# Copy dart2js/pub.
CopyDartScripts(HOME, SDK_tmp)
CopySnapshots(SNAPSHOT, SDK_tmp)

View file

@ -34,7 +34,6 @@
'create_snapshot.dart',
'--output_dir=<(SHARED_INTERMEDIATE_DIR)',
'--dart2js_main=sdk/lib/_internal/compiler/implementation/dart2js.dart',
'--dartdoc_main=sdk/lib/_internal/dartdoc/bin/dartdoc.dart',
'--docgen_main=pkg/docgen/bin/docgen.dart',
'--package_root=<(PRODUCT_DIR)/packages/',
],

View file

@ -18,13 +18,11 @@ Future<String> getVersion(var rootPath) {
Future<String> getSnapshotGenerationFile(var args, var rootPath) {
var dart2js = rootPath.resolve(args["dart2js_main"]);
var dartdoc = rootPath.resolve(args["dartdoc_main"]);
var docgen = rootPath.resolve(args["docgen_main"]);
return getVersion(rootPath).then((version) {
var snapshotGenerationText =
"""
import '${dart2js.toFilePath(windows: false)}' as dart2jsMain;
import '${dartdoc.toFilePath(windows: false)}' as dartdocMain;
import '${docgen.toFilePath(windows: false)}' as docgenMain;
import 'dart:io';
@ -34,8 +32,6 @@ void main(List<String> arguments) {
if (tool == "dart2js") {
dart2jsMain.BUILD_ID = "$version";
dart2jsMain.main(arguments.skip(1).toList());
} else if (tool == "dartdoc") {
dartdocMain.main(arguments.skip(1).toList());
} else if (tool == "docgen") {
docgenMain.main(arguments.skip(1).toList());
}
@ -88,12 +84,11 @@ Future createSnapshot(var dart_file, var packageRoot) {
* Takes the following arguments:
* --output_dir=val The full path to the output_dir.
* --dart2js_main=val The path to the dart2js main script relative to root.
* --dartdoc_main=val The path to the dartdoc main script relative to root.
* --docgen_main=val The path to the docgen main script relative to root.
* --package-root=val The package-root used to find packages for the snapshot.
*/
void main(List<String> arguments) {
var validArguments = ["--output_dir", "--dart2js_main", "--dartdoc_main",
var validArguments = ["--output_dir", "--dart2js_main",
"--docgen_main", "--package_root"];
var args = {};
for (var argument in arguments) {
@ -105,7 +100,6 @@ void main(List<String> arguments) {
args[argumentSplit[0].substring(2)] = argumentSplit[1];
}
if (!args.containsKey("dart2js_main")) throw "Please specify dart2js_main";
if (!args.containsKey("dartdoc_main")) throw "Please specify dartdoc_main";
if (!args.containsKey("docgen_main")) throw "Please specify docgen_main";
if (!args.containsKey("output_dir")) throw "Please specify output_dir";
if (!args.containsKey("package_root")) throw "Please specify package_root";