Merge pull request dart-lang/markdown#63 from srawlins/block-syntax-extension-help-

Rewrite ListSyntax.parse() to be simpler, and extract determineBlockItems()
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index a9291e7..c996795 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -6,13 +6,16 @@
   default extension set, which is `ExtensionSet.commonMark`, which includes
   FencedCodeBlock.
 * Inline HTML syntax support; This is also considered an extension (#18).
-* The text `[foo] (bar)` now parses as an inline link (#53).
+* The text `[foo] (bar)` no longer renders as an inline link (#53).
 * The text `[foo]()` now renders as an inline link.
 * Header identifier support in the HeaderWithIdSyntax and
   SetextHeaderWithIdSyntax extensions.
 * Implement backslash-escaping so that Markdown syntax can be escaped, such as
   `[foo]\(bar) ==> <p>[foo](bar)</p>`.
-* New public method for BlockParser: `peek(int linesAhead)`.
+* New public method for BlockParser: `peek(int linesAhead)`, meant for use in
+  subclasses.
+* New public members for ListSyntax: `blocksInList` and `determineBlockItems()`,
+  meant for use in subclasses.
 
 ## 0.8.0
 
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index cfdb60a..6a1d720 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -413,6 +413,16 @@
 
   const ListSyntax();
 
+  /// A list of patterns that can start a valid block within a list item.
+  static final blocksInList = [
+    _blockquotePattern,
+    _headerPattern,
+    _hrPattern,
+    _indentPattern,
+    _ulPattern,
+    _olPattern
+  ];
+
   Node parse(BlockParser parser) {
     var items = <ListItem>[];
     var childLines = <String>[];
@@ -455,7 +465,29 @@
     }
 
     endItem();
+    determineBlockItems(items);
+    var itemNodes = <Node>[];
 
+    for (var item in items) {
+      if (item.forceBlock) {
+        // Block list item.
+        var children = parser.document.parseLines(item.lines);
+        itemNodes.add(new Element('li', children));
+      } else {
+        // Raw list item.
+        var contents = parser.document.parseInline(item.lines[0]);
+        itemNodes.add(new Element('li', contents));
+      }
+    }
+
+    return new Element(listTag, itemNodes);
+  }
+
+  /// Determines whether each item in [items] is a block item.
+  ///
+  /// Also removes any trailing empty lines and notes which items are separated
+  /// by empty lines.
+  void determineBlockItems(List items) {
     // Markdown, because it hates us, specifies two kinds of list items. If you
     // have a list like:
     //
@@ -498,56 +530,26 @@
     // trailing empty lines on the last item don't force it into being a block.
     for (var i = 0; i < items.length; i++) {
       for (var j = items[i].lines.length - 1; j > 0; j--) {
-        if (_emptyPattern.firstMatch(items[i].lines[j]) != null) {
-          // Found an empty line. This item and the 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;
+        if (!_emptyPattern.hasMatch(items[i].lines[j])) break;
+
+        // Found an empty line. This item and the one after it are blocks.
+        if (i < items.length - 1) {
+          items[i].forceBlock = true;
+          items[i + 1].forceBlock = true;
         }
+        items[i].lines.removeLast();
       }
+
+      // Items with more than one line are block items.
+      items[i].forceBlock = items[i].forceBlock || items[i].lines.length > 1;
+
+      if (items[i].forceBlock) continue;
+
+      // Items (even one-lined items) that start with a block syntax are block
+      // items.
+      items[i].forceBlock =
+          blocksInList.any((p) => p.hasMatch(items[i].lines[0]));
     }
-
-    // Convert the list items to Nodes.
-    var itemNodes = <Node>[];
-    for (var item in items) {
-      var blockItem = item.forceBlock || (item.lines.length > 1);
-
-      // See if it matches some block parser.
-      var blocksInList = [
-        _blockquotePattern,
-        _headerPattern,
-        _hrPattern,
-        _indentPattern,
-        _ulPattern,
-        _olPattern
-      ];
-
-      if (!blockItem) {
-        for (var 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.
-        var children = parser.document.parseLines(item.lines);
-        itemNodes.add(new Element('li', children));
-      } else {
-        // Raw list item.
-        var contents = parser.document.parseInline(item.lines[0]);
-        itemNodes.add(new Element('li', contents));
-      }
-    }
-
-    return new Element(listTag, itemNodes);
   }
 }