diff --git a/assets/icons/rerun.svg b/assets/icons/rerun.svg
new file mode 100644
index 0000000000..4d22f924f5
--- /dev/null
+++ b/assets/icons/rerun.svg
@@ -0,0 +1,7 @@
+
diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs
index 98c1d1bbdf..3f91bd16c7 100644
--- a/crates/project/src/task_inventory.rs
+++ b/crates/project/src/task_inventory.rs
@@ -348,9 +348,20 @@ impl Inventory {
})
}
- /// Returns the last scheduled task, if any of the sources contains one with the matching id.
- pub fn last_scheduled_task(&self) -> Option<(TaskSourceKind, ResolvedTask)> {
- self.last_scheduled_tasks.back().cloned()
+ /// Returns the last scheduled task by task_id if provided.
+ /// Otherwise, returns the last scheduled task.
+ pub fn last_scheduled_task(
+ &self,
+ task_id: Option<&TaskId>,
+ ) -> Option<(TaskSourceKind, ResolvedTask)> {
+ if let Some(task_id) = task_id {
+ self.last_scheduled_tasks
+ .iter()
+ .find(|(_, task)| &task.id == task_id)
+ .cloned()
+ } else {
+ self.last_scheduled_tasks.back().cloned()
+ }
}
/// Registers task "usage" as being scheduled – to be used for LRU sorting when listing all tasks.
diff --git a/crates/task/src/lib.rs b/crates/task/src/lib.rs
index ccd39fb688..10b9b050a4 100644
--- a/crates/task/src/lib.rs
+++ b/crates/task/src/lib.rs
@@ -7,7 +7,7 @@ mod vscode_format;
use collections::{hash_map, HashMap, HashSet};
use gpui::SharedString;
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::str::FromStr;
use std::{borrow::Cow, path::Path};
@@ -17,7 +17,7 @@ pub use vscode_format::VsCodeTaskFile;
/// Task identifier, unique within the application.
/// Based on it, task reruns and terminal tabs are managed.
-#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize)]
pub struct TaskId(pub String);
/// TerminalWorkDir describes where a task should be run
diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs
index 0877b1930e..2106cbe831 100644
--- a/crates/tasks_ui/src/lib.rs
+++ b/crates/tasks_ui/src/lib.rs
@@ -9,7 +9,7 @@ use workspace::{tasks::schedule_resolved_task, Workspace};
mod modal;
mod settings;
-pub use modal::Spawn;
+pub use modal::{Rerun, Spawn};
pub fn init(cx: &mut AppContext) {
settings::TaskSettings::register(cx);
@@ -20,7 +20,10 @@ pub fn init(cx: &mut AppContext) {
.register_action(move |workspace, action: &modal::Rerun, cx| {
if let Some((task_source_kind, mut last_scheduled_task)) =
workspace.project().update(cx, |project, cx| {
- project.task_inventory().read(cx).last_scheduled_task()
+ project
+ .task_inventory()
+ .read(cx)
+ .last_scheduled_task(action.task_id.as_ref())
})
{
if action.reevaluate_context {
diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs
index 6cc7983ea9..6269043339 100644
--- a/crates/tasks_ui/src/modal.rs
+++ b/crates/tasks_ui/src/modal.rs
@@ -9,7 +9,7 @@ use gpui::{
};
use picker::{highlighted_match_with_paths::HighlightedText, Picker, PickerDelegate};
use project::{Project, TaskSourceKind};
-use task::{ResolvedTask, TaskContext, TaskTemplate};
+use task::{ResolvedTask, TaskContext, TaskId, TaskTemplate};
use ui::{
div, h_flex, v_flex, ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color,
FluentBuilder as _, Icon, IconButton, IconButtonShape, IconName, IconSize, IntoElement,
@@ -54,6 +54,9 @@ pub struct Rerun {
/// Default: null
#[serde(default)]
pub use_new_terminal: Option,
+
+ /// If present, rerun the task with this ID, otherwise rerun the last task.
+ pub task_id: Option,
}
impl_actions!(task, [Rerun, Spawn]);
diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs
index 5fd7844300..d2348eb96c 100644
--- a/crates/terminal_view/src/terminal_view.rs
+++ b/crates/terminal_view/src/terminal_view.rs
@@ -24,7 +24,7 @@ use terminal::{
Clear, Copy, Event, MaybeNavigationTarget, Paste, ShowCharacterPalette, TaskStatus, Terminal,
};
use terminal_element::TerminalElement;
-use ui::{h_flex, prelude::*, ContextMenu, Icon, IconName, Label};
+use ui::{h_flex, prelude::*, ContextMenu, Icon, IconName, Label, Tooltip};
use util::{paths::PathLikeWithPosition, ResultExt};
use workspace::{
item::{BreadcrumbText, Item, ItemEvent, TabContentParams},
@@ -787,23 +787,58 @@ impl Item for TerminalView {
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
let terminal = self.terminal().read(cx);
let title = terminal.title(true);
- let (icon, icon_color) = match terminal.task() {
+
+ let (icon, icon_color, rerun_btn) = match terminal.task() {
Some(terminal_task) => match &terminal_task.status {
- TaskStatus::Unknown => (IconName::ExclamationTriangle, Color::Warning),
- TaskStatus::Running => (IconName::Play, Color::Default),
+ TaskStatus::Unknown => (IconName::ExclamationTriangle, Color::Warning, None),
+ TaskStatus::Running => (IconName::Play, Color::Disabled, None),
TaskStatus::Completed { success } => {
+ let task_id = terminal_task.id.clone();
+ let rerun_btn = IconButton::new("rerun-icon", IconName::Rerun)
+ .icon_size(IconSize::Small)
+ .size(ButtonSize::Compact)
+ .icon_color(Color::Default)
+ .shape(ui::IconButtonShape::Square)
+ .tooltip(|cx| Tooltip::text("Rerun task", cx))
+ .on_click(move |_, cx| {
+ cx.dispatch_action(Box::new(tasks_ui::Rerun {
+ task_id: Some(task_id.clone()),
+ ..Default::default()
+ }));
+ });
+
if *success {
- (IconName::Check, Color::Success)
+ (IconName::Check, Color::Success, Some(rerun_btn))
} else {
- (IconName::XCircle, Color::Error)
+ (IconName::XCircle, Color::Error, Some(rerun_btn))
}
}
},
- None => (IconName::Terminal, Color::Muted),
+ None => (IconName::Terminal, Color::Muted, None),
};
+
h_flex()
.gap_2()
- .child(Icon::new(icon).color(icon_color))
+ .group("term-tab-icon")
+ .child(
+ h_flex()
+ .group("term-tab-icon")
+ .child(
+ div()
+ .when(rerun_btn.is_some(), |this| {
+ this.hover(|style| style.invisible().w_0())
+ })
+ .child(Icon::new(icon).color(icon_color)),
+ )
+ .when_some(rerun_btn, |this, rerun_btn| {
+ this.child(
+ div()
+ .absolute()
+ .visible_on_hover("term-tab-icon")
+ .child(rerun_btn),
+ )
+ }),
+ )
.child(Label::new(title).color(if params.selected {
Color::Default
} else {
diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs
index a7a83a91ae..3810e32b7b 100644
--- a/crates/ui/src/components/icon.rs
+++ b/crates/ui/src/components/icon.rs
@@ -163,6 +163,7 @@ pub enum IconName {
ReplaceAll,
ReplaceNext,
ReplyArrowRight,
+ Rerun,
Return,
Reveal,
Save,
@@ -284,6 +285,7 @@ impl IconName {
IconName::ReplaceAll => "icons/replace_all.svg",
IconName::ReplaceNext => "icons/replace_next.svg",
IconName::ReplyArrowRight => "icons/reply_arrow_right.svg",
+ IconName::Rerun => "icons/rerun.svg",
IconName::Return => "icons/return.svg",
IconName::Save => "icons/save.svg",
IconName::Screen => "icons/desktop.svg",