Move task centering code closer to user input (#22082)
Follow-up of https://github.com/zed-industries/zed/pull/22004 * Reuse center terminals for tasks, when requested * Extend task templates with `RevealTarget`, moving it from `TaskSpawnTarget` into the core library * Use `reveal_target` instead of `target` to avoid misinterpretations in the task template context * Do not expose `SpawnInTerminal` to user interface, avoid it implementing `Serialize` and `Deserialize` * Remove `NewCenterTask` action, extending `task::Spawn` interface instead * Do not require any extra unrelated parameters during task resolution, instead, use task overrides on the resolved tasks on the modal side * Add keybindings for opening the task modal in the `RevealTarget::Center` mode Release Notes: - N/A
This commit is contained in:
parent
ea012075fc
commit
bc113e4b51
17 changed files with 356 additions and 285 deletions
|
@ -1,9 +1,9 @@
|
|||
use ::settings::Settings;
|
||||
use editor::{tasks::task_context, Editor};
|
||||
use gpui::{AppContext, Task as AsyncTask, ViewContext, WindowContext};
|
||||
use modal::TasksModal;
|
||||
use modal::{TaskOverrides, TasksModal};
|
||||
use project::{Location, WorktreeId};
|
||||
use task::TaskId;
|
||||
use task::{RevealTarget, TaskId};
|
||||
use workspace::tasks::schedule_task;
|
||||
use workspace::{tasks::schedule_resolved_task, Workspace};
|
||||
|
||||
|
@ -11,7 +11,6 @@ mod modal;
|
|||
mod settings;
|
||||
|
||||
pub use modal::{Rerun, Spawn};
|
||||
use zed_actions::TaskSpawnTarget;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
settings::TaskSettings::register(cx);
|
||||
|
@ -54,7 +53,6 @@ pub fn init(cx: &mut AppContext) {
|
|||
task_source_kind,
|
||||
&original_task,
|
||||
&task_context,
|
||||
Default::default(),
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
|
@ -81,7 +79,7 @@ pub fn init(cx: &mut AppContext) {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
toggle_modal(workspace, cx).detach();
|
||||
toggle_modal(workspace, None, cx).detach();
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -90,14 +88,25 @@ pub fn init(cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewContext<Workspace>) {
|
||||
match &action.task_name {
|
||||
Some(name) => spawn_task_with_name(name.clone(), action.target.unwrap_or_default(), cx)
|
||||
.detach_and_log_err(cx),
|
||||
None => toggle_modal(workspace, cx).detach(),
|
||||
match action {
|
||||
Spawn::ByName {
|
||||
task_name,
|
||||
reveal_target,
|
||||
} => {
|
||||
let overrides = reveal_target.map(|reveal_target| TaskOverrides {
|
||||
reveal_target: Some(reveal_target),
|
||||
});
|
||||
spawn_task_with_name(task_name.clone(), overrides, cx).detach_and_log_err(cx)
|
||||
}
|
||||
Spawn::ViaModal { reveal_target } => toggle_modal(workspace, *reveal_target, cx).detach(),
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_modal(workspace: &mut Workspace, cx: &mut ViewContext<'_, Workspace>) -> AsyncTask<()> {
|
||||
fn toggle_modal(
|
||||
workspace: &mut Workspace,
|
||||
reveal_target: Option<RevealTarget>,
|
||||
cx: &mut ViewContext<'_, Workspace>,
|
||||
) -> AsyncTask<()> {
|
||||
let task_store = workspace.project().read(cx).task_store().clone();
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let can_open_modal = workspace.project().update(cx, |project, cx| {
|
||||
|
@ -110,7 +119,15 @@ fn toggle_modal(workspace: &mut Workspace, cx: &mut ViewContext<'_, Workspace>)
|
|||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.toggle_modal(cx, |cx| {
|
||||
TasksModal::new(task_store.clone(), task_context, workspace_handle, cx)
|
||||
TasksModal::new(
|
||||
task_store.clone(),
|
||||
task_context,
|
||||
reveal_target.map(|target| TaskOverrides {
|
||||
reveal_target: Some(target),
|
||||
}),
|
||||
workspace_handle,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
|
@ -122,7 +139,7 @@ fn toggle_modal(workspace: &mut Workspace, cx: &mut ViewContext<'_, Workspace>)
|
|||
|
||||
fn spawn_task_with_name(
|
||||
name: String,
|
||||
task_target: TaskSpawnTarget,
|
||||
overrides: Option<TaskOverrides>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> AsyncTask<anyhow::Result<()>> {
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
|
@ -157,14 +174,18 @@ fn spawn_task_with_name(
|
|||
|
||||
let did_spawn = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let (task_source_kind, target_task) =
|
||||
let (task_source_kind, mut target_task) =
|
||||
tasks.into_iter().find(|(_, task)| task.label == name)?;
|
||||
if let Some(overrides) = &overrides {
|
||||
if let Some(target_override) = overrides.reveal_target {
|
||||
target_task.reveal_target = target_override;
|
||||
}
|
||||
}
|
||||
schedule_task(
|
||||
workspace,
|
||||
task_source_kind,
|
||||
&target_task,
|
||||
&task_context,
|
||||
task_target,
|
||||
false,
|
||||
cx,
|
||||
);
|
||||
|
@ -174,7 +195,13 @@ fn spawn_task_with_name(
|
|||
if !did_spawn {
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
spawn_task_or_modal(workspace, &Spawn::default(), cx);
|
||||
spawn_task_or_modal(
|
||||
workspace,
|
||||
&Spawn::ViaModal {
|
||||
reveal_target: overrides.and_then(|overrides| overrides.reveal_target),
|
||||
},
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use gpui::{
|
|||
};
|
||||
use picker::{highlighted_match_with_paths::HighlightedText, Picker, PickerDelegate};
|
||||
use project::{task_store::TaskStore, TaskSourceKind};
|
||||
use task::{ResolvedTask, TaskContext, TaskTemplate};
|
||||
use task::{ResolvedTask, RevealTarget, TaskContext, TaskTemplate};
|
||||
use ui::{
|
||||
div, h_flex, v_flex, ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color,
|
||||
FluentBuilder as _, Icon, IconButton, IconButtonShape, IconName, IconSize, IntoElement,
|
||||
|
@ -24,6 +24,7 @@ pub use zed_actions::{Rerun, Spawn};
|
|||
pub(crate) struct TasksModalDelegate {
|
||||
task_store: Model<TaskStore>,
|
||||
candidates: Option<Vec<(TaskSourceKind, ResolvedTask)>>,
|
||||
task_overrides: Option<TaskOverrides>,
|
||||
last_used_candidate_index: Option<usize>,
|
||||
divider_index: Option<usize>,
|
||||
matches: Vec<StringMatch>,
|
||||
|
@ -34,12 +35,28 @@ pub(crate) struct TasksModalDelegate {
|
|||
placeholder_text: Arc<str>,
|
||||
}
|
||||
|
||||
/// Task template amendments to do before resolving the context.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub(crate) struct TaskOverrides {
|
||||
/// See [`RevealTarget`].
|
||||
pub(crate) reveal_target: Option<RevealTarget>,
|
||||
}
|
||||
|
||||
impl TasksModalDelegate {
|
||||
fn new(
|
||||
task_store: Model<TaskStore>,
|
||||
task_context: TaskContext,
|
||||
task_overrides: Option<TaskOverrides>,
|
||||
workspace: WeakView<Workspace>,
|
||||
) -> Self {
|
||||
let placeholder_text = if let Some(TaskOverrides {
|
||||
reveal_target: Some(RevealTarget::Center),
|
||||
}) = &task_overrides
|
||||
{
|
||||
Arc::from("Find a task, or run a command in the central pane")
|
||||
} else {
|
||||
Arc::from("Find a task, or run a command")
|
||||
};
|
||||
Self {
|
||||
task_store,
|
||||
workspace,
|
||||
|
@ -50,7 +67,8 @@ impl TasksModalDelegate {
|
|||
selected_index: 0,
|
||||
prompt: String::default(),
|
||||
task_context,
|
||||
placeholder_text: Arc::from("Find a task, or run a command"),
|
||||
task_overrides,
|
||||
placeholder_text,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,14 +79,20 @@ impl TasksModalDelegate {
|
|||
|
||||
let source_kind = TaskSourceKind::UserInput;
|
||||
let id_base = source_kind.to_id_base();
|
||||
let new_oneshot = TaskTemplate {
|
||||
let mut new_oneshot = TaskTemplate {
|
||||
label: self.prompt.clone(),
|
||||
command: self.prompt.clone(),
|
||||
..TaskTemplate::default()
|
||||
};
|
||||
if let Some(TaskOverrides {
|
||||
reveal_target: Some(reveal_target),
|
||||
}) = &self.task_overrides
|
||||
{
|
||||
new_oneshot.reveal_target = *reveal_target;
|
||||
}
|
||||
Some((
|
||||
source_kind,
|
||||
new_oneshot.resolve_task(&id_base, Default::default(), &self.task_context)?,
|
||||
new_oneshot.resolve_task(&id_base, &self.task_context)?,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -100,12 +124,13 @@ impl TasksModal {
|
|||
pub(crate) fn new(
|
||||
task_store: Model<TaskStore>,
|
||||
task_context: TaskContext,
|
||||
task_overrides: Option<TaskOverrides>,
|
||||
workspace: WeakView<Workspace>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new_view(|cx| {
|
||||
Picker::uniform_list(
|
||||
TasksModalDelegate::new(task_store, task_context, workspace),
|
||||
TasksModalDelegate::new(task_store, task_context, task_overrides, workspace),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
@ -257,9 +282,17 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
.as_ref()
|
||||
.map(|candidates| candidates[ix].clone())
|
||||
});
|
||||
let Some((task_source_kind, task)) = task else {
|
||||
let Some((task_source_kind, mut task)) = task else {
|
||||
return;
|
||||
};
|
||||
if let Some(TaskOverrides {
|
||||
reveal_target: Some(reveal_target),
|
||||
}) = &self.task_overrides
|
||||
{
|
||||
if let Some(resolved_task) = &mut task.resolved {
|
||||
resolved_task.reveal_target = *reveal_target;
|
||||
}
|
||||
}
|
||||
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
|
@ -396,9 +429,18 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
}
|
||||
|
||||
fn confirm_input(&mut self, omit_history_entry: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||
let Some((task_source_kind, task)) = self.spawn_oneshot() else {
|
||||
let Some((task_source_kind, mut task)) = self.spawn_oneshot() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(TaskOverrides {
|
||||
reveal_target: Some(reveal_target),
|
||||
}) = self.task_overrides
|
||||
{
|
||||
if let Some(resolved_task) = &mut task.resolved {
|
||||
resolved_task.reveal_target = reveal_target;
|
||||
}
|
||||
}
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
schedule_resolved_task(workspace, task_source_kind, task, omit_history_entry, cx);
|
||||
|
@ -682,9 +724,9 @@ mod tests {
|
|||
"No query should be added to the list, as it was submitted with secondary action (that maps to omit_history = true)"
|
||||
);
|
||||
|
||||
cx.dispatch_action(Spawn {
|
||||
task_name: Some("example task".to_string()),
|
||||
target: None,
|
||||
cx.dispatch_action(Spawn::ByName {
|
||||
task_name: "example task".to_string(),
|
||||
reveal_target: None,
|
||||
});
|
||||
let tasks_picker = workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
|
@ -995,7 +1037,7 @@ mod tests {
|
|||
workspace: &View<Workspace>,
|
||||
cx: &mut VisualTestContext,
|
||||
) -> View<Picker<TasksModalDelegate>> {
|
||||
cx.dispatch_action(Spawn::default());
|
||||
cx.dispatch_action(Spawn::modal());
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_modal::<TasksModal>(cx)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue