Markdown: support aside|note|promo blocks inside list items

This used to be accepted with the Pegdown parser, but was broken
during the switch to commonmark-java.

Also make the space between *** and the style optional, this was
accepted by the prior Pegdown parser and broke a few documents.
Its still unambiguous as BlockNoteExtension requires matching one
of three well known styles: "*** {aside|note|promo}".

Add a small unit test for the nested inside list behavior.

Change-Id: I34984ac14571e0b860b79096ee737a51cd4ea8ba
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/BlockNoteExtension.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/BlockNoteExtension.java
index 7c18d60..3ae747d 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/BlockNoteExtension.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/BlockNoteExtension.java
@@ -51,10 +51,12 @@
   }
 
   private static class NoteParser extends AbstractBlockParser {
+    private final int indent;
     private final BlockNote block;
     private boolean done;
 
-    NoteParser(String style) {
+    NoteParser(int indent, String style) {
+      this.indent = indent;
       block = new BlockNote();
       block.setClassName(style);
     }
@@ -69,7 +71,7 @@
       if (done) {
         return BlockContinue.none();
       }
-      if (state.getIndent() == 0) {
+      if (state.getIndent() <= indent) {
         int s = state.getNextNonSpaceIndex();
         CharSequence line = state.getLine();
         if ("***".contentEquals(line.subSequence(s, line.length()))) {
@@ -94,23 +96,19 @@
   private static class NoteParserFactory extends AbstractBlockParserFactory {
     @Override
     public BlockStart tryStart(ParserState state, MatchedBlockParser matched) {
-      if (state.getIndent() > 0) {
-        return BlockStart.none();
-      }
-
       int s = state.getNextNonSpaceIndex();
       CharSequence line = state.getLine();
       CharSequence text = line.subSequence(s, line.length());
-      if (text.length() < 4 || !"*** ".contentEquals(text.subSequence(0, 4))) {
+      if (text.length() < 3 || !"***".contentEquals(text.subSequence(0, 3))) {
         return BlockStart.none();
       }
 
-      String style = text.subSequence(4, text.length()).toString().trim();
+      String style = text.subSequence(3, text.length()).toString().trim();
       if (!VALID_STYLES.contains(style)) {
         return BlockStart.none();
       }
 
-      return BlockStart.of(new NoteParser(style)).atIndex(line.length());
+      return BlockStart.of(new NoteParser(state.getIndent(), style)).atIndex(line.length());
     }
   }
 }
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/doc/DocServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/doc/DocServletTest.java
index f73c331..9d2b437 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/doc/DocServletTest.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/doc/DocServletTest.java
@@ -104,6 +104,20 @@
   }
 
   @Test
+  public void noteInList() throws Exception {
+    String markdown =
+        "+ one\n\n" + "    ***aside\n" + "    remember this\n" + "    ***\n" + "\n" + "+ two\n";
+    repo.branch("master").commit().add("index.md", markdown).create();
+
+    String html = buildHtml("/repo/+/master/index.md");
+    System.out.println(html);
+    assertThat(html)
+        .contains(
+            "<ul><li><p>one</p><div class=\"aside\">remember this</div>"
+                + "</li><li><p>two</p></li></ul>");
+  }
+
+  @Test
   public void relativeLink() throws Exception {
     repo.branch("master").commit().add("A/B/README.md", "[c](../../C)").create();