Improve support for tcsh/csh as login shells (#25122)

This commit is contained in:
Peter Tripp 2025-02-19 20:31:42 +00:00 committed by GitHub
parent af6cdd87cd
commit 365dcc5ac7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -277,10 +277,12 @@ async fn load_shell_environment(
(None, Some(message))
}
let marker = "ZED_SHELL_START";
const MARKER: &str = "ZED_SHELL_START";
let Some(shell) = std::env::var("SHELL").log_err() else {
return message("Failed to get login environment. SHELL environment variable is not set");
};
let shell_path = PathBuf::from(&shell);
let shell_name = shell_path.file_name().and_then(|f| f.to_str());
// What we're doing here is to spawn a shell and then `cd` into
// the project directory to get the env in there as if the user
@ -303,22 +305,27 @@ async fn load_shell_environment(
//
// We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
// do that, but it does, and `exit 0` helps.
let additional_command = PathBuf::from(&shell)
.file_name()
.and_then(|f| f.to_str())
.and_then(|shell| match shell {
"fish" => Some("emit fish_prompt;"),
_ => None,
});
let command = format!(
"cd '{}';{} printf '%s' {marker}; /usr/bin/env; exit 0;",
dir.display(),
additional_command.unwrap_or("")
);
let command = match shell_name {
Some("fish") => format!(
"cd '{}'; emit fish_prompt; printf '%s' {MARKER}; /usr/bin/env; exit 0;",
dir.display()
),
_ => format!(
"cd '{}'; printf '%s' {MARKER}; /usr/bin/env; exit 0;",
dir.display()
),
};
// csh/tcsh only supports `-l` if it's the only flag. So this won't be a login shell.
// Users must rely on vars from `~/.tcshrc` or `~/.cshrc` and not `.login` as a result.
let args = match shell_name {
Some("tcsh") | Some("csh") => vec!["-i", "-c", &command],
_ => vec!["-l", "-i", "-c", &command],
};
let Some(output) = smol::process::Command::new(&shell)
.args(["-l", "-i", "-c", &command])
.args(&args)
.output()
.await
.log_err()
@ -332,7 +339,7 @@ async fn load_shell_environment(
}
let stdout = String::from_utf8_lossy(&output.stdout);
let Some(env_output_start) = stdout.find(marker) else {
let Some(env_output_start) = stdout.find(MARKER) else {
let stderr = String::from_utf8_lossy(&output.stderr);
log::error!(
"failed to parse output of `env` command in login shell. stdout: {:?}, stderr: {:?}",
@ -343,7 +350,7 @@ async fn load_shell_environment(
};
let mut parsed_env = HashMap::default();
let env_output = &stdout[env_output_start + marker.len()..];
let env_output = &stdout[env_output_start + MARKER.len()..];
parse_env_output(env_output, |key, value| {
parsed_env.insert(key, value);