acp_thread: Respect terminal settings shell for terminal tool environment (#39349)
When sourcing the project environment for the terminal tool, we will now do so by spawning the shell specified by the users `terminal.shell` setting (or as usual fall back to the login shell). Closes #37687 Release Notes: - N/A
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use futures::{FutureExt, future::Shared};
|
||||
use language::Buffer;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use task::Shell;
|
||||
use util::ResultExt;
|
||||
use worktree::Worktree;
|
||||
|
||||
@@ -16,6 +17,8 @@ use crate::{
|
||||
pub struct ProjectEnvironment {
|
||||
cli_environment: Option<HashMap<String, String>>,
|
||||
environments: HashMap<Arc<Path>, Shared<Task<Option<HashMap<String, String>>>>>,
|
||||
shell_based_environments:
|
||||
HashMap<(Shell, Arc<Path>), Shared<Task<Option<HashMap<String, String>>>>>,
|
||||
environment_error_messages: HashMap<Arc<Path>, EnvironmentErrorMessage>,
|
||||
}
|
||||
|
||||
@@ -30,6 +33,7 @@ impl ProjectEnvironment {
|
||||
Self {
|
||||
cli_environment,
|
||||
environments: Default::default(),
|
||||
shell_based_environments: Default::default(),
|
||||
environment_error_messages: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -134,7 +138,22 @@ impl ProjectEnvironment {
|
||||
|
||||
self.environments
|
||||
.entry(abs_path.clone())
|
||||
.or_insert_with(|| get_directory_env_impl(abs_path.clone(), cx).shared())
|
||||
.or_insert_with(|| {
|
||||
get_directory_env_impl(&Shell::System, abs_path.clone(), cx).shared()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Returns the project environment, if possible, with the given shell.
|
||||
pub fn get_directory_environment_for_shell(
|
||||
&mut self,
|
||||
shell: &Shell,
|
||||
abs_path: Arc<Path>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Shared<Task<Option<HashMap<String, String>>>> {
|
||||
self.shell_based_environments
|
||||
.entry((shell.clone(), abs_path.clone()))
|
||||
.or_insert_with(|| get_directory_env_impl(shell, abs_path.clone(), cx).shared())
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
@@ -176,6 +195,7 @@ impl EnvironmentErrorMessage {
|
||||
}
|
||||
|
||||
async fn load_directory_shell_environment(
|
||||
shell: &Shell,
|
||||
abs_path: &Path,
|
||||
load_direnv: &DirenvSettings,
|
||||
) -> (
|
||||
@@ -198,7 +218,7 @@ async fn load_directory_shell_environment(
|
||||
);
|
||||
};
|
||||
|
||||
load_shell_environment(dir, load_direnv).await
|
||||
load_shell_environment(shell, dir, load_direnv).await
|
||||
}
|
||||
Err(err) => (
|
||||
None,
|
||||
@@ -211,51 +231,8 @@ async fn load_directory_shell_environment(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
async fn load_shell_environment(
|
||||
_dir: &Path,
|
||||
_load_direnv: &DirenvSettings,
|
||||
) -> (
|
||||
Option<HashMap<String, String>>,
|
||||
Option<EnvironmentErrorMessage>,
|
||||
) {
|
||||
let fake_env = [("ZED_FAKE_TEST_ENV".into(), "true".into())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
(Some(fake_env), None)
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", not(any(test, feature = "test-support"))))]
|
||||
async fn load_shell_environment(
|
||||
dir: &Path,
|
||||
_load_direnv: &DirenvSettings,
|
||||
) -> (
|
||||
Option<HashMap<String, String>>,
|
||||
Option<EnvironmentErrorMessage>,
|
||||
) {
|
||||
use util::shell_env;
|
||||
|
||||
let envs = match shell_env::capture(dir).await {
|
||||
Ok(envs) => envs,
|
||||
Err(err) => {
|
||||
util::log_err(&err);
|
||||
return (
|
||||
None,
|
||||
Some(EnvironmentErrorMessage(format!(
|
||||
"Failed to load environment variables: {}",
|
||||
err
|
||||
))),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Note: direnv is not available on Windows, so we skip direnv processing
|
||||
// and just return the shell environment
|
||||
(Some(envs), None)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", test, feature = "test-support")))]
|
||||
async fn load_shell_environment(
|
||||
shell: &Shell,
|
||||
dir: &Path,
|
||||
load_direnv: &DirenvSettings,
|
||||
) -> (
|
||||
@@ -265,55 +242,86 @@ async fn load_shell_environment(
|
||||
use crate::direnv::load_direnv_environment;
|
||||
use util::shell_env;
|
||||
|
||||
let dir_ = dir.to_owned();
|
||||
let mut envs = match shell_env::capture(&dir_).await {
|
||||
Ok(envs) => envs,
|
||||
Err(err) => {
|
||||
util::log_err(&err);
|
||||
return (
|
||||
None,
|
||||
Some(EnvironmentErrorMessage::from_str(
|
||||
"Failed to load environment variables. See log for details",
|
||||
)),
|
||||
);
|
||||
}
|
||||
};
|
||||
if cfg!(any(test, feature = "test-support")) {
|
||||
let fake_env = [("ZED_FAKE_TEST_ENV".into(), "true".into())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
(Some(fake_env), None)
|
||||
} else if cfg!(target_os = "windows",) {
|
||||
let (shell, args) = shell.program_and_args();
|
||||
let envs = match shell_env::capture(shell, args, dir).await {
|
||||
Ok(envs) => envs,
|
||||
Err(err) => {
|
||||
util::log_err(&err);
|
||||
return (
|
||||
None,
|
||||
Some(EnvironmentErrorMessage(format!(
|
||||
"Failed to load environment variables: {}",
|
||||
err
|
||||
))),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// If the user selects `Direct` for direnv, it would set an environment
|
||||
// variable that later uses to know that it should not run the hook.
|
||||
// We would include in `.envs` call so it is okay to run the hook
|
||||
// even if direnv direct mode is enabled.
|
||||
let (direnv_environment, direnv_error) = match load_direnv {
|
||||
DirenvSettings::ShellHook => (None, None),
|
||||
DirenvSettings::Direct => match load_direnv_environment(&envs, dir).await {
|
||||
Ok(env) => (Some(env), None),
|
||||
Err(err) => (None, err.into()),
|
||||
},
|
||||
};
|
||||
if let Some(direnv_environment) = direnv_environment {
|
||||
for (key, value) in direnv_environment {
|
||||
if let Some(value) = value {
|
||||
envs.insert(key, value);
|
||||
} else {
|
||||
envs.remove(&key);
|
||||
// Note: direnv is not available on Windows, so we skip direnv processing
|
||||
// and just return the shell environment
|
||||
(Some(envs), None)
|
||||
} else {
|
||||
let dir_ = dir.to_owned();
|
||||
let (shell, args) = shell.program_and_args();
|
||||
let mut envs = match shell_env::capture(shell, args, &dir_).await {
|
||||
Ok(envs) => envs,
|
||||
Err(err) => {
|
||||
util::log_err(&err);
|
||||
return (
|
||||
None,
|
||||
Some(EnvironmentErrorMessage::from_str(
|
||||
"Failed to load environment variables. See log for details",
|
||||
)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// If the user selects `Direct` for direnv, it would set an environment
|
||||
// variable that later uses to know that it should not run the hook.
|
||||
// We would include in `.envs` call so it is okay to run the hook
|
||||
// even if direnv direct mode is enabled.
|
||||
let (direnv_environment, direnv_error) = match load_direnv {
|
||||
DirenvSettings::ShellHook => (None, None),
|
||||
DirenvSettings::Direct => match load_direnv_environment(&envs, dir).await {
|
||||
Ok(env) => (Some(env), None),
|
||||
Err(err) => (None, err.into()),
|
||||
},
|
||||
};
|
||||
if let Some(direnv_environment) = direnv_environment {
|
||||
for (key, value) in direnv_environment {
|
||||
if let Some(value) = value {
|
||||
envs.insert(key, value);
|
||||
} else {
|
||||
envs.remove(&key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Some(envs), direnv_error)
|
||||
(Some(envs), direnv_error)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_directory_env_impl(
|
||||
shell: &Shell,
|
||||
abs_path: Arc<Path>,
|
||||
cx: &Context<ProjectEnvironment>,
|
||||
) -> Task<Option<HashMap<String, String>>> {
|
||||
let load_direnv = ProjectSettings::get_global(cx).load_direnv.clone();
|
||||
|
||||
let shell = shell.clone();
|
||||
cx.spawn(async move |this, cx| {
|
||||
let (mut shell_env, error_message) = cx
|
||||
.background_spawn({
|
||||
let abs_path = abs_path.clone();
|
||||
async move { load_directory_shell_environment(&abs_path, &load_direnv).await }
|
||||
async move {
|
||||
load_directory_shell_environment(&shell, &abs_path, &load_direnv).await
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user