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 buffer_search::init(cx);
21 project_search::init(cx);
22}
23
24actions!(
25 search,
26 [
27 CycleMode,
28 ToggleWholeWord,
29 ToggleCaseSensitive,
30 ToggleReplace,
31 SelectNextMatch,
32 SelectPrevMatch,
33 SelectAllMatches,
34 NextHistoryQuery,
35 PreviousHistoryQuery,
36 ActivateTextMode,
37 ActivateSemanticMode,
38 ActivateRegexMode,
39 ReplaceAll,
40 ReplaceNext,
41 ]
42);
43
44bitflags! {
45 #[derive(Default)]
46 pub struct SearchOptions: u8 {
47 const NONE = 0b000;
48 const WHOLE_WORD = 0b001;
49 const CASE_SENSITIVE = 0b010;
50 const INCLUDE_IGNORED = 0b100;
51 }
52}
53
54impl SearchOptions {
55 pub fn label(&self) -> &'static str {
56 match *self {
57 SearchOptions::WHOLE_WORD => "Match Whole Word",
58 SearchOptions::CASE_SENSITIVE => "Match Case",
59 _ => panic!("{:?} is not a named SearchOption", self),
60 }
61 }
62
63 pub fn icon(&self) -> ui::Icon {
64 match *self {
65 SearchOptions::WHOLE_WORD => ui::Icon::WholeWord,
66 SearchOptions::CASE_SENSITIVE => ui::Icon::CaseSensitive,
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 _ => panic!("{:?} is not a named SearchOption", self),
76 }
77 }
78
79 pub fn none() -> SearchOptions {
80 SearchOptions::NONE
81 }
82
83 pub fn from_query(query: &SearchQuery) -> SearchOptions {
84 let mut options = SearchOptions::NONE;
85 options.set(SearchOptions::WHOLE_WORD, query.whole_word());
86 options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive());
87 options
88 }
89
90 pub fn as_button(&self, active: bool) -> impl IntoElement {
91 IconButton::new(self.label(), self.icon())
92 .on_click({
93 let action = self.to_toggle_action();
94 move |_, cx| {
95 cx.dispatch_action(action.boxed_clone());
96 }
97 })
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(active: bool) -> impl IntoElement {
109 // todo: add toggle_replace button
110 IconButton::new("buffer-search-bar-toggle-replace-button", Icon::Replace)
111 .on_click(|_, cx| {
112 cx.dispatch_action(Box::new(ToggleReplace));
113 cx.notify();
114 })
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) -> impl IntoElement {
125 let id: SharedString = format!("search-replace-{}", action.name()).into();
126 IconButton::new(id, icon)
127 .tooltip({
128 let action = action.boxed_clone();
129 move |cx| Tooltip::for_action(tooltip, &*action, cx)
130 })
131 .on_click(move |_, cx| {
132 cx.dispatch_action(action.boxed_clone());
133 })
134}