mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 21:31:44 +03:00
Added menu::UseSelectedQuery
command that populates task modal query with the selected task name (#8572)
This commit is contained in:
parent
9bd5ebb74b
commit
b7429bf29d
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -9140,12 +9140,15 @@ name = "tasks_ui"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"editor",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"language",
|
||||||
"menu",
|
"menu",
|
||||||
"picker",
|
"picker",
|
||||||
"project",
|
"project",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"task",
|
"task",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"ctrl-enter": "menu::SecondaryConfirm",
|
"ctrl-enter": "menu::SecondaryConfirm",
|
||||||
"escape": "menu::Cancel",
|
"escape": "menu::Cancel",
|
||||||
"ctrl-c": "menu::Cancel",
|
"ctrl-c": "menu::Cancel",
|
||||||
|
"shift-enter": "menu::UseSelectedQuery",
|
||||||
"ctrl-shift-w": "workspace::CloseWindow",
|
"ctrl-shift-w": "workspace::CloseWindow",
|
||||||
"shift-escape": "workspace::ToggleZoom",
|
"shift-escape": "workspace::ToggleZoom",
|
||||||
"ctrl-o": "workspace::Open",
|
"ctrl-o": "workspace::Open",
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"cmd-enter": "menu::SecondaryConfirm",
|
"cmd-enter": "menu::SecondaryConfirm",
|
||||||
"escape": "menu::Cancel",
|
"escape": "menu::Cancel",
|
||||||
"ctrl-c": "menu::Cancel",
|
"ctrl-c": "menu::Cancel",
|
||||||
|
"shift-enter": "menu::UseSelectedQuery",
|
||||||
"cmd-shift-w": "workspace::CloseWindow",
|
"cmd-shift-w": "workspace::CloseWindow",
|
||||||
"shift-escape": "workspace::ToggleZoom",
|
"shift-escape": "workspace::ToggleZoom",
|
||||||
"cmd-o": "workspace::Open",
|
"cmd-o": "workspace::Open",
|
||||||
|
@ -19,6 +19,7 @@ actions!(
|
|||||||
SelectNext,
|
SelectNext,
|
||||||
SelectFirst,
|
SelectFirst,
|
||||||
SelectLast,
|
SelectLast,
|
||||||
ShowContextMenu
|
ShowContextMenu,
|
||||||
|
UseSelectedQuery,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -32,6 +32,7 @@ pub struct Picker<D: PickerDelegate> {
|
|||||||
|
|
||||||
pub trait PickerDelegate: Sized + 'static {
|
pub trait PickerDelegate: Sized + 'static {
|
||||||
type ListItem: IntoElement;
|
type ListItem: IntoElement;
|
||||||
|
|
||||||
fn match_count(&self) -> usize;
|
fn match_count(&self) -> usize;
|
||||||
fn selected_index(&self) -> usize;
|
fn selected_index(&self) -> usize;
|
||||||
fn separators_after_indices(&self) -> Vec<usize> {
|
fn separators_after_indices(&self) -> Vec<usize> {
|
||||||
@ -57,6 +58,9 @@ pub trait PickerDelegate: Sized + 'static {
|
|||||||
|
|
||||||
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
|
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
|
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
|
||||||
|
fn selected_as_query(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
&self,
|
&self,
|
||||||
@ -239,6 +243,13 @@ impl<D: PickerDelegate> Picker<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn use_selected_query(&mut self, _: &menu::UseSelectedQuery, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(new_query) = self.delegate.selected_as_query() {
|
||||||
|
self.set_query(new_query, cx);
|
||||||
|
cx.stop_propagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_click(&mut self, ix: usize, secondary: bool, cx: &mut ViewContext<Self>) {
|
fn handle_click(&mut self, ix: usize, secondary: bool, cx: &mut ViewContext<Self>) {
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
cx.prevent_default();
|
cx.prevent_default();
|
||||||
@ -384,6 +395,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
|||||||
.on_action(cx.listener(Self::cancel))
|
.on_action(cx.listener(Self::cancel))
|
||||||
.on_action(cx.listener(Self::confirm))
|
.on_action(cx.listener(Self::confirm))
|
||||||
.on_action(cx.listener(Self::secondary_confirm))
|
.on_action(cx.listener(Self::secondary_confirm))
|
||||||
|
.on_action(cx.listener(Self::use_selected_query))
|
||||||
.child(picker_editor)
|
.child(picker_editor)
|
||||||
.child(Divider::horizontal())
|
.child(Divider::horizontal())
|
||||||
.when(self.delegate.match_count() > 0, |el| {
|
.when(self.delegate.match_count() > 0, |el| {
|
||||||
|
@ -99,6 +99,8 @@ pub use language::Location;
|
|||||||
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
|
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
|
||||||
pub use project_core::project_settings;
|
pub use project_core::project_settings;
|
||||||
pub use project_core::worktree::{self, *};
|
pub use project_core::worktree::{self, *};
|
||||||
|
#[cfg(feature = "test-support")]
|
||||||
|
pub use task_inventory::test_inventory::*;
|
||||||
pub use task_inventory::{Inventory, TaskSourceKind};
|
pub use task_inventory::{Inventory, TaskSourceKind};
|
||||||
|
|
||||||
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
|
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
|
||||||
|
@ -54,7 +54,7 @@ impl TaskSourceKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Inventory {
|
impl Inventory {
|
||||||
pub(crate) fn new(cx: &mut AppContext) -> Model<Self> {
|
pub fn new(cx: &mut AppContext) -> Model<Self> {
|
||||||
cx.new_model(|_| Self {
|
cx.new_model(|_| Self {
|
||||||
sources: Vec::new(),
|
sources: Vec::new(),
|
||||||
last_scheduled_tasks: VecDeque::new(),
|
last_scheduled_tasks: VecDeque::new(),
|
||||||
@ -219,12 +219,140 @@ impl Inventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "test-support")]
|
||||||
|
pub mod test_inventory {
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use gpui::{AppContext, Context as _, Model, ModelContext, TestAppContext};
|
||||||
|
use project_core::worktree::WorktreeId;
|
||||||
|
use task::{Task, TaskId, TaskSource};
|
||||||
|
|
||||||
|
use crate::Inventory;
|
||||||
|
|
||||||
|
use super::TaskSourceKind;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct TestTask {
|
||||||
|
pub id: task::TaskId,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task for TestTask {
|
||||||
|
fn id(&self) -> &TaskId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cwd(&self) -> Option<&Path> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec(&self, _cwd: Option<PathBuf>) -> Option<task::SpawnInTerminal> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StaticTestSource {
|
||||||
|
pub tasks: Vec<TestTask>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticTestSource {
|
||||||
|
pub fn new(
|
||||||
|
task_names: impl IntoIterator<Item = String>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Model<Box<dyn TaskSource>> {
|
||||||
|
cx.new_model(|_| {
|
||||||
|
Box::new(Self {
|
||||||
|
tasks: task_names
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, name)| TestTask {
|
||||||
|
id: TaskId(format!("task_{i}_{name}")),
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}) as Box<dyn TaskSource>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskSource for StaticTestSource {
|
||||||
|
fn tasks_for_path(
|
||||||
|
&mut self,
|
||||||
|
_path: Option<&Path>,
|
||||||
|
_cx: &mut ModelContext<Box<dyn TaskSource>>,
|
||||||
|
) -> Vec<Arc<dyn Task>> {
|
||||||
|
self.tasks
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|task| Arc::new(task) as Arc<dyn Task>)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_task_names(
|
||||||
|
inventory: &Model<Inventory>,
|
||||||
|
path: Option<&Path>,
|
||||||
|
worktree: Option<WorktreeId>,
|
||||||
|
lru: bool,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> Vec<String> {
|
||||||
|
inventory.update(cx, |inventory, cx| {
|
||||||
|
inventory
|
||||||
|
.list_tasks(path, worktree, lru, cx)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, task)| task.name().to_string())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_task_used(
|
||||||
|
inventory: &Model<Inventory>,
|
||||||
|
task_name: &str,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) {
|
||||||
|
inventory.update(cx, |inventory, cx| {
|
||||||
|
let task = inventory
|
||||||
|
.list_tasks(None, None, false, cx)
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, task)| task.name() == task_name)
|
||||||
|
.unwrap_or_else(|| panic!("Failed to find task with name {task_name}"));
|
||||||
|
inventory.task_scheduled(task.1.id().clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_tasks(
|
||||||
|
inventory: &Model<Inventory>,
|
||||||
|
path: Option<&Path>,
|
||||||
|
worktree: Option<WorktreeId>,
|
||||||
|
lru: bool,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> Vec<(TaskSourceKind, String)> {
|
||||||
|
inventory.update(cx, |inventory, cx| {
|
||||||
|
inventory
|
||||||
|
.list_tasks(path, worktree, lru, cx)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(source_kind, task)| (source_kind, task.name().to_string()))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
|
|
||||||
|
use super::test_inventory::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
@ -532,114 +660,4 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
struct TestTask {
|
|
||||||
id: TaskId,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for TestTask {
|
|
||||||
fn id(&self) -> &TaskId {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cwd(&self) -> Option<&Path> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exec(&self, _cwd: Option<PathBuf>) -> Option<task::SpawnInTerminal> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StaticTestSource {
|
|
||||||
tasks: Vec<TestTask>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticTestSource {
|
|
||||||
fn new(
|
|
||||||
task_names: impl IntoIterator<Item = String>,
|
|
||||||
cx: &mut AppContext,
|
|
||||||
) -> Model<Box<dyn TaskSource>> {
|
|
||||||
cx.new_model(|_| {
|
|
||||||
Box::new(Self {
|
|
||||||
tasks: task_names
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, name)| TestTask {
|
|
||||||
id: TaskId(format!("task_{i}_{name}")),
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}) as Box<dyn TaskSource>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TaskSource for StaticTestSource {
|
|
||||||
fn tasks_for_path(
|
|
||||||
&mut self,
|
|
||||||
// static task source does not depend on path input
|
|
||||||
_: Option<&Path>,
|
|
||||||
_cx: &mut ModelContext<Box<dyn TaskSource>>,
|
|
||||||
) -> Vec<Arc<dyn Task>> {
|
|
||||||
self.tasks
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|task| Arc::new(task) as Arc<dyn Task>)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_task_names(
|
|
||||||
inventory: &Model<Inventory>,
|
|
||||||
path: Option<&Path>,
|
|
||||||
worktree: Option<WorktreeId>,
|
|
||||||
lru: bool,
|
|
||||||
cx: &mut TestAppContext,
|
|
||||||
) -> Vec<String> {
|
|
||||||
inventory.update(cx, |inventory, cx| {
|
|
||||||
inventory
|
|
||||||
.list_tasks(path, worktree, lru, cx)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, task)| task.name().to_string())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_tasks(
|
|
||||||
inventory: &Model<Inventory>,
|
|
||||||
path: Option<&Path>,
|
|
||||||
worktree: Option<WorktreeId>,
|
|
||||||
lru: bool,
|
|
||||||
cx: &mut TestAppContext,
|
|
||||||
) -> Vec<(TaskSourceKind, String)> {
|
|
||||||
inventory.update(cx, |inventory, cx| {
|
|
||||||
inventory
|
|
||||||
.list_tasks(path, worktree, lru, cx)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(source_kind, task)| (source_kind, task.name().to_string()))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_task_used(inventory: &Model<Inventory>, task_name: &str, cx: &mut TestAppContext) {
|
|
||||||
inventory.update(cx, |inventory, cx| {
|
|
||||||
let (_, task) = inventory
|
|
||||||
.list_tasks(None, None, false, cx)
|
|
||||||
.into_iter()
|
|
||||||
.find(|(_, task)| task.name() == task_name)
|
|
||||||
.unwrap_or_else(|| panic!("Failed to find task with name {task_name}"));
|
|
||||||
inventory.task_scheduled(task.id().clone());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,3 +17,10 @@ serde.workspace = true
|
|||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
editor = { workspace = true, features = ["test-support"] }
|
||||||
|
language = { workspace = true, features = ["test-support"] }
|
||||||
|
project = { workspace = true, features = ["test-support"] }
|
||||||
|
serde_json.workspace = true
|
||||||
|
workspace = { workspace = true, features = ["test-support"] }
|
||||||
|
@ -97,6 +97,7 @@ impl TasksModal {
|
|||||||
impl Render for TasksModal {
|
impl Render for TasksModal {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::prelude::IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::prelude::IntoElement {
|
||||||
v_flex()
|
v_flex()
|
||||||
|
.key_context("TasksModal")
|
||||||
.w(rems(34.))
|
.w(rems(34.))
|
||||||
.child(self.picker.clone())
|
.child(self.picker.clone())
|
||||||
.on_mouse_down_out(cx.listener(|modal, _, cx| {
|
.on_mouse_down_out(cx.listener(|modal, _, cx| {
|
||||||
@ -134,9 +135,10 @@ impl PickerDelegate for TasksModalDelegate {
|
|||||||
|
|
||||||
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
|
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
|
||||||
Arc::from(format!(
|
Arc::from(format!(
|
||||||
"{} runs the selected task, {} spawns a bash-like task from the prompt",
|
"{} use task name as prompt, {} spawns a bash-like task from the prompt, {} runs the selected task",
|
||||||
cx.keystroke_text_for(&menu::Confirm),
|
cx.keystroke_text_for(&menu::UseSelectedQuery),
|
||||||
cx.keystroke_text_for(&menu::SecondaryConfirm),
|
cx.keystroke_text_for(&menu::SecondaryConfirm),
|
||||||
|
cx.keystroke_text_for(&menu::Confirm),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,4 +268,179 @@ impl PickerDelegate for TasksModalDelegate {
|
|||||||
.child(highlighted_location.render(cx)),
|
.child(highlighted_location.render(cx)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn selected_as_query(&self) -> Option<String> {
|
||||||
|
Some(self.matches.get(self.selected_index())?.string.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use gpui::{TestAppContext, VisualTestContext};
|
||||||
|
use project::{FakeFs, Project};
|
||||||
|
use serde_json::json;
|
||||||
|
use workspace::AppState;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_name(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
fs.insert_tree(
|
||||||
|
"/dir",
|
||||||
|
json!({
|
||||||
|
".zed": {
|
||||||
|
"tasks.json": r#"[
|
||||||
|
{
|
||||||
|
"label": "example task",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["4"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "another one",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["55"]
|
||||||
|
},
|
||||||
|
]"#,
|
||||||
|
},
|
||||||
|
"a.ts": "a"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||||
|
project.update(cx, |project, cx| {
|
||||||
|
project.task_inventory().update(cx, |inventory, cx| {
|
||||||
|
inventory.add_source(TaskSourceKind::UserInput, |cx| OneshotSource::new(cx), cx)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
|
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||||
|
assert_eq!(
|
||||||
|
query(&tasks_picker, cx),
|
||||||
|
"",
|
||||||
|
"Initial query should be empty"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec!["another one", "example task"],
|
||||||
|
"Initial tasks should be listed in alphabetical order"
|
||||||
|
);
|
||||||
|
|
||||||
|
let query_str = "tas";
|
||||||
|
cx.simulate_input(query_str);
|
||||||
|
assert_eq!(query(&tasks_picker, cx), query_str);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec!["example task"],
|
||||||
|
"Only one task should match the query {query_str}"
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.dispatch_action(menu::UseSelectedQuery);
|
||||||
|
assert_eq!(
|
||||||
|
query(&tasks_picker, cx),
|
||||||
|
"example task",
|
||||||
|
"Query should be set to the selected task's name"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec!["example task"],
|
||||||
|
"No other tasks should be listed"
|
||||||
|
);
|
||||||
|
cx.dispatch_action(menu::Confirm);
|
||||||
|
|
||||||
|
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||||
|
assert_eq!(
|
||||||
|
query(&tasks_picker, cx),
|
||||||
|
"",
|
||||||
|
"Query should be reset after confirming"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec!["example task", "another one"],
|
||||||
|
"Last recently used task should be listed first"
|
||||||
|
);
|
||||||
|
|
||||||
|
let query_str = "echo 4";
|
||||||
|
cx.simulate_input(query_str);
|
||||||
|
assert_eq!(query(&tasks_picker, cx), query_str);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
Vec::<String>::new(),
|
||||||
|
"No tasks should match custom command query"
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.dispatch_action(menu::SecondaryConfirm);
|
||||||
|
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||||
|
assert_eq!(
|
||||||
|
query(&tasks_picker, cx),
|
||||||
|
"",
|
||||||
|
"Query should be reset after confirming"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec![query_str, "example task", "another one"],
|
||||||
|
"Last recently used one show task should be listed first"
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.dispatch_action(menu::UseSelectedQuery);
|
||||||
|
assert_eq!(
|
||||||
|
query(&tasks_picker, cx),
|
||||||
|
query_str,
|
||||||
|
"Query should be set to the custom task's name"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
task_names(&tasks_picker, cx),
|
||||||
|
vec![query_str],
|
||||||
|
"Only custom task should be listed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_spawn_tasks(
|
||||||
|
workspace: &View<Workspace>,
|
||||||
|
cx: &mut VisualTestContext,
|
||||||
|
) -> View<Picker<TasksModalDelegate>> {
|
||||||
|
cx.dispatch_action(crate::modal::Spawn);
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace
|
||||||
|
.active_modal::<TasksModal>(cx)
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.picker
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query(spawn_tasks: &View<Picker<TasksModalDelegate>>, cx: &mut VisualTestContext) -> String {
|
||||||
|
spawn_tasks.update(cx, |spawn_tasks, cx| spawn_tasks.query(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_names(
|
||||||
|
spawn_tasks: &View<Picker<TasksModalDelegate>>,
|
||||||
|
cx: &mut VisualTestContext,
|
||||||
|
) -> Vec<String> {
|
||||||
|
spawn_tasks.update(cx, |spawn_tasks, _| {
|
||||||
|
spawn_tasks
|
||||||
|
.delegate
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.map(|hit| hit.string.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||||
|
cx.update(|cx| {
|
||||||
|
let state = AppState::test(cx);
|
||||||
|
language::init(cx);
|
||||||
|
crate::init(cx);
|
||||||
|
editor::init(cx);
|
||||||
|
workspace::init_settings(cx);
|
||||||
|
Project::init_settings(cx);
|
||||||
|
state
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ Zed supports ways to spawn (and rerun) commands using its integrated terminal to
|
|||||||
|
|
||||||
Currently, two kinds of tasks are supported, but more will be added in the future.
|
Currently, two kinds of tasks are supported, but more will be added in the future.
|
||||||
|
|
||||||
|
All tasks are are sorted in LRU order and their names can be used (with `menu::UseSelectedQuery`, `shift-enter` by default) as an input text for quicker oneshot task edit-spawn cycle.
|
||||||
|
|
||||||
## Static tasks
|
## Static tasks
|
||||||
|
|
||||||
Tasks, defined in a config file (`tasks.json` in the Zed config directory) that do not depend on the current editor or its content.
|
Tasks, defined in a config file (`tasks.json` in the Zed config directory) that do not depend on the current editor or its content.
|
||||||
|
Loading…
Reference in New Issue
Block a user