Pick up more home dir shell env when spawning (#8273)

Thorsten Ball created

Release Notes:

- Improved how Zed picks up shell environment when spawned.

Change summary

crates/project/src/project.rs |  4 +++-
crates/zed/src/main.rs        | 23 ++++++++++++++++++++++-
2 files changed, 25 insertions(+), 2 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -9318,7 +9318,9 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
                     Ok(command_path) => Some((command_path, shell_env)),
                     Err(error) => {
                         log::warn!(
-                            "failed to determine path for command {:?} in env {shell_env:?}: {error}", command.to_string_lossy()
+                            "failed to determine path for command {:?} in shell PATH {:?}: {error}",
+                            command.to_string_lossy(),
+                            shell_path.map(String::as_str).unwrap_or("")
                         );
                         None
                     }

crates/zed/src/main.rs 🔗

@@ -824,8 +824,29 @@ async fn load_login_shell_environment() -> Result<()> {
     let shell = env::var("SHELL").context(
         "SHELL environment variable is not assigned so we can't source login environment variables",
     )?;
+
+    // If possible, we want to `cd` in the user's `$HOME` to trigger programs
+    // such as direnv, asdf, mise, ... to adjust the PATH. These tools often hook
+    // into shell's `cd` command (and hooks) to manipulate env.
+    // We do this so that we get the env a user would have when spawning a shell
+    // in home directory.
+    let shell_cmd_prefix = std::env::var_os("HOME")
+        .and_then(|home| home.into_string().ok())
+        .map(|home| format!("cd {home};"));
+
+    // The `exit 0` is the result of hours of debugging, trying to find out
+    // why running this command here, without `exit 0`, would mess
+    // up signal process for our process so that `ctrl-c` doesn't work
+    // anymore.
+    // 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 shell_cmd = format!(
+        "{}echo {marker}; /usr/bin/env -0; exit 0;",
+        shell_cmd_prefix.as_deref().unwrap_or("")
+    );
+
     let output = Command::new(&shell)
-        .args(["-l", "-i", "-c", &format!("echo {marker}; /usr/bin/env -0")])
+        .args(["-l", "-i", "-c", &shell_cmd])
         .output()
         .await
         .context("failed to spawn login shell to source login environment variables")?;