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        ToggleSelection,
 29        SelectNextMatch,
 30        SelectPrevMatch,
 31        SelectAllMatches,
 32        NextHistoryQuery,
 33        PreviousHistoryQuery,
 34        ReplaceAll,
 35        ReplaceNext,
 36    ]
 37);
 38
 39bitflags! {
 40    #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
 41    pub struct SearchOptions: u8 {
 42        const NONE = 0b000;
 43        const WHOLE_WORD = 0b001;
 44        const CASE_SENSITIVE = 0b010;
 45        const INCLUDE_IGNORED = 0b100;
 46        const REGEX = 0b1000;
 47    }
 48}
 49
 50impl SearchOptions {
 51    pub fn label(&self) -> &'static str {
 52        match *self {
 53            SearchOptions::WHOLE_WORD => "whole word",
 54            SearchOptions::CASE_SENSITIVE => "match case",
 55            SearchOptions::INCLUDE_IGNORED => "include Ignored",
 56            SearchOptions::REGEX => "regular expression",
 57            _ => panic!("{:?} is not a named SearchOption", self),
 58        }
 59    }
 60
 61    pub fn icon(&self) -> ui::IconName {
 62        match *self {
 63            SearchOptions::WHOLE_WORD => ui::IconName::WholeWord,
 64            SearchOptions::CASE_SENSITIVE => ui::IconName::CaseSensitive,
 65            SearchOptions::INCLUDE_IGNORED => ui::IconName::FileGit,
 66            SearchOptions::REGEX => ui::IconName::Regex,
 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            SearchOptions::REGEX => Box::new(ToggleRegex),
 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.set(SearchOptions::REGEX, query.is_regex());
 91        options
 92    }
 93
 94    pub fn as_button(
 95        &self,
 96        active: bool,
 97        action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
 98    ) -> impl IntoElement {
 99        IconButton::new(self.label(), self.icon())
100            .on_click(action)
101            .style(ButtonStyle::Subtle)
102            .selected(active)
103            .tooltip({
104                let action = self.to_toggle_action();
105                let label: SharedString = format!("Toggle {}", self.label()).into();
106                move |cx| Tooltip::for_action(label.clone(), &*action, cx)
107            })
108    }
109}