From 84e4891d544f14e7e22348b89ecaf261b44ca611 Mon Sep 17 00:00:00 2001 From: Patrick <39161540+patrickJramos@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:28:33 -0300 Subject: [PATCH] file_finder: Add `skip_focus_for_active_in_search` setting (#27624) Closes #27073 Currently, when searching for a file with Ctrl+P, and the first file found is the active one, file_finder skips focus to the second file automatically. This PR adds a setting to disable this and make the first file always the focused one. Default setting is still skipping the active file. Release Notes: - Added the `skip_focus_for_active_in_search` setting for the file finder, which allows turning off the default behavior of skipping focus on the active file while searching in the file finder. --------- Co-authored-by: Smit Barmase --- assets/settings/default.json | 15 ++++- crates/file_finder/src/file_finder.rs | 17 ++--- .../file_finder/src/file_finder_settings.rs | 5 ++ crates/file_finder/src/file_finder_tests.rs | 67 +++++++++++++++++++ docs/src/configuring-zed.md | 12 ++++ 5 files changed, 107 insertions(+), 9 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 9a2b1e58bc..692ac57d60 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -834,7 +834,20 @@ // "modal_max_width": "full" // // Default: small - "modal_max_width": "small" + "modal_max_width": "small", + // Determines whether the file finder should skip focus for the active file in search results. + // There are 2 possible values: + // + // 1. true: When searching for files, if the currently active file appears as the first result, + // auto-focus will skip it and focus the second result instead. + // "skip_focus_for_active_in_search": true + // + // 2. false: When searching for files, the first result will always receive focus, + // even if it's the currently active file. + // "skip_focus_for_active_in_search": false + // + // Default: true + "skip_focus_for_active_in_search": true }, // Whether or not to remove any trailing whitespace from lines of a buffer // before saving it. diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 7a54475cc0..e0819aa14a 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -822,7 +822,6 @@ impl FileFinderDelegate { did_cancel: bool, query: FileSearchQuery, matches: impl IntoIterator, - cx: &mut Context>, ) { if search_id >= self.latest_search_id { @@ -849,7 +848,7 @@ impl FileFinderDelegate { ); self.selected_index = selected_match.map_or_else( - || self.calculate_selected_index(), + || self.calculate_selected_index(cx), |m| { self.matches .position(&m, self.currently_opened_path.as_ref()) @@ -1092,12 +1091,14 @@ impl FileFinderDelegate { } /// Skips first history match (that is displayed topmost) if it's currently opened. - fn calculate_selected_index(&self) -> usize { - if let Some(Match::History { path, .. }) = self.matches.get(0) { - if Some(path) == self.currently_opened_path.as_ref() { - let elements_after_first = self.matches.len() - 1; - if elements_after_first > 0 { - return 1; + fn calculate_selected_index(&self, cx: &mut Context>) -> usize { + if FileFinderSettings::get_global(cx).skip_focus_for_active_in_search { + if let Some(Match::History { path, .. }) = self.matches.get(0) { + if Some(path) == self.currently_opened_path.as_ref() { + let elements_after_first = self.matches.len() - 1; + if elements_after_first > 0 { + return 1; + } } } } diff --git a/crates/file_finder/src/file_finder_settings.rs b/crates/file_finder/src/file_finder_settings.rs index 0a03287ba7..4a2f2bd2a3 100644 --- a/crates/file_finder/src/file_finder_settings.rs +++ b/crates/file_finder/src/file_finder_settings.rs @@ -7,6 +7,7 @@ use settings::{Settings, SettingsSources}; pub struct FileFinderSettings { pub file_icons: bool, pub modal_max_width: Option, + pub skip_focus_for_active_in_search: bool, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] @@ -19,6 +20,10 @@ pub struct FileFinderSettingsContent { /// /// Default: small pub modal_max_width: Option, + /// Determines whether the file finder should skip focus for the active file in search results. + /// + /// Default: true + pub skip_focus_for_active_in_search: Option, } impl Settings for FileFinderSettings { diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index d2a5f1402d..180994c4db 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -1359,6 +1359,73 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one( }); } +#[gpui::test] +async fn test_setting_auto_select_first_and_select_active_file(cx: &mut TestAppContext) { + let app_state = init_test(cx); + + cx.update(|cx| { + let settings = *FileFinderSettings::get_global(cx); + + FileFinderSettings::override_global( + FileFinderSettings { + skip_focus_for_active_in_search: false, + ..settings + }, + cx, + ); + }); + + app_state + .fs + .as_fake() + .insert_tree( + path!("/src"), + json!({ + "test": { + "bar.rs": "// Bar file", + "lib.rs": "// Lib file", + "maaa.rs": "// Maaaaaaa", + "main.rs": "// Main file", + "moo.rs": "// Moooooo", + } + }), + ) + .await; + + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; + let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); + + open_close_queried_buffer("bar", 1, "bar.rs", &workspace, cx).await; + open_close_queried_buffer("lib", 1, "lib.rs", &workspace, cx).await; + open_queried_buffer("main", 1, "main.rs", &workspace, cx).await; + + // main.rs is on top, previously used is selected + let picker = open_file_picker(&workspace, cx); + picker.update(cx, |finder, _| { + assert_eq!(finder.delegate.matches.len(), 3); + assert_match_selection(finder, 0, "main.rs"); + assert_match_at_position(finder, 1, "lib.rs"); + assert_match_at_position(finder, 2, "bar.rs"); + }); + + // all files match, main.rs is on top, and is selected + picker + .update_in(cx, |finder, window, cx| { + finder + .delegate + .update_matches(".rs".to_string(), window, cx) + }) + .await; + picker.update(cx, |finder, _| { + assert_eq!(finder.delegate.matches.len(), 5); + assert_match_selection(finder, 0, "main.rs"); + assert_match_at_position(finder, 1, "bar.rs"); + assert_match_at_position(finder, 2, "lib.rs"); + assert_match_at_position(finder, 3, "moo.rs"); + assert_match_at_position(finder, 4, "maaa.rs"); + }); +} + #[gpui::test] async fn test_non_separate_history_items(cx: &mut TestAppContext) { let app_state = init_test(cx); diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 96dbdc26d4..38895915e4 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -2037,12 +2037,24 @@ Or to set a `socks5` proxy: ## File Finder +### File Icons + +- Description: Whether to show file icons in the file finder. +- Setting: `file_icons` +- Default: `true` + ### Modal Max Width - Description: Max-width of the file finder modal. It can take one of these values: `small`, `medium`, `large`, `xlarge`, and `full`. - Setting: `modal_max_width` - Default: `small` +### Skip Focus For Active In Search + +- Description: Determines whether the file finder should skip focus for the active file in search results. +- Setting: `skip_focus_for_active_in_search` +- Default: `true` + ## Preferred Line Length - Description: The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.