@@ -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.
@@ -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;
+ }
}
}
}
@@ -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 {
@@ -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);
@@ -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.