From 444e47c3c0866a614052a7ba6ff10421bfd088bc Mon Sep 17 00:00:00 2001
From: youdie006
Date: Wed, 19 Aug 2026 14:14:34 +0900
Subject: [PATCH] Close fenced div on CRLF input
With CRLF line endings, a fenced div's closing ::: fence is not detected, so the
div never closes and all following content is swallowed inside it. In the
fenced_div continue callback (src/block.ts), the parser sets this.pos = m.endpos;
the fence pattern (::::*)[ \t]*\r?\n matches through the \n, so on CRLF input
m.endpos lands on the \n, one past the \r that getEol() records as starteol. The
main loop then evaluates isBlank = (pos === starteol) as false, mistakes the
fence line for a lazy paragraph continuation, and never closes the div. On LF the
two positions coincide so it works by coincidence.
Set this.pos = this.starteol, the position just before the line ending, correct
for both LF and CRLF, mirroring the sibling code_block close path. LF output is
byte-identical.
Fixes #113.
---
src/block.ts | 2 +-
src/html.spec.ts | 15 +++++++++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/block.ts b/src/block.ts
index 7df45e4..7ffb864 100644
--- a/src/block.ts
+++ b/src/block.ts
@@ -642,7 +642,7 @@ class EventParser {
if (colons.length >= container.extra.colons) {
container.extra.endFenceStartpos = m.startpos;
container.extra.endFenceEndpos = m.startpos + colons.length - 1;
- this.pos = m.endpos; // before newline
+ this.pos = this.starteol; // before newline (CRLF-safe, see #113)
return false;
}
}
diff --git a/src/html.spec.ts b/src/html.spec.ts
index 2ea00ba..e83a775 100644
--- a/src/html.spec.ts
+++ b/src/html.spec.ts
@@ -36,4 +36,19 @@ rendering the light markup format djot.
);
});
+ it("closes a fenced div with CRLF line endings", () => {
+ // Regression test for issue #113: with CRLF line endings the closing
+ // ::: fence must close the div, so following content ("after") lands
+ // outside the div rather than being swallowed inside an unclosed div.
+ const expected =
+`
+after
+`;
+ expect(renderHTML(parse(":::\r\nhello\r\n:::\r\nafter\r\n"))).toEqual(expected);
+ // The LF equivalent is unchanged and produces byte-identical HTML.
+ expect(renderHTML(parse(":::\nhello\n:::\nafter\n"))).toEqual(expected);
+ });
+
});