| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-04-12 00:33:34 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-04-12 00:33:34 UTC |
| parent | 371077331af25ae70ce5d9764b78ec6031ad7d3f |
| borogove/XEP0393.hx | +29 | -6 |
| test/TestXEP0393.hx | +15 | -3 |
diff --git a/borogove/XEP0393.hx b/borogove/XEP0393.hx index 4ad6c7a..b97c7cf 100644 --- a/borogove/XEP0393.hx +++ b/borogove/XEP0393.hx @@ -2,6 +2,7 @@ package borogove; import borogove.Autolink; import borogove.Stanza; +using StringTools; using borogove.Util; class XEP0393 { @@ -27,7 +28,15 @@ class XEP0393 { final s = new StringBuf(); if (xhtml.name == "pre") { - s.add("\n```\n"); + final code = xhtml.getChild("code"); + var lang = ""; + if (code != null) { + final className = code.attr.get("class") ?? ""; + if (className.startsWith("language-")) { + lang = className.substr(9); + } + } + s.add("\n```" + lang + "\n"); } if (xhtml.name == "b" || xhtml.name == "strong") { @@ -221,30 +230,44 @@ class XEP0393 { public static function parsePreformatted(styled: UnicodeString) { final lines = []; - var line = null; + var line = ""; + var lang = null; var end = 0; final styledLength = styled.length; while (end < styledLength) { while (end < styledLength && styled.charAt(end) != "\n") { - if (line != null) line += styled.charAt(end); + line += styled.charAt(end); end++; } if (end < styledLength && styled.charAt(end) == "\n") { end++; } - if (line != null) lines.push(line+"\n"); + + if (lang == null) { + lang = line.substr(3).trim(); + } else { + lines.push(line + "\n"); + } line = ""; + if (styled.substr(end, 4) == "```\n" || styled.substr(end) == "```") { end += 4; break; } } - return { block: new Stanza("pre").text(lines.join("")), rest: styled.substr(end) }; + final block = new Stanza("pre"); + if (lang != "") { + block.tag("code", {"class": 'language-$lang'}).text(lines.join("")); + } else { + block.text(lines.join("")); + } + + return { block: block, rest: styled.substr(end) }; } private static function isSpace(s: UnicodeString, pos: Int) { // The version in StringTools won't use UnicodeString-aware indices - return StringTools.isSpace(s.charAt(pos), 0); + return s.charAt(pos).isSpace(0); } } diff --git a/test/TestXEP0393.hx b/test/TestXEP0393.hx index 6698278..4e6df20 100644 --- a/test/TestXEP0393.hx +++ b/test/TestXEP0393.hx @@ -20,10 +20,22 @@ as spans* may not escape blocks.") ); } - public function testPreformattedBlock() { + public function testPreformattedBlockSimple() { Assert.equals( "<pre>(println \"Hello, world!\") </pre><div/><div>This should show up as monospace, preformatted text ⤴</div>", + toHtml("``` +(println \"Hello, world!\") +``` + +This should show up as monospace, preformatted text ⤴") + ); + } + + public function testPreformattedBlock() { + Assert.equals( + "<pre><code class=\"language-ignored\">(println \"Hello, world!\") +</code></pre><div/><div>This should show up as monospace, preformatted text ⤴</div>", toHtml("```ignored (println \"Hello, world!\") ``` @@ -34,8 +46,8 @@ This should show up as monospace, preformatted text ⤴") public function testPreformattedBlockUnterminated() { Assert.equals( - "<blockquote><pre>(println \"Hello, world!\") -</pre></blockquote><div/><div>The entire blockquote is a preformatted text block, but this line</div><div>is plaintext!</div>", + "<blockquote><pre><code class=\"language-ignored\">(println \"Hello, world!\") +</code></pre></blockquote><div/><div>The entire blockquote is a preformatted text block, but this line</div><div>is plaintext!</div>", toHtml("> ```ignored > (println \"Hello, world!\")