wrap markdown at render time (#4506)
This results in correctly indenting list items with long lines. <img width="1006" height="251" alt="Screenshot 2025-09-30 at 10 00 48 AM" src="https://github.com/user-attachments/assets/0a076cf6-ca3c-4efb-b3af-dc07617cdb6f" />
This commit is contained in:
@@ -8,13 +8,15 @@ use crate::markdown;
|
||||
pub(crate) struct MarkdownStreamCollector {
|
||||
buffer: String,
|
||||
committed_line_count: usize,
|
||||
width: Option<usize>,
|
||||
}
|
||||
|
||||
impl MarkdownStreamCollector {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(width: Option<usize>) -> Self {
|
||||
Self {
|
||||
buffer: String::new(),
|
||||
committed_line_count: 0,
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +42,7 @@ impl MarkdownStreamCollector {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut rendered: Vec<Line<'static>> = Vec::new();
|
||||
markdown::append_markdown(&source, &mut rendered, config);
|
||||
markdown::append_markdown(&source, self.width, &mut rendered, config);
|
||||
let mut complete_line_count = rendered.len();
|
||||
if complete_line_count > 0
|
||||
&& crate::render::line_utils::is_blank_line_spaces_only(
|
||||
@@ -81,7 +83,7 @@ impl MarkdownStreamCollector {
|
||||
tracing::trace!("markdown finalize (raw source):\n---\n{source}\n---");
|
||||
|
||||
let mut rendered: Vec<Line<'static>> = Vec::new();
|
||||
markdown::append_markdown(&source, &mut rendered, config);
|
||||
markdown::append_markdown(&source, self.width, &mut rendered, config);
|
||||
|
||||
let out = if self.committed_line_count >= rendered.len() {
|
||||
Vec::new()
|
||||
@@ -101,7 +103,7 @@ pub(crate) fn simulate_stream_markdown_for_tests(
|
||||
finalize: bool,
|
||||
config: &Config,
|
||||
) -> Vec<Line<'static>> {
|
||||
let mut collector = MarkdownStreamCollector::new();
|
||||
let mut collector = MarkdownStreamCollector::new(None);
|
||||
let mut out = Vec::new();
|
||||
for d in deltas {
|
||||
collector.push_delta(d);
|
||||
@@ -136,7 +138,7 @@ mod tests {
|
||||
#[test]
|
||||
fn no_commit_until_newline() {
|
||||
let cfg = test_config();
|
||||
let mut c = super::MarkdownStreamCollector::new();
|
||||
let mut c = super::MarkdownStreamCollector::new(None);
|
||||
c.push_delta("Hello, world");
|
||||
let out = c.commit_complete_lines(&cfg);
|
||||
assert!(out.is_empty(), "should not commit without newline");
|
||||
@@ -148,7 +150,7 @@ mod tests {
|
||||
#[test]
|
||||
fn finalize_commits_partial_line() {
|
||||
let cfg = test_config();
|
||||
let mut c = super::MarkdownStreamCollector::new();
|
||||
let mut c = super::MarkdownStreamCollector::new(None);
|
||||
c.push_delta("Line without newline");
|
||||
let out = c.finalize_and_drain(&cfg);
|
||||
assert_eq!(out.len(), 1);
|
||||
@@ -277,7 +279,7 @@ mod tests {
|
||||
|
||||
// Stream a paragraph line, then a heading on the next line.
|
||||
// Expect two distinct rendered lines: "Hello." and "Heading".
|
||||
let mut c = super::MarkdownStreamCollector::new();
|
||||
let mut c = super::MarkdownStreamCollector::new(None);
|
||||
c.push_delta("Hello.\n");
|
||||
let out1 = c.commit_complete_lines(&cfg);
|
||||
let s1: Vec<String> = out1
|
||||
@@ -335,7 +337,7 @@ mod tests {
|
||||
// Paragraph without trailing newline, then a chunk that starts with the newline
|
||||
// and the heading text, then a final newline. The collector should first commit
|
||||
// only the paragraph line, and later commit the heading as its own line.
|
||||
let mut c = super::MarkdownStreamCollector::new();
|
||||
let mut c = super::MarkdownStreamCollector::new(None);
|
||||
c.push_delta("Sounds good!");
|
||||
// No commit yet
|
||||
assert!(c.commit_complete_lines(&cfg).is_empty());
|
||||
@@ -380,7 +382,7 @@ mod tests {
|
||||
|
||||
// Sanity check raw markdown rendering for a simple line does not produce spurious extras.
|
||||
let mut rendered: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
crate::markdown::append_markdown("Hello.\n", &mut rendered, &cfg);
|
||||
crate::markdown::append_markdown("Hello.\n", None, &mut rendered, &cfg);
|
||||
let rendered_strings: Vec<String> = rendered
|
||||
.iter()
|
||||
.map(|l| {
|
||||
@@ -442,7 +444,7 @@ mod tests {
|
||||
let streamed_str = lines_to_plain_strings(&streamed);
|
||||
|
||||
let mut rendered_all: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
crate::markdown::append_markdown(input, &mut rendered_all, &cfg);
|
||||
crate::markdown::append_markdown(input, None, &mut rendered_all, &cfg);
|
||||
let rendered_all_str = lines_to_plain_strings(&rendered_all);
|
||||
|
||||
assert_eq!(
|
||||
@@ -552,7 +554,7 @@ mod tests {
|
||||
|
||||
let full: String = deltas.iter().copied().collect();
|
||||
let mut rendered_all: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
crate::markdown::append_markdown(&full, &mut rendered_all, &cfg);
|
||||
crate::markdown::append_markdown(&full, None, &mut rendered_all, &cfg);
|
||||
let rendered_all_strs = lines_to_plain_strings(&rendered_all);
|
||||
|
||||
assert_eq!(
|
||||
@@ -641,7 +643,7 @@ mod tests {
|
||||
// Compute a full render for diagnostics only.
|
||||
let full: String = deltas.iter().copied().collect();
|
||||
let mut rendered_all: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
crate::markdown::append_markdown(&full, &mut rendered_all, &cfg);
|
||||
crate::markdown::append_markdown(&full, None, &mut rendered_all, &cfg);
|
||||
|
||||
// Also assert exact expected plain strings for clarity.
|
||||
let expected = vec![
|
||||
@@ -669,7 +671,7 @@ mod tests {
|
||||
let streamed_strs = lines_to_plain_strings(&streamed);
|
||||
let full: String = deltas.iter().copied().collect();
|
||||
let mut rendered: Vec<ratatui::text::Line<'static>> = Vec::new();
|
||||
crate::markdown::append_markdown(&full, &mut rendered, &cfg);
|
||||
crate::markdown::append_markdown(&full, None, &mut rendered, &cfg);
|
||||
let rendered_strs = lines_to_plain_strings(&rendered);
|
||||
assert_eq!(streamed_strs, rendered_strs, "full:\n---\n{full}\n---");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user