wsl: Fix folder picker adding wrong slashes (#44886)

Lukas Wirth created

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

Release Notes:

- Fixed folder picker inserting wrong slashes when remoting from windows
to wsl

Change summary

crates/file_finder/src/open_path_prompt.rs          |   6 
crates/file_finder/src/open_path_prompt_tests.rs    |  63 -------
crates/project/src/project.rs                       |   8 +
crates/recent_projects/src/remote_servers.rs        |   6 
crates/toolchain_selector/src/toolchain_selector.rs | 114 +++++++-------
5 files changed, 72 insertions(+), 125 deletions(-)

Detailed changes

crates/file_finder/src/open_path_prompt.rs 🔗

@@ -44,8 +44,9 @@ impl OpenPathDelegate {
         tx: oneshot::Sender<Option<Vec<PathBuf>>>,
         lister: DirectoryLister,
         creating_path: bool,
-        path_style: PathStyle,
+        cx: &App,
     ) -> Self {
+        let path_style = lister.path_style(cx);
         Self {
             tx: Some(tx),
             lister,
@@ -216,8 +217,7 @@ impl OpenPathPrompt {
         cx: &mut Context<Workspace>,
     ) {
         workspace.toggle_modal(window, cx, |window, cx| {
-            let delegate =
-                OpenPathDelegate::new(tx, lister.clone(), creating_path, PathStyle::local());
+            let delegate = OpenPathDelegate::new(tx, lister.clone(), creating_path, cx);
             let picker = Picker::uniform_list(delegate, window, cx).width(rems(34.));
             let query = lister.default_query(cx);
             picker.set_query(query, window, cx);

crates/file_finder/src/open_path_prompt_tests.rs 🔗

@@ -5,7 +5,7 @@ use picker::{Picker, PickerDelegate};
 use project::Project;
 use serde_json::json;
 use ui::rems;
-use util::{path, paths::PathStyle};
+use util::path;
 use workspace::{AppState, Workspace};
 
 use crate::OpenPathDelegate;
@@ -37,7 +37,7 @@ async fn test_open_path_prompt(cx: &mut TestAppContext) {
 
     let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await;
 
-    let (picker, cx) = build_open_path_prompt(project, false, PathStyle::local(), cx);
+    let (picker, cx) = build_open_path_prompt(project, false, cx);
 
     insert_query(path!("sadjaoislkdjasldj"), &picker, cx).await;
     assert_eq!(collect_match_candidates(&picker, cx), Vec::<String>::new());
@@ -119,7 +119,7 @@ async fn test_open_path_prompt_completion(cx: &mut TestAppContext) {
 
     let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await;
 
-    let (picker, cx) = build_open_path_prompt(project, false, PathStyle::local(), cx);
+    let (picker, cx) = build_open_path_prompt(project, false, cx);
 
     // Confirm completion for the query "/root", since it's a directory, it should add a trailing slash.
     let query = path!("/root");
@@ -227,7 +227,7 @@ async fn test_open_path_prompt_on_windows(cx: &mut TestAppContext) {
 
     let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await;
 
-    let (picker, cx) = build_open_path_prompt(project, false, PathStyle::local(), cx);
+    let (picker, cx) = build_open_path_prompt(project, false, cx);
 
     // Support both forward and backward slashes.
     let query = "C:/root/";
@@ -295,56 +295,6 @@ async fn test_open_path_prompt_on_windows(cx: &mut TestAppContext) {
     );
 }
 
-#[gpui::test]
-#[cfg_attr(not(target_os = "windows"), ignore)]
-async fn test_open_path_prompt_on_windows_with_remote(cx: &mut TestAppContext) {
-    let app_state = init_test(cx);
-    app_state
-        .fs
-        .as_fake()
-        .insert_tree(
-            "/root",
-            json!({
-                "a": "A",
-                "dir1": {},
-                "dir2": {}
-            }),
-        )
-        .await;
-
-    let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-
-    let (picker, cx) = build_open_path_prompt(project, false, PathStyle::Posix, cx);
-
-    let query = "/root/";
-    insert_query(query, &picker, cx).await;
-    assert_eq!(
-        collect_match_candidates(&picker, cx),
-        vec!["./", "a", "dir1", "dir2"]
-    );
-    assert_eq!(
-        confirm_completion(query, 1, &picker, cx).unwrap(),
-        "/root/a"
-    );
-
-    // Confirm completion for the query "/root/d", selecting the second candidate "dir2", since it's a directory, it should add a trailing slash.
-    let query = "/root/d";
-    insert_query(query, &picker, cx).await;
-    assert_eq!(collect_match_candidates(&picker, cx), vec!["dir1", "dir2"]);
-    assert_eq!(
-        confirm_completion(query, 1, &picker, cx).unwrap(),
-        "/root/dir2/"
-    );
-
-    let query = "/root/d";
-    insert_query(query, &picker, cx).await;
-    assert_eq!(collect_match_candidates(&picker, cx), vec!["dir1", "dir2"]);
-    assert_eq!(
-        confirm_completion(query, 0, &picker, cx).unwrap(),
-        "/root/dir1/"
-    );
-}
-
 #[gpui::test]
 async fn test_new_path_prompt(cx: &mut TestAppContext) {
     let app_state = init_test(cx);
@@ -372,7 +322,7 @@ async fn test_new_path_prompt(cx: &mut TestAppContext) {
 
     let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await;
 
-    let (picker, cx) = build_open_path_prompt(project, true, PathStyle::local(), cx);
+    let (picker, cx) = build_open_path_prompt(project, true, cx);
 
     insert_query(path!("/root"), &picker, cx).await;
     assert_eq!(collect_match_candidates(&picker, cx), vec!["root"]);
@@ -406,16 +356,15 @@ fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
 fn build_open_path_prompt(
     project: Entity<Project>,
     creating_path: bool,
-    path_style: PathStyle,
     cx: &mut TestAppContext,
 ) -> (Entity<Picker<OpenPathDelegate>>, &mut VisualTestContext) {
     let (tx, _) = futures::channel::oneshot::channel();
     let lister = project::DirectoryLister::Project(project.clone());
-    let delegate = OpenPathDelegate::new(tx, lister.clone(), creating_path, path_style);
 
     let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
     (
         workspace.update_in(cx, |_, window, cx| {
+            let delegate = OpenPathDelegate::new(tx, lister.clone(), creating_path, cx);
             cx.new(|cx| {
                 let picker = Picker::uniform_list(delegate, window, cx)
                     .width(rems(34.))

crates/project/src/project.rs 🔗

@@ -966,6 +966,14 @@ impl DirectoryLister {
             }
         }
     }
+
+    pub fn path_style(&self, cx: &App) -> PathStyle {
+        match self {
+            Self::Local(project, ..) | Self::Project(project, ..) => {
+                project.read(cx).path_style(cx)
+            }
+        }
+    }
 }
 
 #[cfg(any(test, feature = "test-support"))]

crates/recent_projects/src/remote_servers.rs 🔗

@@ -217,14 +217,13 @@ impl ProjectPicker {
         connection: RemoteConnectionOptions,
         project: Entity<Project>,
         home_dir: RemotePathBuf,
-        path_style: PathStyle,
         workspace: WeakEntity<Workspace>,
         window: &mut Window,
         cx: &mut Context<RemoteServerProjects>,
     ) -> Entity<Self> {
         let (tx, rx) = oneshot::channel();
         let lister = project::DirectoryLister::Project(project.clone());
-        let delegate = file_finder::OpenPathDelegate::new(tx, lister, false, path_style);
+        let delegate = file_finder::OpenPathDelegate::new(tx, lister, false, cx);
 
         let picker = cx.new(|cx| {
             let picker = Picker::uniform_list(delegate, window, cx)
@@ -719,7 +718,6 @@ impl RemoteServerProjects {
         connection_options: remote::RemoteConnectionOptions,
         project: Entity<Project>,
         home_dir: RemotePathBuf,
-        path_style: PathStyle,
         window: &mut Window,
         cx: &mut Context<Self>,
         workspace: WeakEntity<Workspace>,
@@ -732,7 +730,6 @@ impl RemoteServerProjects {
             connection_options,
             project,
             home_dir,
-            path_style,
             workspace,
             window,
             cx,
@@ -1030,7 +1027,6 @@ impl RemoteServerProjects {
                                     connection_options,
                                     project,
                                     home_dir,
-                                    path_style,
                                     window,
                                     cx,
                                     weak,

crates/toolchain_selector/src/toolchain_selector.rs 🔗

@@ -128,67 +128,61 @@ impl AddToolchainState {
     ) -> (OpenPathDelegate, oneshot::Receiver<Option<Vec<PathBuf>>>) {
         let (tx, rx) = oneshot::channel();
         let weak = cx.weak_entity();
-        let path_style = project.read(cx).path_style(cx);
-        let lister =
-            OpenPathDelegate::new(tx, DirectoryLister::Project(project), false, path_style)
-                .show_hidden()
-                .with_footer(Arc::new(move |_, cx| {
-                    let error = weak
-                        .read_with(cx, |this, _| {
-                            if let AddState::Path { error, .. } = &this.state {
-                                error.clone()
-                            } else {
-                                None
+        let lister = OpenPathDelegate::new(tx, DirectoryLister::Project(project), false, cx)
+            .show_hidden()
+            .with_footer(Arc::new(move |_, cx| {
+                let error = weak
+                    .read_with(cx, |this, _| {
+                        if let AddState::Path { error, .. } = &this.state {
+                            error.clone()
+                        } else {
+                            None
+                        }
+                    })
+                    .ok()
+                    .flatten();
+                let is_loading = weak
+                    .read_with(cx, |this, _| {
+                        matches!(
+                            this.state,
+                            AddState::Path {
+                                input_state: PathInputState::Resolving(_),
+                                ..
                             }
-                        })
-                        .ok()
-                        .flatten();
-                    let is_loading = weak
-                        .read_with(cx, |this, _| {
-                            matches!(
-                                this.state,
-                                AddState::Path {
-                                    input_state: PathInputState::Resolving(_),
-                                    ..
-                                }
-                            )
-                        })
-                        .unwrap_or_default();
-                    Some(
-                        v_flex()
-                            .child(Divider::horizontal())
-                            .child(
-                                h_flex()
-                                    .p_1()
-                                    .justify_between()
-                                    .gap_2()
-                                    .child(
-                                        Label::new("Select Toolchain Path")
-                                            .color(Color::Muted)
-                                            .map(|this| {
-                                                if is_loading {
-                                                    this.with_animation(
-                                                        "select-toolchain-label",
-                                                        Animation::new(Duration::from_secs(2))
-                                                            .repeat()
-                                                            .with_easing(pulsating_between(
-                                                                0.4, 0.8,
-                                                            )),
-                                                        |label, delta| label.alpha(delta),
-                                                    )
-                                                    .into_any()
-                                                } else {
-                                                    this.into_any_element()
-                                                }
-                                            }),
-                                    )
-                                    .when_some(error, |this, error| {
-                                        this.child(Label::new(error).color(Color::Error))
-                                    }),
-                            )
-                            .into_any(),
-                    )
-                }));
+                        )
+                    })
+                    .unwrap_or_default();
+                Some(
+                    v_flex()
+                        .child(Divider::horizontal())
+                        .child(
+                            h_flex()
+                                .p_1()
+                                .justify_between()
+                                .gap_2()
+                                .child(Label::new("Select Toolchain Path").color(Color::Muted).map(
+                                    |this| {
+                                        if is_loading {
+                                            this.with_animation(
+                                                "select-toolchain-label",
+                                                Animation::new(Duration::from_secs(2))
+                                                    .repeat()
+                                                    .with_easing(pulsating_between(0.4, 0.8)),
+                                                |label, delta| label.alpha(delta),
+                                            )
+                                            .into_any()
+                                        } else {
+                                            this.into_any_element()
+                                        }
+                                    },
+                                ))
+                                .when_some(error, |this, error| {
+                                    this.child(Label::new(error).color(Color::Error))
+                                }),
+                        )
+                        .into_any(),
+                )
+            }));
 
         (lister, rx)
     }