From cbc226597cd172eae41b460a88b021d5916e29a5 Mon Sep 17 00:00:00 2001 From: IViktorov Date: Sat, 14 Dec 2024 10:30:48 +0300 Subject: [PATCH] Prefer project (worktree) tasks to language/global tasks in task::Spawn (#21706) `Inventory::list_tasks()` in `project` crate now is ordered by task types. Worktree tasks comes first, language tasks second and global tasks last. That leads to `spawn_task_with_name()` from `task_ui` crate will find worktree task first, so it's possible to override global tasks at project level. * `Inventory::templates_from_settings()` splitted to `Inventory::global_templates_from_settings()` and `Inventory::worktree_templates_from_settings()`. * In tests function `list_tasks()` renamed to `list_tasks_sorted_by_last_used()`, because it call's `Inventory::used_and_current_resolved_tasks()`. Also added `list_tasks()` which calls `Inventory::list_tasks()`. Closes #20987 Release Notes: - Fix task::Spawn to search for task name in project tasks first. --- crates/project/src/task_inventory.rs | 119 +++++++++++++++++++-------- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index dd6b201687..6aa1f83bb0 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -81,7 +81,8 @@ impl Inventory { } /// Pulls its task sources relevant to the worktree and the language given, - /// returns all task templates with their source kinds, in no specific order. + /// returns all task templates with their source kinds, worktree tasks first, language tasks second + /// and global tasks last. No specific order inside source kinds groups. pub fn list_tasks( &self, file: Option>, @@ -92,13 +93,15 @@ impl Inventory { let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language { name: language.name().0, }); + let global_tasks = self.global_templates_from_settings(); let language_tasks = language .and_then(|language| language.context_provider()?.associated_tasks(file, cx)) .into_iter() .flat_map(|tasks| tasks.0.into_iter()) - .flat_map(|task| Some((task_source_kind.clone()?, task))); + .flat_map(|task| Some((task_source_kind.clone()?, task))) + .chain(global_tasks); - self.templates_from_settings(worktree) + self.worktree_templates_from_settings(worktree) .chain(language_tasks) .collect() } @@ -165,14 +168,18 @@ impl Inventory { .collect::>(); let not_used_score = post_inc(&mut lru_score); + let global_tasks = self.global_templates_from_settings(); let language_tasks = language .and_then(|language| language.context_provider()?.associated_tasks(file, cx)) .into_iter() .flat_map(|tasks| tasks.0.into_iter()) - .flat_map(|task| Some((task_source_kind.clone()?, task))); - let new_resolved_tasks = self - .templates_from_settings(worktree) - .chain(language_tasks) + .flat_map(|task| Some((task_source_kind.clone()?, task))) + .chain(global_tasks); + let worktree_tasks = self + .worktree_templates_from_settings(worktree) + .chain(language_tasks); + + let new_resolved_tasks = worktree_tasks .filter_map(|(kind, task)| { let id_base = kind.to_id_base(); Some(( @@ -235,9 +242,8 @@ impl Inventory { self.last_scheduled_tasks.retain(|(_, task)| &task.id != id); } - fn templates_from_settings( + fn global_templates_from_settings( &self, - worktree: Option, ) -> impl '_ + Iterator { self.templates_from_settings .global @@ -252,28 +258,34 @@ impl Inventory { template, ) }) - .chain(worktree.into_iter().flat_map(|worktree| { - self.templates_from_settings - .worktree - .get(&worktree) - .into_iter() - .flatten() - .flat_map(|(directory, templates)| { - templates.iter().map(move |template| (directory, template)) - }) - .map(move |(directory, template)| { - ( - TaskSourceKind::Worktree { - id: worktree, - directory_in_worktree: directory.to_path_buf(), - id_base: Cow::Owned(format!( - "local worktree tasks from directory {directory:?}" - )), - }, - template.clone(), - ) - }) - })) + } + + fn worktree_templates_from_settings( + &self, + worktree: Option, + ) -> impl '_ + Iterator { + worktree.into_iter().flat_map(|worktree| { + self.templates_from_settings + .worktree + .get(&worktree) + .into_iter() + .flatten() + .flat_map(|(directory, templates)| { + templates.iter().map(move |template| (directory, template)) + }) + .map(move |(directory, template)| { + ( + TaskSourceKind::Worktree { + id: worktree, + directory_in_worktree: directory.to_path_buf(), + id_base: Cow::Owned(format!( + "local worktree tasks from directory {directory:?}" + )), + }, + template.clone(), + ) + }) + }) } /// Updates in-memory task metadata from the JSON string given. @@ -407,6 +419,25 @@ mod test_inventory { inventory: &Model, worktree: Option, cx: &mut TestAppContext, + ) -> Vec<(TaskSourceKind, String)> { + inventory.update(cx, |inventory, cx| { + let task_context = &TaskContext::default(); + inventory + .list_tasks(None, None, worktree, cx) + .into_iter() + .filter_map(|(source_kind, task)| { + let id_base = source_kind.to_id_base(); + Some((source_kind, task.resolve_task(&id_base, task_context)?)) + }) + .map(|(source_kind, resolved_task)| (source_kind, resolved_task.resolved_label)) + .collect() + }) + } + + pub(super) async fn list_tasks_sorted_by_last_used( + inventory: &Model, + worktree: Option, + cx: &mut TestAppContext, ) -> Vec<(TaskSourceKind, String)> { let (used, current) = inventory.update(cx, |inventory, cx| { inventory.used_and_current_resolved_tasks(worktree, None, &TaskContext::default(), cx) @@ -789,6 +820,30 @@ mod tests { .unwrap(); }); + assert_eq!( + list_tasks_sorted_by_last_used(&inventory, None, cx).await, + worktree_independent_tasks, + "Without a worktree, only worktree-independent tasks should be listed" + ); + assert_eq!( + list_tasks_sorted_by_last_used(&inventory, Some(worktree_1), cx).await, + worktree_1_tasks + .iter() + .chain(worktree_independent_tasks.iter()) + .cloned() + .sorted_by_key(|(kind, label)| (task_source_kind_preference(kind), label.clone())) + .collect::>(), + ); + assert_eq!( + list_tasks_sorted_by_last_used(&inventory, Some(worktree_2), cx).await, + worktree_2_tasks + .iter() + .chain(worktree_independent_tasks.iter()) + .cloned() + .sorted_by_key(|(kind, label)| (task_source_kind_preference(kind), label.clone())) + .collect::>(), + ); + assert_eq!( list_tasks(&inventory, None, cx).await, worktree_independent_tasks, @@ -800,7 +855,6 @@ mod tests { .iter() .chain(worktree_independent_tasks.iter()) .cloned() - .sorted_by_key(|(kind, label)| (task_source_kind_preference(kind), label.clone())) .collect::>(), ); assert_eq!( @@ -809,7 +863,6 @@ mod tests { .iter() .chain(worktree_independent_tasks.iter()) .cloned() - .sorted_by_key(|(kind, label)| (task_source_kind_preference(kind), label.clone())) .collect::>(), ); }