From bf295eac90488666b7abdd4c6dc671fb066031ad Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:26:12 +0100 Subject: [PATCH] Task::spawn now takes an optional task name as an argument. If it is not set, we fall back to opening a modal. This allows user to spawn tasks via keybind. --- crates/tasks_ui/src/lib.rs | 60 ++++++++++++++++++++++++++++++------ crates/tasks_ui/src/modal.rs | 21 +++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs index a3fa71bb125bd652917c706c22a15ec36bdf1575..4632cf1b5c2de0896b3be471ee20792305c8f9d8 100644 --- a/crates/tasks_ui/src/lib.rs +++ b/crates/tasks_ui/src/lib.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf}; use editor::Editor; use gpui::{AppContext, ViewContext, WindowContext}; use language::Point; -use modal::TasksModal; +use modal::{Spawn, TasksModal}; use project::{Location, WorktreeId}; use task::{Task, TaskContext}; use util::ResultExt; @@ -15,15 +15,7 @@ pub fn init(cx: &mut AppContext) { cx.observe_new_views( |workspace: &mut Workspace, _: &mut ViewContext| { workspace - .register_action(|workspace, _: &modal::Spawn, cx| { - let inventory = workspace.project().read(cx).task_inventory().clone(); - let workspace_handle = workspace.weak_handle(); - let cwd = task_cwd(workspace, cx).log_err().flatten(); - let task_context = task_context(workspace, cwd, cx); - workspace.toggle_modal(cx, |cx| { - TasksModal::new(inventory, task_context, workspace_handle, cx) - }) - }) + .register_action(spawn_task_or_modal) .register_action(move |workspace, action: &modal::Rerun, cx| { if let Some((task, old_context)) = workspace.project().update(cx, |project, cx| { @@ -47,6 +39,54 @@ pub fn init(cx: &mut AppContext) { .detach(); } +fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewContext) { + let inventory = workspace.project().read(cx).task_inventory().clone(); + let workspace_handle = workspace.weak_handle(); + let cwd = task_cwd(workspace, cx).log_err().flatten(); + let task_context = task_context(workspace, cwd, cx); + if let Some(name) = action.task_name.clone() { + // Do not actually show the modal. + spawn_task_with_name(name.clone(), cx); + } else { + workspace.toggle_modal(cx, |cx| { + TasksModal::new(inventory, task_context, workspace_handle, cx) + }) + } +} + +fn spawn_task_with_name(name: String, cx: &mut ViewContext) { + cx.spawn(|workspace, mut cx| async move { + let did_spawn = workspace + .update(&mut cx, |this, cx| { + let active_item = this + .active_item(cx) + .and_then(|item| item.project_path(cx)) + .map(|path| path.worktree_id); + let tasks = this.project().update(cx, |project, cx| { + project.task_inventory().update(cx, |inventory, cx| { + inventory.list_tasks(None, active_item, false, cx) + }) + }); + let (_, target_task) = tasks.into_iter().find(|(_, task)| task.name() == name)?; + let cwd = task_cwd(this, cx).log_err().flatten(); + let task_context = task_context(this, cwd, cx); + schedule_task(this, target_task.as_ref(), task_context, cx); + Some(()) + }) + .ok() + .flatten() + .is_some(); + if !did_spawn { + workspace + .update(&mut cx, |workspace, cx| { + spawn_task_or_modal(workspace, &Spawn::default(), cx); + }) + .ok(); + } + }) + .detach(); +} + fn task_context( workspace: &Workspace, cwd: Option, diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index 491ed05971bbd95a15447a3a3089fff77b2d61bc..5db35192e66fb843a9c12402d62012a3656485bc 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -2,9 +2,9 @@ use std::{path::PathBuf, sync::Arc}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, impl_actions, rems, AppContext, DismissEvent, EventEmitter, FocusableView, - InteractiveElement, Model, ParentElement, Render, SharedString, Styled, Subscription, View, - ViewContext, VisualContext, WeakView, + impl_actions, rems, AppContext, DismissEvent, EventEmitter, FocusableView, InteractiveElement, + Model, ParentElement, Render, SharedString, Styled, Subscription, View, ViewContext, + VisualContext, WeakView, }; use picker::{ highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText}, @@ -18,7 +18,16 @@ use workspace::{ModalView, Workspace}; use crate::schedule_task; use serde::Deserialize; -actions!(task, [Spawn]); + +/// Spawn a task with name or open tasks modal +#[derive(PartialEq, Clone, Deserialize, Default)] +pub struct Spawn { + #[serde(default)] + /// Name of the task to spawn. + /// If it is not set, a modal with a list of available tasks is opened instead. + /// Defaults to None. + pub task_name: Option, +} /// Rerun last task #[derive(PartialEq, Clone, Deserialize, Default)] @@ -31,7 +40,7 @@ pub struct Rerun { pub reevaluate_context: bool, } -impl_actions!(task, [Rerun]); +impl_actions!(task, [Rerun, Spawn]); /// A modal used to spawn new tasks. pub(crate) struct TasksModalDelegate { @@ -426,7 +435,7 @@ mod tests { workspace: &View, cx: &mut VisualTestContext, ) -> View> { - cx.dispatch_action(crate::modal::Spawn); + cx.dispatch_action(crate::modal::Spawn::default()); workspace.update(cx, |workspace, cx| { workspace .active_modal::(cx)