Support loading environment from plan9 `rc` shell (#33599)

Peter Tripp created

Closes: https://github.com/zed-industries/zed/issues/33511

Add support for loading environment from Plan9 shell
Document esoteric shell behavior.
Remove two useless tests.

Follow-up to: 
- https://github.com/zed-industries/zed/pull/32702
- https://github.com/zed-industries/zed/pull/32637

Release Notes:

- Add support for loading environment variables from Plan9 `rc` shell.

Change summary

crates/util/src/shell_env.rs | 12 +++++----
crates/util/src/util.rs      | 46 --------------------------------------
2 files changed, 7 insertions(+), 51 deletions(-)

Detailed changes

crates/util/src/shell_env.rs 🔗

@@ -16,8 +16,13 @@ pub fn capture(directory: &std::path::Path) -> Result<collections::HashMap<Strin
     let mut command_string = String::new();
     let mut command = std::process::Command::new(&shell_path);
     // In some shells, file descriptors greater than 2 cannot be used in interactive mode,
-    // so file descriptor 0 (stdin) is used instead. [Citation Needed]
+    // so file descriptor 0 (stdin) is used instead. This impacts zsh, old bash; perhaps others.
+    // See: https://github.com/zed-industries/zed/pull/32136#issuecomment-2999645482
     const ENV_OUTPUT_FD: std::os::fd::RawFd = 0;
+    let redir = match shell_name {
+        Some("rc") => format!(">[1={}]", ENV_OUTPUT_FD), // `[1=0]`
+        _ => format!(">&{}", ENV_OUTPUT_FD),             // `>&0`
+    };
     command.stdin(Stdio::null());
     command.stdout(Stdio::piped());
     command.stderr(Stdio::piped());
@@ -38,10 +43,7 @@ pub fn capture(directory: &std::path::Path) -> Result<collections::HashMap<Strin
     }
     // cd into the directory, triggering directory specific side-effects (asdf, direnv, etc)
     command_string.push_str(&format!("cd '{}';", directory.display()));
-    command_string.push_str(&format!(
-        "sh -c \"{} --printenv >&{}\";",
-        zed_path, ENV_OUTPUT_FD
-    ));
+    command_string.push_str(&format!("{} --printenv {}", zed_path, redir));
     command.args(["-i", "-c", &command_string]);
 
     super::set_pre_exec_to_start_new_session(&mut command);

crates/util/src/util.rs 🔗

@@ -1097,52 +1097,6 @@ mod tests {
         assert_eq!(vec, &[1000, 101, 21, 19, 17, 13, 9, 8]);
     }
 
-    #[test]
-    fn test_get_shell_safe_zed_path_with_spaces() {
-        // Test that shlex::try_quote handles paths with spaces correctly
-        let path_with_spaces = "/Applications/Zed Nightly.app/Contents/MacOS/zed";
-        let quoted = shlex::try_quote(path_with_spaces).unwrap();
-
-        // The quoted path should be properly escaped for shell use
-        assert!(quoted.contains(path_with_spaces));
-
-        // When used in a shell command, it should not be split at spaces
-        let command = format!("sh -c '{} --printenv'", quoted);
-        println!("Command would be: {}", command);
-
-        // Test that shlex can parse it back correctly
-        let parsed = shlex::split(&format!("{} --printenv", quoted)).unwrap();
-        assert_eq!(parsed.len(), 2);
-        assert_eq!(parsed[0], path_with_spaces);
-        assert_eq!(parsed[1], "--printenv");
-    }
-
-    #[test]
-    fn test_shell_command_construction_with_quoted_path() {
-        // Test the specific pattern used in shell_env.rs to ensure proper quoting
-        let path_with_spaces = "/Applications/Zed Nightly.app/Contents/MacOS/zed";
-        let quoted_path = shlex::try_quote(path_with_spaces).unwrap();
-
-        // This should be: '/Applications/Zed Nightly.app/Contents/MacOS/zed'
-        assert_eq!(
-            quoted_path,
-            "'/Applications/Zed Nightly.app/Contents/MacOS/zed'"
-        );
-
-        // Test the command construction pattern from shell_env.rs
-        // The fixed version should use double quotes around the entire sh -c argument
-        let env_fd = 0;
-        let command = format!("sh -c \"{} --printenv >&{}\";", quoted_path, env_fd);
-
-        // This should produce: sh -c "'/Applications/Zed Nightly.app/Contents/MacOS/zed' --printenv >&0";
-        let expected =
-            "sh -c \"'/Applications/Zed Nightly.app/Contents/MacOS/zed' --printenv >&0\";";
-        assert_eq!(command, expected);
-
-        // The command should not contain the problematic double single-quote pattern
-        assert!(!command.contains("''"));
-    }
-
     #[test]
     fn test_truncate_to_bottom_n_sorted_by() {
         let mut vec: Vec<u32> = vec![5, 2, 3, 4, 1];