Merge pull request dart-lang/markdown#8 from kevmoo/master

A bunch of cleanup
diff --git a/pkgs/markdown/lib/markdown.dart b/pkgs/markdown/lib/markdown.dart
index 33d7c37..f127450 100644
--- a/pkgs/markdown/lib/markdown.dart
+++ b/pkgs/markdown/lib/markdown.dart
@@ -5,116 +5,8 @@
 /// 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, 
-                      bool inlineOnly: false}) {
-  final document = new Document(inlineSyntaxes: inlineSyntaxes,
-      linkResolver: linkResolver);
-
-  if (inlineOnly) {
-    return renderToHtml(document.parseInline(markdown));
-  } else {
-    // 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);
-}
+export 'src/ast.dart';
+export 'src/block_parser.dart';
+export 'src/document.dart';
+export 'src/html_renderer.dart';
+export 'src/inline_parser.dart';
diff --git a/pkgs/markdown/lib/src/markdown/ast.dart b/pkgs/markdown/lib/src/ast.dart
similarity index 96%
rename from pkgs/markdown/lib/src/markdown/ast.dart
rename to pkgs/markdown/lib/src/ast.dart
index c966cea..befd75e 100644
--- a/pkgs/markdown/lib/src/markdown/ast.dart
+++ b/pkgs/markdown/lib/src/ast.dart
@@ -2,7 +2,9 @@
 // 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;
+library markdown.ast;
+
+typedef Node Resolver(String name);
 
 /// Base class for any AST item. Roughly corresponds to Node in the DOM. Will
 /// be either an Element or Text.
diff --git a/pkgs/markdown/lib/src/markdown/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
similarity index 92%
rename from pkgs/markdown/lib/src/markdown/block_parser.dart
rename to pkgs/markdown/lib/src/block_parser.dart
index d163e9c..c34f7db 100644
--- a/pkgs/markdown/lib/src/markdown/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -2,7 +2,11 @@
 // 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;
+library markdown.block_parser;
+
+import 'ast.dart';
+import 'document.dart';
+import 'util.dart';
 
 /// The line contains only whitespace or is empty.
 final _RE_EMPTY = new RegExp(r'^([ \t]*)$');
@@ -51,26 +55,26 @@
   final Document document;
 
   /// Index of the current line.
-  int pos;
+  int _pos;
 
   BlockParser(this.lines, this.document)
-    : pos = 0;
+    : _pos = 0;
 
   /// Gets the current line.
-  String get current => lines[pos];
+  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];
+    if (_pos >= lines.length - 1) return null;
+    return lines[_pos + 1];
   }
 
   void advance() {
-    pos++;
+    _pos++;
   }
 
-  bool get isDone => pos >= lines.length;
+  bool get isDone => _pos >= lines.length;
 
   /// Gets whether or not the current line matches the given pattern.
   bool matches(RegExp regex) {
@@ -88,28 +92,21 @@
 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 FencedCodeBlockSyntax(),
-          new BlockquoteSyntax(),
-          new HorizontalRuleSyntax(),
-          new UnorderedListSyntax(),
-          new OrderedListSyntax(),
-          new ParagraphSyntax()
-        ];
-    }
+  static const List<BlockSyntax> syntaxes = const[
+    const EmptyBlockSyntax(),
+    const BlockHtmlSyntax(),
+    const SetextHeaderSyntax(),
+    const HeaderSyntax(),
+    const CodeBlockSyntax(),
+    const FencedCodeBlockSyntax(),
+    const BlockquoteSyntax(),
+    const HorizontalRuleSyntax(),
+    const UnorderedListSyntax(),
+    const OrderedListSyntax(),
+    const ParagraphSyntax()
+  ];
 
-    return _syntaxes;
-  }
-
-  static List<BlockSyntax> _syntaxes;
+  const BlockSyntax();
 
   /// Gets the regex used to identify the beginning of this block, if any.
   RegExp get pattern => null;
