462086: correct defect in Markdown DocumentBuilder sublist handling

Change-Id: Iefbddfa5ca263e80d7edec37cae61f265ffb5eb3
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462086
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownDocumentBuilder.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownDocumentBuilder.java
index b7a101d..2f9a6eb 100644
--- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownDocumentBuilder.java
+++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/MarkdownDocumentBuilder.java
@@ -54,20 +54,20 @@
 
 	}
 
-	private class ContentBlock extends Block implements MarkdownBlock {
+	private class ContentBlock extends NewlineDelimitedBlock implements MarkdownBlock {
 
 		protected String prefix;
 
 		protected String suffix;
 
-		ContentBlock(BlockType blockType, String prefix, String suffix) {
-			super(blockType);
+		ContentBlock(BlockType blockType, String prefix, String suffix, int leadingNewlines, int trailingNewlines) {
+			super(blockType, leadingNewlines, trailingNewlines);
 			this.prefix = prefix;
 			this.suffix = suffix;
 		}
 
-		ContentBlock(String prefix, String suffix) {
-			this(null, prefix, suffix);
+		ContentBlock(String prefix, String suffix, int leadingNewlines, int trailingNewlines) {
+			this(null, prefix, suffix, leadingNewlines, trailingNewlines);
 		}
 
 		@Override
@@ -112,7 +112,7 @@
 	private class ImplicitParagraphBlock extends ContentBlock {
 
 		ImplicitParagraphBlock() {
-			super(BlockType.PARAGRAPH, "", "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			super(BlockType.PARAGRAPH, "", "", 2, 2); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 
 		@Override
@@ -124,8 +124,9 @@
 
 	private class PrefixedLineContentBlock extends ContentBlock {
 
-		PrefixedLineContentBlock(BlockType blockType, String prefix, String suffix) {
-			super(blockType, prefix, suffix);
+		PrefixedLineContentBlock(BlockType blockType, String prefix, String suffix, int leadingNewlines,
+				int trailingNewlines) {
+			super(blockType, prefix, suffix, leadingNewlines, trailingNewlines);
 		}
 
 		@Override
@@ -159,8 +160,8 @@
 
 		private int count = 0;
 
-		ListBlock(BlockType blockType, String prefix, String suffix) {
-			super(blockType, prefix, suffix);
+		ListBlock(BlockType blockType, int leadingNewlines) {
+			super(blockType, "", "", leadingNewlines, 1); //$NON-NLS-1$//$NON-NLS-2$
 		}
 
 		@Override
@@ -187,8 +188,8 @@
 
 		private int count;
 
-		private ListItemBlock(String prefix, String suffix) {
-			super(BlockType.LIST_ITEM, prefix, suffix);
+		private ListItemBlock(String prefix) {
+			super(BlockType.LIST_ITEM, prefix, "", 1, 1); //$NON-NLS-1$
 		}
 
 		@Override
@@ -206,6 +207,8 @@
 			if (getPreviousBlock().getBlockType() == BlockType.NUMERIC_LIST) {
 				prefix = count + ". "; //$NON-NLS-1$
 			}
+			String indent = Strings.repeat(" ", prefix.length()); //$NON-NLS-1$			
+
 			MarkdownDocumentBuilder.this.emitContent(prefix);
 			// split out content by line
 			Matcher matcher = PATTERN_LINE_BREAK.matcher(content);
@@ -214,7 +217,7 @@
 				// indent each line hanging past the initial line item
 				String line = matcher.group(0);
 				if (lines > 0 && !line.trim().isEmpty()) {
-					line = "    " + line; //$NON-NLS-1$
+					line = indent + line;
 				}
 				MarkdownDocumentBuilder.this.emitContent(line);
 				lines++;
@@ -232,7 +235,7 @@
 		private final LinkAttributes attributes;
 
 		LinkBlock(LinkAttributes attributes) {
-			super("", ""); //$NON-NLS-1$ //$NON-NLS-2$
+			super("", "", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 			this.attributes = attributes;
 		}
 
@@ -259,7 +262,7 @@
 	private class CodeSpan extends ContentBlock {
 
 		private CodeSpan() {
-			super("`", "`"); //$NON-NLS-1$ //$NON-NLS-2$
+			super("`", "`", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 
 		@Override
@@ -282,22 +285,29 @@
 	protected Block computeBlock(BlockType type, Attributes attributes) {
 		switch (type) {
 		case PARAGRAPH:
-			return new ContentBlock(type, "", "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock(type, "", "", 2, 2); //$NON-NLS-1$ //$NON-NLS-2$
 		case QUOTE:
-			return new PrefixedLineContentBlock(type, "> ", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new PrefixedLineContentBlock(type, "> ", "", 1, 1); //$NON-NLS-1$ //$NON-NLS-2$
 		case BULLETED_LIST:
 		case NUMERIC_LIST:
-			return new ListBlock(type, "", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			if (currentBlock != null) {
+				BlockType currentBlockType = currentBlock.getBlockType();
+				if (currentBlockType == BlockType.LIST_ITEM || currentBlockType == BlockType.DEFINITION_ITEM
+						|| currentBlockType == BlockType.DEFINITION_TERM) {
+					return new ListBlock(type, 1);
+				}
+			}
+			return new ListBlock(type, 2);
 		case LIST_ITEM:
 			if (computeCurrentListType() == BlockType.NUMERIC_LIST) {
-				return new ListItemBlock("1. ", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+				return new ListItemBlock("1. "); //$NON-NLS-1$
 			}
-			return new ListItemBlock("* ", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ListItemBlock("* "); //$NON-NLS-1$
 		case CODE:
-			return new PrefixedLineContentBlock(type, "    ", "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new PrefixedLineContentBlock(type, "    ", "", 1, 2); //$NON-NLS-1$ //$NON-NLS-2$
 		default:
 			Logger.getLogger(getClass().getName()).warning("Unexpected block type: " + type); //$NON-NLS-1$
-			return new ContentBlock(type, "", "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock(type, "", "", 2, 2); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 
@@ -308,24 +318,24 @@
 			if (attributes instanceof LinkAttributes) {
 				return new LinkBlock((LinkAttributes) attributes);
 			}
-			return new ContentBlock("<", ">"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock("<", ">", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 		case ITALIC:
 		case EMPHASIS:
-			return new ContentBlock("*", "*"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock("*", "*", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 		case BOLD:
 		case STRONG:
-			return new ContentBlock("**", "**"); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock("**", "**", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 		case CODE:
 			return new CodeSpan();
 		default:
 			Logger.getLogger(getClass().getName()).warning("Unexpected block type: " + type); //$NON-NLS-1$
-			return new ContentBlock("", ""); //$NON-NLS-1$ //$NON-NLS-2$
+			return new ContentBlock("", "", 0, 0); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 
 	@Override
 	protected Block computeHeading(int level, Attributes attributes) {
-		return new ContentBlock(computePrefix('#', level) + " ", "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
+		return new ContentBlock(computePrefix('#', level) + " ", "", 1, 2); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 
 	@Override
diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownDocumentBuilderTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownDocumentBuilderTest.java
index db493c4..ce2d6de 100644
--- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownDocumentBuilderTest.java
+++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownDocumentBuilderTest.java
@@ -322,7 +322,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("* X\n* Y\n* Z\n\n");
+		assertMarkup("* X\n* Y\n* Z\n");
 	}
 
 	public void testListNumeric() {
@@ -339,7 +339,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("1. One\n2. Two\n3. Three\n\n");
+		assertMarkup("1. One\n2. Two\n3. Three\n");
 	}
 
 	public void testListConsecutive() {
@@ -355,7 +355,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("* Food\n\n1. Drink\n\n");
+		assertMarkup("* Food\n\n1. Drink\n");
 	}
 
 	public void testListWithParagraphs() {
@@ -397,7 +397,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("* List item with two paragraphs.\n\n    Second paragraph.\n\n* Simple list item.\n\n");
+		assertMarkup("* List item with two paragraphs.\n\n  Second paragraph.\n\n* Simple list item.\n");
 	}
 
 	public void testListItemWithBlockQuote() {
@@ -415,7 +415,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("* A list item with a blockquote:\n\n    > This is a blockquote  \n    > inside a list item.\n\n");
+		assertMarkup("* A list item with a blockquote:\n\n  > This is a blockquote  \n  > inside a list item.\n");
 	}
 
 	public void testListItemWithCodeBlock() {
@@ -431,7 +431,7 @@
 		builder.endBlock();
 		builder.endBlock();
 		builder.endDocument();
-		assertMarkup("* A list item with a code block:\n\n        code goes here\n\n");
+		assertMarkup("* A list item with a code block:\n\n      code goes here\n\n");
 	}
 
 	public void testCodeBlock() {
@@ -783,6 +783,58 @@
 		assertMarkup("&copy; XY&Z 2014\n\n");
 	}
 
+	public void testBulletedListWithNestedSublist2() {
+		builder.beginDocument();
+
+		builder.beginBlock(BlockType.BULLETED_LIST, new Attributes());
+
+		emitListItem("first");
+
+		builder.beginBlock(BlockType.LIST_ITEM, new Attributes());
+		builder.characters("second");
+
+		builder.beginBlock(BlockType.BULLETED_LIST, new Attributes());
+		emitListItem("second.1");
+		builder.endBlock(); // list
+		builder.endBlock(); // list item
+		builder.endBlock(); // list
+
+		builder.beginBlock(BlockType.NUMERIC_LIST, new Attributes());
+		emitListItem("third");
+
+		builder.beginBlock(BlockType.LIST_ITEM, new Attributes());
+		builder.characters("fourth");
+		builder.beginBlock(BlockType.NUMERIC_LIST, new Attributes());
+		emitListItem("fourth.1");
+
+		builder.beginBlock(BlockType.LIST_ITEM, new Attributes());
+		builder.characters("fourth.2");
+		builder.lineBreak();
+		builder.characters("fourth.2 line 2");
+		builder.endBlock(); // list item
+
+		builder.endBlock(); // list
+		builder.endBlock(); // list item
+
+		builder.endBlock(); // list
+
+		builder.endDocument();
+
+		String markup = out.toString();
+
+		TestUtil.println(markup);
+
+		assertEquals(
+				"* first\n* second\n  * second.1\n\n1. third\n2. fourth\n   1. fourth.1\n   2. fourth.2  \n      fourth.2 line 2\n",
+				markup);
+	}
+
+	private void emitListItem(String text) {
+		builder.beginBlock(BlockType.LIST_ITEM, new Attributes());
+		builder.characters(text);
+		builder.endBlock();
+	}
+
 	private void assertMarkup(String expected) {
 		String markup = out.toString();
 		TestUtil.println(markup);