diff options
author | Stefan Seelmann | 2013-06-21 11:39:43 -0400 |
---|---|---|
committer | Stefan Seelmann | 2013-06-27 16:00:50 -0400 |
commit | b85fd5e471a32dce346530f1b550b9072e88cae2 (patch) | |
tree | b662846d4d3c9f6cf3662813d71c92486721563d | |
parent | 44e446a4cd59170c6c1c62fa315a923395466072 (diff) | |
download | org.eclipse.mylyn.docs-b85fd5e471a32dce346530f1b550b9072e88cae2.zip org.eclipse.mylyn.docs-b85fd5e471a32dce346530f1b550b9072e88cae2.tar.gz org.eclipse.mylyn.docs-b85fd5e471a32dce346530f1b550b9072e88cae2.tar.xz |
404096: [Markdown] Add basic support for lists
* simple ordered and unordered lists
* basic line wrapping for list items
* updated content assist and cheat sheet and extracted some messages
Change-Id: Ib8814d69003bd874bcab055d571b8d5f2701939d
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=404096
7 files changed, 240 insertions, 4 deletions
diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ListBlock.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ListBlock.java new file mode 100644 index 0000000..c7517f6 --- /dev/null +++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/internal/wikitext/markdown/core/block/ListBlock.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2013 Stefan Seelmann and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Seelmann - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.wikitext.markdown.core.block; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.mylyn.wikitext.core.parser.Attributes; +import org.eclipse.mylyn.wikitext.core.parser.DocumentBuilder.BlockType; + +/** + * Markdown lists. + * + * @author Stefan Seelmann + */ +public class ListBlock extends NestableBlock { + + private static final Pattern itemStartPattern = Pattern.compile(" {0,3}(?:([\\*\\+\\-])|([0-9]+\\.))\\s+(.+?)"); //$NON-NLS-1$ + + private int blockLineCount = 0; + + @Override + public boolean canStart(String line, int lineOffset) { + Matcher matcher = itemStartPattern.matcher(line.substring(lineOffset)); + return matcher.matches(); + } + + @Override + protected int processLineContent(String line, int offset) { + + String text = line.substring(offset); + + // check start of block/item + String content; + Matcher itemStartMatcher = itemStartPattern.matcher(text); + if (itemStartMatcher.matches()) { + if (blockLineCount == 0) { + // start list block + BlockType blockType = itemStartMatcher.group(1) != null + ? BlockType.BULLETED_LIST + : BlockType.NUMERIC_LIST; + builder.beginBlock(blockType, new Attributes()); + } else { + // end previous item + builder.endBlock(); + } + + // start item + builder.beginBlock(BlockType.LIST_ITEM, new Attributes()); + + // extract content + content = itemStartMatcher.group(3); + } else if (!text.trim().isEmpty()) { + // TODO: improve handling of wrapped lines, e.g. trim left + builder.characters("\n"); //$NON-NLS-1$ + content = text; + } else { + // TODO: check for multiple paragraphs and nested blocks, for now just close the list block + setClosed(true); + return offset; + } + + int textStart = 0; + markupLanguage.emitMarkupLine(getParser(), state, content, textStart); + + blockLineCount++; + return -1; + } + + @Override + public void setClosed(boolean closed) { + if (closed && !isClosed()) { + // end list item + builder.endBlock(); + // end list block + builder.endBlock(); + } + super.setClosed(closed); + } + +} diff --git a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java index 54200ca..748fb49 100644 --- a/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java +++ b/org.eclipse.mylyn.wikitext.markdown.core/src/org/eclipse/mylyn/wikitext/markdown/core/MarkdownLanguage.java @@ -19,6 +19,7 @@ import org.eclipse.mylyn.internal.wikitext.markdown.core.block.HeadingBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.HorizontalRuleBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.InlineHtmlBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.LinkDefinitionBlock; +import org.eclipse.mylyn.internal.wikitext.markdown.core.block.ListBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.ParagraphBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.QuoteBlock; import org.eclipse.mylyn.internal.wikitext.markdown.core.block.UnderlinedHeadingBlock; @@ -105,6 +106,7 @@ public class MarkdownLanguage extends AbstractMarkupLanguage { blocks.add(new HeadingBlock()); blocks.add(new InlineHtmlBlock()); blocks.add(new QuoteBlock()); + blocks.add(new ListBlock()); blocks.add(new LinkDefinitionBlock()); } diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java index 1fe83cc..5fcc9dd 100644 --- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java +++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageBlockElementsTest.java @@ -281,6 +281,18 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase parseAndAssert(markup, expectedHtml); } + public void testBlockquotesContainingList() { + String markup = "> * Black\n> * White"; + String expectedHtml = "<blockquote><ul><li>Black</li><li>White</li></ul></blockquote>"; + parseAndAssert(markup, expectedHtml); + } + + public void testBlockquotesContainingListWithWrappedItem() { + String markup = "> * Wrapped\n line\n> * Next\nitem"; + String expectedHtml = "<blockquote><ul><li>Wrapped\n line</li><li>Next\nitem</li></ul></blockquote>"; + parseAndAssert(markup, expectedHtml); + } + public void testBlockquoteSimple() { String markup = "> a\n> b"; String expectedHtml = "<blockquote><p>a\nb</p></blockquote>"; @@ -288,6 +300,106 @@ public class MarkdownLanguageBlockElementsTest extends MarkdownLanguageTestBase } /* + * Unordered lists use asterisks, pluses, and hyphens - interchangably - as list markers. + */ + public void testUnorderedListUsingAsteriskMarker() { + String markup = "* Red\n* Green\n* Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + public void testUnorderedListUsingPlusMarkers() { + String markup = "+ Red\n+ Green\n+ Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + public void testUnorderedListUsingHyphenMarkers() { + String markup = "- Red\n- Green\n- Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + public void testUnorderedListUsingMixedMarkers() { + String markup = "* Red\n- Green\n+ Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * Ordered lists use numbers followed by periods. + */ + public void testOrderedListUsingSequentialNumbers() { + String markup = "1. Bird\n2. McHale\n3. Parish"; + String expectedHtml = "<ol><li>Bird</li><li>McHale</li><li>Parish</li></ol>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * It's important to note that the actual numbers you use to mark the list + * have no effect on the HTML output Markdown produces. + */ + public void testOrderedListUsingSameNumbers() { + String markup = "1. Bird\n1. McHale\n1. Parish"; + String expectedHtml = "<ol><li>Bird</li><li>McHale</li><li>Parish</li></ol>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * List markers typically start at the left margin, but may be indented by up to three spaces. + */ + public void testListMarkersIndentedBySpaces() { + String markup = " * Red\n * Green\n * Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + public void testListMarkersIndentedByMoreThanThreeSpacesIsNotRecognizedAsList() { + String markup = " * Red\n * Green\n * Blue"; + String expectedHtml = "<pre><code>* Red\n * Green\n * Blue</code></pre>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * List markers must be followed by one or more spaces or a tab. + */ + public void testListMarkersFollowedBySpaces() { + String markup = "* Red\n* Green\n* Blue"; + String expectedHtml = "<ul><li>Red</li><li>Green</li><li>Blue</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + public void testListMarkersFollowedByTab() { + String markup = "1.\tBird\n1.\tMcHale\n1.\tParish"; + String expectedHtml = "<ol><li>Bird</li><li>McHale</li><li>Parish</li></ol>"; + parseAndAssert(markup, expectedHtml); + } + + public void testListMarkersNotFollowedBySpaceOrTabIsNotRecognizedAsList() { + String markup = "*Red\n*Green\n*Blue"; + String expectedHtml = "<p>*Red\n*Green\n*Blue</p>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * To make lists look nice, you can wrap items with hanging indents. + */ + public void testListWithWrappedItemAndHangingIndents() { + String markup = "* Lorem ipsum\n sit amet.\n* Donec sit\n amet nisl."; + String expectedHtml = "<ul><li>Lorem ipsum\n sit amet.</li><li>Donec sit\n amet nisl.</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + /* + * But if you want to be lazy, you don't have to. + */ + public void testListWithWrappedItemAndNoHangingIndents() { + String markup = "* Lorem ipsum\nsit amet.\n* Donec sit\namet nisl."; + String expectedHtml = "<ul><li>Lorem ipsum\nsit amet.</li><li>Donec sit\namet nisl.</li></ul>"; + parseAndAssert(markup, expectedHtml); + } + + /* * Markdown wraps a code block in both pre and code tags. To produce a code block in Markdown, simply indent every * line of the block by at least 4 spaces or 1 tab. */ diff --git a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java index ac310c6..693a615 100644 --- a/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java +++ b/org.eclipse.mylyn.wikitext.markdown.tests/src/org/eclipse/mylyn/internal/wikitext/markdown/tests/MarkdownLanguageTest.java @@ -37,6 +37,9 @@ public class MarkdownLanguageTest extends MarkdownLanguageTestBase { text.append("\n"); text.append(" Code block\n"); text.append("\n"); + text.append("* List item 1\n"); + text.append("* List item 2\n"); + text.append("\n"); text.append("I get 10 times more traffic from [Google] [1] than from [Yahoo][] or [MSN] [].\n"); text.append("\n"); text.append(" [1]: http://google.com/ \"Google\"\n"); @@ -54,6 +57,9 @@ public class MarkdownLanguageTest extends MarkdownLanguageTestBase { assertTrue(html.contains("<h2>Header 2<")); assertTrue(html.contains("<blockquote><p>Blockquote")); assertTrue(html.contains("<pre><code>Code block")); + assertTrue(html.contains("<ul>")); + assertTrue(html.contains("<li>List item 1</li>")); + assertTrue(html.contains("<li>List item 2</li>")); assertTrue(html.contains("<a href=\"http://google.com/\" title=\"Google\">Google</a>")); assertTrue(html.contains("<a href=\"http://search.yahoo.com/\" title=\"Yahoo Search\">Yahoo</a>")); assertTrue(html.contains("<a href=\"http://search.msn.com/\" title=\"MSN Search\">MSN</a>")); diff --git a/org.eclipse.mylyn.wikitext.markdown.ui/help/cheatSheet/Markdown.md b/org.eclipse.mylyn.wikitext.markdown.ui/help/cheatSheet/Markdown.md index fac847f..77b8cd2 100644 --- a/org.eclipse.mylyn.wikitext.markdown.ui/help/cheatSheet/Markdown.md +++ b/org.eclipse.mylyn.wikitext.markdown.ui/help/cheatSheet/Markdown.md @@ -31,6 +31,18 @@ more spaces to create a line break. \#\# This is also an H2 \#\# \#\#\#\#\# This is an H5 +##### Lists + +Unordered lists use `*`, `+`, or `-` as bullets. + +* one +* two + +Numbered lists use numbers followed by periods: + +1. one +2. two + ##### Block Quotes > Block quotes use email-style quoting diff --git a/org.eclipse.mylyn.wikitext.markdown.ui/plugin.properties b/org.eclipse.mylyn.wikitext.markdown.ui/plugin.properties index a3fedc4..7fad82f 100644 --- a/org.eclipse.mylyn.wikitext.markdown.ui/plugin.properties +++ b/org.eclipse.mylyn.wikitext.markdown.ui/plugin.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2012 Stefan Seelmann and others. +# Copyright (c) 2012, 2013 Stefan Seelmann and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -19,3 +19,11 @@ template.description.h3 = Heading 3 template.description.h4 = Heading 4 template.description.h5 = Heading 5 template.description.h6 = Heading 6 +template.description.lb = List (bulleted) +template.description.ln = List (numeric) +template.description.bq = Block quote +template.description.li = Link (inline) - [Text](http://www.example.com "Optional title") +template.description.lr = Link (footnote) - [Text][id] +template.description.ls = Link (simple) - <http://www.example.com> +template.description.ii = Image (inline) -  +template.description.ir = Image (footnote) - ![Alt text][id] diff --git a/org.eclipse.mylyn.wikitext.markdown.ui/plugin.xml b/org.eclipse.mylyn.wikitext.markdown.ui/plugin.xml index 9025e9a..0227fe8 100644 --- a/org.eclipse.mylyn.wikitext.markdown.ui/plugin.xml +++ b/org.eclipse.mylyn.wikitext.markdown.ui/plugin.xml @@ -11,9 +11,15 @@ <template name="##### " description="%template.description.h5" content="\n##### ${text}\n\n" block="true"/> <template name="###### " description="%template.description.h6" content="\n###### ${text}\n\n" block="true"/> - <template name="link" description="Link - [Text](http://www.example.com "Optional title")" content="[${text}](${text} "${title}") $"/> - <template name="link (simple)" description="Link - <http://www.example.com>" content="<${text}> $"/> - <template name="!image" description="Image - " content=" $"/> + <template name="*" description="%template.description.lb" content="\n* ${first item}\n* ${second item}\n" block="true"/> + <template name="1." description="%template.description.ln" content="\n1. ${first item}\n2. ${second item}\n" block="true"/> + <template name=">" description="%template.description.bq" content="\n> ${text}\n" block="true"/> + + <template name="[]" description="%template.description.li" content="[${text}](${url} "${title}") $"/> + <template name="[id]" description="%template.description.lr" content="[${text}][${id}] ${cursor}\n\n[${id}]: ${url} "${title}""/> + <template name="<>" description="%template.description.ls" content="<http://${url}> $"/> + <template name="![]" description="%template.description.ii" content=" $"/> + <template name="![id]" description="%template.description.ir" content="![${altText}][${id}] ${cursor}\n\n[${id}]: ${image}.png "${title}""/> <template name="\n" description="Line break (end of line)" content="^ \n"/> <template name="\t" description="Code block (indented with tab)" content="\n\t${text}\n"/> |