feat(jobs): add cancel-all shell job action
Harvested from PR #1536 by @jieshu666. Co-authored-by: jieshu666 <jieshu666@users.noreply.github.com>
This commit is contained in:
@@ -54,8 +54,11 @@ pub fn jobs(_app: &mut App, args: Option<&str>) -> CommandResult {
|
||||
})),
|
||||
None => CommandResult::error("Usage: /jobs cancel <id>"),
|
||||
},
|
||||
"cancel-all" | "kill-all" | "stop-all" => {
|
||||
CommandResult::action(AppAction::ShellJob(ShellJobAction::CancelAll))
|
||||
}
|
||||
_ => CommandResult::error(
|
||||
"Usage: /jobs [list|show <id>|poll <id>|wait <id>|stdin <id> <input>|close-stdin <id>|cancel <id>]",
|
||||
"Usage: /jobs [list|show <id>|poll <id>|wait <id>|stdin <id> <input>|close-stdin <id>|cancel <id>|cancel-all]",
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -109,5 +112,11 @@ mod tests {
|
||||
Some(AppAction::ShellJob(ShellJobAction::SendStdin { id, input, close: false }))
|
||||
if id == "shell_abcd" && input == "y"
|
||||
));
|
||||
|
||||
let cancel_all = jobs(&mut app, Some("cancel-all"));
|
||||
assert!(matches!(
|
||||
cancel_all.action,
|
||||
Some(AppAction::ShellJob(ShellJobAction::CancelAll))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +427,6 @@ impl BackgroundShell {
|
||||
}
|
||||
|
||||
/// Kill the process
|
||||
#[allow(dead_code)]
|
||||
fn kill(&mut self) -> Result<()> {
|
||||
if let Some(ref mut child) = self.child {
|
||||
child.kill().context("Failed to kill process")?;
|
||||
@@ -1250,7 +1249,6 @@ impl ShellManager {
|
||||
}
|
||||
|
||||
/// Kill a running background process
|
||||
#[allow(dead_code)]
|
||||
pub fn kill(&mut self, task_id: &str) -> Result<ShellResult> {
|
||||
let shell = self
|
||||
.processes
|
||||
|
||||
@@ -4069,6 +4069,7 @@ pub enum ShellJobAction {
|
||||
Cancel {
|
||||
id: String,
|
||||
},
|
||||
CancelAll,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
||||
@@ -64,7 +64,7 @@ pub(super) fn format_shell_job_list(jobs: &[ShellJobSnapshot]) -> String {
|
||||
}
|
||||
}
|
||||
lines.push(
|
||||
"Controls: /jobs show <id>, /jobs poll <id>, /jobs wait <id>, /jobs stdin <id> <input>, /jobs cancel <id>."
|
||||
"Controls: /jobs show <id>, /jobs poll <id>, /jobs wait <id>, /jobs stdin <id> <input>, /jobs cancel <id>, /jobs cancel-all."
|
||||
.to_string(),
|
||||
);
|
||||
lines.join("\n")
|
||||
|
||||
@@ -611,6 +611,19 @@ fn task_panel_lines(app: &App, content_width: usize, max_rows: usize) -> Vec<Lin
|
||||
Style::default().fg(palette::TEXT_DIM),
|
||||
)));
|
||||
}
|
||||
|
||||
if lines.len() < max_rows
|
||||
&& background_rows
|
||||
.iter()
|
||||
.any(|task| task.id.starts_with("shell_") && task.status == "running")
|
||||
{
|
||||
lines.push(Line::from(Span::styled(
|
||||
truncate_line_to_width("Ctrl+K -> /jobs cancel-all", content_width.max(1)),
|
||||
Style::default()
|
||||
.fg(palette::TEXT_MUTED)
|
||||
.add_modifier(ratatui::style::Modifier::ITALIC),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if lines.len() < max_rows {
|
||||
|
||||
@@ -2287,6 +2287,19 @@ async fn run_event_loop(
|
||||
}
|
||||
|
||||
if key.code == KeyCode::Char('k') && key.modifiers.contains(KeyModifiers::CONTROL) {
|
||||
if app.view_stack.is_empty()
|
||||
&& app.sidebar_focus == SidebarFocus::Tasks
|
||||
&& app
|
||||
.task_panel
|
||||
.iter()
|
||||
.any(|task| task.id.starts_with("shell_") && task.status == "running")
|
||||
{
|
||||
app.input = "/jobs cancel-all".to_string();
|
||||
app.cursor_position = app.input.len();
|
||||
app.status_message =
|
||||
Some("Press Enter to kill all running shell jobs".to_string());
|
||||
continue;
|
||||
}
|
||||
// When the composer is the active input target (no modal/pager
|
||||
// intercepting keys), Ctrl+K performs an emacs-style kill to
|
||||
// end-of-line. If the kill is a no-op (cursor at end of empty
|
||||
@@ -5698,6 +5711,24 @@ fn handle_shell_job_action(app: &mut App, action: crate::tui::app::ShellJobActio
|
||||
Ok(result) => add_shell_job_message(app, format_shell_poll(&result)),
|
||||
Err(err) => add_shell_job_message(app, format!("Shell job cancel failed: {err}")),
|
||||
},
|
||||
crate::tui::app::ShellJobAction::CancelAll => match manager.kill_running() {
|
||||
Ok(results) => {
|
||||
let count = results.len();
|
||||
if count == 0 {
|
||||
add_shell_job_message(app, "No running shell jobs to cancel.".to_string());
|
||||
} else {
|
||||
let tasks: Vec<String> = results
|
||||
.iter()
|
||||
.filter_map(|result| result.task_id.clone())
|
||||
.collect();
|
||||
add_shell_job_message(
|
||||
app,
|
||||
format!("Killed {count} shell job(s): {}", tasks.join(", ")),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => add_shell_job_message(app, format!("Shell job cancel-all failed: {err}")),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user