diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 61a3e469c1570620eae65de62eed79bc918ac07c..979cfa72fffffd0ef9ffc74cec5a8f33aa23488c 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1172,18 +1172,25 @@ impl FileFinderDelegate { ) } + /// Attempts to resolve an absolute file path and update the search matches if found. + /// + /// If the query path resolves to an absolute file that exists in the project, + /// this method will find the corresponding worktree and relative path, create a + /// match for it, and update the picker's search results. + /// + /// Returns `true` if the absolute path exists, otherwise returns `false`. fn lookup_absolute_path( &self, query: FileSearchQuery, window: &mut Window, cx: &mut Context>, - ) -> Task<()> { + ) -> Task { cx.spawn_in(window, async move |picker, cx| { let Some(project) = picker .read_with(cx, |picker, _| picker.delegate.project.clone()) .log_err() else { - return; + return false; }; let query_path = Path::new(query.path_query()); @@ -1216,7 +1223,7 @@ impl FileFinderDelegate { }) .log_err(); if update_result.is_none() { - return; + return abs_file_exists; } } @@ -1229,6 +1236,7 @@ impl FileFinderDelegate { anyhow::Ok(()) }) .log_err(); + abs_file_exists }) } @@ -1377,13 +1385,14 @@ impl PickerDelegate for FileFinderDelegate { } else { let path_position = PathWithPosition::parse_str(raw_query); let raw_query = raw_query.trim().trim_end_matches(':').to_owned(); - let path = path_position.path.to_str(); - let path_trimmed = path.unwrap_or(&raw_query).trim_end_matches(':'); + let path = path_position.path.clone(); + let path_str = path_position.path.to_str(); + let path_trimmed = path_str.unwrap_or(&raw_query).trim_end_matches(':'); let file_query_end = if path_trimmed == raw_query { None } else { // Safe to unwrap as we won't get here when the unwrap in if fails - Some(path.unwrap().len()) + Some(path_str.unwrap().len()) }; let query = FileSearchQuery { @@ -1392,11 +1401,29 @@ impl PickerDelegate for FileFinderDelegate { path_position, }; - if Path::new(query.path_query()).is_absolute() { - self.lookup_absolute_path(query, window, cx) - } else { - self.spawn_search(query, window, cx) - } + cx.spawn_in(window, async move |this, cx| { + let _ = maybe!(async move { + let is_absolute_path = path.is_absolute(); + let did_resolve_abs_path = is_absolute_path + && this + .update_in(cx, |this, window, cx| { + this.delegate + .lookup_absolute_path(query.clone(), window, cx) + })? + .await; + + // Only check for relative paths if no absolute paths were + // found. + if !did_resolve_abs_path { + this.update_in(cx, |this, window, cx| { + this.delegate.spawn_search(query, window, cx) + })? + .await; + } + anyhow::Ok(()) + }) + .await; + }) } } diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index 6df23c9dfc86f45e4173ff181ffce4d4f0c5941c..00b47bf44ec0817d0ab37ae9a610316fcdf5d3e1 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -3069,3 +3069,49 @@ async fn test_filename_precedence(cx: &mut TestAppContext) { ); }); } + +#[gpui::test] +async fn test_paths_with_starting_slash(cx: &mut TestAppContext) { + let app_state = init_test(cx); + app_state + .fs + .as_fake() + .insert_tree( + path!("/root"), + json!({ + "a": { + "file1.txt": "", + "b": { + "file2.txt": "", + }, + } + }), + ) + .await; + + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; + + let (picker, workspace, cx) = build_find_picker(project, cx); + + let matching_abs_path = "/file1.txt".to_string(); + picker + .update_in(cx, |picker, window, cx| { + picker + .delegate + .update_matches(matching_abs_path, window, cx) + }) + .await; + picker.update(cx, |picker, _| { + assert_eq!( + collect_search_matches(picker).search_paths_only(), + vec![rel_path("a/file1.txt").into()], + "Relative path starting with slash should match" + ) + }); + cx.dispatch_action(SelectNext); + cx.dispatch_action(Confirm); + cx.read(|cx| { + let active_editor = workspace.read(cx).active_item_as::(cx).unwrap(); + assert_eq!(active_editor.read(cx).title(cx), "file1.txt"); + }); +}