diff --git a/dumptree.c b/dumptree.c index 2791221..a61bcae 100644 --- a/dumptree.c +++ b/dumptree.c @@ -22,6 +22,7 @@ Pptype(int typ) switch (typ) { case WHITESPACE: return "whitespace"; case CODE : return "code"; + case FENCEDCODE: return "fenced code"; case QUOTE : return "quote"; case MARKUP : return "markup"; case HTML : return "html"; diff --git a/generate.c b/generate.c index 10c3d9f..5ec1c5b 100644 --- a/generate.c +++ b/generate.c @@ -1867,11 +1867,7 @@ printblock(Paragraph *pp, MMIOT *f) Qstring(Begin[align], f); do { - if ( t->is_fenced ) { - text(f); - t = printfenced(t, f); - } - else if ( S(t->text) ) { + if ( S(t->text) ) { if ( t->next && S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' ' && T(t->text)[S(t->text)-1] == ' ' ) { @@ -2060,6 +2056,10 @@ display(Paragraph *p, MMIOT *f) printhtml(p->text, f); break; + case FENCEDCODE: + printfenced(p->text, f); + break; + case CODE: printcode(p->text, p->lang, f); break; diff --git a/markdown.c b/markdown.c index ce8da8e..1fff940 100644 --- a/markdown.c +++ b/markdown.c @@ -632,8 +632,10 @@ codeblock(Paragraph *p) static int -iscodefence(Line *r, int size, line_type kind, mkd_flag_t *flags) +iscodefence(Line *r, Line *orig, mkd_flag_t *flags) { + int kind = 0, size = 2, dle = 0; + if ( !is_flag_set(flags, MKD_FENCEDCODE) || is_flag_set(flags, MKD_STRICT) ) return 0; @@ -644,6 +646,15 @@ iscodefence(Line *r, int size, line_type kind, mkd_flag_t *flags) return 0; + if ( orig ) { + kind = orig->kind; + size = orig->count; + dle = orig->dle; + } + + if ( (dle>>2) != (r->dle>>2) ) + return 0; + if ( kind ) return (r->kind == kind) && (r->count >= size); else @@ -652,19 +663,20 @@ iscodefence(Line *r, int size, line_type kind, mkd_flag_t *flags) static Line * -fencedcodechunk(Line *first, mkd_flag_t *flags) +fencedcodechunk(Line *first, mkd_flag_t *flags, int *yes) { Line *r, *q; /* don't allow zero-length code fences */ - if ( (first->next == 0) || iscodefence(first->next, first->count, first->kind, flags) ) { + if ( (first->next == 0) || iscodefence(first->next, first, flags) ) { first->kind = chk_text; first->is_fenced = 0; if ( first->next) { first->next->kind = chk_text; first->next->is_fenced = 0; } + *yes = 0; return first->next; } @@ -672,7 +684,7 @@ fencedcodechunk(Line *first, mkd_flag_t *flags) * past the closing fence */ for ( r = first->next; r; r = r->next ) { - if ( iscodefence(r, first->count, first->kind, flags) ) { + if ( iscodefence(r, first, flags) ) { if (S(first->text) - first->count > 0) { char *lang_attr = T(first->text) + first->count; @@ -689,9 +701,13 @@ fencedcodechunk(Line *first, mkd_flag_t *flags) S(first->text) = S(r->text) = 0; r->is_fenced = 0; - return r->next; + *yes = 1; + q = r->next; + r->next = 0; + return q; } } + *yes = 0; first->is_fenced = 0; first->kind = chk_text; return first; @@ -742,9 +758,7 @@ textblock(Paragraph *p, int toplevel, mkd_flag_t *flags) Line *t, *next; for ( t = p->text; t ; t = next ) { - if ( iscodefence(t, t->count, 0, flags) ) - next = fencedcodechunk(t, flags); - else if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { + if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { p->align = centered(p->text, t); t->next = 0; return next; @@ -1296,24 +1310,31 @@ compile_document(Line *ptr, MMIOT *f) ptr = consume(addfootnote(ptr, f), &eaten); previous_was_break = 1; } - else if (iscodefence(ptr, 2, 0, &(f->flags))) { - Line *more = ptr; - + else if ( previous_was_break && iscodefence(ptr, 0, &(f->flags)) ) { /* scoop up _everything_ in a fenced code block, including html & footnotes */ + int yes = 0; + Line *last = fencedcodechunk(ptr, &(f->flags), &yes ); - ATTACH(source,more); + if ( yes ) { + int dummy; - { Cache checkpoint = source; - while ( (more = more->next) && !iscodefence(more, ptr->count, ptr->kind, &(f->flags)) ) - ATTACH(source,more); + /* dump out any previously cached text + */ + uncache(&source, &d, f); - if ( more ) - ptr = more; - else { - source = checkpoint; - ptr = ptr->next; - } + p = Pp(&d, ptr, FENCEDCODE); + + ptr = consume(last, &dummy); + previous_was_break = 1; + } + else { + /* unterminated fenced code; treat as regular markdown & cache until the next + * fenced code, htl, or style block + */ + ATTACH(source, ptr); + previous_was_break = 0; + ptr = ptr->next; } } else { @@ -1474,14 +1495,25 @@ compile(Line *ptr, int toplevel, MMIOT *f) ptr = htmlblock(p, tag, &unclosed); if ( ! unclosed ) { p->typ = HTML; + continue; } } - if ( unclosed ) { - ptr = textblock(p, toplevel, &(f->flags) ); - /* tables are a special kind of paragraph */ - if ( actually_a_table(f, p->text) ) - p->typ = TABLE; + + if ( iscodefence(p->text, 0, &(f->flags)) ) { + int yes = 0; + + ptr = fencedcodechunk(p->text, &(f->flags), &yes); + + if ( yes ) { + p->typ = FENCEDCODE; + continue; + } } + + ptr = textblock(p, toplevel, &(f->flags) ); + /* tables are a special kind of paragraph */ + if ( actually_a_table(f, p->text) ) + p->typ = TABLE; } if ( (para||toplevel) && !p->align ) p->align = PARA; diff --git a/markdown.h b/markdown.h index c53b830..ace005f 100644 --- a/markdown.h +++ b/markdown.h @@ -102,7 +102,7 @@ typedef struct paragraph { char *label; /* toc label, uniqued */ char *ident; /* %id% tag for QUOTE */ char *lang; /* lang attribute for CODE */ - enum { WHITESPACE=0, CODE, QUOTE, MARKUP, + enum { WHITESPACE=0, CODE, FENCEDCODE, QUOTE, MARKUP, HTML, STYLE, DL, UL, OL, AL, LISTITEM, HDR, HR, TABLE, SOURCE } typ; enum { IMPLICIT=0, PARA, CENTER} align; diff --git a/tests/codeblock.t b/tests/codeblock.t index 37202c8..16ea529 100644 --- a/tests/codeblock.t +++ b/tests/codeblock.t @@ -50,19 +50,17 @@ code! still code! ~~~' \ - '
code!
+ 'code!
still code!
-
-'
+'
try -ffencedcode 'fenced code block' \
'~~~
code!
~~~' \
- 'code!
-
-'
+ 'code!
+'
try -ffencedcode 'fenced code block in list' \
'1. ~~~
@@ -78,9 +76,9 @@ try -ffencedcode 'fenced code block in blockquote' \
'>~~~
code
~~~' \
-'' try -ffencedcode 'unterminated fenced code block' \ '~~~ @@ -94,11 +92,10 @@ try -ffencedcode 'fenced code block with tildes' \ code with tildes ~~~ ~~~~~' \ -'code +'' +-code
~~~
+'~~~
code with tildes
~~~
-
-'
+'
try -ffencedcode 'paragraph with trailing fenced block' \
'text text text
@@ -108,17 +105,16 @@ code code code?
~~~' \
'text text text text text text -
code code code?
-
-'
+~~~
+code code code?
+~~~'
try -ffencedcode 'fenced code blocks with backtick delimiters' \
'```
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with mismatched delimters' \
'```
@@ -132,57 +128,50 @@ try -ffencedcode 'fenced code block with lang attribute' \
'```lang
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang-name attribute' \
'```lang-name
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang_name attribute' \
'```lang_name
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang attribute and space' \
'``` lang
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang attribute and multiple spaces' \
'``` lang
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang-name attribute and space' \
'``` lang-name
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with lang_name attribute and space' \
'``` lang_name
code
```' \
-'code
-
-'
+'code
+'
try -ffencedcode 'fenced code block with blank line in the middle' \
'```
@@ -190,20 +179,18 @@ hello
sailor
```' \
-'hello
+'hello
sailor
-
-'
+'
try -ffencedcode 'fenced code block with html in the middle' \
'~~~~
<h1>hello, sailor</h1>
-
-'
+'<h1>hello, sailor</h1>
+'
try -ffencedcode 'fenced code block with trailing spaces in list item' \
'1. ~~~~
@@ -229,9 +216,8 @@ bar
~~~~' \
'foo
-bar
-
-'
+bar
+'
try -ffencedcode 'checkline misparse as fenced code' \
@@ -241,10 +227,9 @@ content
```
' \
'label
-
content
-
-'
-
+class
+content
+'
summary $0
exit $rc
diff --git a/tests/crash.t b/tests/crash.t
index 0d47746..aa07f51 100644
--- a/tests/crash.t
+++ b/tests/crash.t
@@ -76,21 +76,10 @@ RESULT='| : Y: | -
|---|
| - |
| - |
: Y:|
+|
+|
+|