From 222034cacf96fb20af658e23ef104070bd620409 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 19 Apr 2024 10:51:50 +0300 Subject: [PATCH] Always provide default task context (#10764) Based on https://github.com/zed-industries/zed/issues/8324?notification_referrer_id=NT_kwDOACkO1bI5NTk0NjM0NzkyOjI2OTA3NzM¬ifications_query=repo%3Azed-industries%2Fzed+is%3Aunread#issuecomment-2065551553 Release Notes: - Fixed certain files' task modal not showing context-based tasks --- crates/language/src/task_context.rs | 6 +- crates/languages/src/lib.rs | 16 +---- crates/tasks_ui/src/lib.rs | 4 +- crates/tasks_ui/src/modal.rs | 100 +++++++++++++++++++++++++++- 4 files changed, 106 insertions(+), 20 deletions(-) diff --git a/crates/language/src/task_context.rs b/crates/language/src/task_context.rs index 901dce0697..06b8b0a418 100644 --- a/crates/language/src/task_context.rs +++ b/crates/language/src/task_context.rs @@ -15,9 +15,9 @@ pub trait ContextProvider: Send + Sync { /// Builds a specific context to be placed on top of the basic one (replacing all conflicting entries) and to be used for task resolving later. fn build_context( &self, - _: Option<&Path>, - _: &Location, - _: &mut AppContext, + _worktree_abs_path: Option<&Path>, + _location: &Location, + _cx: &mut AppContext, ) -> Result { Ok(TaskVariables::default()) } diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 4b10cb0de1..44d9c02b4a 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -85,13 +85,7 @@ pub fn init( config.name.clone(), config.grammar.clone(), config.matcher.clone(), - move || { - Ok(( - config.clone(), - load_queries($name), - Some(Arc::new(language::BasicContextProvider)), - )) - }, + move || Ok((config.clone(), load_queries($name), None)), ); }; ($name:literal, $adapters:expr) => { @@ -105,13 +99,7 @@ pub fn init( config.name.clone(), config.grammar.clone(), config.matcher.clone(), - move || { - Ok(( - config.clone(), - load_queries($name), - Some(Arc::new(language::BasicContextProvider)), - )) - }, + move || Ok((config.clone(), load_queries($name), None)), ); }; ($name:literal, $adapters:expr, $context_provider:expr) => { diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs index 37f1aed9cd..6458aca7f5 100644 --- a/crates/tasks_ui/src/lib.rs +++ b/crates/tasks_ui/src/lib.rs @@ -168,8 +168,8 @@ fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContex let language_context_provider = buffer .read(cx) .language() - .and_then(|language| language.context_provider())?; - + .and_then(|language| language.context_provider()) + .unwrap_or_else(|| Arc::new(BasicContextProvider)); let selection_range = selection.range(); let start = editor_snapshot .display_snapshot diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index 2662997cf2..2fa045f481 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -418,7 +418,11 @@ impl PickerDelegate for TasksModalDelegate { #[cfg(test)] mod tests { + use std::path::PathBuf; + + use editor::Editor; use gpui::{TestAppContext, VisualTestContext}; + use language::Point; use project::{FakeFs, Project}; use serde_json::json; @@ -574,6 +578,100 @@ mod tests { ); } + #[gpui::test] + async fn test_basic_context_for_simple_files(cx: &mut TestAppContext) { + crate::tests::init_test(cx); + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + "/dir", + json!({ + ".zed": { + "tasks.json": r#"[ + { + "label": "hello from $ZED_FILE:$ZED_ROW:$ZED_COLUMN", + "command": "echo", + "args": ["hello", "from", "$ZED_FILE", ":", "$ZED_ROW", ":", "$ZED_COLUMN"] + }, + { + "label": "opened now: $ZED_WORKTREE_ROOT", + "command": "echo", + "args": ["opened", "now:", "$ZED_WORKTREE_ROOT"] + } + ]"#, + }, + "file_without_extension": "aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaa", + "file_with.odd_extension": "b", + }), + ) + .await; + + let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + + let tasks_picker = open_spawn_tasks(&workspace, cx); + assert_eq!( + task_names(&tasks_picker, cx), + Vec::::new(), + "Should list no file or worktree context-dependent when no file is open" + ); + tasks_picker.update(cx, |_, cx| { + cx.emit(DismissEvent); + }); + drop(tasks_picker); + cx.executor().run_until_parked(); + + let _ = workspace + .update(cx, |workspace, cx| { + workspace.open_abs_path(PathBuf::from("/dir/file_with.odd_extension"), true, cx) + }) + .await + .unwrap(); + cx.executor().run_until_parked(); + let tasks_picker = open_spawn_tasks(&workspace, cx); + assert_eq!( + task_names(&tasks_picker, cx), + vec![ + "hello from …th.odd_extension:1:1".to_string(), + "opened now: /dir".to_string() + ], + "Second opened buffer should fill the context, labels should be trimmed if long enough" + ); + tasks_picker.update(cx, |_, cx| { + cx.emit(DismissEvent); + }); + drop(tasks_picker); + cx.executor().run_until_parked(); + + let second_item = workspace + .update(cx, |workspace, cx| { + workspace.open_abs_path(PathBuf::from("/dir/file_without_extension"), true, cx) + }) + .await + .unwrap(); + + let editor = cx.update(|cx| second_item.act_as::(cx)).unwrap(); + editor.update(cx, |editor, cx| { + editor.change_selections(None, cx, |s| { + s.select_ranges(Some(Point::new(1, 2)..Point::new(1, 5))) + }) + }); + cx.executor().run_until_parked(); + let tasks_picker = open_spawn_tasks(&workspace, cx); + assert_eq!( + task_names(&tasks_picker, cx), + vec![ + "hello from …ithout_extension:2:3".to_string(), + "opened now: /dir".to_string() + ], + "Opened buffer should fill the context, labels should be trimmed if long enough" + ); + tasks_picker.update(cx, |_, cx| { + cx.emit(DismissEvent); + }); + drop(tasks_picker); + cx.executor().run_until_parked(); + } + fn open_spawn_tasks( workspace: &View, cx: &mut VisualTestContext, @@ -582,7 +680,7 @@ mod tests { workspace.update(cx, |workspace, cx| { workspace .active_modal::(cx) - .unwrap() + .expect("no task modal after `Spawn` action was dispatched") .read(cx) .picker .clone()