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:
Jeremy Rose
2025-09-30 16:13:55 -07:00
committed by GitHub
parent 9c259737d3
commit 01e6503672
8 changed files with 347 additions and 103 deletions

View File

@@ -2,17 +2,47 @@ use codex_core::config::Config;
use codex_core::config_types::UriBasedFileOpener;
use ratatui::text::Line;
use std::path::Path;
use std::path::PathBuf;
pub(crate) fn append_markdown(
markdown_source: &str,
lines: &mut Vec<Line<'static>>,
config: &Config,
) {
append_markdown_with_opener_and_cwd(markdown_source, lines, config.file_opener, &config.cwd);
#[derive(Clone, Debug)]
pub struct MarkdownCitationContext {
file_opener: UriBasedFileOpener,
cwd: PathBuf,
}
fn append_markdown_with_opener_and_cwd(
impl MarkdownCitationContext {
pub(crate) fn new(file_opener: UriBasedFileOpener, cwd: PathBuf) -> Self {
Self { file_opener, cwd }
}
}
impl From<&Config> for MarkdownCitationContext {
fn from(config: &Config) -> Self {
MarkdownCitationContext::new(config.file_opener, config.cwd.clone())
}
}
pub(crate) fn append_markdown<C>(
markdown_source: &str,
width: Option<usize>,
lines: &mut Vec<Line<'static>>,
citation_context: C,
) where
C: Into<MarkdownCitationContext>,
{
let citation_context: MarkdownCitationContext = citation_context.into();
append_markdown_with_opener_and_cwd(
markdown_source,
width,
lines,
citation_context.file_opener,
&citation_context.cwd,
);
}
pub(crate) fn append_markdown_with_opener_and_cwd(
markdown_source: &str,
width: Option<usize>,
lines: &mut Vec<Line<'static>>,
file_opener: UriBasedFileOpener,
cwd: &Path,
@@ -20,6 +50,7 @@ fn append_markdown_with_opener_and_cwd(
// Render via pulldown-cmark and rewrite citations during traversal (outside code blocks).
let rendered = crate::markdown_render::render_markdown_text_with_citations(
markdown_source,
width,
file_opener.get_scheme(),
cwd,
);
@@ -36,7 +67,7 @@ mod tests {
let src = "Before 【F:/x.rs†L1】\n```\nInside 【F:/x.rs†L2】\n```\nAfter 【F:/x.rs†L3】\n";
let cwd = Path::new("/");
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(src, &mut out, UriBasedFileOpener::VsCode, cwd);
append_markdown_with_opener_and_cwd(src, None, &mut out, UriBasedFileOpener::VsCode, cwd);
let rendered: Vec<String> = out
.iter()
.map(|l| {
@@ -69,7 +100,7 @@ mod tests {
let src = "Before\n\n code 1\n\nAfter\n";
let cwd = Path::new("/");
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(src, &mut out, UriBasedFileOpener::None, cwd);
append_markdown_with_opener_and_cwd(src, None, &mut out, UriBasedFileOpener::None, cwd);
let lines: Vec<String> = out
.iter()
.map(|l| {
@@ -87,7 +118,7 @@ mod tests {
let src = "Start 【F:/x.rs†L1】\n\n Inside 【F:/x.rs†L2】\n\nEnd 【F:/x.rs†L3】\n";
let cwd = Path::new("/");
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(src, &mut out, UriBasedFileOpener::VsCode, cwd);
append_markdown_with_opener_and_cwd(src, None, &mut out, UriBasedFileOpener::VsCode, cwd);
let rendered: Vec<String> = out
.iter()
.map(|l| {
@@ -117,7 +148,7 @@ mod tests {
let src = "Hi! How can I help with codex-rs today? Want me to explore the repo, run tests, or work on a specific change?\n";
let cwd = Path::new("/");
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(src, &mut out, UriBasedFileOpener::None, cwd);
append_markdown_with_opener_and_cwd(src, None, &mut out, UriBasedFileOpener::None, cwd);
assert_eq!(
out.len(),
1,
@@ -143,6 +174,7 @@ mod tests {
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(
"1. Tight item\n",
None,
&mut out,
UriBasedFileOpener::None,
cwd,
@@ -166,7 +198,7 @@ mod tests {
let src = "Loose vs. tight list items:\n1. Tight item\n";
let cwd = Path::new("/");
let mut out = Vec::new();
append_markdown_with_opener_and_cwd(src, &mut out, UriBasedFileOpener::None, cwd);
append_markdown_with_opener_and_cwd(src, None, &mut out, UriBasedFileOpener::None, cwd);
let lines: Vec<String> = out
.iter()