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.

<img width="1262" height="496" alt="CleanShot 2025-08-09 at 13 01 05"
src="https://github.com/user-attachments/assets/9d989eb6-f81e-4118-9745-d20728eeef71"
/>


------
https://chatgpt.com/codex/tasks/task_i_68978cd19f9c832cac4975e44dcd99a0
This commit is contained in:
Gabriel Peal
2025-08-10 21:32:56 -07:00
committed by GitHub
parent f146981b73
commit 9d8d7d8704

View File

@@ -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<Line<'static>> {
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(""));