search.rs

  1use bitflags::bitflags;
  2pub use buffer_search::BufferSearchBar;
  3use gpui::{actions, Action, AppContext, IntoElement};
  4use project::search::SearchQuery;
  5pub use project_search::ProjectSearchView;
  6use ui::{prelude::*, Tooltip};
  7use ui::{ButtonStyle, IconButton};
  8
  9pub mod buffer_search;
 10pub mod project_search;
 11pub(crate) mod search_bar;
 12
 13pub fn init(cx: &mut AppContext) {
 14    menu::init();
 15    buffer_search::init(cx);
 16    project_search::init(cx);
 17}
 18
 19actions!(
 20    search,
 21    [
 22        FocusSearch,
 23        ToggleWholeWord,
 24        ToggleCaseSensitive,
 25        ToggleIncludeIgnored,
 26        ToggleRegex,
 27        ToggleReplace,
 28        SelectNextMatch,
 29        SelectPrevMatch,
 30        SelectAllMatches,
 31        NextHistoryQuery,
 32        PreviousHistoryQuery,
 33        ReplaceAll,
 34        ReplaceNext,
 35    ]
 36);
 37
 38bitflags! {
 39    #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
 40    pub struct SearchOptions: u8 {
 41        const NONE = 0b000;
 42        const WHOLE_WORD = 0b001;
 43        const CASE_SENSITIVE = 0b010;
 44        const INCLUDE_IGNORED = 0b100;
 45        const REGEX = 0b1000;
 46    }
 47}
 48
 49impl SearchOptions {
 50    pub fn label(&self) -> &'static str {
 51        match *self {
 52            SearchOptions::WHOLE_WORD => "whole word",
 53            SearchOptions::CASE_SENSITIVE => "match case",
 54            SearchOptions::INCLUDE_IGNORED => "include Ignored",
 55            SearchOptions::REGEX => "regular expression",
 56            _ => panic!("{:?} is not a named SearchOption", self),
 57        }
 58    }
 59
 60    pub fn icon(&self) -> ui::IconName {
 61        match *self {
 62            SearchOptions::WHOLE_WORD => ui::IconName::WholeWord,
 63            SearchOptions::CASE_SENSITIVE => ui::IconName::CaseSensitive,
 64            SearchOptions::INCLUDE_IGNORED => ui::IconName::FileGit,
 65            SearchOptions::REGEX => ui::IconName::Regex,
 66            _ => panic!("{:?} is not a named SearchOption", self),
 67        }
 68    }
 69
 70    pub fn to_toggle_action(&self) -> Box<dyn Action + Sync + Send + 'static> {
 71        match *self {
 72            SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
 73            SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
 74            SearchOptions::INCLUDE_IGNORED => Box::new(ToggleIncludeIgnored),
 75            SearchOptions::REGEX => Box::new(ToggleRegex),
 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.set(SearchOptions::REGEX, query.is_regex());
 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}