Implement Setext headers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
diff --git a/README.md b/README.md
index ad74f0f..c1232bf 100644
--- a/README.md
+++ b/README.md
@@ -83,7 +83,7 @@ more or less forms our to do list.
- **Leaf Blocks:**
- [x] 4.1 Thematic breaks
- [x] 4.2 ATX headings
- - [ ] 4.3 Setext headings
+ - [x] 4.3 Setext headings
- [ ] 4.4 Indented code blocks
- [ ] 4.5 Fenced code blocks
- [ ] 4.6 HTML blocks
diff --git a/md4c/md4c.c b/md4c/md4c.c
index e439735..9eaa4a6 100644
--- a/md4c/md4c.c
+++ b/md4c/md4c.c
@@ -85,6 +85,8 @@ enum MD_LINETYPE_tag {
MD_LINE_BLANK,
MD_LINE_HR,
MD_LINE_ATXHEADER,
+ MD_LINE_SETEXTHEADER,
+ MD_LINE_SETEXTUNDERLINE,
MD_LINE_TEXT
};
@@ -304,6 +306,29 @@ md_is_atxheader_line(MD_CTX* ctx, OFF beg, OFF* p_beg, OFF* p_end)
return 0;
}
+static int
+md_is_setext_underline(MD_CTX* ctx, OFF beg, OFF* p_end)
+{
+ OFF off = beg + 1;
+
+ while(off < ctx->size && CH(off) == CH(beg))
+ off++;
+
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+
+ /* Optionally, space(s) can follow. */
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+
+ /* But nothing more is allowed on the line. */
+ if(off < ctx->size && !ISNEWLINE(off))
+ return -1;
+
+ ctx->header_level = (CH(beg) == _T('=') ? 1 : 2);
+ return 0;
+}
+
/* Analyze type of the line and find some its properties. This serves as a
* main input for determining type and boundaries of a block. */
static void
@@ -335,6 +360,14 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, const MD_LINE* pivot_line, MD_
}
}
+ /* Check whether we are setext underline. */
+ if(pivot_line->type == MD_LINE_TEXT && (CH(off) == _T('=') || CH(off) == _T('-'))) {
+ if(md_is_setext_underline(ctx, off, &off) == 0) {
+ line->type = MD_LINE_SETEXTUNDERLINE;
+ goto done;
+ }
+ }
+
/* Check whether we are thematic break line. */
if(ISANYOF(off, _T("-_*"))) {
if(md_is_hr_line(ctx, off, &off) == 0) {
@@ -403,6 +436,7 @@ md_process_block(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
break;
case MD_LINE_ATXHEADER:
+ case MD_LINE_SETEXTHEADER:
block_type = MD_BLOCK_H;
det.header.level = ctx->header_level;
break;
@@ -410,6 +444,11 @@ md_process_block(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
case MD_LINE_TEXT:
block_type = MD_BLOCK_P;
break;
+
+ case MD_LINE_SETEXTUNDERLINE:
+ default:
+ MD_UNREACHABLE();
+ break;
}
MD_ENTER_BLOCK(block_type, (void*) &det);
@@ -486,6 +525,13 @@ md_process_doc(MD_CTX *ctx)
continue;
}
+ /* MD_LINE_SETEXTUNDERLINE changes meaning of the previous block. */
+ if(line->type == MD_LINE_SETEXTUNDERLINE) {
+ MD_ASSERT(n_lines > 0);
+ lines[0].type = MD_LINE_SETEXTHEADER;
+ line->type = MD_LINE_BLANK;
+ }
+
/* New block also starts if line type changes. */
if(line->type != pivot_line->type) {
ret = md_process_block(ctx, lines, n_lines);