]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4681 Add support for ordered lists in SQ Markdown
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 7 May 2014 15:09:00 +0000 (17:09 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 7 May 2014 16:21:07 +0000 (18:21 +0200)
sonar-markdown/src/main/java/org/sonar/markdown/HtmlListChannel.java
sonar-markdown/src/test/java/org/sonar/markdown/MarkdownTest.java

index 7267db63b267743a2b99910dbdde4b3130646aa0..c8842f0a1c880069089a399c43141545bb9b594e 100644 (file)
@@ -23,21 +23,61 @@ import org.sonar.channel.Channel;
 import org.sonar.channel.CodeReader;
 import org.sonar.channel.RegexChannel;
 
+/**
+ * Lists come in two flavors:
+ * <ul>
+ *  <li>Unordered lists, triggered by lines that start with a <code>*</code></li>
+ *  <li>Ordered lists (added in 4.4), triggered by lines that start with a digit followed by a <code>.</code></li>
+ * </ul>
+
+ * E.g., the input:
+ * <pre>
+ * * One
+ * * Two
+ * * Three
+ * </pre>
+ * will produce:
+ * {@literal<ul>}{@literal<li>}One{@literal</li>}
+ * {@literal<li>}Two{@literal</li>}
+ * {@literal<li>}Three{@literal</li>}{@literal</ul>}
+ *
+ * Whereas the input:
+ * <pre>
+ * 1. One
+ * 1. Two
+ * 1. Three
+ * </pre>
+ * will produce:
+ * {@literal<ol>}{@literal<li>}One{@literal</li>}
+ * {@literal<li>}Two{@literal</li>}
+ * {@literal<li>}Three{@literal</li>}{@literal</ol>}
+ *
+ * @since 2.10.1
+ */
 class HtmlListChannel extends Channel<MarkdownOutput> {
 
-  private ListElementChannel listElement = new ListElementChannel();
+  private OrderedListElementChannel orderedListElement = new OrderedListElementChannel();
+  private UnorderedListElementChannel unorderedListElement = new UnorderedListElementChannel();
   private EndOfLine endOfLine = new EndOfLine();
   private boolean pendingListConstruction;
 
   @Override
   public boolean consume(CodeReader code, MarkdownOutput output) {
     try {
-      if (code.getColumnPosition() == 0 && listElement.consume(code, output)) {
-        while (endOfLine.consume(code, output) && listElement.consume(code, output)) {
-          // consume input
+      ListElementChannel currentChannel = null;
+      if (code.getColumnPosition() == 0) {
+        if (orderedListElement.consume(code, output)) {
+          currentChannel = orderedListElement;
+        } else if (unorderedListElement.consume(code, output)) {
+          currentChannel = unorderedListElement;
+        }
+        if (currentChannel != null) {
+          while (endOfLine.consume(code, output) && currentChannel.consume(code, output)) {
+            // consume input
+          }
+          output.append("</" + currentChannel.listElement + ">");
+          return true;
         }
-        output.append("</ul>");
-        return true;
       }
       return false;
     } finally {
@@ -45,16 +85,31 @@ class HtmlListChannel extends Channel<MarkdownOutput> {
     }
   }
 
-  private class ListElementChannel extends RegexChannel<MarkdownOutput> {
+  private class OrderedListElementChannel extends ListElementChannel {
+    public OrderedListElementChannel() {
+      super("\\d\\.", "ol");
+    }
+  }
+
+  private class UnorderedListElementChannel extends ListElementChannel {
+    public UnorderedListElementChannel() {
+      super("\\*", "ul");
+    }
+  }
 
-    public ListElementChannel() {
-      super("\\s*+\\*\\s[^\r\n]*+");
+  private abstract class ListElementChannel extends RegexChannel<MarkdownOutput> {
+
+    private String listElement;
+
+    protected ListElementChannel(String markerRegexp, String listElement) {
+      super("\\s*+" + markerRegexp + "\\s[^\r\n]*+");
+      this.listElement = listElement;
     }
 
     @Override
     protected void consume(CharSequence token, MarkdownOutput output) {
       if (!pendingListConstruction) {
-        output.append("<ul>");
+        output.append("<" + listElement + ">");
         pendingListConstruction = true;
       }
       output.append("<li>");
@@ -64,8 +119,12 @@ class HtmlListChannel extends Channel<MarkdownOutput> {
 
     private int searchIndexOfFirstCharacter(CharSequence token) {
       for (int index = 0; index < token.length(); index++) {
-        if (token.charAt(index) == '*') {
-          while (++index<token.length()) {
+        if (token.charAt(index) == '*'
+          || Character.isDigit(token.charAt(index))) {
+          if (token.charAt(index + 1) == '.') {
+            index ++;
+          }
+          while (++ index < token.length()) {
             if (token.charAt(index) != ' ') {
               return index;
             }
index 62048e6e5a69a57882740e5bb587cc11c3ec747e..13b0099976a47c7e69dc9da0bc6042c63073078f 100644 (file)
@@ -37,10 +37,25 @@ public class MarkdownTest {
   }
 
   @Test
-  public void shouldDecorateList() {
+  public void shouldDecorateUnorderedList() {
     assertThat(Markdown.convertToHtml("  * one\r* two\r\n* three\n * \n *five"))
         .isEqualTo("<ul><li>one</li>\r<li>two</li>\r\n<li>three</li>\n<li> </li>\n</ul> *five");
     assertThat(Markdown.convertToHtml("  * one\r* two")).isEqualTo("<ul><li>one</li>\r<li>two</li></ul>");
+    assertThat(Markdown.convertToHtml("* \r*")).isEqualTo("<ul><li> </li>\r</ul>*");
+  }
+
+  @Test
+  public void shouldDecorateOrderedList() {
+    assertThat(Markdown.convertToHtml("  1. one\r1. two\r\n1. three\n 1. \n 1.five"))
+        .isEqualTo("<ol><li>one</li>\r<li>two</li>\r\n<li>three</li>\n<li> </li>\n</ol> 1.five");
+    assertThat(Markdown.convertToHtml("  1. one\r1. two")).isEqualTo("<ol><li>one</li>\r<li>two</li></ol>");
+    assertThat(Markdown.convertToHtml("1. \r1.")).isEqualTo("<ol><li> </li>\r</ol>1.");
+  }
+
+  @Test
+  public void shouldDecorateMixedOrderedAndUnorderedList() {
+    assertThat(Markdown.convertToHtml("  1. one\r* two\r\n1. three\n * \n 1.five"))
+        .isEqualTo("<ol><li>one</li>\r</ol><ul><li>two</li>\r\n</ul><ol><li>three</li>\n</ol><ul><li> </li>\n</ul> 1.five");
   }
 
   @Test