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:
After:
---
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);