1use bitflags::bitflags;
2pub use buffer_search::BufferSearchBar;
3use gpui::{actions, Action, AppContext, IntoElement};
4pub use mode::SearchMode;
5use project::search::SearchQuery;
6use ui::{prelude::*, Tooltip};
7use ui::{ButtonStyle, Icon, IconButton};
8//pub use project_search::{ProjectSearchBar, ProjectSearchView};
9// use theme::components::{
10// action_button::Button, svg::Svg, ComponentExt, IconButtonStyle, ToggleIconButtonStyle,
11// };
12
13pub mod buffer_search;
14mod history;
15mod mode;
16pub mod project_search;
17pub(crate) mod search_bar;
18
19pub fn init(cx: &mut AppContext) {
20 menu::init();
21 buffer_search::init(cx);
22 project_search::init(cx);
23}
24
25actions!(
26 search,
27 [
28 CycleMode,
29 ToggleWholeWord,
30 ToggleCaseSensitive,
31 ToggleReplace,
32 SelectNextMatch,
33 SelectPrevMatch,
34 SelectAllMatches,
35 NextHistoryQuery,
36 PreviousHistoryQuery,
37 ActivateTextMode,
38 ActivateSemanticMode,
39 ActivateRegexMode,
40 ReplaceAll,
41 ReplaceNext,
42 ]
43);
44
45bitflags! {
46 #[derive(Default)]
47 pub struct SearchOptions: u8 {
48 const NONE = 0b000;
49 const WHOLE_WORD = 0b001;
50 const CASE_SENSITIVE = 0b010;
51 const INCLUDE_IGNORED = 0b100;
52 }
53}
54
55impl SearchOptions {
56 pub fn label(&self) -> &'static str {
57 match *self {
58 SearchOptions::WHOLE_WORD => "Match Whole Word",
59 SearchOptions::CASE_SENSITIVE => "Match Case",
60 _ => panic!("{:?} is not a named SearchOption", self),
61 }
62 }
63
64 pub fn icon(&self) -> ui::Icon {
65 match *self {
66 SearchOptions::WHOLE_WORD => ui::Icon::WholeWord,
67 SearchOptions::CASE_SENSITIVE => ui::Icon::CaseSensitive,
68 _ => panic!("{:?} is not a named SearchOption", self),
69 }
70 }
71
72 pub fn to_toggle_action(&self) -> Box<dyn Action + Sync + Send + 'static> {
73 match *self {
74 SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
75 SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
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
89 }
90
91 pub fn as_button(
92 &self,
93 active: bool,
94 action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
95 ) -> impl IntoElement {
96 IconButton::new(self.label(), self.icon())
97 .on_click(action)
98 .style(ButtonStyle::Subtle)
99 .when(active, |button| button.style(ButtonStyle::Filled))
100 .tooltip({
101 let action = self.to_toggle_action();
102 let label: SharedString = format!("Toggle {}", self.label()).into();
103 move |cx| Tooltip::for_action(label.clone(), &*action, cx)
104 })
105 }
106}
107
108fn toggle_replace_button(
109 active: bool,
110 action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
111) -> impl IntoElement {
112 // todo: add toggle_replace button
113 IconButton::new("buffer-search-bar-toggle-replace-button", Icon::Replace)
114 .on_click(action)
115 .style(ButtonStyle::Subtle)
116 .when(active, |button| button.style(ButtonStyle::Filled))
117 .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx))
118}
119
120fn render_replace_button(
121 action: impl Action + 'static + Send + Sync,
122 icon: Icon,
123 tooltip: &'static str,
124 on_click: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
125) -> impl IntoElement {
126 let id: SharedString = format!("search-replace-{}", action.name()).into();
127 IconButton::new(id, icon)
128 .tooltip({
129 let action = action.boxed_clone();
130 move |cx| Tooltip::for_action(tooltip, &*action, cx)
131 })
132 .on_click(on_click)
133}