Add absolute paths to historic elements

Kirill Bulatov created

Change summary

crates/file_finder/src/file_finder.rs | 136 ++++++++++++++++++++--------
1 file changed, 94 insertions(+), 42 deletions(-)

Detailed changes

crates/file_finder/src/file_finder.rs 🔗

@@ -6,7 +6,7 @@ use gpui::{
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
 use std::{
-    path::Path,
+    path::{Path, PathBuf},
     sync::{
         atomic::{self, AtomicBool},
         Arc,
@@ -25,11 +25,17 @@ pub struct FileFinderDelegate {
     latest_search_id: usize,
     latest_search_did_cancel: bool,
     latest_search_query: Option<PathLikeWithPosition<FileSearchQuery>>,
-    currently_opened_path: Option<ProjectPath>,
+    currently_opened_path: Option<FoundPath>,
     matches: Vec<PathMatch>,
     selected: Option<(usize, Arc<Path>)>,
     cancel_flag: Arc<AtomicBool>,
-    history_items: Vec<ProjectPath>,
+    history_items: Vec<FoundPath>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct FoundPath {
+    project: ProjectPath,
+    absolute: Option<PathBuf>,
 }
 
 actions!(file_finder, [Toggle]);
@@ -43,10 +49,36 @@ const MAX_RECENT_SELECTIONS: usize = 20;
 
 fn toggle_file_finder(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
     workspace.toggle_modal(cx, |workspace, cx| {
-        let history_items = workspace.recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx);
+        let project = workspace.project().read(cx);
+        let project_to_found_path = |project_path: ProjectPath| FoundPath {
+            absolute: project
+                .worktree_for_id(project_path.worktree_id, cx)
+                .map(|worktree| worktree.read(cx).abs_path().join(&project_path.path)),
+            project: project_path,
+        };
+
         let currently_opened_path = workspace
             .active_item(cx)
-            .and_then(|item| item.project_path(cx));
+            .and_then(|item| item.project_path(cx))
+            .map(project_to_found_path);
+
+        // if exists, bubble the currently opened path to the top
+        let history_items = currently_opened_path
+            .clone()
+            .into_iter()
+            .chain(
+                workspace
+                    .recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
+                    .into_iter()
+                    .filter(|history_path| {
+                        Some(history_path)
+                            != currently_opened_path
+                                .as_ref()
+                                .map(|found_path| &found_path.project)
+                    })
+                    .map(project_to_found_path),
+            )
+            .collect();
 
         let project = workspace.project().clone();
         let workspace = cx.handle().downgrade();
@@ -113,11 +145,11 @@ impl FileFinderDelegate {
         (file_name, file_name_positions, full_path, path_positions)
     }
 
-    pub fn new(
+    fn new(
         workspace: WeakViewHandle<Workspace>,
         project: ModelHandle<Project>,
-        currently_opened_path: Option<ProjectPath>,
-        history_items: Vec<ProjectPath>,
+        currently_opened_path: Option<FoundPath>,
+        history_items: Vec<FoundPath>,
         cx: &mut ViewContext<FileFinder>,
     ) -> Self {
         cx.observe(&project, |picker, _, cx| {
@@ -147,7 +179,7 @@ impl FileFinderDelegate {
         let relative_to = self
             .currently_opened_path
             .as_ref()
-            .map(|project_path| Arc::clone(&project_path.path));
+            .map(|found_path| Arc::clone(&found_path.project.path));
         let worktrees = self
             .project
             .read(cx)
@@ -255,20 +287,33 @@ impl PickerDelegate for FileFinderDelegate {
             self.latest_search_id = post_inc(&mut self.search_count);
             self.matches.clear();
 
+            let project = self.project.read(cx);
             self.matches = self
-                .currently_opened_path
-                .iter() // if exists, bubble the currently opened path to the top
-                .chain(self.history_items.iter().filter(|history_item| {
-                    Some(*history_item) != self.currently_opened_path.as_ref()
-                }))
+                .history_items
+                .iter()
                 .enumerate()
-                .map(|(i, history_item)| PathMatch {
-                    score: i as f64,
-                    positions: Vec::new(),
-                    worktree_id: history_item.worktree_id.to_usize(),
-                    path: Arc::clone(&history_item.path),
-                    path_prefix: "".into(),
-                    distance_to_relative_ancestor: usize::MAX,
+                .map(|(i, found_path)| {
+                    let worktree_id = found_path.project.worktree_id;
+                    // TODO kb wrong:
+                    // * for no worktree, check the project path
+                    // * if no project path exists — filter out(?), otherwise take it and open a workspace for it (enum for match kinds?)
+                    let path = if project.worktree_for_id(worktree_id, cx).is_some() {
+                        Arc::clone(&found_path.project.path)
+                    } else {
+                        found_path
+                            .absolute
+                            .as_ref()
+                            .map(|abs_path| Arc::from(abs_path.as_path()))
+                            .unwrap_or_else(|| Arc::clone(&found_path.project.path))
+                    };
+                    PathMatch {
+                        score: i as f64,
+                        positions: Vec::new(),
+                        worktree_id: worktree_id.to_usize(),
+                        path,
+                        path_prefix: "".into(),
+                        distance_to_relative_ancestor: usize::MAX,
+                    }
                 })
                 .collect();
             cx.notify();
@@ -876,10 +921,10 @@ mod tests {
         // When workspace has an active item, sort items which are closer to that item
         // first when they have the same name. In this case, b.txt is closer to dir2's a.txt
         // so that one should be sorted earlier
-        let b_path = Some(ProjectPath {
+        let b_path = Some(dummy_found_path(ProjectPath {
             worktree_id,
             path: Arc::from(Path::new("/root/dir2/b.txt")),
-        });
+        }));
         let (_, finder) = cx.add_window(|cx| {
             Picker::new(
                 FileFinderDelegate::new(
@@ -1012,10 +1057,10 @@ mod tests {
         .await;
         assert_eq!(
             history_after_first,
-            vec![ProjectPath {
+            vec![dummy_found_path(ProjectPath {
                 worktree_id,
                 path: Arc::from(Path::new("test/first.rs")),
-            }],
+            })],
             "Should show 1st opened item in the history when opening the 2nd item"
         );
 
@@ -1032,14 +1077,14 @@ mod tests {
         assert_eq!(
             history_after_second,
             vec![
-                ProjectPath {
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/second.rs")),
-                },
-                ProjectPath {
+                }),
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/first.rs")),
-                },
+                }),
             ],
             "Should show 1st and 2nd opened items in the history when opening the 3rd item. \
 2nd item should be the first in the history, as the last opened."
@@ -1058,18 +1103,18 @@ mod tests {
         assert_eq!(
             history_after_third,
             vec![
-                ProjectPath {
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/third.rs")),
-                },
-                ProjectPath {
+                }),
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/second.rs")),
-                },
-                ProjectPath {
+                }),
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/first.rs")),
-                },
+                }),
             ],
             "Should show 1st, 2nd and 3rd opened items in the history when opening the 2nd item again. \
 3rd item should be the first in the history, as the last opened."
@@ -1088,18 +1133,18 @@ mod tests {
         assert_eq!(
             history_after_second_again,
             vec![
-                ProjectPath {
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/second.rs")),
-                },
-                ProjectPath {
+                }),
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/third.rs")),
-                },
-                ProjectPath {
+                }),
+                dummy_found_path(ProjectPath {
                     worktree_id,
                     path: Arc::from(Path::new("test/first.rs")),
-                },
+                }),
             ],
             "Should show 1st, 2nd and 3rd opened items in the history when opening the 3rd item again. \
 2nd item, as the last opened, 3rd item should go next as it was opened right before."
@@ -1114,7 +1159,7 @@ mod tests {
         workspace: &ViewHandle<Workspace>,
         deterministic: &gpui::executor::Deterministic,
         cx: &mut gpui::TestAppContext,
-    ) -> Vec<ProjectPath> {
+    ) -> Vec<FoundPath> {
         cx.dispatch_action(window_id, Toggle);
         let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
         finder
@@ -1216,4 +1261,11 @@ mod tests {
         })
         .unwrap()
     }
+
+    fn dummy_found_path(project_path: ProjectPath) -> FoundPath {
+        FoundPath {
+            project: project_path,
+            absolute: None,
+        }
+    }
 }