task: Allow Rerun action to override properties of task being reran (#10468)

Piotr Osiewicz created

For example:
```
"alt-t": [
    "task::Rerun",
     { "reevaluate_context": true, "allow_concurrent_runs": true }
],
```
Overriding `allow_concurrent_runs` to `true` by itself should terminate
current instance of the task, if there's any.

This PR also fixes task deduplication in terminal panel to use expanded
label and not the id, which depends on task context. It kinda aligns
with how task rerun worked prior to #10341 . That's omitted in the
release notes though, as it's not in Preview yet.

Release Notes:

- `Task::Rerun` action can now override `allow_concurrent_runs` and
`use_new_terminal` properties of the task that is being reran.

Change summary

crates/task/src/task_template.rs           |  3 ++-
crates/tasks_ui/src/lib.rs                 | 19 +++++++++++++++++--
crates/tasks_ui/src/modal.rs               | 10 +++++++++-
crates/terminal_view/src/terminal_panel.rs |  6 +++---
4 files changed, 31 insertions(+), 7 deletions(-)

Detailed changes

crates/task/src/task_template.rs 🔗

@@ -102,7 +102,8 @@ impl TaskTemplate {
         let full_label = substitute_all_template_variables_in_str(&self.label, &task_variables)?;
         let command = substitute_all_template_variables_in_str(&self.command, &task_variables)?;
         let args = substitute_all_template_variables_in_vec(self.args.clone(), &task_variables)?;
-        let task_hash = to_hex_hash(self)
+
+        let task_hash = to_hex_hash(&self)
             .context("hashing task template")
             .log_err()?;
         let variables_hash = to_hex_hash(&task_variables)

crates/tasks_ui/src/lib.rs 🔗

@@ -23,13 +23,19 @@ pub fn init(cx: &mut AppContext) {
             workspace
                 .register_action(spawn_task_or_modal)
                 .register_action(move |workspace, action: &modal::Rerun, cx| {
-                    if let Some((task_source_kind, last_scheduled_task)) =
+                    if let Some((task_source_kind, mut last_scheduled_task)) =
                         workspace.project().update(cx, |project, cx| {
                             project.task_inventory().read(cx).last_scheduled_task()
                         })
                     {
                         if action.reevaluate_context {
-                            let original_task = last_scheduled_task.original_task;
+                            let mut original_task = last_scheduled_task.original_task;
+                            if let Some(allow_concurrent_runs) = action.allow_concurrent_runs {
+                                original_task.allow_concurrent_runs = allow_concurrent_runs;
+                            }
+                            if let Some(use_new_terminal) = action.use_new_terminal {
+                                original_task.use_new_terminal = use_new_terminal;
+                            }
                             let cwd = task_cwd(workspace, cx).log_err().flatten();
                             let task_context = task_context(workspace, cwd, cx);
                             schedule_task(
@@ -41,6 +47,15 @@ pub fn init(cx: &mut AppContext) {
                                 cx,
                             )
                         } else {
+                            if let Some(resolved) = last_scheduled_task.resolved.as_mut() {
+                                if let Some(allow_concurrent_runs) = action.allow_concurrent_runs {
+                                    resolved.allow_concurrent_runs = allow_concurrent_runs;
+                                }
+                                if let Some(use_new_terminal) = action.use_new_terminal {
+                                    resolved.use_new_terminal = use_new_terminal;
+                                }
+                            }
+
                             schedule_resolved_task(
                                 workspace,
                                 task_source_kind,

crates/tasks_ui/src/modal.rs 🔗

@@ -38,12 +38,20 @@ impl Spawn {
 /// Rerun last task
 #[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct Rerun {
-    #[serde(default)]
     /// Controls whether the task context is reevaluated prior to execution of a task.
     /// If it is not, environment variables such as ZED_COLUMN, ZED_FILE are gonna be the same as in the last execution of a task
     /// If it is, these variables will be updated to reflect current state of editor at the time task::Rerun is executed.
     /// default: false
+    #[serde(default)]
     pub reevaluate_context: bool,
+    /// Overrides `allow_concurrent_runs` property of the task being reran.
+    /// Default: null
+    #[serde(default)]
+    pub allow_concurrent_runs: Option<bool>,
+    /// Overrides `use_new_terminal` property of the task being reran.
+    /// Default: null
+    #[serde(default)]
+    pub use_new_terminal: Option<bool>,
 }
 
 impl_actions!(task, [Rerun, Spawn]);

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -334,7 +334,7 @@ impl TerminalPanel {
             return;
         }
 
-        let terminals_for_task = self.terminals_for_task(&spawn_in_terminal.id, cx);
+        let terminals_for_task = self.terminals_for_task(&spawn_in_terminal.label, cx);
         if terminals_for_task.is_empty() {
             self.spawn_in_new_terminal(spawn_task, working_directory, cx);
             return;
@@ -435,7 +435,7 @@ impl TerminalPanel {
 
     fn terminals_for_task(
         &self,
-        id: &TaskId,
+        label: &str,
         cx: &mut AppContext,
     ) -> Vec<(usize, View<TerminalView>)> {
         self.pane
@@ -445,7 +445,7 @@ impl TerminalPanel {
             .filter_map(|(index, item)| Some((index, item.act_as::<TerminalView>(cx)?)))
             .filter_map(|(index, terminal_view)| {
                 let task_state = terminal_view.read(cx).terminal().read(cx).task()?;
-                if &task_state.id == id {
+                if &task_state.label == label {
                     Some((index, terminal_view))
                 } else {
                     None