Fix code fence closing without trailing newline
When a code block ended without a trailing newline after the closing \`\`\`, two bugs occurred in flush_incomplete(): 1. The closing \`\`\` was included as part of the code block content (displayed with syntax highlighting) 2. The same \`\`\` was then emitted again as literal text because current_line was not cleared after being pushed to block_buffer The fix: - Check if current_line is the closing fence before adding to block_buffer - Always clear current_line after processing in the CodeBlock case Added two tests: - test_code_fence_after_blank_line: code fence with trailing newline - test_code_fence_no_trailing_newline: code fence without trailing newline
This commit is contained in:
@@ -697,7 +697,16 @@ impl StreamingMarkdownFormatter {
|
|||||||
// Unclosed code block - emit as-is
|
// Unclosed code block - emit as-is
|
||||||
if !self.block_buffer.is_empty() || !self.current_line.is_empty() {
|
if !self.block_buffer.is_empty() || !self.current_line.is_empty() {
|
||||||
if !self.current_line.is_empty() {
|
if !self.current_line.is_empty() {
|
||||||
self.block_buffer.push(self.current_line.clone());
|
// Check if current_line is the closing fence (``` without trailing newline)
|
||||||
|
let trimmed = self.current_line.trim_start();
|
||||||
|
let leading_spaces = self.current_line.len() - trimmed.len();
|
||||||
|
if trimmed == "```" && leading_spaces <= 3 {
|
||||||
|
// This is the closing fence - don't include it in content
|
||||||
|
// Just clear it and emit the block
|
||||||
|
} else {
|
||||||
|
self.block_buffer.push(self.current_line.clone());
|
||||||
|
}
|
||||||
|
self.current_line.clear();
|
||||||
}
|
}
|
||||||
self.emit_code_block();
|
self.emit_code_block();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1879,3 +1879,49 @@ fn strip_ansi(s: &str) -> String {
|
|||||||
let re = regex::Regex::new(r"\x1b\[[0-9;]*m").unwrap();
|
let re = regex::Regex::new(r"\x1b\[[0-9;]*m").unwrap();
|
||||||
re.replace_all(s, "").to_string()
|
re.replace_all(s, "").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_fence_after_blank_line() {
|
||||||
|
let skin = MadSkin::default();
|
||||||
|
let mut fmt = StreamingMarkdownFormatter::new(skin);
|
||||||
|
|
||||||
|
// Simulate the exact input from the bug - text followed by blank line followed by code fence
|
||||||
|
let input = "Done! The agent mode header now looks like:\n\n```\n>> agent mode | fowler\n```\n";
|
||||||
|
|
||||||
|
// Process character by character like streaming would
|
||||||
|
let mut output = String::new();
|
||||||
|
for ch in input.chars() {
|
||||||
|
let chunk = fmt.process(&ch.to_string());
|
||||||
|
output.push_str(&chunk);
|
||||||
|
}
|
||||||
|
output.push_str(&fmt.finish());
|
||||||
|
|
||||||
|
println!("Input: {:?}", input);
|
||||||
|
println!("Output: {:?}", output);
|
||||||
|
|
||||||
|
// Check if backticks appear literally - they shouldn't
|
||||||
|
assert!(!output.contains("```"), "Literal backticks should not appear in output. Got: {}", output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_fence_no_trailing_newline() {
|
||||||
|
// Test code fence without trailing newline after closing ```
|
||||||
|
let skin = MadSkin::default();
|
||||||
|
let mut fmt = StreamingMarkdownFormatter::new(skin);
|
||||||
|
|
||||||
|
// Note: no newline after closing ```
|
||||||
|
let input = "Done!\n\n```\n>> agent mode | fowler\n-> ~/src/g3\n ✓ README | ✓ AGENTS.md | ✓ Memory\n```";
|
||||||
|
|
||||||
|
let mut output = String::new();
|
||||||
|
for ch in input.chars() {
|
||||||
|
let chunk = fmt.process(&ch.to_string());
|
||||||
|
output.push_str(&chunk);
|
||||||
|
}
|
||||||
|
output.push_str(&fmt.finish());
|
||||||
|
|
||||||
|
println!("Input: {:?}", input);
|
||||||
|
println!("Output: {:?}", output);
|
||||||
|
|
||||||
|
// The closing ``` should NOT appear literally
|
||||||
|
assert!(!output.contains("```"), "Literal backticks in output: {}", output);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user