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