search.rs

  1use bitflags::bitflags;
  2pub use buffer_search::BufferSearchBar;
  3use gpui::{
  4    actions,
  5    elements::{Component, SafeStylable, TooltipStyle},
  6    Action, AnyElement, AppContext, Element, View,
  7};
  8pub use mode::SearchMode;
  9use project::search::SearchQuery;
 10pub use project_search::{ProjectSearchBar, ProjectSearchView};
 11use theme::components::{
 12    action_button::Button, svg::Svg, ComponentExt, IconButtonStyle, ToggleIconButtonStyle,
 13};
 14
 15pub mod buffer_search;
 16mod history;
 17mod mode;
 18pub mod project_search;
 19pub(crate) mod search_bar;
 20
 21pub fn init(cx: &mut AppContext) {
 22    buffer_search::init(cx);
 23    project_search::init(cx);
 24}
 25
 26actions!(
 27    search,
 28    [
 29        CycleMode,
 30        ToggleWholeWord,
 31        ToggleCaseSensitive,
 32        ToggleIncludeIgnored,
 33        ToggleReplace,
 34        SelectNextMatch,
 35        SelectPrevMatch,
 36        SelectAllMatches,
 37        NextHistoryQuery,
 38        PreviousHistoryQuery,
 39        ActivateTextMode,
 40        ActivateSemanticMode,
 41        ActivateRegexMode,
 42        ReplaceAll,
 43        ReplaceNext,
 44    ]
 45);
 46
 47bitflags! {
 48    #[derive(Default)]
 49    pub struct SearchOptions: u8 {
 50        const NONE = 0b000;
 51        const WHOLE_WORD = 0b001;
 52        const CASE_SENSITIVE = 0b010;
 53        const INCLUDE_IGNORED = 0b100;
 54    }
 55}
 56
 57impl SearchOptions {
 58    pub fn label(&self) -> &'static str {
 59        match *self {
 60            Self::WHOLE_WORD => "Match Whole Word",
 61            Self::CASE_SENSITIVE => "Match Case",
 62            Self::INCLUDE_IGNORED => "Include Ignored",
 63            _ => panic!("{self:?} is not a named SearchOption"),
 64        }
 65    }
 66
 67    pub fn icon(&self) -> &'static str {
 68        match *self {
 69            Self::WHOLE_WORD => "icons/word_search.svg",
 70            Self::CASE_SENSITIVE => "icons/case_insensitive.svg",
 71            Self::INCLUDE_IGNORED => "icons/case_insensitive.svg",
 72            _ => panic!("{self:?} is not a named SearchOption"),
 73        }
 74    }
 75
 76    pub fn to_toggle_action(&self) -> Box<dyn Action> {
 77        match *self {
 78            Self::WHOLE_WORD => Box::new(ToggleWholeWord),
 79            Self::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
 80            Self::INCLUDE_IGNORED => Box::new(ToggleIncludeIgnored),
 81            _ => panic!("{self:?} is not a named SearchOption"),
 82        }
 83    }
 84
 85    pub fn none() -> SearchOptions {
 86        SearchOptions::NONE
 87    }
 88
 89    pub fn from_query(query: &SearchQuery) -> SearchOptions {
 90        let mut options = SearchOptions::NONE;
 91        options.set(SearchOptions::WHOLE_WORD, query.whole_word());
 92        options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive());
 93        options.set(SearchOptions::INCLUDE_IGNORED, query.include_ignored());
 94        options
 95    }
 96
 97    pub fn as_button<V: View>(
 98        &self,
 99        active: bool,
100        tooltip_style: TooltipStyle,
101        button_style: ToggleIconButtonStyle,
102    ) -> AnyElement<V> {
103        Button::dynamic_action(self.to_toggle_action())
104            .with_tooltip(format!("Toggle {}", self.label()), tooltip_style)
105            .with_contents(Svg::new(self.icon()))
106            .toggleable(active)
107            .with_style(button_style)
108            .element()
109            .into_any()
110    }
111}
112
113fn toggle_replace_button<V: View>(
114    active: bool,
115    tooltip_style: TooltipStyle,
116    button_style: ToggleIconButtonStyle,
117) -> AnyElement<V> {
118    Button::dynamic_action(Box::new(ToggleReplace))
119        .with_tooltip("Toggle Replace", tooltip_style)
120        .with_contents(theme::components::svg::Svg::new("icons/replace.svg"))
121        .toggleable(active)
122        .with_style(button_style)
123        .element()
124        .into_any()
125}
126
127fn replace_action<V: View>(
128    action: impl Action,
129    name: &'static str,
130    icon_path: &'static str,
131    tooltip_style: TooltipStyle,
132    button_style: IconButtonStyle,
133) -> AnyElement<V> {
134    Button::dynamic_action(Box::new(action))
135        .with_tooltip(name, tooltip_style)
136        .with_contents(theme::components::svg::Svg::new(icon_path))
137        .with_style(button_style)
138        .element()
139        .into_any()
140}