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}