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 <heysmitbarmase@gmail.com>
This commit is contained in:
Patrick 2025-04-30 14:28:33 -03:00 committed by GitHub
parent d03d8ccec1
commit 84e4891d54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 107 additions and 9 deletions

View file

@ -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.

View file

@ -822,7 +822,6 @@ impl FileFinderDelegate {
did_cancel: bool,
query: FileSearchQuery,
matches: impl IntoIterator<Item = ProjectPanelOrdMatch>,
cx: &mut Context<Picker<Self>>,
) {
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<Picker<Self>>) -> 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;
}
}
}
}

View file

@ -7,6 +7,7 @@ use settings::{Settings, SettingsSources};
pub struct FileFinderSettings {
pub file_icons: bool,
pub modal_max_width: Option<FileFinderWidth>,
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<FileFinderWidth>,
/// 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<bool>,
}
impl Settings for FileFinderSettings {

View file

@ -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);

View file

@ -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.