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}