From 9d8d7d8704083fea6cefe6e7a6863f9775807385 Mon Sep 17 00:00:00 2001 From: Gabriel Peal Date: Sun, 10 Aug 2025 21:32:56 -0700 Subject: [PATCH] Middle-truncate tool output and show more lines (#2096) Command output can contain important bits of information at the beginning or end. This shows a bit more output and truncates in the middle. This will work better paired with https://github.com/openai/codex/pull/2095 which will omit output for simple successful reads/searches/etc. CleanShot 2025-08-09 at 13 01 05 ------ https://chatgpt.com/codex/tasks/task_i_68978cd19f9c832cac4975e44dcd99a0 --- codex-rs/tui/src/history_cell.rs | 78 ++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 3658dc7e..43144a64 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -133,7 +133,7 @@ pub(crate) enum HistoryCell { PatchApplyResult { view: TextBlock }, } -const TOOL_CALL_MAX_LINES: usize = 3; +const TOOL_CALL_MAX_LINES: usize = 5; fn title_case(s: &str) -> String { if s.is_empty() { @@ -194,6 +194,48 @@ impl HistoryCell { .unwrap_or(0) } + fn output_lines(src: &str) -> Vec> { + let lines: Vec<&str> = src.lines().collect(); + let total = lines.len(); + let limit = TOOL_CALL_MAX_LINES; + + let mut out = Vec::new(); + + let head_end = total.min(limit); + for (i, raw) in lines[..head_end].iter().enumerate() { + let mut line = ansi_escape_line(raw); + let prefix = if i == 0 { " ⎿ " } else { " " }; + line.spans.insert(0, prefix.into()); + line.spans.iter_mut().for_each(|span| { + span.style = span.style.add_modifier(Modifier::DIM); + }); + out.push(line); + } + + // If we will ellipsize less than the limit, just show it. + let show_ellipsis = total > 2 * limit; + if show_ellipsis { + let omitted = total - 2 * limit; + out.push(Line::from(format!("… +{omitted} lines"))); + } + + let tail_start = if show_ellipsis { + total - limit + } else { + head_end + }; + for raw in lines[tail_start..].iter() { + let mut line = ansi_escape_line(raw); + line.spans.insert(0, " ".into()); + line.spans.iter_mut().for_each(|span| { + span.style = span.style.add_modifier(Modifier::DIM); + }); + out.push(line); + } + + out + } + pub(crate) fn new_session_info( config: &Config, event: SessionConfiguredEvent, @@ -308,27 +350,7 @@ impl HistoryCell { } let src = if exit_code == 0 { stdout } else { stderr }; - - let mut lines_iter = src.lines(); - for (idx, raw) in lines_iter.by_ref().take(TOOL_CALL_MAX_LINES).enumerate() { - let mut line = ansi_escape_line(raw); - let prefix = if idx == 0 { " ⎿ " } else { " " }; - line.spans.insert(0, prefix.into()); - line.spans.iter_mut().for_each(|span| { - span.style = span.style.add_modifier(Modifier::DIM); - }); - lines.push(line); - } - let remaining = lines_iter.count(); - if remaining > 0 { - let mut more = Line::from(format!("... +{remaining} lines")); - // Continuation/ellipsis is treated as a subsequent line for prefixing - more.spans.insert(0, " ".into()); - more.spans.iter_mut().for_each(|span| { - span.style = span.style.add_modifier(Modifier::DIM); - }); - lines.push(more); - } + lines.extend(Self::output_lines(&src)); lines.push(Line::from("")); HistoryCell::CompletedExecCommand { @@ -813,17 +835,7 @@ impl HistoryCell { lines.push(Line::from("✘ Failed to apply patch".magenta().bold())); if !stderr.trim().is_empty() { - let mut iter = stderr.lines(); - for (i, raw) in iter.by_ref().take(TOOL_CALL_MAX_LINES).enumerate() { - let prefix = if i == 0 { " ⎿ " } else { " " }; - let s = format!("{prefix}{raw}"); - lines.push(ansi_escape_line(&s).dim()); - } - let remaining = iter.count(); - if remaining > 0 { - lines.push(Line::from("")); - lines.push(Line::from(format!("... +{remaining} lines")).dim()); - } + lines.extend(Self::output_lines(&stderr)); } lines.push(Line::from(""));