Commit 54028ed7053c243a24fdd10a68f1aaca4b11a4f5

Martin Mitas 2016-11-24T14:16:47

Implement ordered lists.

diff --git a/README.md b/README.md
index 7b26efb..216646e 100644
--- a/README.md
+++ b/README.md
@@ -93,8 +93,8 @@ more or less forms our to do list.
 
 - **Container Blocks:**
   - [x] 5.1 Block quotes
-  - [ ] 5.2 List items
-  - [ ] 5.3 Lists
+  - [x] 5.2 List items
+  - [x] 5.3 Lists
 
 - **Inlines:**
   - [x] 6.1 Backslash escapes
diff --git a/md2html/md2html.c b/md2html/md2html.c
index 355a6f0..ddbcd7b 100644
--- a/md2html/md2html.c
+++ b/md2html/md2html.c
@@ -178,6 +178,20 @@ membuf_append_url_escaped(struct membuffer* buf, const char* data, MD_SIZE size)
  *****************************************/
 
 static void
+open_ol_block(struct membuffer* out, const MD_BLOCK_OL_DETAIL* det)
+{
+    char buf[64];
+
+    if(det->start == 1) {
+        MEMBUF_APPEND_LITERAL(out, "<ol>");
+        return;
+    }
+
+    snprintf(buf, sizeof(buf), "<ol start=\"%u\">", det->start);
+    MEMBUF_APPEND_LITERAL(out, buf);
+}
+
+static void
 open_code_block(struct membuffer* out, const MD_BLOCK_CODE_DETAIL* det)
 {
     MEMBUF_APPEND_LITERAL(out, "<pre><code");
@@ -339,6 +353,7 @@ enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
         case MD_BLOCK_DOC:      /* noop */ break;
         case MD_BLOCK_QUOTE:    MEMBUF_APPEND_LITERAL(out, "<blockquote>\n"); break;
         case MD_BLOCK_UL:       MEMBUF_APPEND_LITERAL(out, "<ul>\n"); break;
+        case MD_BLOCK_OL:       open_ol_block(out, (const MD_BLOCK_OL_DETAIL*)detail); break;
         case MD_BLOCK_LI:       MEMBUF_APPEND_LITERAL(out, "<li>"); break;
         case MD_BLOCK_HR:       MEMBUF_APPEND_LITERAL(out, "<hr>\n"); break;
         case MD_BLOCK_H:        MEMBUF_APPEND_LITERAL(out, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
@@ -366,6 +381,7 @@ leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
         case MD_BLOCK_DOC:      /*noop*/ break;
         case MD_BLOCK_QUOTE:    MEMBUF_APPEND_LITERAL(out, "</blockquote>\n"); break;
         case MD_BLOCK_UL:       MEMBUF_APPEND_LITERAL(out, "</ul>\n"); break;
+        case MD_BLOCK_OL:       MEMBUF_APPEND_LITERAL(out, "</ol>\n"); break;
         case MD_BLOCK_LI:       MEMBUF_APPEND_LITERAL(out, "</li>\n"); break;
         case MD_BLOCK_HR:       /*noop*/ break;
         case MD_BLOCK_H:        MEMBUF_APPEND_LITERAL(out, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
diff --git a/md4c/md4c.c b/md4c/md4c.c
index 9872909..9e33802 100644
--- a/md4c/md4c.c
+++ b/md4c/md4c.c
@@ -3583,7 +3583,8 @@ struct MD_BLOCK_tag {
 
 struct MD_CONTAINER_tag {
     CHAR ch;
-    int is_loose;
+    unsigned is_loose    : 8;
+    unsigned start;
     unsigned mark_indent;
     unsigned contents_indent;
     OFF block_byte_off;
@@ -3779,19 +3780,26 @@ md_process_all_blocks(MD_CTX* ctx)
 
     while(byte_off < ctx->n_block_bytes) {
         MD_BLOCK* block = (MD_BLOCK*)((char*)ctx->block_bytes + byte_off);
+        MD_BLOCK_OL_DETAIL ol_det;
+        void* det = NULL;
+
+        if(block->type == MD_BLOCK_OL) {
+            ol_det.start = block->n_lines;
+            det = &ol_det;
+        }
 
         if(block->flags & MD_BLOCK_CONTAINER) {
             if(block->flags & MD_BLOCK_CONTAINER_CLOSER) {
-                MD_LEAVE_BLOCK(block->type, NULL);
+                MD_LEAVE_BLOCK(block->type, det);
 
-                if(block->type == MD_BLOCK_UL)
+                if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL)
                     ctx->n_containers--;
             }
 
             if(block->flags & MD_BLOCK_CONTAINER_OPENER) {
-                MD_ENTER_BLOCK(block->type, NULL);
+                MD_ENTER_BLOCK(block->type, det);
 
-                if(block->type == MD_BLOCK_UL) {
+                if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL) {
                     ctx->containers[ctx->n_containers].is_loose = (block->flags & MD_BLOCK_LOOSE_LIST);
                     ctx->n_containers++;
                 }
@@ -4003,7 +4011,7 @@ md_add_line_into_current_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* analysis)
 }
 
 static int
-md_push_container_bytes(MD_CTX* ctx, MD_BLOCKTYPE type, unsigned flags)
+md_push_container_bytes(MD_CTX* ctx, MD_BLOCKTYPE type, unsigned start, unsigned flags)
 {
     MD_BLOCK* block;
     int ret = 0;
@@ -4017,7 +4025,7 @@ md_push_container_bytes(MD_CTX* ctx, MD_BLOCKTYPE type, unsigned flags)
     block->type = type;
     block->flags = flags;
     block->data = 0;
-    block->n_lines = 0;
+    block->n_lines = start;
 
 abort:
     return ret;
@@ -4482,20 +4490,28 @@ md_enter_child_containers(MD_CTX* ctx, int n_children)
 
     for(i = ctx->n_containers - n_children; i < ctx->n_containers; i++) {
         MD_CONTAINER* c = &ctx->containers[i];
+        int is_ordered_list = FALSE;
 
         switch(c->ch) {
+            case _T(')'):
+            case _T('.'):
+                is_ordered_list = TRUE;
+                /* Pass through */
+
             case _T('-'):
             case _T('+'):
             case _T('*'):
                 /* Remember offset in ctx->block_bytes so we can revisit the
                  * block if we detect it is a loose list. */
                 c->block_byte_off = ctx->n_block_bytes;
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_UL, MD_BLOCK_CONTAINER_OPENER));
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, MD_BLOCK_CONTAINER_OPENER));
+                MD_CHECK(md_push_container_bytes(ctx,
+                                (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL),
+                                c->start, MD_BLOCK_CONTAINER_OPENER));
+                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, 0, MD_BLOCK_CONTAINER_OPENER));
                 break;
 
             case _T('>'):
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, MD_BLOCK_CONTAINER_OPENER));
+                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0, MD_BLOCK_CONTAINER_OPENER));
                 break;
 
             default:
