diff --git a/assets/settings/default.json b/assets/settings/default.json index 2bf4611b90..65254afb7c 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -279,6 +279,13 @@ "relative_line_numbers": false, // If 'search_wrap' is disabled, search result do not wrap around the end of the file. "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. // This setting can take the following three values: // diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d009a40748..cb4ae63afc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -59,7 +59,9 @@ use convert_case::{Case, Casing}; use debounced_delay::DebouncedDelay; use display_map::*; 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::*; use element::LineWithInvisibles; pub use element::{ diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 04403b1547..8dadc0154b 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -28,6 +28,8 @@ pub struct EditorSettings { #[serde(default)] pub double_click_in_multibuffer: DoubleClickInMultibuffer, pub search_wrap: bool, + #[serde(default)] + pub search: SearchSettings, pub auto_signature_help: bool, pub show_signature_help_after_edits: bool, pub jupyter: Jupyter, @@ -156,6 +158,40 @@ pub enum ScrollBeyondLastLine { 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, + pub case_sensitive: Option, + pub include_ignored: Option, + pub regex: Option, +} + +impl Settings for SearchSettings { + const KEY: Option<&'static str> = Some("search"); + + type FileContent = SearchSettingsContent; + + fn load( + sources: SettingsSources, + _: &mut gpui::AppContext, + ) -> anyhow::Result { + sources.json_merge() + } +} + #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct EditorSettingsContent { /// Whether the cursor blinks in the editor. @@ -251,6 +287,11 @@ pub struct EditorSettingsContent { /// Default: true pub search_wrap: Option, + /// Defaults to use when opening a new buffer and project search items. + /// + /// Default: nothing is enabled + pub search: Option, + /// Whether to automatically show a signature help pop-up or not. /// /// Default: false diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index bea470dedd..ba10a45282 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -9,7 +9,7 @@ use any_vec::AnyVec; use collections::HashMap; use editor::{ actions::{Tab, TabPrev}, - DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle, + DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle, SearchSettings, }; use futures::channel::oneshot; use gpui::{ @@ -22,7 +22,7 @@ use project::{ search_history::{SearchHistory, SearchHistoryCursor}, }; use serde::Deserialize; -use settings::Settings; +use settings::{Settings, SettingsStore}; use std::sync::Arc; use theme::ThemeSettings; @@ -96,6 +96,7 @@ pub struct BufferSearchBar { scroll_handle: ScrollHandle, editor_scroll_handle: ScrollHandle, editor_needed_width: Pixels, + _subscriptions: Vec, } impl BufferSearchBar { @@ -505,6 +506,12 @@ impl BufferSearchBar { cx.subscribe(&replacement_editor, Self::on_replacement_editor_event) .detach(); + let search_options = SearchOptions::from_settings(&SearchSettings::get_global(cx)); + + let settings_subscription = cx.observe_global::(move |this, cx| { + this.default_options = SearchOptions::from_settings(&SearchSettings::get_global(cx)); + }); + Self { query_editor, query_editor_focused: false, @@ -514,8 +521,8 @@ impl BufferSearchBar { active_searchable_item_subscription: None, active_match_index: None, searchable_items_with_matches: Default::default(), - default_options: SearchOptions::NONE, - search_options: SearchOptions::NONE, + default_options: search_options, + search_options, pending_search: None, query_contains_error: false, dismissed: true, @@ -530,6 +537,7 @@ impl BufferSearchBar { scroll_handle: ScrollHandle::new(), editor_scroll_handle: ScrollHandle::new(), 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 { return false; }; + if self.default_options != self.search_options { + self.search_options = self.default_options; + } self.dismissed = false; handle.search_bar_visibility_changed(true, cx); @@ -1203,6 +1214,7 @@ mod tests { language::init(cx); Project::init_settings(cx); theme::init(theme::LoadThemes::JustBase, cx); + crate::init(cx); }); } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index f4e08af6da..c43d4ed454 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -668,7 +668,9 @@ impl ProjectSearchView { let (mut options, filters_enabled) = if let Some(settings) = settings { (settings.search_options, settings.filters_enabled) } 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); workspace::init_settings(cx); Project::init_settings(cx); - super::init(cx); + crate::init(cx); }); } diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 0466930f90..f8d8da87c5 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -1,8 +1,10 @@ use bitflags::bitflags; pub use buffer_search::BufferSearchBar; +use editor::SearchSettings; use gpui::{actions, Action, AppContext, IntoElement}; use project::search::SearchQuery; pub use project_search::ProjectSearchView; +use settings::Settings; use ui::{prelude::*, Tooltip}; use ui::{ButtonStyle, IconButton}; use workspace::notifications::NotificationId; @@ -13,6 +15,7 @@ pub mod project_search; pub(crate) mod search_bar; pub fn init(cx: &mut AppContext) { + SearchSettings::register(cx); menu::init(); buffer_search::init(cx); project_search::init(cx); @@ -93,6 +96,15 @@ impl SearchOptions { 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( &self, active: bool, diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index 5ae4d517b0..c985f68e70 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -16,12 +16,12 @@ impl VimTestContext { return; } cx.update(|cx| { - search::init(cx); let settings = SettingsStore::test(cx); cx.set_global(settings); release_channel::init(SemanticVersion::default(), cx); command_palette::init(cx); crate::init(cx); + search::init(cx); }); } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 74bdfb666b..9ec43d607a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -3442,6 +3442,7 @@ mod tests { ); tasks_ui::init(cx); initialize_workspace(app_state.clone(), prompt_builder, cx); + search::init(cx); app_state }) }