Add a `terminal::RerunTask` action (#30288)

Kirill Bulatov created

Bounded this action to the same defaults `task::Rerun` is bound to.

Unlike the `task::Rerun` which will always rerun the latest task, this
command reruns the current task tab, if focused.
The task is not in scope when the terminal pane is not focused, and
falls back to the regular rerun if invoked on a task-less terminal tab.

This way, we can add a proper tooltip to the terminal tab reruns:

<img width="231" alt="image"
src="https://github.com/user-attachments/assets/2cdd7458-5ba2-4cc7-a10b-3e2db059f1ca"
/>


Release Notes:

- Added `terminal::RerunTask` task action

Change summary

assets/keymaps/default-linux.json         |  5 ++
assets/keymaps/default-macos.json         |  3 +
crates/terminal_view/src/terminal_view.rs | 42 ++++++++++++++++++------
3 files changed, 37 insertions(+), 13 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -951,7 +951,10 @@
       "shift-down": "terminal::ScrollLineDown",
       "shift-home": "terminal::ScrollToTop",
       "shift-end": "terminal::ScrollToBottom",
-      "ctrl-shift-space": "terminal::ToggleViMode"
+      "ctrl-shift-space": "terminal::ToggleViMode",
+      "ctrl-shift-r": "terminal::RerunTask",
+      "ctrl-alt-r": "terminal::RerunTask",
+      "alt-t": "terminal::RerunTask"
     }
   },
   {

assets/keymaps/default-macos.json 🔗

@@ -1037,7 +1037,8 @@
       "ctrl-alt-up": "pane::SplitUp",
       "ctrl-alt-down": "pane::SplitDown",
       "ctrl-alt-left": "pane::SplitLeft",
-      "ctrl-alt-right": "pane::SplitRight"
+      "ctrl-alt-right": "pane::SplitRight",
+      "cmd-alt-r": "terminal::RerunTask"
     }
   },
   {

crates/terminal_view/src/terminal_view.rs 🔗

@@ -8,12 +8,14 @@ use editor::{Editor, EditorSettings, actions::SelectAll, scroll::ScrollbarAutoHi
 use gpui::{
     AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, KeyContext,
     KeyDownEvent, Keystroke, MouseButton, MouseDownEvent, Pixels, Render, ScrollWheelEvent,
-    Stateful, Styled, Subscription, Task, WeakEntity, anchored, deferred, div, impl_actions,
+    Stateful, Styled, Subscription, Task, WeakEntity, actions, anchored, deferred, div,
+    impl_actions,
 };
 use itertools::Itertools;
 use persistence::TERMINAL_DB;
 use project::{Entry, Metadata, Project, search::SearchQuery, terminals::TerminalKind};
 use schemars::JsonSchema;
+use task::TaskId;
 use terminal::{
     Clear, Copy, Event, HoveredWord, MaybeNavigationTarget, Paste, ScrollLineDown, ScrollLineUp,
     ScrollPageDown, ScrollPageUp, ScrollToBottom, ScrollToTop, ShowCharacterPalette, TaskState,
@@ -71,6 +73,8 @@ pub struct SendText(String);
 #[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq)]
 pub struct SendKeystroke(String);
 
+actions!(terminal, [RerunTask]);
+
 impl_actions!(terminal, [SendText, SendKeystroke]);
 
 pub fn init(cx: &mut App) {
@@ -334,6 +338,18 @@ impl TerminalView {
         cx.notify();
     }
 
+    fn rerun_task(&mut self, _: &RerunTask, window: &mut Window, cx: &mut Context<Self>) {
+        let task = self
+            .terminal
+            .update(cx, |terminal, _| {
+                terminal
+                    .task()
+                    .map(|task| terminal_rerun_override(&task.id))
+            })
+            .unwrap_or_default();
+        window.dispatch_action(Box::new(task), cx);
+    }
+
     fn clear(&mut self, _: &Clear, _: &mut Window, cx: &mut Context<Self>) {
         self.scroll_top = px(0.);
         self.terminal.update(cx, |term, _| term.clear());
@@ -826,22 +842,25 @@ impl TerminalView {
                 .size(ButtonSize::Compact)
                 .icon_color(Color::Default)
                 .shape(ui::IconButtonShape::Square)
-                .tooltip(Tooltip::text("Rerun task"))
+                .tooltip(move |window, cx| {
+                    Tooltip::for_action("Rerun task", &RerunTask, window, cx)
+                })
                 .on_click(move |_, window, cx| {
-                    window.dispatch_action(
-                        Box::new(zed_actions::Rerun {
-                            task_id: Some(task_id.0.clone()),
-                            allow_concurrent_runs: Some(true),
-                            use_new_terminal: Some(false),
-                            reevaluate_context: false,
-                        }),
-                        cx,
-                    );
+                    window.dispatch_action(Box::new(terminal_rerun_override(&task_id)), cx);
                 }),
         )
     }
 }
 
+fn terminal_rerun_override(task: &TaskId) -> zed_actions::Rerun {
+    zed_actions::Rerun {
+        task_id: Some(task.0.clone()),
+        allow_concurrent_runs: Some(true),
+        use_new_terminal: Some(false),
+        reevaluate_context: false,
+    }
+}
+
 fn subscribe_for_terminal_events(
     terminal: &Entity<Terminal>,
     workspace: WeakEntity<Workspace>,
@@ -1360,6 +1379,7 @@ impl Render for TerminalView {
             .on_action(cx.listener(TerminalView::toggle_vi_mode))
             .on_action(cx.listener(TerminalView::show_character_palette))
             .on_action(cx.listener(TerminalView::select_all))
+            .on_action(cx.listener(TerminalView::rerun_task))
             .on_key_down(cx.listener(Self::key_down))
             .on_mouse_down(
                 MouseButton::Right,