@@ -4514,16 +4530,26 @@ md_leave_child_containers(MD_CTX* ctx, int n_keep)
     int ret = 0;
 
     while(ctx->n_containers > n_keep) {
-        switch(ctx->containers[ctx->n_containers-1].ch) {
+        MD_CONTAINER* c = &ctx->containers[ctx->n_containers-1];
+        int is_ordered_list = FALSE;
+
+        switch(c->ch) {
+            case _T(')'):
+            case _T('.'):
+                is_ordered_list = TRUE;
+                /* Pass through */
+
             case _T('-'):
             case _T('+'):
             case _T('*'):
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, MD_BLOCK_CONTAINER_CLOSER));
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_UL, MD_BLOCK_CONTAINER_CLOSER));
+                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, 0, MD_BLOCK_CONTAINER_CLOSER));
+                MD_CHECK(md_push_container_bytes(ctx,
+                                (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL), 0,
+                                MD_BLOCK_CONTAINER_CLOSER));
                 break;
 
             case _T('>'):
-                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, MD_BLOCK_CONTAINER_CLOSER));
+                MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0, MD_BLOCK_CONTAINER_CLOSER));
                 break;
 
             default:
@@ -4542,6 +4568,7 @@ static int
 md_is_container_mark(MD_CTX* ctx, unsigned indent, OFF beg, OFF* p_end, MD_CONTAINER* p_container)
 {
     OFF off = beg;
+    OFF max_end;
 
     /* Check for block quote mark. */
     if(off < ctx->size  &&  CH(off) == _T('>')) {
@@ -4566,6 +4593,25 @@ md_is_container_mark(MD_CTX* ctx, unsigned indent, OFF beg, OFF* p_end, MD_CONTA
         return TRUE;
     }
 
+    /* Check for ordered list item marks. */
+    max_end = off + 9;
+    if(max_end > ctx->size)
+        max_end = ctx->size;
+    p_container->start = 0;
+    while(off < max_end  &&  ISDIGIT(off)) {
+        p_container->start = p_container->start * 10 + CH(off) - _T('0');
+        off++;
+    }
+    if(off+1 < ctx->size  &&  (CH(off) == _T('.') || CH(off) == _T(')'))   &&  CH(off+1) == _T(' ')) {
+        p_container->ch = CH(off);
+        p_container->is_loose = FALSE;
+        p_container->mark_indent = indent;
+        p_container->contents_indent = indent + off - beg;
+        off++;
+        *p_end = off;
+        return TRUE;
+    }
+
     return FALSE;
 }
 
@@ -4881,7 +4927,7 @@ done:
     /* Enter any container we found a mark for. */
     if(n_brothers > 0) {
         MD_ASSERT(n_brothers == 1);
-        MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI,
+        MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI, 0,
                 MD_BLOCK_CONTAINER_CLOSER | MD_BLOCK_CONTAINER_OPENER));
     }
 
diff --git a/md4c/md4c.h b/md4c/md4c.h
index eeba0bc..4d52604 100644
--- a/md4c/md4c.h
+++ b/md4c/md4c.h
@@ -65,6 +65,10 @@ enum MD_BLOCKTYPE_tag {
     /* <ul>...</ul> */
     MD_BLOCK_UL,
 
+    /* <ol>...</ol>
+     * Detail: Structure MD_BLOCK_OL_DETAIL. */
+    MD_BLOCK_OL,
+
     /* <li>...</li> */
     MD_BLOCK_LI,
 
@@ -172,6 +176,12 @@ enum MD_ALIGN_tag {
 
 
 /* Detailed info for MD_BLOCK_H. */
+typedef struct MD_BLOCK_OL_DETAIL_tag MD_BLOCK_OL_DETAIL;
+struct MD_BLOCK_OL_DETAIL_tag {
+    unsigned start;         /* Start index of the ordered list. */
+};
+
+/* Detailed info for MD_BLOCK_H. */
 typedef struct MD_BLOCK_H_DETAIL_tag MD_BLOCK_H_DETAIL;
 struct MD_BLOCK_H_DETAIL_tag {
     unsigned level;         /* Header level (1 - 6) */