fix(tui): preserve trailing blank lines in fenced code blocks

trim_end_matches('\n') stripped all trailing newlines from the code
block buffer before highlighting, discarding intentional blank lines
authored inside fences. pulldown-cmark appends exactly one trailing
'\n' to the text content, and LinesWithEndings handles it correctly
as a line terminator, so no trimming is needed. Remove the trim to
preserve user-authored blank lines faithfully.
This commit is contained in:
Felipe Coury
2026-02-08 23:11:45 -03:00
parent 82fc47e317
commit f00f7d49dc
2 changed files with 34 additions and 3 deletions

View File

@@ -434,10 +434,8 @@ where
// If we buffered code for a known language, syntax-highlight it now.
if let Some(lang) = self.code_block_lang.take() {
let code = std::mem::take(&mut self.code_block_buffer);
// Trim trailing newline to avoid a spurious empty line.
let code = code.trim_end_matches('\n');
if !code.is_empty() {
let highlighted = highlight_code_to_lines(code, &lang);
let highlighted = highlight_code_to_lines(&code, &lang);
for hl_line in highlighted {
self.push_line(Line::default());
for span in hl_line.spans {

View File

@@ -1063,3 +1063,36 @@ fn nested_item_continuation_paragraph_is_indented() {
]);
assert_eq!(text, expected);
}
#[test]
fn code_block_preserves_trailing_blank_lines() {
// A fenced code block with an intentional trailing blank line must keep it.
let md = "```rust\nfn main() {}\n\n```\n";
let text = render_markdown_text(md);
let content: Vec<String> = text
.lines
.iter()
.map(|l| {
l.spans
.iter()
.map(|s| s.content.clone())
.collect::<String>()
})
.collect();
// Should have: "fn main() {}" then "" (the blank line).
// Filter only to content lines (skip leading/trailing empty from rendering).
assert!(
content.iter().any(|c| c == "fn main() {}"),
"expected code line, got {content:?}"
);
// The trailing blank line inside the fence should be preserved.
let code_start = content.iter().position(|c| c == "fn main() {}").unwrap();
assert!(
content.len() > code_start + 1,
"expected a line after 'fn main() {{}}' but content ends: {content:?}"
);
assert_eq!(
content[code_start + 1], "",
"trailing blank line inside code fence was lost: {content:?}"
);
}