From 8a6c65c63b6b2b8071ad2380ea392e4f51226c8f Mon Sep 17 00:00:00 2001 From: Stanislav Alekseev <43210583+WeetHet@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:49:12 +0300 Subject: [PATCH] Allow task context providers to access project env (#17964) Closes #13106 Release Notes: - Task context providers now have access to the local shell environment, allowing local rust tool installations to work Before: Screenshot 2024-09-17 at 22 09 38 After: Screenshot 2024-09-17 at 22 09 58 --- crates/language/src/task_context.rs | 1 + crates/languages/src/go.rs | 2 ++ crates/languages/src/python.rs | 2 ++ crates/languages/src/rust.rs | 31 +++++++++++++++++++++------- crates/project/src/project.rs | 26 ++++++++++++----------- crates/project/src/task_inventory.rs | 3 ++- 6 files changed, 45 insertions(+), 20 deletions(-) diff --git a/crates/language/src/task_context.rs b/crates/language/src/task_context.rs index cc3f29558e0fbb2932a7cfedb651cb82564f665c..73150eb8e70d5c242eaa4af00183ba53955ca2a4 100644 --- a/crates/language/src/task_context.rs +++ b/crates/language/src/task_context.rs @@ -25,6 +25,7 @@ pub trait ContextProvider: Send + Sync { &self, _variables: &TaskVariables, _location: &Location, + _project_env: Option<&HashMap>, _cx: &mut AppContext, ) -> Result { Ok(TaskVariables::default()) diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index a83a11fd49ee342bae9969bd828b5681f02680d5..2ddf7796817e81939e62071d8f134ea36c6ff19e 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; +use collections::HashMap; use futures::StreamExt; use gpui::{AppContext, AsyncAppContext, Task}; use http_client::github::latest_github_release; @@ -454,6 +455,7 @@ impl ContextProvider for GoContextProvider { &self, variables: &TaskVariables, location: &Location, + _: Option<&HashMap>, cx: &mut gpui::AppContext, ) -> Result { let local_abs_path = location diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 99018647ead465a33111abf15be3679fe3b9c526..ee127c00cca846c0c705f1707d10fce91e02bf6a 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -1,5 +1,6 @@ use anyhow::Result; use async_trait::async_trait; +use collections::HashMap; use gpui::AppContext; use gpui::AsyncAppContext; use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate}; @@ -215,6 +216,7 @@ impl ContextProvider for PythonContextProvider { &self, variables: &task::TaskVariables, _location: &project::Location, + _: Option<&HashMap>, _cx: &mut gpui::AppContext, ) -> Result { let python_module_name = python_module_name_from_relative_path( diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 5055bb69c1e6f58890135d34c75bba062235f74c..b55f350b9d91877062992bab937ad28c2b66c654 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, bail, Context, Result}; use async_compression::futures::bufread::GzipDecoder; use async_trait::async_trait; +use collections::HashMap; use futures::{io::BufReader, StreamExt}; use gpui::{AppContext, AsyncAppContext}; use http_client::github::{latest_github_release, GitHubLspBinaryVersion}; @@ -434,6 +435,7 @@ impl ContextProvider for RustContextProvider { &self, task_variables: &TaskVariables, location: &Location, + project_env: Option<&HashMap>, cx: &mut gpui::AppContext, ) -> Result { let local_abs_path = location @@ -449,8 +451,8 @@ impl ContextProvider for RustContextProvider { .is_some(); if is_main_function { - if let Some((package_name, bin_name)) = - local_abs_path.and_then(package_name_and_bin_name_from_abs_path) + if let Some((package_name, bin_name)) = local_abs_path + .and_then(|path| package_name_and_bin_name_from_abs_path(path, project_env)) { return Ok(TaskVariables::from_iter([ (RUST_PACKAGE_TASK_VARIABLE.clone(), package_name), @@ -461,7 +463,7 @@ impl ContextProvider for RustContextProvider { if let Some(package_name) = local_abs_path .and_then(|local_abs_path| local_abs_path.parent()) - .and_then(human_readable_package_name) + .and_then(|path| human_readable_package_name(path, project_env)) { return Ok(TaskVariables::from_iter([( RUST_PACKAGE_TASK_VARIABLE.clone(), @@ -615,8 +617,15 @@ struct CargoTarget { src_path: String, } -fn package_name_and_bin_name_from_abs_path(abs_path: &Path) -> Option<(String, String)> { - let output = std::process::Command::new("cargo") +fn package_name_and_bin_name_from_abs_path( + abs_path: &Path, + project_env: Option<&HashMap>, +) -> Option<(String, String)> { + let mut command = std::process::Command::new("cargo"); + if let Some(envs) = project_env { + command.envs(envs); + } + let output = command .current_dir(abs_path.parent()?) .arg("metadata") .arg("--no-deps") @@ -654,9 +663,17 @@ fn retrieve_package_id_and_bin_name_from_metadata( None } -fn human_readable_package_name(package_directory: &Path) -> Option { +fn human_readable_package_name( + package_directory: &Path, + project_env: Option<&HashMap>, +) -> Option { + let mut command = std::process::Command::new("cargo"); + if let Some(envs) = project_env { + command.envs(envs); + } + let pkgid = String::from_utf8( - std::process::Command::new("cargo") + command .current_dir(package_directory) .arg("pkgid") .output() diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5d9ac5e8214efba007139781dc7aef650c4455f4..4318737e3875b889f41ceccac0383d5381ed9691 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4890,11 +4890,22 @@ impl Project { }; cx.spawn(|project, mut cx| async move { + let project_env = project + .update(&mut cx, |project, cx| { + let worktree_abs_path = worktree_abs_path.clone(); + project.environment.update(cx, |environment, cx| { + environment.get_environment(worktree_id, worktree_abs_path, cx) + }) + }) + .ok()? + .await; + let mut task_variables = cx .update(|cx| { combine_task_variables( captured_variables, location, + project_env.as_ref(), BasicContextProvider::new(project.upgrade()?), cx, ) @@ -4905,16 +4916,6 @@ impl Project { // Remove all custom entries starting with _, as they're not intended for use by the end user. task_variables.sweep(); - let project_env = project - .update(&mut cx, |project, cx| { - let worktree_abs_path = worktree_abs_path.clone(); - project.environment.update(cx, |environment, cx| { - environment.get_environment(worktree_id, worktree_abs_path, cx) - }) - }) - .ok()? - .await; - Some(TaskContext { project_env: project_env.unwrap_or_default(), cwd: worktree_abs_path.map(|p| p.to_path_buf()), @@ -5111,6 +5112,7 @@ impl Project { fn combine_task_variables( mut captured_variables: TaskVariables, location: Location, + project_env: Option<&HashMap>, baseline: BasicContextProvider, cx: &mut AppContext, ) -> anyhow::Result { @@ -5120,13 +5122,13 @@ fn combine_task_variables( .language() .and_then(|language| language.context_provider()); let baseline = baseline - .build_context(&captured_variables, &location, cx) + .build_context(&captured_variables, &location, project_env, cx) .context("building basic default context")?; captured_variables.extend(baseline); if let Some(provider) = language_context_provider { captured_variables.extend( provider - .build_context(&captured_variables, &location, cx) + .build_context(&captured_variables, &location, project_env, cx) .context("building provider context")?, ); } diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 314903ec5da439828675d20c780cd2b3143880f0..83c9c1f8e58be934c6a7048282178d10c7236bd2 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -8,7 +8,7 @@ use std::{ }; use anyhow::Result; -use collections::{btree_map, BTreeMap, VecDeque}; +use collections::{btree_map, BTreeMap, HashMap, VecDeque}; use futures::{ channel::mpsc::{unbounded, UnboundedSender}, StreamExt, @@ -543,6 +543,7 @@ impl ContextProvider for BasicContextProvider { &self, _: &TaskVariables, location: &Location, + _: Option<&HashMap>, cx: &mut AppContext, ) -> Result { let buffer = location.buffer.read(cx);