use bitflags::bitflags; pub use buffer_search::BufferSearchBar; use gpui::{ actions, elements::{Component, SafeStylable, TooltipStyle}, Action, AnyElement, AppContext, Element, View, }; pub use mode::SearchMode; use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; use theme::components::{ action_button::Button, svg::Svg, ComponentExt, IconButtonStyle, ToggleIconButtonStyle, }; pub mod buffer_search; mod history; mod mode; pub mod project_search; pub(crate) mod search_bar; pub fn init(cx: &mut AppContext) { buffer_search::init(cx); project_search::init(cx); } actions!( search, [ CycleMode, ToggleWholeWord, ToggleCaseSensitive, ToggleIncludeIgnored, ToggleReplace, SelectNextMatch, SelectPrevMatch, SelectAllMatches, NextHistoryQuery, PreviousHistoryQuery, ActivateTextMode, ActivateSemanticMode, ActivateRegexMode, ReplaceAll, ReplaceNext, ] ); bitflags! { #[derive(Default)] pub struct SearchOptions: u8 { const NONE = 0b000; const WHOLE_WORD = 0b001; const CASE_SENSITIVE = 0b010; const INCLUDE_IGNORED = 0b100; } } impl SearchOptions { pub fn label(&self) -> &'static str { match *self { Self::WHOLE_WORD => "Match Whole Word", Self::CASE_SENSITIVE => "Match Case", Self::INCLUDE_IGNORED => "Include Ignored", _ => panic!("{self:?} is not a named SearchOption"), } } pub fn icon(&self) -> &'static str { match *self { Self::WHOLE_WORD => "icons/word_search.svg", Self::CASE_SENSITIVE => "icons/case_insensitive.svg", Self::INCLUDE_IGNORED => "icons/case_insensitive.svg", _ => panic!("{self:?} is not a named SearchOption"), } } pub fn to_toggle_action(&self) -> Box { match *self { Self::WHOLE_WORD => Box::new(ToggleWholeWord), Self::CASE_SENSITIVE => Box::new(ToggleCaseSensitive), Self::INCLUDE_IGNORED => Box::new(ToggleIncludeIgnored), _ => panic!("{self:?} is not a named SearchOption"), } } pub fn none() -> SearchOptions { SearchOptions::NONE } pub fn from_query(query: &SearchQuery) -> SearchOptions { let mut options = SearchOptions::NONE; options.set(SearchOptions::WHOLE_WORD, query.whole_word()); options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive()); options.set(SearchOptions::INCLUDE_IGNORED, query.include_ignored()); options } pub fn as_button( &self, active: bool, tooltip_style: TooltipStyle, button_style: ToggleIconButtonStyle, ) -> AnyElement { Button::dynamic_action(self.to_toggle_action()) .with_tooltip(format!("Toggle {}", self.label()), tooltip_style) .with_contents(Svg::new(self.icon())) .toggleable(active) .with_style(button_style) .element() .into_any() } } fn toggle_replace_button( active: bool, tooltip_style: TooltipStyle, button_style: ToggleIconButtonStyle, ) -> AnyElement { Button::dynamic_action(Box::new(ToggleReplace)) .with_tooltip("Toggle Replace", tooltip_style) .with_contents(theme::components::svg::Svg::new("icons/replace.svg")) .toggleable(active) .with_style(button_style) .element() .into_any() } fn replace_action( action: impl Action, name: &'static str, icon_path: &'static str, tooltip_style: TooltipStyle, button_style: IconButtonStyle, ) -> AnyElement { Button::dynamic_action(Box::new(action)) .with_tooltip(name, tooltip_style) .with_contents(theme::components::svg::Svg::new(icon_path)) .with_style(button_style) .element() .into_any() }