diff --git a/Cargo.lock b/Cargo.lock index 7253bd082a..bdc1909ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9640,6 +9640,7 @@ dependencies = [ "editor", "fuzzy", "gpui", + "itertools 0.11.0", "language", "menu", "picker", diff --git a/crates/task/src/oneshot_source.rs b/crates/task/src/oneshot_source.rs index 6f7bb39a88..b819c118be 100644 --- a/crates/task/src/oneshot_source.rs +++ b/crates/task/src/oneshot_source.rs @@ -66,9 +66,14 @@ impl OneshotSource { /// Spawns a certain task based on the user prompt. pub fn spawn(&mut self, prompt: String) -> Arc { - let ret = Arc::new(OneshotTask::new(prompt)); - self.tasks.push(ret.clone()); - ret + if let Some(task) = self.tasks.iter().find(|task| task.id().0 == prompt) { + // If we already have an oneshot task with that command, let's just reuse it. + task.clone() + } else { + let new_oneshot = Arc::new(OneshotTask::new(prompt)); + self.tasks.push(new_oneshot.clone()); + new_oneshot + } } } diff --git a/crates/tasks_ui/Cargo.toml b/crates/tasks_ui/Cargo.toml index 446179890c..a2dfd94340 100644 --- a/crates/tasks_ui/Cargo.toml +++ b/crates/tasks_ui/Cargo.toml @@ -22,6 +22,7 @@ ui.workspace = true util.workspace = true workspace.workspace = true language.workspace = true +itertools.workspace = true [dev-dependencies] editor = { workspace = true, features = ["test-support"] } diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index f2740a24a8..c8d5df4c0c 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -310,7 +310,20 @@ impl PickerDelegate for TasksModalDelegate { } fn selected_as_query(&self) -> Option { - Some(self.matches.get(self.selected_index())?.string.clone()) + use itertools::intersperse; + let task_index = self.matches.get(self.selected_index())?.candidate_id; + let tasks = self.candidates.as_ref()?; + let (_, task) = tasks.get(task_index)?; + // .exec doesn't actually spawn anything; it merely prepares a spawning command, + // which we can use for substitution. + let mut spawn_prompt = task.exec(self.task_context.clone())?; + if !spawn_prompt.args.is_empty() { + spawn_prompt.command.push(' '); + spawn_prompt + .command + .extend(intersperse(spawn_prompt.args, " ".to_string())); + } + Some(spawn_prompt.command) } } @@ -381,15 +394,15 @@ mod tests { cx.dispatch_action(menu::UseSelectedQuery); assert_eq!( query(&tasks_picker, cx), - "example task", - "Query should be set to the selected task's name" + "echo 4", + "Query should be set to the selected task's command" ); assert_eq!( task_names(&tasks_picker, cx), - vec!["example task"], - "No other tasks should be listed" + Vec::::new(), + "No task should be listed" ); - cx.dispatch_action(menu::Confirm); + cx.dispatch_action(menu::SecondaryConfirm); let tasks_picker = open_spawn_tasks(&workspace, cx); assert_eq!( @@ -399,8 +412,8 @@ mod tests { ); assert_eq!( task_names(&tasks_picker, cx), - vec!["example task", "another one"], - "Last recently used task should be listed first" + vec!["echo 4", "another one", "example task"], + "New oneshot task should be listed first" ); let query_str = "echo 4"; @@ -408,8 +421,8 @@ mod tests { assert_eq!(query(&tasks_picker, cx), query_str); assert_eq!( task_names(&tasks_picker, cx), - Vec::::new(), - "No tasks should match custom command query" + vec!["echo 4"], + "New oneshot should match custom command query" ); cx.dispatch_action(menu::SecondaryConfirm); @@ -421,7 +434,7 @@ mod tests { ); assert_eq!( task_names(&tasks_picker, cx), - vec![query_str, "example task", "another one"], + vec![query_str, "another one", "example task"], "Last recently used one show task should be listed first" );