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)) (None, Some(message))
} }
let marker = "ZED_SHELL_START"; const MARKER: &str = "ZED_SHELL_START";
let Some(shell) = std::env::var("SHELL").log_err() else { let Some(shell) = std::env::var("SHELL").log_err() else {
return message("Failed to get login environment. SHELL environment variable is not set"); 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 // 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 // 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 // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
// do that, but it does, and `exit 0` helps. // 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!( let command = match shell_name {
"cd '{}';{} printf '%s' {marker}; /usr/bin/env; exit 0;", Some("fish") => format!(
dir.display(), "cd '{}'; emit fish_prompt; printf '%s' {MARKER}; /usr/bin/env; exit 0;",
additional_command.unwrap_or("") 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) let Some(output) = smol::process::Command::new(&shell)
.args(["-l", "-i", "-c", &command]) .args(&args)
.output() .output()
.await .await
.log_err() .log_err()
@ -332,7 +339,7 @@ async fn load_shell_environment(
} }
let stdout = String::from_utf8_lossy(&output.stdout); 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); let stderr = String::from_utf8_lossy(&output.stderr);
log::error!( log::error!(
"failed to parse output of `env` command in login shell. stdout: {:?}, stderr: {:?}", "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 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| { parse_env_output(env_output, |key, value| {
parsed_env.insert(key, value); parsed_env.insert(key, value);