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        ToggleReplace,
 33        SelectNextMatch,
 34        SelectPrevMatch,
 35        SelectAllMatches,
 36        NextHistoryQuery,
 37        PreviousHistoryQuery,
 38        ActivateTextMode,
 39        ActivateSemanticMode,
 40        ActivateRegexMode,
 41        ReplaceAll,
 42        ReplaceNext
 43    ]
 44);
 45
 46bitflags! {
 47    #[derive(Default)]
 48    pub struct SearchOptions: u8 {
 49        const NONE = 0b000;
 50        const WHOLE_WORD = 0b001;
 51        const CASE_SENSITIVE = 0b010;
 52    }
 53}
 54
 55impl SearchOptions {
 56    pub fn label(&self) -> &'static str {
 57        match *self {
 58            SearchOptions::WHOLE_WORD => "Match Whole Word",
 59            SearchOptions::CASE_SENSITIVE => "Match Case",
 60            _ => panic!("{:?} is not a named SearchOption", self),
 61        }
 62    }
 63
 64    pub fn icon(&self) -> &'static str {
 65        match *self {
 66            SearchOptions::WHOLE_WORD => "icons/word_search_12.svg",
 67            SearchOptions::CASE_SENSITIVE => "icons/case_insensitive_12.svg",
 68            _ => panic!("{:?} is not a named SearchOption", self),
 69        }
 70    }
 71
 72    pub fn to_toggle_action(&self) -> Box<dyn Action> {
 73        match *self {
 74            SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
 75            SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
 76            _ => panic!("{:?} is not a named SearchOption", self),
 77        }
 78    }
 79
 80    pub fn none() -> SearchOptions {
 81        SearchOptions::NONE
 82    }
 83
 84    pub fn from_query(query: &SearchQuery) -> SearchOptions {
 85        let mut options = SearchOptions::NONE;
 86        options.set(SearchOptions::WHOLE_WORD, query.whole_word());
 87        options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive());
 88        options
 89    }
 90
 91    pub fn as_button<V: View>(
 92        &self,
 93        active: bool,
 94        tooltip_style: TooltipStyle,
 95        button_style: ToggleIconButtonStyle,
 96    ) -> AnyElement<V> {
 97        Button::dynamic_action(self.to_toggle_action())
 98            .with_tooltip(format!("Toggle {}", self.label()), tooltip_style)
 99            .with_contents(Svg::new(self.icon()))
100            .toggleable(active)
101            .with_style(button_style)
102            .element()
103            .into_any()
104    }
105}
106
107fn toggle_replace_button<V: View>(
108    active: bool,
109    tooltip_style: TooltipStyle,
110    button_style: ToggleIconButtonStyle,
111) -> AnyElement<V> {
112    Button::dynamic_action(Box::new(ToggleReplace))
113        .with_tooltip("Toggle Replace", tooltip_style)
114        .with_contents(theme::components::svg::Svg::new("icons/replace.svg"))
115        .toggleable(active)
116        .with_style(button_style)
117        .element()
118        .into_any()
119}
120
121fn replace_action<V: View>(
122    action: impl Action,
123    name: &'static str,
124    icon_path: &'static str,
125    tooltip_style: TooltipStyle,
126    button_style: IconButtonStyle,
127) -> AnyElement<V> {
128    Button::dynamic_action(Box::new(action))
129        .with_tooltip(name, tooltip_style)
130        .with_contents(theme::components::svg::Svg::new(icon_path))
131        .with_style(button_style)
132        .element()
133        .into_any()
134}