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(&self, active: bool) -> impl IntoElement {
92 IconButton::new(self.label(), self.icon())
93 .on_click({
94 let action = self.to_toggle_action();
95 move |_, cx| {
96 cx.dispatch_action(action.boxed_clone());
97 }
98 })
99 .style(ButtonStyle::Subtle)
100 .when(active, |button| button.style(ButtonStyle::Filled))
101 .tooltip({
102 let action = self.to_toggle_action();
103 let label: SharedString = format!("Toggle {}", self.label()).into();
104 move |cx| Tooltip::for_action(label.clone(), &*action, cx)
105 })
106 }
107}
108
109fn toggle_replace_button(active: bool) -> impl IntoElement {
110 // todo: add toggle_replace button
111 IconButton::new("buffer-search-bar-toggle-replace-button", Icon::Replace)
112 .on_click(|_, cx| {
113 cx.dispatch_action(Box::new(ToggleReplace));
114 cx.notify();
115 })
116 .style(ButtonStyle::Subtle)
117 .when(active, |button| button.style(ButtonStyle::Filled))
118 .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx))
119}
120
121fn render_replace_button(
122 action: impl Action + 'static + Send + Sync,
123 icon: Icon,
124 tooltip: &'static str,
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(move |_, cx| {
133 cx.dispatch_action(action.boxed_clone());
134 })
135}