From 5d9e96049598dc025839294aa1d65e95ae8ac887 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Sat, 18 Jan 2025 16:44:13 -0800 Subject: [PATCH] start working on higher level impl --- Cargo.lock | 1 + crates/editor/src/editor.rs | 53 ++++++++++++++++++++++++++-- crates/project/src/task_inventory.rs | 42 ++++++++++++---------- crates/task/Cargo.toml | 1 + crates/task/src/lib.rs | 2 ++ crates/task/src/task_template.rs | 17 +++++++++ crates/tasks_ui/src/lib.rs | 4 +-- crates/tasks_ui/src/modal.rs | 46 ++++++++++++++++++++---- crates/workspace/src/tasks.rs | 15 ++++++-- crates/workspace/src/workspace.rs | 2 +- 10 files changed, 150 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1fc9a5216946b..46faa226e6cd4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12856,6 +12856,7 @@ dependencies = [ "futures 0.3.31", "gpui", "hex", + "itertools 0.14.0", "parking_lot", "schemars", "serde", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1212084b067c5e..a2a7fed6f9b975 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -109,6 +109,7 @@ pub use proposed_changes_editor::{ ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar, }; use similar::{ChangeTag, TextDiff}; +use tasks::task_context; use std::iter::Peekable; use task::{ResolvedTask, TaskTemplate, TaskVariables}; @@ -4205,12 +4206,37 @@ impl Editor { let buffer = actions_menu.buffer; let workspace = self.workspace()?; + let task_context = task_context(workspace, cx); + match action { CodeActionsItem::Task(task_source_kind, resolved_task) => { workspace.update(cx, |workspace, cx| { - workspace::tasks::schedule_resolved_task( + let worktree = match task_source_kind { + TaskSourceKind::Worktree { id, .. } => Some(id), + _ => None + }; + + let pre_tasks = workspace + .project() + .read(cx) + .task_store() + .read(cx) + .task_inventory() + .map_or(vec![], |inventory| { + inventory + .read(cx) + .build_pre_task_list( + &resolved_task, + worktree, + &task_context + ) + .unwrap_or(vec![]) + }); + + workspace::tasks::schedule_resolved_tasks( workspace, task_source_kind, + pre_task, resolved_task, false, cx, @@ -5087,9 +5113,32 @@ impl Editor { workspace .update(&mut cx, |workspace, cx| { - workspace::tasks::schedule_resolved_task( + let worktree = match task_source_kind { + TaskSourceKind::Worktree { id, .. } => Some(id), + _ => None + }; + + let pre_tasks = workspace + .project() + .read(cx) + .task_store() + .read(cx) + .task_inventory() + .map_or(vec![], |inventory| { + inventory + .read(cx) + .build_pre_task_list( + &resolved_task, + worktree, + task_context + ) + .unwrap_or(vec![]) + }); + + workspace::tasks::schedule_resolved_tasks( workspace, task_source_kind, + pre_tasks, resolved_task, false, cx, diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index cc201012a3c7da..72ed9ab297150a 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -123,37 +123,42 @@ impl Inventory { } /// Topological sort of the dependency graph of `task` - pub fn build_pre_task_list(&self, task: &TaskTemplate) -> anyhow::Result> { - // collect all tasks from all available worktrees - let tasks = self - .templates_from_settings - .worktree - .iter() - .flat_map(|leaf| { - self.worktree_templates_from_settings(Some(*leaf.0)) - .chain(self.global_templates_from_settings()) - .collect_vec() + pub fn build_pre_task_list( + &self, + base_task: &ResolvedTask, + worktree: Option, + task_context: &TaskContext, + ) -> anyhow::Result> { + let tasks_in_scope = self + .worktree_templates_from_settings(worktree) + .chain(self.global_templates_from_settings()) + .filter_map(|(kind, task)| { + let base_id = kind.to_id_base(); + task + .resolve_task(&base_id, task_context) + .map(|task| (kind, task)) }) - .unique_by(|(_, task)| task.label.clone()) + .unique_by(|(_, task)| task.resolved_label.clone()) .collect_vec(); - if let None = tasks + if let None = tasks_in_scope .iter() - .find(|(_, TaskTemplate { label, .. })| task.label.as_str() == label.as_str()) + .find(|(_, task)| task.resolved_label == base_task.resolved_label) { - return Err(anyhow::anyhow!("couldn't find with label {} in available tasks", &task.label)); + return Err(anyhow::anyhow!("couldn't find with label {} in available tasks", base_task.resolved_label)); } // map task labels to their dep graph node idx, source, and dependencies - let nodes = tasks + let nodes = tasks_in_scope .iter() .enumerate() .map(|(idx, (source, task))| ( - task.label.as_str(), + task.display_label(), ( idx as u32, source, - task.pre + task + .resolved_pre_labels .iter() .map(|s| s.as_str()) .unique() @@ -181,9 +186,8 @@ impl Inventory { } } - dep_graph - .subgraph(nodes.get(task.label.as_str()).unwrap().0) + .subgraph(nodes.get(base_task.resolved_label.as_str()).unwrap().0) .topo_sort() .map(|tasks| { tasks diff --git a/crates/task/Cargo.toml b/crates/task/Cargo.toml index 6bc7489d86eaff..e765af9d5b0263 100644 --- a/crates/task/Cargo.toml +++ b/crates/task/Cargo.toml @@ -22,6 +22,7 @@ sha2.workspace = true shellexpand.workspace = true util.workspace = true zed_actions.workspace = true +itertools.workspace = true [dev-dependencies] gpui = { workspace = true, features = ["test-support"] } diff --git a/crates/task/src/lib.rs b/crates/task/src/lib.rs index 0470e1e3a2841e..ed2fe329b3317d 100644 --- a/crates/task/src/lib.rs +++ b/crates/task/src/lib.rs @@ -78,6 +78,8 @@ pub struct ResolvedTask { /// Further actions that need to take place after the resolved task is spawned, /// with all task variables resolved. pub resolved: Option, + /// Pretasks with their variables expanded + pub resolved_pre_labels: Vec } impl ResolvedTask { diff --git a/crates/task/src/task_template.rs b/crates/task/src/task_template.rs index d1e10e490a4f4b..8176adb47d2cc7 100644 --- a/crates/task/src/task_template.rs +++ b/crates/task/src/task_template.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use itertools::Itertools as _; use util::serde::default_true; use anyhow::{bail, Context}; @@ -160,6 +161,7 @@ impl TaskTemplate { None => None, } .or(cx.cwd.clone()); + let human_readable_label = substitute_all_template_variables_in_str( &self.label, &truncated_variables, @@ -176,18 +178,32 @@ impl TaskTemplate { } string }); + let full_label = substitute_all_template_variables_in_str( &self.label, &task_variables, &variable_names, &mut substituted_variables, )?; + + let pre_labels = self + .pre + .iter() + .filter_map(|pre_label| substitute_all_template_variables_in_str( + pre_label, + &truncated_variables, + &variable_names, + &mut substituted_variables + )) + .collect_vec(); + let command = substitute_all_template_variables_in_str( &self.command, &task_variables, &variable_names, &mut substituted_variables, )?; + let args_with_substitutions = substitute_all_template_variables_in_vec( &self.args, &task_variables, @@ -228,6 +244,7 @@ impl TaskTemplate { substituted_variables, original_task: self.clone(), resolved_label: full_label.clone(), + resolved_pre_labels: pre_labels, resolved: Some(SpawnInTerminal { id, cwd, diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs index 21625cbe249831..5f18d474a8353d 100644 --- a/crates/tasks_ui/src/lib.rs +++ b/crates/tasks_ui/src/lib.rs @@ -5,7 +5,7 @@ use modal::{TaskOverrides, TasksModal}; use project::{Location, WorktreeId}; use task::{RevealTarget, TaskId}; use workspace::tasks::schedule_task; -use workspace::{tasks::schedule_resolved_task, Workspace}; +use workspace::{tasks::schedule_resolved_tasks, Workspace}; mod modal; mod settings; @@ -70,7 +70,7 @@ pub fn init(cx: &mut AppContext) { } } - schedule_resolved_task( + schedule_resolved_tasks( workspace, task_source_kind, last_scheduled_task, diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index 1dc63c4852a09e..00d0a25bc4f354 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -3,9 +3,7 @@ use std::sync::Arc; use crate::active_item_selection_properties; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusableView, - InteractiveElement, Model, ParentElement, Render, SharedString, Styled, Subscription, Task, - View, ViewContext, VisualContext, WeakView, + http_client::http::header::InvalidHeaderName, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusableView, InteractiveElement, Model, ParentElement, Render, SharedString, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView }; use picker::{highlighted_match_with_paths::HighlightedText, Picker, PickerDelegate}; use project::{task_store::TaskStore, TaskSourceKind}; @@ -17,7 +15,7 @@ use ui::{ WindowContext, }; use util::ResultExt; -use workspace::{tasks::schedule_resolved_task, ModalView, Workspace}; +use workspace::{tasks::schedule_resolved_tasks, ModalView, Workspace}; pub use zed_actions::{Rerun, Spawn}; /// A modal used to spawn new tasks. @@ -296,7 +294,36 @@ impl PickerDelegate for TasksModalDelegate { self.workspace .update(cx, |workspace, cx| { - schedule_resolved_task(workspace, task_source_kind, task, omit_history_entry, cx); + let worktree = match task_source_kind { + TaskSourceKind::Worktree { id, .. } => Some(id), + _ => None + }; + + let pre_tasks = workspace + .project() + .read(cx) + .task_store() + .read(cx) + .task_inventory() + .map_or(vec![], |inventory| { + inventory + .read(cx) + .build_pre_task_list( + &task, + worktree, + &self.task_context + ) + .unwrap_or(vec![]) + }); + + schedule_resolved_tasks( + workspace, + task_source_kind, + pre_tasks, + task, + omit_history_entry, + cx + ); }) .ok(); cx.emit(DismissEvent); @@ -443,7 +470,14 @@ impl PickerDelegate for TasksModalDelegate { } self.workspace .update(cx, |workspace, cx| { - schedule_resolved_task(workspace, task_source_kind, task, omit_history_entry, cx); + schedule_resolved_tasks( + workspace, + task_source_kind, + vec![], + task, + omit_history_entry, + cx + ); }) .ok(); cx.emit(DismissEvent); diff --git a/crates/workspace/src/tasks.rs b/crates/workspace/src/tasks.rs index c01e2ae52be327..f79b0e74b46648 100644 --- a/crates/workspace/src/tasks.rs +++ b/crates/workspace/src/tasks.rs @@ -1,3 +1,4 @@ +use itertools::Itertools as _; use project::TaskSourceKind; use remote::ConnectionState; use task::{ResolvedTask, TaskContext, TaskTemplate}; @@ -29,7 +30,7 @@ pub fn schedule_task( if let Some(spawn_in_terminal) = task_to_resolve.resolve_task(&task_source_kind.to_id_base(), task_cx) { - schedule_resolved_task( + schedule_resolved_tasks( workspace, task_source_kind, spawn_in_terminal, @@ -39,9 +40,10 @@ pub fn schedule_task( } } -pub fn schedule_resolved_task( +pub fn schedule_resolved_tasks( workspace: &mut Workspace, task_source_kind: TaskSourceKind, + pre_tasks: Vec, mut resolved_task: ResolvedTask, omit_history: bool, cx: &mut ViewContext, @@ -60,8 +62,15 @@ pub fn schedule_resolved_task( }); } + let mut all_tasks = pre_tasks + .into_iter() + .filter_map(|mut task| task.resolved.take()) + .collect_vec(); + + all_tasks.push(spawn_in_terminal); + cx.emit(crate::Event::SpawnTask { - action: Box::new(spawn_in_terminal), + action: all_tasks, }); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e4488650ec3b6d..af60f558076522 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -728,7 +728,7 @@ pub enum Event { ContactRequestedJoin(u64), WorkspaceCreated(WeakView), SpawnTask { - action: Box, + action: Vec, }, OpenBundledFile { text: Cow<'static, str>,