Format multiline commands (#2333)

<img width="966" height="729" alt="image"
src="https://github.com/user-attachments/assets/fa45b7e1-cd46-427f-b2bc-8501e9e4760b"
/>
<img width="797" height="530" alt="image"
src="https://github.com/user-attachments/assets/6993eec5-e157-4df7-b558-15643ad10d64"
/>
This commit is contained in:
pakrym-oai
2025-08-14 19:49:42 -07:00
committed by GitHub
parent 76df07350a
commit 5552688621
3 changed files with 75 additions and 63 deletions

View File

@@ -43,7 +43,6 @@ use crate::bottom_pane::BottomPane;
use crate::bottom_pane::BottomPaneParams;
use crate::bottom_pane::CancellationEvent;
use crate::bottom_pane::InputResult;
use crate::exec_command::strip_bash_lc_and_escape;
use crate::history_cell;
use crate::history_cell::CommandOutput;
use crate::history_cell::ExecCell;
@@ -391,17 +390,6 @@ impl ChatWidget<'_> {
pub(crate) fn handle_exec_approval_now(&mut self, id: String, ev: ExecApprovalRequestEvent) {
self.flush_answer_stream_with_separator();
// Log a background summary immediately so the history is chronological.
let cmdline = strip_bash_lc_and_escape(&ev.command);
let text = format!(
"command requires approval:\n$ {cmdline}{reason}",
reason = ev
.reason
.as_ref()
.map(|r| format!("\n{r}"))
.unwrap_or_default()
);
self.add_to_history(&history_cell::new_background_event(text));
let request = ApprovalRequest::Exec {
id,

View File

@@ -136,14 +136,6 @@ fn pretty_provider_name(id: &str) -> String {
}
}
pub(crate) fn new_background_event(message: String) -> PlainHistoryCell {
let mut lines: Vec<Line<'static>> = Vec::new();
lines.push(Line::from("event".dim()));
lines.extend(message.lines().map(|line| ansi_escape_line(line).dim()));
lines.push(Line::from(""));
PlainHistoryCell { lines }
}
pub(crate) fn new_session_info(
config: &Config,
event: SessionConfiguredEvent,

View File

@@ -109,6 +109,34 @@ pub(crate) struct UserApprovalWidget<'a> {
done: bool,
}
fn to_command_display<'a>(
first_line: Vec<Span<'a>>,
cmd: String,
last_line: Vec<Span<'a>>,
) -> Vec<Line<'a>> {
let command_lines: Vec<Span> = cmd
.lines()
.map(|line| Span::from(line.to_string()).style(Style::new().add_modifier(Modifier::DIM)))
.collect();
let mut lines: Vec<Line<'a>> = vec![];
let mut first_line = first_line.clone();
if command_lines.len() == 1 {
first_line.push(command_lines[0].clone());
first_line.extend(last_line);
} else {
for line in command_lines {
lines.push(Line::from(vec![Span::from(" "), line]));
}
let last_line = last_line.clone();
lines.push(Line::from(last_line));
}
lines.insert(0, Line::from(first_line));
lines
}
impl UserApprovalWidget<'_> {
pub(crate) fn new(approval_request: ApprovalRequest, app_event_tx: AppEventSender) -> Self {
let confirmation_prompt = match &approval_request {
@@ -116,17 +144,13 @@ impl UserApprovalWidget<'_> {
command, reason, ..
} => {
let cmd = strip_bash_lc_and_escape(command);
// Present a single-line summary without cwd: "codex wants to run: <cmd>"
let mut cmd_span: Span = cmd.clone().into();
cmd_span.style = cmd_span.style.add_modifier(Modifier::DIM);
let mut contents: Vec<Line> = vec![
Line::from(vec![
"? ".fg(Color::Blue),
"Codex wants to run ".bold(),
cmd_span,
]),
Line::from(""),
];
let mut contents: Vec<Line> = to_command_display(
vec!["? ".fg(Color::Blue), "Codex wants to run ".bold()],
cmd,
vec![],
);
contents.push(Line::from(""));
if let Some(reason) = reason {
contents.push(Line::from(reason.clone().italic()));
contents.push(Line::from(""));
@@ -244,44 +268,52 @@ impl UserApprovalWidget<'_> {
// Result line based on decision.
match decision {
ReviewDecision::Approved => {
lines.push(Line::from(vec![
"".fg(Color::Green),
"You ".into(),
"approved".bold(),
" codex to run ".into(),
cmd_span,
" ".into(),
"this time".bold(),
]));
lines.extend(to_command_display(
vec![
" ".fg(Color::Green),
"You ".into(),
"approved".bold(),
" codex to run ".into(),
],
cmd,
vec![" this time".bold()],
));
}
ReviewDecision::ApprovedForSession => {
lines.push(Line::from(vec![
"".fg(Color::Green),
"You ".into(),
"approved".bold(),
" codex to run ".into(),
cmd_span,
" ".into(),
"every time this session".bold(),
]));
lines.extend(to_command_display(
vec![
" ".fg(Color::Green),
"You ".into(),
"approved".bold(),
"codex to run ".into(),
],
cmd,
vec![" every time this session".bold()],
));
}
ReviewDecision::Denied => {
lines.push(Line::from(vec![
"".fg(Color::Red),
"You ".into(),
"did not approve".bold(),
" codex to run ".into(),
cmd_span,
]));
lines.extend(to_command_display(
vec![
" ".fg(Color::Red),
"You ".into(),
"did not approve".bold(),
" codex to run ".into(),
],
cmd,
vec![],
));
}
ReviewDecision::Abort => {
lines.push(Line::from(vec![
"".fg(Color::Red),
"You ".into(),
"canceled".bold(),
" the request to run ".into(),
cmd_span,
]));
lines.extend(to_command_display(
vec![
" ".fg(Color::Red),
"You ".into(),
"canceled".bold(),
" the request to run ".into(),
],
cmd,
vec![],
));
}
}
}