Fix zed cli in NixOS WSL instances (#43433)

Julia Ryan and John Tur created

This fixes running `zed <path>` inside nixos wsl instances. We're
copying the approach used elsewhere which is to try using `--exec`
first, and if that fails use an actual shell which should cover the
nixos case because it only puts binaries on your PATH inside the
`/etc/profile` script which is sourced on shell startup.

Release Notes:

- N/A

---------

Co-authored-by: John Tur <john-tur@outlook.com>

Change summary

crates/cli/src/main.rs | 35 +++++++++++++++++++++++++----------
1 file changed, 25 insertions(+), 10 deletions(-)

Detailed changes

crates/cli/src/main.rs 🔗

@@ -12,7 +12,9 @@ use clap::Parser;
 use cli::{CliRequest, CliResponse, IpcHandshake, ipc::IpcOneShotServer};
 use parking_lot::Mutex;
 use std::{
-    env, fs, io,
+    env,
+    ffi::OsStr,
+    fs, io,
     path::{Path, PathBuf},
     process::ExitStatus,
     sync::Arc,
@@ -300,7 +302,6 @@ mod tests {
 
 fn parse_path_in_wsl(source: &str, wsl: &str) -> Result<String> {
     let mut source = PathWithPosition::parse_str(source);
-    let mut command = util::command::new_std_command("wsl.exe");
 
     let (user, distro_name) = if let Some((user, distro)) = wsl.split_once('@') {
         if user.is_empty() {
@@ -311,20 +312,34 @@ fn parse_path_in_wsl(source: &str, wsl: &str) -> Result<String> {
         (None, wsl)
     };
 
+    let mut args = vec!["--distribution", distro_name];
     if let Some(user) = user {
-        command.arg("--user").arg(user);
+        args.push("--user");
+        args.push(user);
     }
 
-    let output = command
-        .arg("--distribution")
-        .arg(distro_name)
+    let command = [
+        OsStr::new("realpath"),
+        OsStr::new("-s"),
+        source.path.as_ref(),
+    ];
+
+    let output = util::command::new_std_command("wsl.exe")
+        .args(&args)
         .arg("--exec")
-        .arg("realpath")
-        .arg("-s")
-        .arg(&source.path)
+        .args(&command)
         .output()?;
+    let result = if output.status.success() {
+        String::from_utf8_lossy(&output.stdout).to_string()
+    } else {
+        let fallback = util::command::new_std_command("wsl.exe")
+            .args(&args)
+            .arg("--")
+            .args(&command)
+            .output()?;
+        String::from_utf8_lossy(&fallback.stdout).to_string()
+    };
 
-    let result = String::from_utf8_lossy(&output.stdout);
     source.path = Path::new(result.trim()).to_owned();
 
     Ok(source.to_string(|path| path.to_string_lossy().into_owned()))