From 39be61ef2cd9033e4f4891d47274f0c2561d35de Mon Sep 17 00:00:00 2001
From: jessica parsons
Date: Sun, 31 May 2026 14:12:03 -0700
Subject: [PATCH] go back and tweak the effing fenced code disaster area so
that it will break fenced code out into its own div and consume html &
footnotes at the top level.
---
dumptree.c | 1 +
generate.c | 10 +++---
markdown.c | 84 +++++++++++++++++++++++++++++++--------------
markdown.h | 2 +-
tests/codeblock.t | 87 ++++++++++++++++++++---------------------------
tests/crash.t | 19 +++--------
6 files changed, 105 insertions(+), 98 deletions(-)
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
~~~' \
-'code
+'code
-
'
+
'
try -ffencedcode 'unterminated fenced code block' \
'~~~
@@ -94,11 +92,10 @@ try -ffencedcode 'fenced code block with tildes' \
code with tildes
~~~
~~~~~' \
-'~~~
+'~~~
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' \
'~~~~
hello, sailor
~~~~' \
-'<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='
./rep '#header' ' ' 400 | try -T 'a header with a bunch of trailing spaces' -heredoc "$RESULT"
-RESULT='
-
-
-| : Y: |
-
-
-
-
- |
-
-
- |
-
-
-
'
+RESULT=': Y:|
+|
+|
+|
'
cat << \EOF | try '-F 0x03000000' 'random input that looks like a table' -heredoc "$RESULT"
: Y:|