search: Allow running a search with different options

Refactor search options to use bitflags so that we can represent
the entire set of settings in one place.
This commit is contained in:
Conrad Irwin 2023-06-27 21:46:08 -06:00
parent 20d8a2a1ec
commit 75fe77c11d
5 changed files with 196 additions and 100 deletions

1
Cargo.lock generated
View file

@ -6428,6 +6428,7 @@ name = "search"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags",
"client", "client",
"collections", "collections",
"editor", "editor",

View file

@ -9,6 +9,7 @@ path = "src/search.rs"
doctest = false doctest = false
[dependencies] [dependencies]
bitflags = "1"
collections = { path = "../collections" } collections = { path = "../collections" }
editor = { path = "../editor" } editor = { path = "../editor" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
SearchOption, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex,
ToggleWholeWord, ToggleWholeWord,
}; };
use collections::HashMap; use collections::HashMap;
@ -42,12 +42,12 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(BufferSearchBar::select_next_match_on_pane); cx.add_action(BufferSearchBar::select_next_match_on_pane);
cx.add_action(BufferSearchBar::select_prev_match_on_pane); cx.add_action(BufferSearchBar::select_prev_match_on_pane);
cx.add_action(BufferSearchBar::handle_editor_cancel); cx.add_action(BufferSearchBar::handle_editor_cancel);
add_toggle_option_action::<ToggleCaseSensitive>(SearchOption::CaseSensitive, cx); add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx);
add_toggle_option_action::<ToggleWholeWord>(SearchOption::WholeWord, cx); add_toggle_option_action::<ToggleWholeWord>(SearchOptions::WHOLE_WORD, cx);
add_toggle_option_action::<ToggleRegex>(SearchOption::Regex, cx); add_toggle_option_action::<ToggleRegex>(SearchOptions::REGEX, cx);
} }
fn add_toggle_option_action<A: Action>(option: SearchOption, cx: &mut AppContext) { fn add_toggle_option_action<A: Action>(option: SearchOptions, cx: &mut AppContext) {
cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext<Pane>| { cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext<Pane>| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
if search_bar.update(cx, |search_bar, cx| search_bar.show(false, false, cx)) { if search_bar.update(cx, |search_bar, cx| search_bar.show(false, false, cx)) {
@ -69,9 +69,8 @@ pub struct BufferSearchBar {
seachable_items_with_matches: seachable_items_with_matches:
HashMap<Box<dyn WeakSearchableItemHandle>, Vec<Box<dyn Any + Send>>>, HashMap<Box<dyn WeakSearchableItemHandle>, Vec<Box<dyn Any + Send>>>,
pending_search: Option<Task<()>>, pending_search: Option<Task<()>>,
case_sensitive: bool, search_options: SearchOptions,
whole_word: bool, default_options: SearchOptions,
regex: bool,
query_contains_error: bool, query_contains_error: bool,
dismissed: bool, dismissed: bool,
} }
@ -153,19 +152,19 @@ impl View for BufferSearchBar {
.with_children(self.render_search_option( .with_children(self.render_search_option(
supported_options.case, supported_options.case,
"Case", "Case",
SearchOption::CaseSensitive, SearchOptions::CASE_SENSITIVE,
cx, cx,
)) ))
.with_children(self.render_search_option( .with_children(self.render_search_option(
supported_options.word, supported_options.word,
"Word", "Word",
SearchOption::WholeWord, SearchOptions::WHOLE_WORD,
cx, cx,
)) ))
.with_children(self.render_search_option( .with_children(self.render_search_option(
supported_options.regex, supported_options.regex,
"Regex", "Regex",
SearchOption::Regex, SearchOptions::REGEX,
cx, cx,
)) ))
.contained() .contained()
@ -250,9 +249,8 @@ impl BufferSearchBar {
active_searchable_item_subscription: None, active_searchable_item_subscription: None,
active_match_index: None, active_match_index: None,
seachable_items_with_matches: Default::default(), seachable_items_with_matches: Default::default(),
case_sensitive: false, default_options: SearchOptions::NONE,
whole_word: false, search_options: SearchOptions::NONE,
regex: false,
pending_search: None, pending_search: None,
query_contains_error: false, query_contains_error: false,
dismissed: true, dismissed: true,
@ -280,6 +278,17 @@ impl BufferSearchBar {
} }
pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool { pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
self.show_with_options(focus, suggest_query, self.default_options, cx)
}
pub fn show_with_options(
&mut self,
focus: bool,
suggest_query: bool,
search_option: SearchOptions,
cx: &mut ViewContext<Self>,
) -> bool {
self.search_options = search_option;
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item { let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
SearchableItemHandle::boxed_clone(searchable_item.as_ref()) SearchableItemHandle::boxed_clone(searchable_item.as_ref())
} else { } else {
@ -320,7 +329,7 @@ impl BufferSearchBar {
&self, &self,
option_supported: bool, option_supported: bool,
icon: &'static str, icon: &'static str,
option: SearchOption, option: SearchOptions,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<AnyElement<Self>> { ) -> Option<AnyElement<Self>> {
if !option_supported { if !option_supported {
@ -328,9 +337,9 @@ impl BufferSearchBar {
} }
let tooltip_style = theme::current(cx).tooltip.clone(); let tooltip_style = theme::current(cx).tooltip.clone();
let is_active = self.is_search_option_enabled(option); let is_active = self.search_options.contains(option);
Some( Some(
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| { MouseEventHandler::<Self, _>::new(option.bits as usize, cx, |state, cx| {
let theme = theme::current(cx); let theme = theme::current(cx);
let style = theme let style = theme
.search .search
@ -346,7 +355,7 @@ impl BufferSearchBar {
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
.with_tooltip::<Self>( .with_tooltip::<Self>(
option as usize, option.bits as usize,
format!("Toggle {}", option.label()), format!("Toggle {}", option.label()),
Some(option.to_toggle_action()), Some(option.to_toggle_action()),
tooltip_style, tooltip_style,
@ -461,21 +470,10 @@ impl BufferSearchBar {
} }
} }
fn is_search_option_enabled(&self, search_option: SearchOption) -> bool { fn toggle_search_option(&mut self, search_option: SearchOptions, cx: &mut ViewContext<Self>) {
match search_option { self.search_options.toggle(search_option);
SearchOption::WholeWord => self.whole_word, self.default_options = self.search_options;
SearchOption::CaseSensitive => self.case_sensitive,
SearchOption::Regex => self.regex,
}
}
fn toggle_search_option(&mut self, search_option: SearchOption, cx: &mut ViewContext<Self>) {
let value = match search_option {
SearchOption::WholeWord => &mut self.whole_word,
SearchOption::CaseSensitive => &mut self.case_sensitive,
SearchOption::Regex => &mut self.regex,
};
*value = !*value;
self.update_matches(false, cx); self.update_matches(false, cx);
cx.notify(); cx.notify();
} }
@ -571,11 +569,11 @@ impl BufferSearchBar {
self.active_match_index.take(); self.active_match_index.take();
active_searchable_item.clear_matches(cx); active_searchable_item.clear_matches(cx);
} else { } else {
let query = if self.regex { let query = if self.search_options.contains(SearchOptions::REGEX) {
match SearchQuery::regex( match SearchQuery::regex(
query, query,
self.whole_word, self.search_options.contains(SearchOptions::WHOLE_WORD),
self.case_sensitive, self.search_options.contains(SearchOptions::CASE_SENSITIVE),
Vec::new(), Vec::new(),
Vec::new(), Vec::new(),
) { ) {
@ -589,8 +587,8 @@ impl BufferSearchBar {
} else { } else {
SearchQuery::text( SearchQuery::text(
query, query,
self.whole_word, self.search_options.contains(SearchOptions::WHOLE_WORD),
self.case_sensitive, self.search_options.contains(SearchOptions::CASE_SENSITIVE),
Vec::new(), Vec::new(),
Vec::new(), Vec::new(),
) )
@ -656,8 +654,7 @@ mod tests {
use language::Buffer; use language::Buffer;
use unindent::Unindent as _; use unindent::Unindent as _;
#[gpui::test] fn init_test(cx: &mut TestAppContext) -> (ViewHandle<Editor>, ViewHandle<BufferSearchBar>) {
async fn test_search_simple(cx: &mut TestAppContext) {
crate::project_search::tests::init_test(cx); crate::project_search::tests::init_test(cx);
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {
@ -684,6 +681,13 @@ mod tests {
search_bar search_bar
}); });
(editor, search_bar)
}
#[gpui::test]
async fn test_search_simple(cx: &mut TestAppContext) {
let (editor, search_bar) = init_test(cx);
// Search for a string that appears with different casing. // Search for a string that appears with different casing.
// By default, search is case-insensitive. // By default, search is case-insensitive.
search_bar.update(cx, |search_bar, cx| { search_bar.update(cx, |search_bar, cx| {
@ -708,7 +712,7 @@ mod tests {
// Switch to a case sensitive search. // Switch to a case sensitive search.
search_bar.update(cx, |search_bar, cx| { search_bar.update(cx, |search_bar, cx| {
search_bar.toggle_search_option(SearchOption::CaseSensitive, cx); search_bar.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
}); });
editor.next_notification(cx).await; editor.next_notification(cx).await;
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -765,7 +769,7 @@ mod tests {
// Switch to a whole word search. // Switch to a whole word search.
search_bar.update(cx, |search_bar, cx| { search_bar.update(cx, |search_bar, cx| {
search_bar.toggle_search_option(SearchOption::WholeWord, cx); search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
}); });
editor.next_notification(cx).await; editor.next_notification(cx).await;
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -966,4 +970,99 @@ mod tests {
assert_eq!(search_bar.active_match_index, Some(2)); assert_eq!(search_bar.active_match_index, Some(2));
}); });
} }
#[gpui::test]
async fn test_search_with_options(cx: &mut TestAppContext) {
let (editor, search_bar) = init_test(cx);
// show with options should make current search case sensitive
search_bar.update(cx, |search_bar, cx| {
search_bar.show_with_options(false, false, SearchOptions::CASE_SENSITIVE, cx);
search_bar.set_query("us", cx);
});
editor.next_notification(cx).await;
editor.update(cx, |editor, cx| {
assert_eq!(
editor.all_background_highlights(cx),
&[(
DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
Color::red(),
)]
);
});
// show should return to the default options (case insensitive)
search_bar.update(cx, |search_bar, cx| {
search_bar.show(true, true, cx);
});
editor.next_notification(cx).await;
editor.update(cx, |editor, cx| {
assert_eq!(
editor.all_background_highlights(cx),
&[
(
DisplayPoint::new(2, 17)..DisplayPoint::new(2, 19),
Color::red(),
),
(
DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
Color::red(),
)
]
);
});
// toggling a search option (even in show_with_options mode) should update the defaults
search_bar.update(cx, |search_bar, cx| {
search_bar.set_query("regex", cx);
search_bar.show_with_options(false, false, SearchOptions::CASE_SENSITIVE, cx);
search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx)
});
editor.next_notification(cx).await;
editor.update(cx, |editor, cx| {
assert_eq!(
editor.all_background_highlights(cx),
&[(
DisplayPoint::new(0, 35)..DisplayPoint::new(0, 40),
Color::red(),
),]
);
});
// defaults should still include whole word
search_bar.update(cx, |search_bar, cx| {
search_bar.show(true, true, cx);
});
editor.next_notification(cx).await;
editor.update(cx, |editor, cx| {
assert_eq!(
editor.all_background_highlights(cx),
&[(
DisplayPoint::new(0, 35)..DisplayPoint::new(0, 40),
Color::red(),
),]
);
});
// removing whole word changes the search again
search_bar.update(cx, |search_bar, cx| {
search_bar.toggle_search_option(SearchOptions::WHOLE_WORD, cx)
});
editor.next_notification(cx).await;
editor.update(cx, |editor, cx| {
assert_eq!(
editor.all_background_highlights(cx),
&[
(
DisplayPoint::new(0, 35)..DisplayPoint::new(0, 40),
Color::red(),
),
(
DisplayPoint::new(0, 44)..DisplayPoint::new(0, 49),
Color::red()
)
]
);
});
}
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
SearchOption, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex,
ToggleWholeWord, ToggleWholeWord,
}; };
use anyhow::Result; use anyhow::Result;
@ -51,12 +51,12 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(ProjectSearchBar::select_prev_match); cx.add_action(ProjectSearchBar::select_prev_match);
cx.capture_action(ProjectSearchBar::tab); cx.capture_action(ProjectSearchBar::tab);
cx.capture_action(ProjectSearchBar::tab_previous); cx.capture_action(ProjectSearchBar::tab_previous);
add_toggle_option_action::<ToggleCaseSensitive>(SearchOption::CaseSensitive, cx); add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx);
add_toggle_option_action::<ToggleWholeWord>(SearchOption::WholeWord, cx); add_toggle_option_action::<ToggleWholeWord>(SearchOptions::WHOLE_WORD, cx);
add_toggle_option_action::<ToggleRegex>(SearchOption::Regex, cx); add_toggle_option_action::<ToggleRegex>(SearchOptions::REGEX, cx);
} }
fn add_toggle_option_action<A: Action>(option: SearchOption, cx: &mut AppContext) { fn add_toggle_option_action<A: Action>(option: SearchOptions, cx: &mut AppContext) {
cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext<Pane>| { cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext<Pane>| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<ProjectSearchBar>() { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<ProjectSearchBar>() {
if search_bar.update(cx, |search_bar, cx| { if search_bar.update(cx, |search_bar, cx| {
@ -89,9 +89,7 @@ pub struct ProjectSearchView {
model: ModelHandle<ProjectSearch>, model: ModelHandle<ProjectSearch>,
query_editor: ViewHandle<Editor>, query_editor: ViewHandle<Editor>,
results_editor: ViewHandle<Editor>, results_editor: ViewHandle<Editor>,
case_sensitive: bool, search_options: SearchOptions,
whole_word: bool,
regex: bool,
panels_with_errors: HashSet<InputPanel>, panels_with_errors: HashSet<InputPanel>,
active_match_index: Option<usize>, active_match_index: Option<usize>,
search_id: usize, search_id: usize,
@ -408,9 +406,7 @@ impl ProjectSearchView {
let project; let project;
let excerpts; let excerpts;
let mut query_text = String::new(); let mut query_text = String::new();
let mut regex = false; let mut options = SearchOptions::NONE;
let mut case_sensitive = false;
let mut whole_word = false;
{ {
let model = model.read(cx); let model = model.read(cx);
@ -418,9 +414,7 @@ impl ProjectSearchView {
excerpts = model.excerpts.clone(); excerpts = model.excerpts.clone();
if let Some(active_query) = model.active_query.as_ref() { if let Some(active_query) = model.active_query.as_ref() {
query_text = active_query.as_str().to_string(); query_text = active_query.as_str().to_string();
regex = active_query.is_regex(); options = SearchOptions::from_query(active_query);
case_sensitive = active_query.case_sensitive();
whole_word = active_query.whole_word();
} }
} }
cx.observe(&model, |this, _, cx| this.model_changed(cx)) cx.observe(&model, |this, _, cx| this.model_changed(cx))
@ -496,9 +490,7 @@ impl ProjectSearchView {
model, model,
query_editor, query_editor,
results_editor, results_editor,
case_sensitive, search_options: options,
whole_word,
regex,
panels_with_errors: HashSet::new(), panels_with_errors: HashSet::new(),
active_match_index: None, active_match_index: None,
query_editor_was_focused: false, query_editor_was_focused: false,
@ -594,11 +586,11 @@ impl ProjectSearchView {
return None; return None;
} }
}; };
if self.regex { if self.search_options.contains(SearchOptions::REGEX) {
match SearchQuery::regex( match SearchQuery::regex(
text, text,
self.whole_word, self.search_options.contains(SearchOptions::WHOLE_WORD),
self.case_sensitive, self.search_options.contains(SearchOptions::CASE_SENSITIVE),
included_files, included_files,
excluded_files, excluded_files,
) { ) {
@ -615,8 +607,8 @@ impl ProjectSearchView {
} else { } else {
Some(SearchQuery::text( Some(SearchQuery::text(
text, text,
self.whole_word, self.search_options.contains(SearchOptions::WHOLE_WORD),
self.case_sensitive, self.search_options.contains(SearchOptions::CASE_SENSITIVE),
included_files, included_files,
excluded_files, excluded_files,
)) ))
@ -765,9 +757,7 @@ impl ProjectSearchBar {
search_view.query_editor.update(cx, |editor, cx| { search_view.query_editor.update(cx, |editor, cx| {
editor.set_text(old_query.as_str(), cx); editor.set_text(old_query.as_str(), cx);
}); });
search_view.regex = old_query.is_regex(); search_view.search_options = SearchOptions::from_query(&old_query);
search_view.whole_word = old_query.whole_word();
search_view.case_sensitive = old_query.case_sensitive();
} }
} }
new_query new_query
@ -855,15 +845,10 @@ impl ProjectSearchBar {
}); });
} }
fn toggle_search_option(&mut self, option: SearchOption, cx: &mut ViewContext<Self>) -> bool { fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext<Self>) -> bool {
if let Some(search_view) = self.active_project_search.as_ref() { if let Some(search_view) = self.active_project_search.as_ref() {
search_view.update(cx, |search_view, cx| { search_view.update(cx, |search_view, cx| {
let value = match option { search_view.search_options.toggle(option);
SearchOption::WholeWord => &mut search_view.whole_word,
SearchOption::CaseSensitive => &mut search_view.case_sensitive,
SearchOption::Regex => &mut search_view.regex,
};
*value = !*value;
search_view.search(cx); search_view.search(cx);
}); });
cx.notify(); cx.notify();
@ -920,12 +905,12 @@ impl ProjectSearchBar {
fn render_option_button( fn render_option_button(
&self, &self,
icon: &'static str, icon: &'static str,
option: SearchOption, option: SearchOptions,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> AnyElement<Self> { ) -> AnyElement<Self> {
let tooltip_style = theme::current(cx).tooltip.clone(); let tooltip_style = theme::current(cx).tooltip.clone();
let is_active = self.is_option_enabled(option, cx); let is_active = self.is_option_enabled(option, cx);
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| { MouseEventHandler::<Self, _>::new(option.bits as usize, cx, |state, cx| {
let theme = theme::current(cx); let theme = theme::current(cx);
let style = theme let style = theme
.search .search
@ -941,7 +926,7 @@ impl ProjectSearchBar {
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
.with_tooltip::<Self>( .with_tooltip::<Self>(
option as usize, option.bits as usize,
format!("Toggle {}", option.label()), format!("Toggle {}", option.label()),
Some(option.to_toggle_action()), Some(option.to_toggle_action()),
tooltip_style, tooltip_style,
@ -950,14 +935,9 @@ impl ProjectSearchBar {
.into_any() .into_any()
} }
fn is_option_enabled(&self, option: SearchOption, cx: &AppContext) -> bool { fn is_option_enabled(&self, option: SearchOptions, cx: &AppContext) -> bool {
if let Some(search) = self.active_project_search.as_ref() { if let Some(search) = self.active_project_search.as_ref() {
let search = search.read(cx); search.read(cx).search_options.contains(option)
match option {
SearchOption::WholeWord => search.whole_word,
SearchOption::CaseSensitive => search.case_sensitive,
SearchOption::Regex => search.regex,
}
} else { } else {
false false
} }
@ -1048,17 +1028,17 @@ impl View for ProjectSearchBar {
Flex::row() Flex::row()
.with_child(self.render_option_button( .with_child(self.render_option_button(
"Case", "Case",
SearchOption::CaseSensitive, SearchOptions::CASE_SENSITIVE,
cx, cx,
)) ))
.with_child(self.render_option_button( .with_child(self.render_option_button(
"Word", "Word",
SearchOption::WholeWord, SearchOptions::WHOLE_WORD,
cx, cx,
)) ))
.with_child(self.render_option_button( .with_child(self.render_option_button(
"Regex", "Regex",
SearchOption::Regex, SearchOptions::REGEX,
cx, cx,
)) ))
.contained() .contained()

View file

@ -1,5 +1,7 @@
use bitflags::bitflags;
pub use buffer_search::BufferSearchBar; pub use buffer_search::BufferSearchBar;
use gpui::{actions, Action, AppContext}; use gpui::{actions, Action, AppContext};
use project::search::SearchQuery;
pub use project_search::{ProjectSearchBar, ProjectSearchView}; pub use project_search::{ProjectSearchBar, ProjectSearchView};
pub mod buffer_search; pub mod buffer_search;
@ -21,27 +23,40 @@ actions!(
] ]
); );
#[derive(Clone, Copy, PartialEq)] bitflags! {
pub enum SearchOption { #[derive(Default)]
WholeWord, pub struct SearchOptions: u8 {
CaseSensitive, const NONE = 0b000;
Regex, const WHOLE_WORD = 0b001;
const CASE_SENSITIVE = 0b010;
const REGEX = 0b100;
}
} }
impl SearchOption { impl SearchOptions {
pub fn label(&self) -> &'static str { pub fn label(&self) -> &'static str {
match self { match *self {
SearchOption::WholeWord => "Match Whole Word", SearchOptions::WHOLE_WORD => "Match Whole Word",
SearchOption::CaseSensitive => "Match Case", SearchOptions::CASE_SENSITIVE => "Match Case",
SearchOption::Regex => "Use Regular Expression", SearchOptions::REGEX => "Use Regular Expression",
_ => panic!("{:?} is not a named SearchOption", self),
} }
} }
pub fn to_toggle_action(&self) -> Box<dyn Action> { pub fn to_toggle_action(&self) -> Box<dyn Action> {
match self { match *self {
SearchOption::WholeWord => Box::new(ToggleWholeWord), SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord),
SearchOption::CaseSensitive => Box::new(ToggleCaseSensitive), SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive),
SearchOption::Regex => Box::new(ToggleRegex), SearchOptions::REGEX => Box::new(ToggleRegex),
_ => panic!("{:?} is not a named SearchOption", self),
} }
} }
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::REGEX, query.is_regex());
options
}
} }