Commit 74b2026 h
Changes: 2 files +46 -2
Changes (Side-by-Side)
share/jenny/lib/md2html.awk +7 -2
BEGIN { | |||
| 451 | id = substr($0, 1, RSTART - 1); |
451 | id = substr($0, 1, RSTART - 1); |
| 452 | sub(id "\\]:[ ]+", ""); |
452 | sub(id "\\]:[ ]+", ""); |
| 453 | sub(/[ ]+".*"$/, ""); |
453 | sub(/[ ]+".*"$/, ""); |
| 454 | url = eschtml($0); |
||
| 455 | fnref[id] = url; |
||
| 454 | # Stored raw, like a citation's body: inline() escapes what needs it and |
||
| 455 | # resolves what does not. Pre-escaping turned every "<" into "<" before |
||
| 456 | # the parser could see it, so an autolink -- <https://example.com> -- came |
||
| 457 | # out as literal text in a footnote while working in prose and in |
||
| 458 | # citations. The variable was called "url" because this rule began life |
||
| 459 | # handling link-reference definitions, where a bare URL was all it held. |
||
| 460 | fnref[id] = $0; |
||
| 456 | |
461 | |
| 457 | subref(id); |
462 | subref(id); |
| 458 | next; |
463 | next; |
test/regressions.sh +39 -0
it "the build is clean" | |||
| 2521 | assert_eq "$stderr_output" "" |
2521 | assert_eq "$stderr_output" "" |
| 2522 | |
2522 | |
| 2523 | summary |
2523 | summary |
| 2524 | |
||
| 2525 | suite "Footnote bodies go through the inline parser" |
||
| 2526 | |
||
| 2527 | # A footnote body must be parsed like prose and like a citation body. It used |
||
| 2528 | # to be HTML-escaped on the way in, which turned every "<" into "<" before |
||
| 2529 | # the parser could see it -- so an autolink rendered as literal text inside a |
||
| 2530 | # footnote while working everywhere else. |
||
| 2531 | fi="$TMPROOT/footnote-inline" |
||
| 2532 | make_blog_min "$fi" 1 10 |
||
| 2533 | printf '# Inline\n\nProse <https://example.com/prose> here.\n\nOne[^a] two[^b] three[^c].\n\n[^a]: See <https://example.com/auto> for details.\n\n[^b]: Tom & Jerry, 5 < 6.\n\n[^c]: With *emphasis* and [a link](https://example.com/md).\n' > "$fi/2024-01-01 inline.md" |
||
| 2534 | run_jenny "$fi" |
||
| 2535 | fi_post="$fi/.dist/post/inline.html" |
||
| 2536 | fi_flat=$(tr '\n' ' ' < "$fi_post") |
||
| 2537 | fi_a=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-a".\{0,200\}' | head -1) |
||
| 2538 | fi_b=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-b".\{0,200\}' | head -1) |
||
| 2539 | fi_c=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-c".\{0,200\}' | head -1) |
||
| 2540 | |
||
| 2541 | it "an autolink inside a footnote becomes a link" |
||
| 2542 | # The behaviour the fix exists for. |
||
| 2543 | assert_contains "$fi_a" '<a href="https://example.com/auto">' |
||
| 2544 | |
||
| 2545 | it "an autolink inside a footnote leaves no literal angle bracket" |
||
| 2546 | # The old output was "<https://example.com/auto>" -- escaped, then never parsed. |
||
| 2547 | assert_not_contains "$fi_a" '<https://example.com/auto' |
||
| 2548 | |
||
| 2549 | it "an autolink in prose still becomes a link" |
||
| 2550 | # The footnote fix must not disturb the path that already worked. |
||
| 2551 | assert_contains "$fi_flat" '<a href="https://example.com/prose">' |
||
| 2552 | |
||
| 2553 | it "ampersand and less-than in a footnote are still escaped" |
||
| 2554 | # Storing the body raw does not mean skipping escaping: inline() escapes what |
||
| 2555 | # is not markup. Unescaped "&" or "<" here would be invalid HTML. |
||
| 2556 | assert_contains "$fi_b" 'Tom & Jerry, 5 < 6.' |
||
| 2557 | |
||
| 2558 | it "emphasis inside a footnote still renders" |
||
| 2559 | assert_contains "$fi_c" '<em>emphasis</em>' |
||
| 2560 | |
||
| 2561 | it "a markdown link inside a footnote still renders" |
||
| 2562 | assert_contains "$fi_c" '<a href="https://example.com/md">a link</a>' |
||
Raw
commit 74b2026127978ece522402c6a667fb777d14a08b
Author: Pat Ambrosio <cp.ambrosio@gmail.com>
Date: Thu Aug 27 19:04:36 2026 +0800
Parse footnote bodies instead of pre-escaping them
A footnote body was run through eschtml() before being stored, so every
"<" became "<" before the inline parser could see it. An autolink --
<https://example.com> -- therefore came out as literal text inside a
footnote while resolving normally in prose and in citation bodies, which
already stored raw and let inline() do the escaping.
Store the body raw, like a citation. inline() escapes what is not markup,
so "&" and "<" in ordinary prose are still escaped; what changes is that
markup in a footnote is now parsed rather than blinded. That also makes
inline HTML behave in footnotes as it already does elsewhere, removing
the last place where a footnote body was parsed differently from prose.
The local was named "url" because the rule began life handling link
reference definitions, where a bare URL was the whole body.
diff --git a/share/jenny/lib/md2html.awk b/share/jenny/lib/md2html.awk
index 3fd54c9..b5f2dce 100755
--- a/share/jenny/lib/md2html.awk
+++ b/share/jenny/lib/md2html.awk
@@ -451,8 +451,13 @@ BEGIN {
id = substr($0, 1, RSTART - 1);
sub(id "\\]:[ ]+", "");
sub(/[ ]+".*"$/, "");
- url = eschtml($0);
- fnref[id] = url;
+ # Stored raw, like a citation's body: inline() escapes what needs it and
+ # resolves what does not. Pre-escaping turned every "<" into "<" before
+ # the parser could see it, so an autolink -- <https://example.com> -- came
+ # out as literal text in a footnote while working in prose and in
+ # citations. The variable was called "url" because this rule began life
+ # handling link-reference definitions, where a bare URL was all it held.
+ fnref[id] = $0;
subref(id);
next;
diff --git a/test/regressions.sh b/test/regressions.sh
index a098835..354b596 100755
--- a/test/regressions.sh
+++ b/test/regressions.sh
@@ -2521,3 +2521,42 @@ it "the build is clean"
assert_eq "$stderr_output" ""
summary
+
+suite "Footnote bodies go through the inline parser"
+
+# A footnote body must be parsed like prose and like a citation body. It used
+# to be HTML-escaped on the way in, which turned every "<" into "<" before
+# the parser could see it -- so an autolink rendered as literal text inside a
+# footnote while working everywhere else.
+fi="$TMPROOT/footnote-inline"
+make_blog_min "$fi" 1 10
+printf '# Inline\n\nProse <https://example.com/prose> here.\n\nOne[^a] two[^b] three[^c].\n\n[^a]: See <https://example.com/auto> for details.\n\n[^b]: Tom & Jerry, 5 < 6.\n\n[^c]: With *emphasis* and [a link](https://example.com/md).\n' > "$fi/2024-01-01 inline.md"
+run_jenny "$fi"
+fi_post="$fi/.dist/post/inline.html"
+fi_flat=$(tr '\n' ' ' < "$fi_post")
+fi_a=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-a".\{0,200\}' | head -1)
+fi_b=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-b".\{0,200\}' | head -1)
+fi_c=$(printf '%s' "$fi_flat" | "$GREP" -o 'id="fn-c".\{0,200\}' | head -1)
+
+it "an autolink inside a footnote becomes a link"
+# The behaviour the fix exists for.
+assert_contains "$fi_a" '<a href="https://example.com/auto">'
+
+it "an autolink inside a footnote leaves no literal angle bracket"
+# The old output was "<https://example.com/auto>" -- escaped, then never parsed.
+assert_not_contains "$fi_a" '<https://example.com/auto'
+
+it "an autolink in prose still becomes a link"
+# The footnote fix must not disturb the path that already worked.
+assert_contains "$fi_flat" '<a href="https://example.com/prose">'
+
+it "ampersand and less-than in a footnote are still escaped"
+# Storing the body raw does not mean skipping escaping: inline() escapes what
+# is not markup. Unescaped "&" or "<" here would be invalid HTML.
+assert_contains "$fi_b" 'Tom & Jerry, 5 < 6.'
+
+it "emphasis inside a footnote still renders"
+assert_contains "$fi_c" '<em>emphasis</em>'
+
+it "a markdown link inside a footnote still renders"
+assert_contains "$fi_c" '<a href="https://example.com/md">a link</a>'