tui: drop citation rendering (#4855)

We don't instruct the model to use citations, so it never emits them.
Further, ratatui [doesn't currently support rendering links into the
terminal with OSC 8](https://github.com/ratatui/ratatui/issues/1028), so
even if we did parse citations, we can't correctly render them.

So, remove all the code related to rendering them.
This commit is contained in:
Jeremy Rose
2025-10-20 14:08:19 -07:00
committed by GitHub
parent 9c903c4716
commit 39a2446716
11 changed files with 79 additions and 418 deletions

View File

@@ -1,4 +1,3 @@
use codex_core::config::Config;
use ratatui::text::Line;
use crate::markdown;
@@ -33,7 +32,7 @@ impl MarkdownStreamCollector {
/// Render the full buffer and return only the newly completed logical lines
/// since the last commit. When the buffer does not end with a newline, the
/// final rendered line is considered incomplete and is not emitted.
pub fn commit_complete_lines(&mut self, config: &Config) -> Vec<Line<'static>> {
pub fn commit_complete_lines(&mut self) -> Vec<Line<'static>> {
let source = self.buffer.clone();
let last_newline_idx = source.rfind('\n');
let source = if let Some(last_newline_idx) = last_newline_idx {
@@ -42,7 +41,7 @@ impl MarkdownStreamCollector {
return Vec::new();
};
let mut rendered: Vec<Line<'static>> = Vec::new();
markdown::append_markdown(&source, self.width, &mut rendered, config);
markdown::append_markdown(&source, self.width, &mut rendered);
let mut complete_line_count = rendered.len();
if complete_line_count > 0
&& crate::render::line_utils::is_blank_line_spaces_only(
@@ -67,7 +66,7 @@ impl MarkdownStreamCollector {
/// If the buffer does not end with a newline, a temporary one is appended
/// for rendering. Optionally unwraps ```markdown language fences in
/// non-test builds.
pub fn finalize_and_drain(&mut self, config: &Config) -> Vec<Line<'static>> {
pub fn finalize_and_drain(&mut self) -> Vec<Line<'static>> {
let raw_buffer = self.buffer.clone();
let mut source: String = raw_buffer.clone();
if !source.ends_with('\n') {
@@ -83,7 +82,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, self.width, &mut rendered, config);
markdown::append_markdown(&source, self.width, &mut rendered);
let out = if self.committed_line_count >= rendered.len() {
Vec::new()
@@ -101,18 +100,17 @@ impl MarkdownStreamCollector {
pub(crate) fn simulate_stream_markdown_for_tests(
deltas: &[&str],
finalize: bool,
config: &Config,
) -> Vec<Line<'static>> {
let mut collector = MarkdownStreamCollector::new(None);
let mut out = Vec::new();
for d in deltas {
collector.push_delta(d);
if d.contains('\n') {
out.extend(collector.commit_complete_lines(config));
out.extend(collector.commit_complete_lines());
}
}
if finalize {
out.extend(collector.finalize_and_drain(config));
out.extend(collector.finalize_and_drain());
}
out
}
@@ -120,45 +118,30 @@ pub(crate) fn simulate_stream_markdown_for_tests(
#[cfg(test)]
mod tests {
use super::*;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use ratatui::style::Color;
async fn test_config() -> Config {
let overrides = ConfigOverrides {
cwd: std::env::current_dir().ok(),
..Default::default()
};
Config::load_with_cli_overrides(vec![], overrides)
.await
.expect("load test config")
}
#[tokio::test]
async fn no_commit_until_newline() {
let cfg = test_config().await;
let mut c = super::MarkdownStreamCollector::new(None);
c.push_delta("Hello, world");
let out = c.commit_complete_lines(&cfg);
let out = c.commit_complete_lines();
assert!(out.is_empty(), "should not commit without newline");
c.push_delta("!\n");
let out2 = c.commit_complete_lines(&cfg);
let out2 = c.commit_complete_lines();
assert_eq!(out2.len(), 1, "one completed line after newline");
}
#[tokio::test]
async fn finalize_commits_partial_line() {
let cfg = test_config().await;
let mut c = super::MarkdownStreamCollector::new(None);
c.push_delta("Line without newline");
let out = c.finalize_and_drain(&cfg);
let out = c.finalize_and_drain();
assert_eq!(out.len(), 1);
}
#[tokio::test]
async fn e2e_stream_blockquote_simple_is_green() {
let cfg = test_config().await;
let out = super::simulate_stream_markdown_for_tests(&["> Hello\n"], true, &cfg);
let out = super::simulate_stream_markdown_for_tests(&["> Hello\n"], true);
assert_eq!(out.len(), 1);
let l = &out[0];
assert_eq!(
@@ -171,9 +154,7 @@ mod tests {
#[tokio::test]
async fn e2e_stream_blockquote_nested_is_green() {
let cfg = test_config().await;
let out =
super::simulate_stream_markdown_for_tests(&["> Level 1\n>> Level 2\n"], true, &cfg);
let out = super::simulate_stream_markdown_for_tests(&["> Level 1\n>> Level 2\n"], true);
// Filter out any blank lines that may be inserted at paragraph starts.
let non_blank: Vec<_> = out
.into_iter()
@@ -196,9 +177,7 @@ mod tests {
#[tokio::test]
async fn e2e_stream_blockquote_with_list_items_is_green() {
let cfg = test_config().await;
let out =
super::simulate_stream_markdown_for_tests(&["> - item 1\n> - item 2\n"], true, &cfg);
let out = super::simulate_stream_markdown_for_tests(&["> - item 1\n> - item 2\n"], true);
assert_eq!(out.len(), 2);
assert_eq!(out[0].style.fg, Some(Color::Green));
assert_eq!(out[1].style.fg, Some(Color::Green));
@@ -206,7 +185,6 @@ mod tests {
#[tokio::test]
async fn e2e_stream_nested_mixed_lists_ordered_marker_is_light_blue() {
let cfg = test_config().await;
let md = [
"1. First\n",
" - Second level\n",
@@ -214,7 +192,7 @@ mod tests {
" - Fourth level (bullet)\n",
" - Fifth level to test indent consistency\n",
];
let out = super::simulate_stream_markdown_for_tests(&md, true, &cfg);
let out = super::simulate_stream_markdown_for_tests(&md, true);
// Find the line that contains the third-level ordered text
let find_idx = out.iter().position(|l| {
l.spans
@@ -238,9 +216,8 @@ mod tests {
#[tokio::test]
async fn e2e_stream_blockquote_wrap_preserves_green_style() {
let cfg = test_config().await;
let long = "> This is a very long quoted line that should wrap across multiple columns to verify style preservation.";
let out = super::simulate_stream_markdown_for_tests(&[long, "\n"], true, &cfg);
let out = super::simulate_stream_markdown_for_tests(&[long, "\n"], true);
// Wrap to a narrow width to force multiple output lines.
let wrapped =
crate::wrapping::word_wrap_lines(out.iter(), crate::wrapping::RtOptions::new(24));
@@ -274,13 +251,11 @@ mod tests {
#[tokio::test]
async fn heading_starts_on_new_line_when_following_paragraph() {
let cfg = test_config().await;
// 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(None);
c.push_delta("Hello.\n");
let out1 = c.commit_complete_lines(&cfg);
let out1 = c.commit_complete_lines();
let s1: Vec<String> = out1
.iter()
.map(|l| {
@@ -300,7 +275,7 @@ mod tests {
);
c.push_delta("## Heading\n");
let out2 = c.commit_complete_lines(&cfg);
let out2 = c.commit_complete_lines();
let s2: Vec<String> = out2
.iter()
.map(|l| {
@@ -331,19 +306,17 @@ mod tests {
#[tokio::test]
async fn heading_not_inlined_when_split_across_chunks() {
let cfg = test_config().await;
// 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(None);
c.push_delta("Sounds good!");
// No commit yet
assert!(c.commit_complete_lines(&cfg).is_empty());
assert!(c.commit_complete_lines().is_empty());
// Introduce the newline that completes the paragraph and the start of the heading.
c.push_delta("\n## Adding Bird subcommand");
let out1 = c.commit_complete_lines(&cfg);
let out1 = c.commit_complete_lines();
let s1: Vec<String> = out1
.iter()
.map(|l| {
@@ -362,7 +335,7 @@ mod tests {
// Now finish the heading line with the trailing newline.
c.push_delta("\n");
let out2 = c.commit_complete_lines(&cfg);
let out2 = c.commit_complete_lines();
let s2: Vec<String> = out2
.iter()
.map(|l| {
@@ -381,7 +354,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", None, &mut rendered, &cfg);
crate::markdown::append_markdown("Hello.\n", None, &mut rendered);
let rendered_strings: Vec<String> = rendered
.iter()
.map(|l| {
@@ -423,8 +396,6 @@ mod tests {
#[tokio::test]
async fn utf8_boundary_safety_and_wide_chars() {
let cfg = test_config().await;
// Emoji (wide), CJK, control char, digit + combining macron sequences
let input = "🙂🙂🙂\n汉字漢字\nA\u{0003}0\u{0304}\n";
let deltas = vec![
@@ -439,11 +410,11 @@ mod tests {
"\n",
];
let streamed = simulate_stream_markdown_for_tests(&deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(&deltas, true);
let streamed_str = lines_to_plain_strings(&streamed);
let mut rendered_all: Vec<ratatui::text::Line<'static>> = Vec::new();
crate::markdown::append_markdown(input, None, &mut rendered_all, &cfg);
crate::markdown::append_markdown(input, None, &mut rendered_all);
let rendered_all_str = lines_to_plain_strings(&rendered_all);
assert_eq!(
@@ -454,9 +425,8 @@ mod tests {
#[tokio::test]
async fn e2e_stream_deep_nested_third_level_marker_is_light_blue() {
let cfg = test_config().await;
let md = "1. First\n - Second level\n 1. Third level (ordered)\n - Fourth level (bullet)\n - Fifth level to test indent consistency\n";
let streamed = super::simulate_stream_markdown_for_tests(&[md], true, &cfg);
let streamed = super::simulate_stream_markdown_for_tests(&[md], true);
let streamed_strs = lines_to_plain_strings(&streamed);
// Locate the third-level line in the streamed output; avoid relying on exact indent.
@@ -504,11 +474,10 @@ mod tests {
#[tokio::test]
async fn empty_fenced_block_is_dropped_and_separator_preserved_before_heading() {
let cfg = test_config().await;
// An empty fenced code block followed by a heading should not render the fence,
// but should preserve a blank separator line so the heading starts on a new line.
let deltas = vec!["```bash\n```\n", "## Heading\n"]; // empty block and close in same commit
let streamed = simulate_stream_markdown_for_tests(&deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(&deltas, true);
let texts = lines_to_plain_strings(&streamed);
assert!(
texts.iter().all(|s| !s.contains("```")),
@@ -523,9 +492,8 @@ mod tests {
#[tokio::test]
async fn paragraph_then_empty_fence_then_heading_keeps_heading_on_new_line() {
let cfg = test_config().await;
let deltas = vec!["Para.\n", "```\n```\n", "## Title\n"]; // empty fence block in one commit
let streamed = simulate_stream_markdown_for_tests(&deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(&deltas, true);
let texts = lines_to_plain_strings(&streamed);
let para_idx = match texts.iter().position(|s| s == "Para.") {
Some(i) => i,
@@ -543,17 +511,16 @@ mod tests {
#[tokio::test]
async fn loose_list_with_split_dashes_matches_full_render() {
let cfg = test_config().await;
// Minimized failing sequence discovered by the helper: two chunks
// that still reproduce the mismatch.
let deltas = vec!["- item.\n\n", "-"];
let streamed = simulate_stream_markdown_for_tests(&deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(&deltas, true);
let streamed_strs = lines_to_plain_strings(&streamed);
let full: String = deltas.iter().copied().collect();
let mut rendered_all: Vec<ratatui::text::Line<'static>> = Vec::new();
crate::markdown::append_markdown(&full, None, &mut rendered_all, &cfg);
crate::markdown::append_markdown(&full, None, &mut rendered_all);
let rendered_all_strs = lines_to_plain_strings(&rendered_all);
assert_eq!(
@@ -564,7 +531,6 @@ mod tests {
#[tokio::test]
async fn loose_vs_tight_list_items_streaming_matches_full() {
let cfg = test_config().await;
// Deltas extracted from the session log around 2025-08-27T00:33:18.216Z
let deltas = vec![
"\n\n",
@@ -636,13 +602,13 @@ mod tests {
"\n\n",
];
let streamed = simulate_stream_markdown_for_tests(&deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(&deltas, true);
let streamed_strs = lines_to_plain_strings(&streamed);
// 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, None, &mut rendered_all, &cfg);
crate::markdown::append_markdown(&full, None, &mut rendered_all);
// Also assert exact expected plain strings for clarity.
let expected = vec![
@@ -665,12 +631,11 @@ mod tests {
// Targeted tests derived from fuzz findings. Each asserts streamed == full render.
async fn assert_streamed_equals_full(deltas: &[&str]) {
let cfg = test_config().await;
let streamed = simulate_stream_markdown_for_tests(deltas, true, &cfg);
let streamed = simulate_stream_markdown_for_tests(deltas, true);
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, None, &mut rendered, &cfg);
crate::markdown::append_markdown(&full, None, &mut rendered);
let rendered_strs = lines_to_plain_strings(&rendered);
assert_eq!(streamed_strs, rendered_strs, "full:\n---\n{full}\n---");
}