diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 635adfb64d..6b8bf4ffb2 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -15,7 +15,7 @@ "shift-cmd-}": "pane::ActivateNextItem", "cmd-w": "pane::CloseActiveItem", "cmd-shift-W": "workspace::CloseWindow", - "alt-cmd-w": "pane::CloseInactiveItems", + "alt-cmd-t": "pane::CloseInactiveItems", "cmd-s": "workspace::Save", "cmd-shift-S": "workspace::SaveAs", "cmd-=": "zed::IncreaseBufferFontSize", @@ -141,14 +141,6 @@ ] } }, - { - "context": "Pane", - "bindings": { - "cmd-f": "project_search::ToggleFocus", - "cmd-g": "search::SelectNextMatch", - "cmd-shift-G": "search::SelectPrevMatch" - } - }, { "context": "BufferSearchBar", "bindings": { @@ -158,6 +150,17 @@ "shift-enter": "search::SelectPrevMatch" } }, + { + "context": "Pane", + "bindings": { + "cmd-f": "project_search::ToggleFocus", + "cmd-g": "search::SelectNextMatch", + "cmd-shift-G": "search::SelectPrevMatch", + "alt-cmd-c": "search::ToggleCaseSensitive", + "alt-cmd-w": "search::ToggleWholeWord", + "alt-cmd-r": "search::ToggleRegex" + } + }, // Bindings from VS Code { "context": "Editor", diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index ce36e63ad2..de9e8af9c4 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1,12 +1,13 @@ use crate::{ active_match_index, match_index_for_direction, query_suggestion_for_editor, Direction, - SearchOption, SelectNextMatch, SelectPrevMatch, + SearchOption, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, + ToggleWholeWord, }; use collections::HashMap; use editor::{Anchor, Autoscroll, Editor}; use gpui::{ - actions, elements::*, impl_actions, impl_internal_actions, platform::CursorStyle, AppContext, - Entity, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, + actions, elements::*, impl_actions, platform::CursorStyle, Action, AppContext, Entity, + MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use language::OffsetRangeExt; @@ -21,12 +22,8 @@ pub struct Deploy { pub focus: bool, } -#[derive(Clone, PartialEq)] -pub struct ToggleSearchOption(pub SearchOption); - actions!(buffer_search, [Dismiss, FocusEditor]); impl_actions!(buffer_search, [Deploy]); -impl_internal_actions!(buffer_search, [ToggleSearchOption]); pub enum Event { UpdateLocation, @@ -36,12 +33,28 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(BufferSearchBar::deploy); cx.add_action(BufferSearchBar::dismiss); cx.add_action(BufferSearchBar::focus_editor); - cx.add_action(BufferSearchBar::toggle_search_option); cx.add_action(BufferSearchBar::select_next_match); cx.add_action(BufferSearchBar::select_prev_match); cx.add_action(BufferSearchBar::select_next_match_on_pane); cx.add_action(BufferSearchBar::select_prev_match_on_pane); cx.add_action(BufferSearchBar::handle_editor_cancel); + add_toggle_option_action::(SearchOption::CaseSensitive, cx); + add_toggle_option_action::(SearchOption::WholeWord, cx); + add_toggle_option_action::(SearchOption::Regex, cx); +} + +fn add_toggle_option_action(option: SearchOption, cx: &mut MutableAppContext) { + cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + if search_bar.update(cx, |search_bar, cx| search_bar.show(false, false, cx)) { + search_bar.update(cx, |search_bar, cx| { + search_bar.toggle_search_option(option, cx); + }); + return; + } + } + cx.propagate_action(); + }); } pub struct BufferSearchBar { @@ -215,16 +228,18 @@ impl BufferSearchBar { cx.notify(); } - fn show(&mut self, focus: bool, cx: &mut ViewContext) -> bool { + fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext) -> bool { let editor = if let Some(editor) = self.active_editor.clone() { editor } else { return false; }; - let text = query_suggestion_for_editor(&editor, cx); - if !text.is_empty() { - self.set_query(&text, cx); + if suggest_query { + let text = query_suggestion_for_editor(&editor, cx); + if !text.is_empty() { + self.set_query(&text, cx); + } } if focus { @@ -253,11 +268,12 @@ impl BufferSearchBar { fn render_search_option( &self, icon: &str, - search_option: SearchOption, + option: SearchOption, cx: &mut RenderContext, ) -> ElementBox { - let is_active = self.is_search_option_enabled(search_option); - MouseEventHandler::new::(search_option as usize, cx, |state, cx| { + let tooltip_style = cx.global::().theme.tooltip.clone(); + let is_active = self.is_search_option_enabled(option); + MouseEventHandler::new::(option as usize, cx, |state, cx| { let style = &cx .global::() .theme @@ -269,8 +285,15 @@ impl BufferSearchBar { .with_style(style.container) .boxed() }) - .on_click(move |_, _, cx| cx.dispatch_action(ToggleSearchOption(search_option))) + .on_click(move |_, _, cx| cx.dispatch_any_action(option.to_toggle_action())) .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + option as usize, + format!("Toggle {}", option.label()), + Some(option.to_toggle_action()), + tooltip_style, + cx, + ) .boxed() } @@ -280,6 +303,20 @@ impl BufferSearchBar { direction: Direction, cx: &mut RenderContext, ) -> ElementBox { + let action: Box; + let tooltip; + match direction { + Direction::Prev => { + action = Box::new(SelectPrevMatch); + tooltip = "Select Previous Match"; + } + Direction::Next => { + action = Box::new(SelectNextMatch); + tooltip = "Select Next Match"; + } + }; + let tooltip_style = cx.global::().theme.tooltip.clone(); + enum NavButton {} MouseEventHandler::new::(direction as usize, cx, |state, cx| { let style = &cx @@ -293,17 +330,24 @@ impl BufferSearchBar { .with_style(style.container) .boxed() }) - .on_click(move |_, _, cx| match direction { - Direction::Prev => cx.dispatch_action(SelectPrevMatch), - Direction::Next => cx.dispatch_action(SelectNextMatch), + .on_click({ + let action = action.boxed_clone(); + move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()) }) .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + direction as usize, + tooltip.to_string(), + Some(action), + tooltip_style, + cx, + ) .boxed() } fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { - if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, cx)) { + if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, true, cx)) { return; } } @@ -334,18 +378,14 @@ impl BufferSearchBar { } } - fn toggle_search_option( - &mut self, - ToggleSearchOption(search_option): &ToggleSearchOption, - cx: &mut ViewContext, - ) { + fn toggle_search_option(&mut self, search_option: SearchOption, cx: &mut ViewContext) { 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(true, cx); + self.update_matches(false, cx); cx.notify(); } @@ -591,7 +631,7 @@ mod tests { let search_bar = cx.add_view(Default::default(), |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); - search_bar.show(false, cx); + search_bar.show(false, true, cx); search_bar }); @@ -619,7 +659,7 @@ mod tests { // Switch to a case sensitive search. search_bar.update(cx, |search_bar, cx| { - search_bar.toggle_search_option(&ToggleSearchOption(SearchOption::CaseSensitive), cx); + search_bar.toggle_search_option(SearchOption::CaseSensitive, cx); }); editor.next_notification(&cx).await; editor.update(cx, |editor, cx| { @@ -676,7 +716,7 @@ mod tests { // Switch to a whole word search. search_bar.update(cx, |search_bar, cx| { - search_bar.toggle_search_option(&ToggleSearchOption(SearchOption::WholeWord), cx); + search_bar.toggle_search_option(SearchOption::WholeWord, cx); }); editor.next_notification(&cx).await; editor.update(cx, |editor, cx| { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index a7cfb020b5..bf862f0d9d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,13 +1,14 @@ use crate::{ active_match_index, match_index_for_direction, query_suggestion_for_editor, Direction, - SearchOption, SelectNextMatch, SelectPrevMatch, ToggleSearchOption, + SearchOption, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, + ToggleWholeWord, }; use collections::HashMap; use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll}; use gpui::{ - actions, elements::*, platform::CursorStyle, AppContext, ElementBox, Entity, ModelContext, - ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, - ViewHandle, WeakModelHandle, WeakViewHandle, + actions, elements::*, platform::CursorStyle, Action, AppContext, ElementBox, Entity, + ModelContext, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, + ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; use menu::Confirm; use project::{search::SearchQuery, Project}; @@ -35,11 +36,26 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(ProjectSearchView::deploy); cx.add_action(ProjectSearchBar::search); cx.add_action(ProjectSearchBar::search_in_new); - cx.add_action(ProjectSearchBar::toggle_search_option); cx.add_action(ProjectSearchBar::select_next_match); cx.add_action(ProjectSearchBar::select_prev_match); cx.add_action(ProjectSearchBar::toggle_focus); cx.capture_action(ProjectSearchBar::tab); + add_toggle_option_action::(SearchOption::CaseSensitive, cx); + add_toggle_option_action::(SearchOption::WholeWord, cx); + add_toggle_option_action::(SearchOption::Regex, cx); +} + +fn add_toggle_option_action(option: SearchOption, cx: &mut MutableAppContext) { + cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + if search_bar.update(cx, |search_bar, cx| { + search_bar.toggle_search_option(option, cx) + }) { + return; + } + } + cx.propagate_action(); + }); } struct ProjectSearch { @@ -653,11 +669,7 @@ impl ProjectSearchBar { } } - fn toggle_search_option( - &mut self, - ToggleSearchOption(option): &ToggleSearchOption, - cx: &mut ViewContext, - ) { + fn toggle_search_option(&mut self, option: SearchOption, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { let value = match option { @@ -669,6 +681,9 @@ impl ProjectSearchBar { search_view.search(cx); }); cx.notify(); + true + } else { + false } } @@ -678,6 +693,20 @@ impl ProjectSearchBar { direction: Direction, cx: &mut RenderContext, ) -> ElementBox { + let action: Box; + let tooltip; + match direction { + Direction::Prev => { + action = Box::new(SelectPrevMatch); + tooltip = "Select Previous Match"; + } + Direction::Next => { + action = Box::new(SelectNextMatch); + tooltip = "Select Next Match"; + } + }; + let tooltip_style = cx.global::().theme.tooltip.clone(); + enum NavButton {} MouseEventHandler::new::(direction as usize, cx, |state, cx| { let style = &cx @@ -691,11 +720,18 @@ impl ProjectSearchBar { .with_style(style.container) .boxed() }) - .on_click(move |_, _, cx| match direction { - Direction::Prev => cx.dispatch_action(SelectPrevMatch), - Direction::Next => cx.dispatch_action(SelectNextMatch), + .on_click({ + let action = action.boxed_clone(); + move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()) }) .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + direction as usize, + tooltip.to_string(), + Some(action), + tooltip_style, + cx, + ) .boxed() } @@ -705,8 +741,9 @@ impl ProjectSearchBar { option: SearchOption, cx: &mut RenderContext, ) -> ElementBox { + let tooltip_style = cx.global::().theme.tooltip.clone(); let is_active = self.is_option_enabled(option, cx); - MouseEventHandler::new::(option as usize, cx, |state, cx| { + MouseEventHandler::new::(option as usize, cx, |state, cx| { let style = &cx .global::() .theme @@ -718,8 +755,15 @@ impl ProjectSearchBar { .with_style(style.container) .boxed() }) - .on_click(move |_, _, cx| cx.dispatch_action(ToggleSearchOption(option))) + .on_click(move |_, _, cx| cx.dispatch_any_action(option.to_toggle_action())) .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + option as usize, + format!("Toggle {}", option.label()), + Some(option.to_toggle_action()), + tooltip_style, + cx, + ) .boxed() } diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 68bdc78c7a..5d6014eaeb 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -1,6 +1,6 @@ pub use buffer_search::BufferSearchBar; use editor::{display_map::ToDisplayPoint, Anchor, Bias, Editor, MultiBufferSnapshot}; -use gpui::{actions, impl_internal_actions, MutableAppContext, ViewHandle}; +use gpui::{actions, Action, MutableAppContext, ViewHandle}; pub use project_search::{ProjectSearchBar, ProjectSearchView}; use std::{ cmp::{self, Ordering}, @@ -15,11 +15,16 @@ pub fn init(cx: &mut MutableAppContext) { project_search::init(cx); } -#[derive(Clone, PartialEq)] -pub struct ToggleSearchOption(pub SearchOption); - -actions!(search, [SelectNextMatch, SelectPrevMatch]); -impl_internal_actions!(search, [ToggleSearchOption]); +actions!( + search, + [ + ToggleWholeWord, + ToggleCaseSensitive, + ToggleRegex, + SelectNextMatch, + SelectPrevMatch + ] +); #[derive(Clone, Copy, PartialEq)] pub enum SearchOption { @@ -28,6 +33,24 @@ pub enum SearchOption { Regex, } +impl SearchOption { + pub fn label(&self) -> &'static str { + match self { + SearchOption::WholeWord => "Match Whole Word", + SearchOption::CaseSensitive => "Match Case", + SearchOption::Regex => "Use Regular Expression", + } + } + + pub fn to_toggle_action(&self) -> Box { + match self { + SearchOption::WholeWord => Box::new(ToggleWholeWord), + SearchOption::CaseSensitive => Box::new(ToggleCaseSensitive), + SearchOption::Regex => Box::new(ToggleRegex), + } + } +} + #[derive(Clone, Copy, PartialEq, Eq)] pub enum Direction { Prev,