Fix streaming markdown code fence detection bug
The code fence (```) was not being properly detected during streaming, causing it to be rendered as inline code instead of a code block. Root cause: When buffering a code fence after seeing ```, the code was returning early for ALL characters including newlines. This meant handle_newline() was never called and block_state was never set to BlockState::CodeBlock. Fixes: - Don't return early for newlines when buffering code fence, allow them to fall through to handle_newline() - Support indented code fences (up to 3 spaces per CommonMark spec) by using trim_start() when checking for ``` at line start
This commit is contained in:
@@ -197,13 +197,25 @@ impl StreamingMarkdownFormatter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're already buffering a code fence (```), continue buffering until newline
|
||||||
|
// This handles the language identifier after ``` (e.g., ```rust)
|
||||||
|
let trimmed = self.current_line.trim_start();
|
||||||
|
if trimmed.starts_with("```") && ch != '\n' {
|
||||||
|
// Continue buffering non-newline characters
|
||||||
|
self.current_line.push(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If ch == '\n', fall through to the newline handler below
|
||||||
|
|
||||||
if ch == '`' {
|
if ch == '`' {
|
||||||
self.current_line.push(ch);
|
self.current_line.push(ch);
|
||||||
// Check if this might be starting a code fence
|
// Check if this might be starting a code fence
|
||||||
if self.current_line.starts_with("```") {
|
let trimmed = self.current_line.trim_start();
|
||||||
|
if trimmed.starts_with("```") {
|
||||||
// Don't emit yet - wait for the full fence line
|
// Don't emit yet - wait for the full fence line
|
||||||
} else if self.current_line == "`" || self.current_line == "``" {
|
} else if trimmed == "`" || trimmed == "``" {
|
||||||
// Might become a fence, keep buffering
|
// Might become a fence, keep buffering
|
||||||
|
// (current_line may have leading whitespace)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if ch == '>' && self.current_line.is_empty() {
|
} else if ch == '>' && self.current_line.is_empty() {
|
||||||
@@ -399,8 +411,11 @@ impl StreamingMarkdownFormatter {
|
|||||||
/// Handle a newline character.
|
/// Handle a newline character.
|
||||||
fn handle_newline(&mut self) {
|
fn handle_newline(&mut self) {
|
||||||
// Check if we were building a code fence
|
// Check if we were building a code fence
|
||||||
if self.current_line.starts_with("```") {
|
// Support indented code fences (up to 3 spaces per CommonMark spec)
|
||||||
let lang = self.current_line[3..].trim().to_string();
|
let trimmed = self.current_line.trim_start();
|
||||||
|
let leading_spaces = self.current_line.len() - trimmed.len();
|
||||||
|
if trimmed.starts_with("```") && leading_spaces <= 3 {
|
||||||
|
let lang = trimmed[3..].trim().to_string();
|
||||||
let lang = if lang.is_empty() { None } else { Some(lang) };
|
let lang = if lang.is_empty() { None } else { Some(lang) };
|
||||||
self.block_state = BlockState::CodeBlock {
|
self.block_state = BlockState::CodeBlock {
|
||||||
lang,
|
lang,
|
||||||
|
|||||||
Reference in New Issue
Block a user