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,7 +1,6 @@
|
||||
use crate::environment::EnvironmentErrorMessage;
|
||||
use std::process::ExitStatus;
|
||||
|
||||
#[cfg(not(any(target_os = "windows", test, feature = "test-support")))]
|
||||
use {collections::HashMap, std::path::Path, util::ResultExt};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -28,7 +27,6 @@ impl From<DirenvError> for Option<EnvironmentErrorMessage> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", test, feature = "test-support")))]
|
||||
pub async fn load_direnv_environment(
|
||||
env: &HashMap<String, String>,
|
||||
dir: &Path,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ pub mod search_history;
|
||||
mod yarn;
|
||||
|
||||
use dap::inline_value::{InlineValueLocation, VariableLookupKind, VariableScope};
|
||||
use task::Shell;
|
||||
|
||||
use crate::{
|
||||
agent_server_store::{AgentServerStore, AllAgentServersSettings},
|
||||
@@ -1894,11 +1895,12 @@ impl Project {
|
||||
|
||||
pub fn directory_environment(
|
||||
&self,
|
||||
shell: &Shell,
|
||||
abs_path: Arc<Path>,
|
||||
cx: &mut App,
|
||||
) -> Shared<Task<Option<HashMap<String, String>>>> {
|
||||
self.environment.update(cx, |environment, cx| {
|
||||
environment.get_directory_environment(abs_path, cx)
|
||||
environment.get_directory_environment_for_shell(shell, abs_path, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use task::{Shell, ShellBuilder, ShellKind, SpawnInTerminal};
|
||||
use terminal::{
|
||||
TaskState, TaskStatus, Terminal, TerminalBuilder, terminal_settings::TerminalSettings,
|
||||
};
|
||||
use util::{get_default_system_shell, get_system_shell, maybe, rel_path::RelPath};
|
||||
use util::{get_default_system_shell, maybe, rel_path::RelPath};
|
||||
|
||||
use crate::{Project, ProjectPath};
|
||||
|
||||
@@ -98,15 +98,7 @@ impl Project {
|
||||
.read(cx)
|
||||
.shell()
|
||||
.unwrap_or_else(get_default_system_shell),
|
||||
None => match &settings.shell {
|
||||
Shell::Program(program) => program.clone(),
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args: _,
|
||||
title_override: _,
|
||||
} => program.clone(),
|
||||
Shell::System => get_system_shell(),
|
||||
},
|
||||
None => settings.shell.program(),
|
||||
};
|
||||
|
||||
let project_path_contexts = self
|
||||
@@ -332,15 +324,7 @@ impl Project {
|
||||
.read(cx)
|
||||
.shell()
|
||||
.unwrap_or_else(get_default_system_shell),
|
||||
None => match &settings.shell {
|
||||
Shell::Program(program) => program.clone(),
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args: _,
|
||||
title_override: _,
|
||||
} => program.clone(),
|
||||
Shell::System => get_system_shell(),
|
||||
},
|
||||
None => settings.shell.program(),
|
||||
});
|
||||
|
||||
let lang_registry = self.languages.clone();
|
||||
|
||||
Reference in New Issue
Block a user