Add a way to configure default search options (#17179)

Closes https://github.com/zed-industries/zed/issues/4646

```json
// Search options to enable by default when opening new project and buffer searches.
"search": {
  "whole_word": false,
  "case_sensitive": false,
  "include_ignored": false,
  "regex": false
}
```

Release Notes:

- Added `search` settings section to configure default options enabled
in buffer and project searches
([#4646](https://github.com/zed-industries/zed/issues/4646))

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
thataboy 2024-09-07 07:25:41 -07:00 committed by GitHub
parent 8985fd87c2
commit 65961b80fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 85 additions and 8 deletions

View file

@ -279,6 +279,13 @@
"relative_line_numbers": false, "relative_line_numbers": false,
// If 'search_wrap' is disabled, search result do not wrap around the end of the file. // If 'search_wrap' is disabled, search result do not wrap around the end of the file.
"search_wrap": true, "search_wrap": true,
// Search options to enable by default when opening new project and buffer searches.
"search": {
"whole_word": false,
"case_sensitive": false,
"include_ignored": false,
"regex": false
},
// When to populate a new search's query based on the text under the cursor. // When to populate a new search's query based on the text under the cursor.
// This setting can take the following three values: // This setting can take the following three values:
// //

View file

@ -59,7 +59,9 @@ use convert_case::{Case, Casing};
use debounced_delay::DebouncedDelay; use debounced_delay::DebouncedDelay;
use display_map::*; use display_map::*;
pub use display_map::{DisplayPoint, FoldPlaceholder}; pub use display_map::{DisplayPoint, FoldPlaceholder};
pub use editor_settings::{CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine}; pub use editor_settings::{
CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings,
};
pub use editor_settings_controls::*; pub use editor_settings_controls::*;
use element::LineWithInvisibles; use element::LineWithInvisibles;
pub use element::{ pub use element::{

View file

@ -28,6 +28,8 @@ pub struct EditorSettings {
#[serde(default)] #[serde(default)]
pub double_click_in_multibuffer: DoubleClickInMultibuffer, pub double_click_in_multibuffer: DoubleClickInMultibuffer,
pub search_wrap: bool, pub search_wrap: bool,
#[serde(default)]
pub search: SearchSettings,
pub auto_signature_help: bool, pub auto_signature_help: bool,
pub show_signature_help_after_edits: bool, pub show_signature_help_after_edits: bool,
pub jupyter: Jupyter, pub jupyter: Jupyter,
@ -156,6 +158,40 @@ pub enum ScrollBeyondLastLine {
VerticalScrollMargin, VerticalScrollMargin,
} }
/// Default options for buffer and project search items.
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct SearchSettings {
#[serde(default)]
pub whole_word: bool,
#[serde(default)]
pub case_sensitive: bool,
#[serde(default)]
pub include_ignored: bool,
#[serde(default)]
pub regex: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct SearchSettingsContent {
pub whole_word: Option<bool>,
pub case_sensitive: Option<bool>,
pub include_ignored: Option<bool>,
pub regex: Option<bool>,
}
impl Settings for SearchSettings {
const KEY: Option<&'static str> = Some("search");
type FileContent = SearchSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
sources.json_merge()
}
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent { pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor. /// Whether the cursor blinks in the editor.
@ -251,6 +287,11 @@ pub struct EditorSettingsContent {
/// Default: true /// Default: true
pub search_wrap: Option<bool>, pub search_wrap: Option<bool>,
/// Defaults to use when opening a new buffer and project search items.
///
/// Default: nothing is enabled
pub search: Option<SearchSettings>,
/// Whether to automatically show a signature help pop-up or not. /// Whether to automatically show a signature help pop-up or not.
/// ///
/// Default: false /// Default: false

View file

@ -9,7 +9,7 @@ use any_vec::AnyVec;
use collections::HashMap; use collections::HashMap;
use editor::{ use editor::{
actions::{Tab, TabPrev}, actions::{Tab, TabPrev},
DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle, DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle, SearchSettings,
}; };
use futures::channel::oneshot; use futures::channel::oneshot;
use gpui::{ use gpui::{
@ -22,7 +22,7 @@ use project::{
search_history::{SearchHistory, SearchHistoryCursor}, search_history::{SearchHistory, SearchHistoryCursor},
}; };
use serde::Deserialize; use serde::Deserialize;
use settings::Settings; use settings::{Settings, SettingsStore};
use std::sync::Arc; use std::sync::Arc;
use theme::ThemeSettings; use theme::ThemeSettings;
@ -96,6 +96,7 @@ pub struct BufferSearchBar {
scroll_handle: ScrollHandle, scroll_handle: ScrollHandle,
editor_scroll_handle: ScrollHandle, editor_scroll_handle: ScrollHandle,
editor_needed_width: Pixels, editor_needed_width: Pixels,
_subscriptions: Vec<Subscription>,
} }
impl BufferSearchBar { impl BufferSearchBar {
@ -505,6 +506,12 @@ impl BufferSearchBar {
cx.subscribe(&replacement_editor, Self::on_replacement_editor_event) cx.subscribe(&replacement_editor, Self::on_replacement_editor_event)
.detach(); .detach();
let search_options = SearchOptions::from_settings(&SearchSettings::get_global(cx));
let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
this.default_options = SearchOptions::from_settings(&SearchSettings::get_global(cx));
});
Self { Self {
query_editor, query_editor,
query_editor_focused: false, query_editor_focused: false,
@ -514,8 +521,8 @@ impl BufferSearchBar {
active_searchable_item_subscription: None, active_searchable_item_subscription: None,
active_match_index: None, active_match_index: None,
searchable_items_with_matches: Default::default(), searchable_items_with_matches: Default::default(),
default_options: SearchOptions::NONE, default_options: search_options,
search_options: SearchOptions::NONE, search_options,
pending_search: None, pending_search: None,
query_contains_error: false, query_contains_error: false,
dismissed: true, dismissed: true,
@ -530,6 +537,7 @@ impl BufferSearchBar {
scroll_handle: ScrollHandle::new(), scroll_handle: ScrollHandle::new(),
editor_scroll_handle: ScrollHandle::new(), editor_scroll_handle: ScrollHandle::new(),
editor_needed_width: px(0.), editor_needed_width: px(0.),
_subscriptions: vec![settings_subscription],
} }
} }
@ -602,6 +610,9 @@ impl BufferSearchBar {
let Some(handle) = self.active_searchable_item.as_ref() else { let Some(handle) = self.active_searchable_item.as_ref() else {
return false; return false;
}; };
if self.default_options != self.search_options {
self.search_options = self.default_options;
}
self.dismissed = false; self.dismissed = false;
handle.search_bar_visibility_changed(true, cx); handle.search_bar_visibility_changed(true, cx);
@ -1203,6 +1214,7 @@ mod tests {
language::init(cx); language::init(cx);
Project::init_settings(cx); Project::init_settings(cx);
theme::init(theme::LoadThemes::JustBase, cx); theme::init(theme::LoadThemes::JustBase, cx);
crate::init(cx);
}); });
} }

View file

@ -668,7 +668,9 @@ impl ProjectSearchView {
let (mut options, filters_enabled) = if let Some(settings) = settings { let (mut options, filters_enabled) = if let Some(settings) = settings {
(settings.search_options, settings.filters_enabled) (settings.search_options, settings.filters_enabled)
} else { } else {
(SearchOptions::NONE, false) let search_options =
SearchOptions::from_settings(&EditorSettings::get_global(cx).search);
(search_options, false)
}; };
{ {
@ -3537,7 +3539,7 @@ pub mod tests {
editor::init(cx); editor::init(cx);
workspace::init_settings(cx); workspace::init_settings(cx);
Project::init_settings(cx); Project::init_settings(cx);
super::init(cx); crate::init(cx);
}); });
} }

View file

@ -1,8 +1,10 @@
use bitflags::bitflags; use bitflags::bitflags;
pub use buffer_search::BufferSearchBar; pub use buffer_search::BufferSearchBar;
use editor::SearchSettings;
use gpui::{actions, Action, AppContext, IntoElement}; use gpui::{actions, Action, AppContext, IntoElement};
use project::search::SearchQuery; use project::search::SearchQuery;
pub use project_search::ProjectSearchView; pub use project_search::ProjectSearchView;
use settings::Settings;
use ui::{prelude::*, Tooltip}; use ui::{prelude::*, Tooltip};
use ui::{ButtonStyle, IconButton}; use ui::{ButtonStyle, IconButton};
use workspace::notifications::NotificationId; use workspace::notifications::NotificationId;
@ -13,6 +15,7 @@ pub mod project_search;
pub(crate) mod search_bar; pub(crate) mod search_bar;
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
SearchSettings::register(cx);
menu::init(); menu::init();
buffer_search::init(cx); buffer_search::init(cx);
project_search::init(cx); project_search::init(cx);
@ -93,6 +96,15 @@ impl SearchOptions {
options options
} }
pub fn from_settings(settings: &SearchSettings) -> SearchOptions {
let mut options = SearchOptions::NONE;
options.set(SearchOptions::WHOLE_WORD, settings.whole_word);
options.set(SearchOptions::CASE_SENSITIVE, settings.case_sensitive);
options.set(SearchOptions::INCLUDE_IGNORED, settings.include_ignored);
options.set(SearchOptions::REGEX, settings.regex);
options
}
pub fn as_button( pub fn as_button(
&self, &self,
active: bool, active: bool,

View file

@ -16,12 +16,12 @@ impl VimTestContext {
return; return;
} }
cx.update(|cx| { cx.update(|cx| {
search::init(cx);
let settings = SettingsStore::test(cx); let settings = SettingsStore::test(cx);
cx.set_global(settings); cx.set_global(settings);
release_channel::init(SemanticVersion::default(), cx); release_channel::init(SemanticVersion::default(), cx);
command_palette::init(cx); command_palette::init(cx);
crate::init(cx); crate::init(cx);
search::init(cx);
}); });
} }

View file

@ -3442,6 +3442,7 @@ mod tests {
); );
tasks_ui::init(cx); tasks_ui::init(cx);
initialize_workspace(app_state.clone(), prompt_builder, cx); initialize_workspace(app_state.clone(), prompt_builder, cx);
search::init(cx);
app_state app_state
}) })
} }