@@ -146,6 +143,8 @@
 class EmptyBlockSyntax extends BlockSyntax {
   RegExp get pattern => _RE_EMPTY;
 
+  const EmptyBlockSyntax();
+
   Node parse(BlockParser parser) {
     parser.advance();
 
@@ -156,6 +155,9 @@
 
 /// Parses setext-style headers.
 class SetextHeaderSyntax extends BlockSyntax {
+
+  const SetextHeaderSyntax();
+
   bool canParse(BlockParser parser) {
     // Note: matches *next* line, not the current one. We're looking for the
     // underlining after this line.
@@ -178,6 +180,8 @@
 class HeaderSyntax extends BlockSyntax {
   RegExp get pattern => _RE_HEADER;
 
+  const HeaderSyntax();
+
   Node parse(BlockParser parser) {
     final match = pattern.firstMatch(parser.current);
     parser.advance();
@@ -191,6 +195,8 @@
 class BlockquoteSyntax extends BlockSyntax {
   RegExp get pattern => _RE_BLOCKQUOTE;
 
+  const BlockquoteSyntax();
+
   Node parse(BlockParser parser) {
     final childLines = parseChildLines(parser);
 
@@ -205,6 +211,8 @@
 class CodeBlockSyntax extends BlockSyntax {
   RegExp get pattern => _RE_INDENT;
 
+  const CodeBlockSyntax();
+
   List<String> parseChildLines(BlockParser parser) {
     final childLines = <String>[];
 
@@ -249,7 +257,11 @@
 class FencedCodeBlockSyntax extends BlockSyntax {
   RegExp get pattern => _RE_CODE;
 
-  List<String> parseChildLines(BlockParser parser, String endBlock) {
+  const FencedCodeBlockSyntax();
+
+  List<String> parseChildLines(BlockParser parser, [String endBlock]) {
+    if(endBlock == null) endBlock = '';
+
     final childLines = <String>[];
     parser.advance();
     while (!parser.isDone) {
@@ -285,16 +297,14 @@
     }
     return element;
   }
-
-  void _addElementAttributes(Element element) {
-
-  }
 }
 
 /// Parses horizontal rules like `---`, `_ _ _`, `*  *  *`, etc.
 class HorizontalRuleSyntax extends BlockSyntax {
   RegExp get pattern => _RE_HR;
 
+  const HorizontalRuleSyntax();
+
   Node parse(BlockParser parser) {
     final match = pattern.firstMatch(parser.current);
     parser.advance();
@@ -317,6 +327,8 @@
 
   bool get canEndBlock => false;
 
+  const BlockHtmlSyntax();
+
   Node parse(BlockParser parser) {
     final childLines = [];
 
@@ -343,6 +355,8 @@
 
   String get listTag;
 
+  const ListSyntax();
+
   Node parse(BlockParser parser) {
     final items = <ListItem>[];
     var childLines = <String>[];
@@ -485,18 +499,24 @@
 class UnorderedListSyntax extends ListSyntax {
   RegExp get pattern => _RE_UL;
   String get listTag => 'ul';
+
+  const UnorderedListSyntax();
 }
 
 /// Parses ordered lists.
 class OrderedListSyntax extends ListSyntax {
   RegExp get pattern => _RE_OL;
   String get listTag => 'ol';
+
+  const OrderedListSyntax();
 }
 
 /// Parses paragraphs of regular text.
 class ParagraphSyntax extends BlockSyntax {
   bool get canEndBlock => false;
 
+  const ParagraphSyntax();
+
   bool canParse(BlockParser parser) => true;
 
   Node parse(BlockParser parser) {
diff --git a/pkgs/markdown/lib/src/document.dart b/pkgs/markdown/lib/src/document.dart
new file mode 100644
index 0000000..7ed01ca
--- /dev/null
+++ b/pkgs/markdown/lib/src/document.dart
@@ -0,0 +1,87 @@
+library markdown.document;
+
+import 'ast.dart';
+import 'block_parser.dart';
+import 'inline_parser.dart';
+
+/// 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);
+}
diff --git a/pkgs/markdown/lib/src/markdown/html_renderer.dart b/pkgs/markdown/lib/src/html_renderer.dart
similarity index 70%
rename from pkgs/markdown/lib/src/markdown/html_renderer.dart
rename to pkgs/markdown/lib/src/html_renderer.dart
index 5494394..97b900b 100644
--- a/pkgs/markdown/lib/src/markdown/html_renderer.dart
+++ b/pkgs/markdown/lib/src/html_renderer.dart
@@ -2,7 +2,27 @@
 // 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;
+library markdown.html_renderer;
+
+import 'ast.dart';
+import 'document.dart';
+
+/// Converts the given string of markdown to HTML.
+String markdownToHtml(String markdown, {inlineSyntaxes, linkResolver,
+    bool inlineOnly: false}) {
+  var document = new Document(inlineSyntaxes: inlineSyntaxes,
+      linkResolver: linkResolver);
+
+  if (inlineOnly) {
+    return renderToHtml(document.parseInline(markdown));
+  } else {
+    // Replace windows line endings with unix line endings, and split.
+    var lines = markdown.replaceAll('\r\n','\n').split('\n');
+    document.parseRefLinks(lines);
+    var blocks = document.parseLines(lines);
+    return renderToHtml(blocks);
+  }
+}
 
 String renderToHtml(List<Node> nodes) => new HtmlRenderer().render(nodes);
 
diff --git a/pkgs/markdown/lib/src/markdown/inline_parser.dart b/pkgs/markdown/lib/src/inline_parser.dart
similarity index 98%
rename from pkgs/markdown/lib/src/markdown/inline_parser.dart
rename to pkgs/markdown/lib/src/inline_parser.dart
index d141659..392ca69 100644
--- a/pkgs/markdown/lib/src/markdown/inline_parser.dart
+++ b/pkgs/markdown/lib/src/inline_parser.dart
@@ -2,7 +2,11 @@
 // 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;
+library markdown.inline_parser;
+
+import 'ast.dart';
+import 'document.dart';
+import 'util.dart';
 
 /// Maintains the internal state needed to parse inline span elements in
 /// markdown.
diff --git a/pkgs/markdown/lib/src/util.dart b/pkgs/markdown/lib/src/util.dart
new file mode 100644
index 0000000..a6d52de
--- /dev/null
+++ b/pkgs/markdown/lib/src/util.dart
@@ -0,0 +1,8 @@
+library markdown.util;
+
+/// Replaces `<`, `&`, and `>`, with their HTML entity equivalents.
+String escapeHtml(String html) {
+  return html.replaceAll('&', '&amp;')
+             .replaceAll('<', '&lt;')
+             .replaceAll('>', '&gt;');
+}
diff --git a/pkgs/markdown/pubspec.yaml b/pkgs/markdown/pubspec.yaml
index 331513f..cb5bc77 100644
--- a/pkgs/markdown/pubspec.yaml
+++ b/pkgs/markdown/pubspec.yaml
@@ -1,7 +1,9 @@
 name: markdown
-version: 0.5.0
+version: 0.5.1-dev
 author: Dart Team <misc@dartlang.org>
 description: A library for converting markdown to HTML.
 homepage: https://github.com/dpeek/dart-markdown
+environment:
+  sdk: '>=1.0.0 <2.0.0'
 dev_dependencies:
-  unittest: any
+  unittest: '>=0.9.0 <0.11.0'
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 91019fe..3c4564b 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -6,9 +6,7 @@
 library markdownTests;
 
 import 'package:unittest/unittest.dart';
-
-// TODO(rnystrom): Use "package:" URL (#4968).
-import '../lib/markdown.dart';
+import 'package:markdown/markdown.dart';
 
 /// Most of these tests are based on observing how showdown behaves:
 /// http://softwaremaniacs.org/playground/showdown-highlight/
@@ -961,7 +959,7 @@
   return lines.join('\n');
 }
 
-validate(String description, String markdown, String html,
+void validate(String description, String markdown, String html,
          {bool verbose: false, inlineSyntaxes, linkResolver,
           bool inlineOnly: false}) {
   test(description, () {
@@ -976,19 +974,18 @@
       // 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('');
-    }
+      var sb = new StringBuffer();
+      sb.writeln('Expected: ${html.replaceAll("\n", "\n          ")}');
+      sb.writeln('  Actual: ${result.replaceAll("\n", "\n          ")}');
 
-    expect(passed, isTrue, verbose: verbose);
+      fail(sb.toString());
+    }
   });
 }
 
 /// Does a loose comparison of the two strings of HTML. Ignores differences in
 /// newlines and indentation.
-compareOutput(String a, String b) {
+bool compareOutput(String a, String b) {
   int i = 0;
   int j = 0;