Detailed changes
@@ -1,12 +1,10 @@
mod registrar;
use crate::{
- FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions,
- SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex,
- ToggleReplace, ToggleSelection, ToggleWholeWord,
- search_bar::{
- input_base_styles, render_action_button, render_text_input, toggle_replace_button,
- },
+ FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOption,
+ SearchOptions, SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive,
+ ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord,
+ search_bar::{input_base_styles, render_action_button, render_text_input},
};
use any_vec::AnyVec;
use anyhow::Context as _;
@@ -215,31 +213,22 @@ impl Render for BufferSearchBar {
h_flex()
.gap_1()
.when(case, |div| {
- div.child(SearchOptions::CASE_SENSITIVE.as_button(
- self.search_options.contains(SearchOptions::CASE_SENSITIVE),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_case_sensitive(&ToggleCaseSensitive, window, cx)
- }),
- ))
+ div.child(
+ SearchOption::CaseSensitive
+ .as_button(self.search_options, focus_handle.clone()),
+ )
})
.when(word, |div| {
- div.child(SearchOptions::WHOLE_WORD.as_button(
- self.search_options.contains(SearchOptions::WHOLE_WORD),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_whole_word(&ToggleWholeWord, window, cx)
- }),
- ))
+ div.child(
+ SearchOption::WholeWord
+ .as_button(self.search_options, focus_handle.clone()),
+ )
})
.when(regex, |div| {
- div.child(SearchOptions::REGEX.as_button(
- self.search_options.contains(SearchOptions::REGEX),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_regex(&ToggleRegex, window, cx)
- }),
- ))
+ div.child(
+ SearchOption::Regex
+ .as_button(self.search_options, focus_handle.clone()),
+ )
}),
)
});
@@ -248,13 +237,13 @@ impl Render for BufferSearchBar {
.gap_1()
.min_w_64()
.when(replacement, |this| {
- this.child(toggle_replace_button(
- "buffer-search-bar-toggle-replace-button",
- focus_handle.clone(),
+ this.child(render_action_button(
+ "buffer-search-bar-toggle",
+ IconName::Replace,
self.replace_enabled,
- cx.listener(|this, _: &ClickEvent, window, cx| {
- this.toggle_replace(&ToggleReplace, window, cx);
- }),
+ "Toggle Replace",
+ &ToggleReplace,
+ focus_handle.clone(),
))
})
.when(selection, |this| {
@@ -1,36 +0,0 @@
-use gpui::{Action, SharedString};
-
-use crate::{ActivateRegexMode, ActivateTextMode};
-
-// TODO: Update the default search mode to get from config
-#[derive(Copy, Clone, Debug, Default, PartialEq)]
-pub enum SearchMode {
- #[default]
- Text,
- Regex,
-}
-
-impl SearchMode {
- pub(crate) fn label(&self) -> &'static str {
- match self {
- SearchMode::Text => "Text",
- SearchMode::Regex => "Regex",
- }
- }
- pub(crate) fn tooltip(&self) -> SharedString {
- format!("Activate {} Mode", self.label()).into()
- }
- pub(crate) fn action(&self) -> Box<dyn Action> {
- match self {
- SearchMode::Text => ActivateTextMode.boxed_clone(),
- SearchMode::Regex => ActivateRegexMode.boxed_clone(),
- }
- }
-}
-
-pub(crate) fn next_mode(mode: &SearchMode) -> SearchMode {
- match mode {
- SearchMode::Text => SearchMode::Regex,
- SearchMode::Regex => SearchMode::Text,
- }
-}
@@ -1,11 +1,9 @@
use crate::{
BufferSearchBar, FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext,
- SearchOptions, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleIncludeIgnored,
- ToggleRegex, ToggleReplace, ToggleWholeWord,
+ SearchOption, SearchOptions, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive,
+ ToggleIncludeIgnored, ToggleRegex, ToggleReplace, ToggleWholeWord,
buffer_search::Deploy,
- search_bar::{
- input_base_styles, render_action_button, render_text_input, toggle_replace_button,
- },
+ search_bar::{input_base_styles, render_action_button, render_text_input},
};
use anyhow::Context as _;
use collections::HashMap;
@@ -1784,14 +1782,6 @@ impl ProjectSearchBar {
}
}
- fn is_option_enabled(&self, option: SearchOptions, cx: &App) -> bool {
- if let Some(search) = self.active_project_search.as_ref() {
- search.read(cx).search_options.contains(option)
- } else {
- false
- }
- }
-
fn next_history_query(
&mut self,
_: &NextHistoryQuery,
@@ -1972,27 +1962,17 @@ impl Render for ProjectSearchBar {
.child(
h_flex()
.gap_1()
- .child(SearchOptions::CASE_SENSITIVE.as_button(
- self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_search_option(SearchOptions::CASE_SENSITIVE, window, cx);
- }),
- ))
- .child(SearchOptions::WHOLE_WORD.as_button(
- self.is_option_enabled(SearchOptions::WHOLE_WORD, cx),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_search_option(SearchOptions::WHOLE_WORD, window, cx);
- }),
- ))
- .child(SearchOptions::REGEX.as_button(
- self.is_option_enabled(SearchOptions::REGEX, cx),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_search_option(SearchOptions::REGEX, window, cx);
- }),
- )),
+ .child(
+ SearchOption::CaseSensitive
+ .as_button(search.search_options, focus_handle.clone()),
+ )
+ .child(
+ SearchOption::WholeWord
+ .as_button(search.search_options, focus_handle.clone()),
+ )
+ .child(
+ SearchOption::Regex.as_button(search.search_options, focus_handle.clone()),
+ ),
);
let mode_column = h_flex()
@@ -2026,16 +2006,16 @@ impl Render for ProjectSearchBar {
}
}),
)
- .child(toggle_replace_button(
- "project-search-toggle-replace",
- focus_handle.clone(),
+ .child(render_action_button(
+ "project-search",
+ IconName::Replace,
self.active_project_search
.as_ref()
.map(|search| search.read(cx).replace_enabled)
.unwrap_or_default(),
- cx.listener(|this, _, window, cx| {
- this.toggle_replace(&ToggleReplace, window, cx);
- }),
+ "Toggle Replace",
+ &ToggleReplace,
+ focus_handle.clone(),
));
let query_focus = search.query_editor.focus_handle(cx);
@@ -2149,15 +2129,8 @@ impl Render for ProjectSearchBar {
})),
)
.child(
- SearchOptions::INCLUDE_IGNORED.as_button(
- search
- .search_options
- .contains(SearchOptions::INCLUDE_IGNORED),
- focus_handle.clone(),
- cx.listener(|this, _, window, cx| {
- this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, window, cx);
- }),
- ),
+ SearchOption::IncludeIgnored
+ .as_button(search.search_options, focus_handle.clone()),
);
h_flex()
.w_full()
@@ -9,6 +9,8 @@ use ui::{Tooltip, prelude::*};
use workspace::notifications::NotificationId;
use workspace::{Toast, Workspace};
+pub use search_status_button::SEARCH_ICON;
+
pub mod buffer_search;
pub mod project_search;
pub(crate) mod search_bar;
@@ -59,48 +61,87 @@ actions!(
bitflags! {
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct SearchOptions: u8 {
- const NONE = 0b000;
- const WHOLE_WORD = 0b001;
- const CASE_SENSITIVE = 0b010;
- const INCLUDE_IGNORED = 0b100;
- const REGEX = 0b1000;
- const ONE_MATCH_PER_LINE = 0b100000;
+ const NONE = 0;
+ const WHOLE_WORD = 1 << SearchOption::WholeWord as u8;
+ const CASE_SENSITIVE = 1 << SearchOption::CaseSensitive as u8;
+ const INCLUDE_IGNORED = 1 << SearchOption::IncludeIgnored as u8;
+ const REGEX = 1 << SearchOption::Regex as u8;
+ const ONE_MATCH_PER_LINE = 1 << SearchOption::OneMatchPerLine as u8;
/// If set, reverse direction when finding the active match
- const BACKWARDS = 0b10000;
+ const BACKWARDS = 1 << SearchOption::Backwards as u8;
}
}
-impl SearchOptions {
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum SearchOption {
+ WholeWord = 0,
+ CaseSensitive,
+ IncludeIgnored,
+ Regex,
+ OneMatchPerLine,
+ Backwards,
+}
+
+impl SearchOption {
+ pub fn as_options(self) -> SearchOptions {
+ SearchOptions::from_bits(1 << self as u8).unwrap()
+ }
+
pub fn label(&self) -> &'static str {
- match *self {
- SearchOptions::WHOLE_WORD => "Match Whole Words",
- SearchOptions::CASE_SENSITIVE => "Match Case Sensitively",
- SearchOptions::INCLUDE_IGNORED => "Also search files ignored by configuration",
- SearchOptions::REGEX => "Use Regular Expressions",
- _ => panic!("{:?} is not a named SearchOption", self),
+ match self {
+ SearchOption::WholeWord => "Match Whole Words",
+ SearchOption::CaseSensitive => "Match Case Sensitively",
+ SearchOption::IncludeIgnored => "Also search files ignored by configuration",
+ SearchOption::Regex => "Use Regular Expressions",
+ SearchOption::OneMatchPerLine => "One Match Per Line",
+ SearchOption::Backwards => "Search Backwards",
}
}
pub fn icon(&self) -> ui::IconName {
- match *self {
- SearchOptions::WHOLE_WORD => ui::IconName::WholeWord,
- SearchOptions::CASE_SENSITIVE => ui::IconName::CaseSensitive,
- SearchOptions::INCLUDE_IGNORED => ui::IconName::Sliders,
- SearchOptions::REGEX => ui::IconName::Regex,
- _ => panic!("{:?} is not a named SearchOption", self),
+ match self {
+ SearchOption::WholeWord => ui::IconName::WholeWord,
+ SearchOption::CaseSensitive => ui::IconName::CaseSensitive,
+ SearchOption::IncludeIgnored => ui::IconName::Sliders,
+ SearchOption::Regex => ui::IconName::Regex,
+ _ => panic!("{self:?} is not a named SearchOption"),
}
}
- pub fn to_toggle_action(&self) -> Box<dyn Action + Sync + Send + 'static> {
+ pub fn to_toggle_action(&self) -> &'static dyn Action {
match *self {
- SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
- SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
- SearchOptions::INCLUDE_IGNORED => Box::new(ToggleIncludeIgnored),
- SearchOptions::REGEX => Box::new(ToggleRegex),
- _ => panic!("{:?} is not a named SearchOption", self),
+ SearchOption::WholeWord => &ToggleWholeWord,
+ SearchOption::CaseSensitive => &ToggleCaseSensitive,
+ SearchOption::IncludeIgnored => &ToggleIncludeIgnored,
+ SearchOption::Regex => &ToggleRegex,
+ _ => panic!("{self:?} is not a toggle action"),
}
}
+ pub fn as_button(&self, active: SearchOptions, focus_handle: FocusHandle) -> impl IntoElement {
+ let action = self.to_toggle_action();
+ let label = self.label();
+ IconButton::new(label, self.icon())
+ .on_click({
+ let focus_handle = focus_handle.clone();
+ move |_, window, cx| {
+ if !focus_handle.is_focused(&window) {
+ window.focus(&focus_handle);
+ }
+ window.dispatch_action(action.boxed_clone(), cx)
+ }
+ })
+ .style(ButtonStyle::Subtle)
+ .shape(IconButtonShape::Square)
+ .toggle_state(active.contains(self.as_options()))
+ .tooltip({
+ move |window, cx| Tooltip::for_action_in(label, action, &focus_handle, window, cx)
+ })
+ }
+}
+
+impl SearchOptions {
pub fn none() -> SearchOptions {
SearchOptions::NONE
}
@@ -122,24 +163,6 @@ impl SearchOptions {
options.set(SearchOptions::REGEX, settings.regex);
options
}
-
- pub fn as_button<Action: Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static>(
- &self,
- active: bool,
- focus_handle: FocusHandle,
- action: Action,
- ) -> impl IntoElement + use<Action> {
- IconButton::new(self.label(), self.icon())
- .on_click(action)
- .style(ButtonStyle::Subtle)
- .shape(IconButtonShape::Square)
- .toggle_state(active)
- .tooltip({
- let action = self.to_toggle_action();
- let label = self.label();
- move |window, cx| Tooltip::for_action_in(label, &*action, &focus_handle, window, cx)
- })
- }
}
pub(crate) fn show_no_more_matches(window: &mut Window, cx: &mut App) {
@@ -5,8 +5,6 @@ use theme::ThemeSettings;
use ui::{IconButton, IconButtonShape};
use ui::{Tooltip, prelude::*};
-use crate::ToggleReplace;
-
pub(super) fn render_action_button(
id_prefix: &'static str,
icon: ui::IconName,
@@ -46,25 +44,6 @@ pub(crate) fn input_base_styles(border_color: Hsla, map: impl FnOnce(Div) -> Div
.rounded_lg()
}
-pub(crate) fn toggle_replace_button(
- id: &'static str,
- focus_handle: FocusHandle,
- replace_enabled: bool,
- on_click: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
-) -> IconButton {
- IconButton::new(id, IconName::Replace)
- .shape(IconButtonShape::Square)
- .style(ButtonStyle::Subtle)
- .when(replace_enabled, |button| button.style(ButtonStyle::Filled))
- .on_click(on_click)
- .toggle_state(replace_enabled)
- .tooltip({
- move |window, cx| {
- Tooltip::for_action_in("Toggle Replace", &ToggleReplace, &focus_handle, window, cx)
- }
- })
-}
-
pub(crate) fn render_text_input(
editor: &Entity<Editor>,
color_override: Option<Color>,
@@ -3,6 +3,8 @@ use settings::Settings as _;
use ui::{ButtonCommon, Clickable, Context, Render, Tooltip, Window, prelude::*};
use workspace::{ItemHandle, StatusItemView};
+pub const SEARCH_ICON: IconName = IconName::MagnifyingGlass;
+
pub struct SearchButton;
impl SearchButton {
@@ -20,7 +22,7 @@ impl Render for SearchButton {
}
button.child(
- IconButton::new("project-search-indicator", IconName::MagnifyingGlass)
+ IconButton::new("project-search-indicator", SEARCH_ICON)
.icon_size(IconSize::Small)
.tooltip(|window, cx| {
Tooltip::for_action(
@@ -140,7 +140,7 @@ impl Render for QuickActionBar {
let search_button = editor.is_singleton(cx).then(|| {
QuickActionBarButton::new(
"toggle buffer search",
- IconName::MagnifyingGlass,
+ search::SEARCH_ICON,
!self.buffer_search_bar.read(cx).is_dismissed(),
Box::new(buffer_search::Deploy::find()),
focus_handle.clone(),