Trim trailing whitespace in pre blocks in Markdown

This commit is contained in:
Adrià Arrufat
2026-02-16 21:34:46 +09:00
parent b49b2af11f
commit 3c14dbe382

View File

@@ -36,24 +36,25 @@ const State = struct {
list_depth: usize = 0, list_depth: usize = 0,
list_stack: [32]ListState = undefined, list_stack: [32]ListState = undefined,
in_pre: bool = false, in_pre: bool = false,
pre_node: ?*Node = null,
in_code: bool = false, in_code: bool = false,
in_blockquote: bool = false, in_blockquote: bool = false,
in_table: bool = false, in_table: bool = false,
table_row_index: usize = 0, table_row_index: usize = 0,
table_col_count: usize = 0, table_col_count: usize = 0,
last_char_was_newline: bool = true, last_char_was_newline: bool = true,
}; };
pub fn dump(node: *Node, opts: Opts, writer: *std.Io.Writer, page: *Page) !void { pub fn dump(node: *Node, opts: Opts, writer: *std.Io.Writer, page: *Page) !void {
_ = opts; _ = opts;
var state = State{}; var state = State{};
try render(node, &state, writer, page); try render(node, &state, writer, page);
if (!state.last_char_was_newline) { if (!state.last_char_was_newline) {
try writer.writeByte('\n'); try writer.writeByte('\n');
} }
} }
fn render(node: *Node, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void { fn render(node: *Node, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void {
switch (node._type) { switch (node._type) {
.document, .document_fragment => { .document, .document_fragment => {
try renderChildren(node, state, writer, page); try renderChildren(node, state, writer, page);
@@ -63,21 +64,29 @@ const State = struct {
}, },
.cdata => |cd| { .cdata => |cd| {
if (node.is(Node.CData.Text)) |_| { if (node.is(Node.CData.Text)) |_| {
try renderText(cd.getData(), state, writer); var text = cd.getData();
if (state.in_pre) {
if (state.pre_node) |pre| {
if (node.parentNode() == pre and node.nextSibling() == null) {
text = std.mem.trimRight(u8, text, " \t\r\n");
}
}
}
try renderText(text, state, writer);
} }
}, },
else => {}, // Ignore other node types else => {}, // Ignore other node types
} }
} }
fn renderChildren(parent: *Node, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void { fn renderChildren(parent: *Node, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void {
var it = parent.childrenIterator(); var it = parent.childrenIterator();
while (it.next()) |child| { while (it.next()) |child| {
try render(child, state, writer, page); try render(child, state, writer, page);
} }
} }
fn renderElement(el: *Element, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void { fn renderElement(el: *Element, state: *State, writer: *std.Io.Writer, page: *Page) anyerror!void {
const tag = el.getTag(); const tag = el.getTag();
// Skip hidden/metadata elements // Skip hidden/metadata elements
@@ -163,13 +172,15 @@ const State = struct {
// Add spacing // Add spacing
try writer.writeByte(' '); try writer.writeByte(' ');
}, },
.blockquote => { try writer.writeAll("> "); .blockquote => {
try writer.writeAll("> ");
state.in_blockquote = true; state.in_blockquote = true;
state.last_char_was_newline = false; state.last_char_was_newline = false;
}, },
.pre => { .pre => {
try writer.writeAll("```\n"); try writer.writeAll("```\n");
state.in_pre = true; state.in_pre = true;
state.pre_node = el.asNode();
state.last_char_was_newline = true; state.last_char_was_newline = true;
}, },
.code => { .code => {
@@ -259,6 +270,7 @@ const State = struct {
} }
try writer.writeAll("```\n"); try writer.writeAll("```\n");
state.in_pre = false; state.in_pre = false;
state.pre_node = null;
state.last_char_was_newline = true; state.last_char_was_newline = true;
}, },
.code => { .code => {