search.rs

  1use bitflags::bitflags;
  2pub use buffer_search::BufferSearchBar;
  3use gpui::{actions, Action, AppContext, IntoElement};
  4pub use mode::SearchMode;
  5use project::search::SearchQuery;
  6pub use project_search::ProjectSearchView;
  7use ui::{prelude::*, Tooltip};
  8use ui::{ButtonStyle, IconButton};
  9
 10pub mod buffer_search;
 11mod history;
 12mod mode;
 13pub mod project_search;
 14pub(crate) mod search_bar;
 15
 16pub fn init(cx: &mut AppContext) {
 17    menu::init();
 18    buffer_search::init(cx);
 19    project_search::init(cx);
 20}
 21
 22actions!(
 23    search,
 24    [
 25        CycleMode,
 26        ToggleWholeWord,
 27        ToggleCaseSensitive,
 28        ToggleIncludeIgnored,
 29        ToggleReplace,
 30        SelectNextMatch,
 31        SelectPrevMatch,
 32        SelectAllMatches,
 33        NextHistoryQuery,
 34        PreviousHistoryQuery,
 35        ActivateTextMode,
 36        ActivateSemanticMode,
 37        ActivateRegexMode,
 38        ReplaceAll,
 39        ReplaceNext,
 40    ]
 41);
 42
 43bitflags! {
 44    #[derive(Default)]
 45    pub struct SearchOptions: u8 {
 46        const NONE = 0b000;
 47        const WHOLE_WORD = 0b001;
 48        const CASE_SENSITIVE = 0b010;
 49        const INCLUDE_IGNORED = 0b100;
 50    }
 51}
 52
 53impl SearchOptions {
 54    pub fn label(&self) -> &'static str {
 55        match *self {
 56            SearchOptions::WHOLE_WORD => "Match Whole Word",
 57            SearchOptions::CASE_SENSITIVE => "Match Case",
 58            SearchOptions::INCLUDE_IGNORED => "Include ignored",
 59            _ => panic!("{:?} is not a named SearchOption", self),
 60        }
 61    }
 62
 63    pub fn icon(&self) -> ui::IconName {
 64        match *self {
 65            SearchOptions::WHOLE_WORD => ui::IconName::WholeWord,
 66            SearchOptions::CASE_SENSITIVE => ui::IconName::CaseSensitive,
 67            SearchOptions::INCLUDE_IGNORED => ui::IconName::FileGit,
 68            _ => panic!("{:?} is not a named SearchOption", self),
 69        }
 70    }
 71
 72    pub fn to_toggle_action(&self) -> Box<dyn Action + Sync + Send + 'static> {
 73        match *self {
 74            SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
 75            SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
 76            SearchOptions::INCLUDE_IGNORED => Box::new(ToggleIncludeIgnored),
 77            _ => panic!("{:?} is not a named SearchOption", self),
 78        }
 79    }
 80
 81    pub fn none() -> SearchOptions {
 82        SearchOptions::NONE
 83    }
 84
 85    pub fn from_query(query: &SearchQuery) -> SearchOptions {
 86        let mut options = SearchOptions::NONE;
 87        options.set(SearchOptions::WHOLE_WORD, query.whole_word());
 88        options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive());
 89        options.set(SearchOptions::INCLUDE_IGNORED, query.include_ignored());
 90        options
 91    }
 92
 93    pub fn as_button(
 94        &self,
 95        active: bool,
 96        action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
 97    ) -> impl IntoElement {
 98        IconButton::new(self.label(), self.icon())
 99            .on_click(action)
100            .style(ButtonStyle::Subtle)
101            .selected(active)
102            .tooltip({
103                let action = self.to_toggle_action();
104                let label: SharedString = format!("Toggle {}", self.label()).into();
105                move |cx| Tooltip::for_action(label.clone(), &*action, cx)
106            })
107    }
108}