Add more context to the save new file path picker (#19863)

Mikayla Maki and Conrad created

Release Notes:

- N/A

Co-authored-by: Conrad <conrad@zed.dev>

Change summary

crates/file_finder/src/new_path_prompt.rs  | 38 ++++++++++++++++++++++-
crates/file_finder/src/open_path_prompt.rs |  6 +++
crates/picker/src/picker.rs                |  8 +++-
crates/tasks_ui/src/modal.rs               |  6 +++
4 files changed, 52 insertions(+), 6 deletions(-)

Detailed changes

crates/file_finder/src/new_path_prompt.rs 🔗

@@ -4,7 +4,7 @@ use gpui::{HighlightStyle, Model, StyledText};
 use picker::{Picker, PickerDelegate};
 use project::{Entry, PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
 use std::{
-    path::PathBuf,
+    path::{Path, PathBuf},
     sync::{
         atomic::{self, AtomicBool},
         Arc,
@@ -254,6 +254,7 @@ impl PickerDelegate for NewPathDelegate {
             .trim()
             .trim_start_matches("./")
             .trim_start_matches('/');
+
         let (dir, suffix) = if let Some(index) = query.rfind('/') {
             let suffix = if index + 1 < query.len() {
                 Some(query[index + 1..].to_string())
@@ -317,6 +318,14 @@ impl PickerDelegate for NewPathDelegate {
         })
     }
 
+    fn confirm_completion(
+        &mut self,
+        _: String,
+        cx: &mut ViewContext<Picker<Self>>,
+    ) -> Option<String> {
+        self.confirm_update_query(cx)
+    }
+
     fn confirm_update_query(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<String> {
         let m = self.matches.get(self.selected_index)?;
         if m.is_dir(self.project.read(cx), cx) {
@@ -422,7 +431,32 @@ impl NewPathDelegate {
     ) {
         cx.notify();
         if query.is_empty() {
-            self.matches = vec![];
+            self.matches = self
+                .project
+                .read(cx)
+                .worktrees(cx)
+                .flat_map(|worktree| {
+                    let worktree_id = worktree.read(cx).id();
+                    worktree
+                        .read(cx)
+                        .child_entries(Path::new(""))
+                        .filter_map(move |entry| {
+                            entry.is_dir().then(|| Match {
+                                path_match: Some(PathMatch {
+                                    score: 1.0,
+                                    positions: Default::default(),
+                                    worktree_id: worktree_id.to_usize(),
+                                    path: entry.path.clone(),
+                                    path_prefix: "".into(),
+                                    is_dir: entry.is_dir(),
+                                    distance_to_relative_ancestor: 0,
+                                }),
+                                suffix: None,
+                            })
+                        })
+                })
+                .collect();
+
             return;
         }
 

crates/file_finder/src/open_path_prompt.rs 🔗

@@ -220,7 +220,11 @@ impl PickerDelegate for OpenPathDelegate {
         })
     }
 
-    fn confirm_completion(&self, query: String) -> Option<String> {
+    fn confirm_completion(
+        &mut self,
+        query: String,
+        _: &mut ViewContext<Picker<Self>>,
+    ) -> Option<String> {
         Some(
             maybe!({
                 let m = self.matches.get(self.selected_index)?;

crates/picker/src/picker.rs 🔗

@@ -108,7 +108,11 @@ pub trait PickerDelegate: Sized + 'static {
     fn should_dismiss(&self) -> bool {
         true
     }
-    fn confirm_completion(&self, _query: String) -> Option<String> {
+    fn confirm_completion(
+        &mut self,
+        _query: String,
+        _: &mut ViewContext<Picker<Self>>,
+    ) -> Option<String> {
         None
     }
 
@@ -370,7 +374,7 @@ impl<D: PickerDelegate> Picker<D> {
     }
 
     fn confirm_completion(&mut self, _: &ConfirmCompletion, cx: &mut ViewContext<Self>) {
-        if let Some(new_query) = self.delegate.confirm_completion(self.query(cx)) {
+        if let Some(new_query) = self.delegate.confirm_completion(self.query(cx), cx) {
             self.set_query(new_query, cx);
         } else {
             cx.propagate()

crates/tasks_ui/src/modal.rs 🔗

@@ -425,7 +425,11 @@ impl PickerDelegate for TasksModalDelegate {
         )
     }
 
-    fn confirm_completion(&self, _: String) -> Option<String> {
+    fn confirm_completion(
+        &mut self,
+        _: String,
+        _: &mut ViewContext<Picker<Self>>,
+    ) -> Option<String> {
         let task_index = self.matches.get(self.selected_index())?.candidate_id;
         let tasks = self.candidates.as_ref()?;
         let (_, task) = tasks.get(task_index)?;