From 91c32ed30728017f73dc3f7b19c690f44ed383ab Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 11:43:32 +0200 Subject: [PATCH 001/119] WIP: project search redesign --- crates/search/src/project_search.rs | 84 +++++++++++++---------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 52ee12c26d..56c1d6f1d0 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -505,6 +505,7 @@ impl ProjectSearchView { Some(Arc::new(|theme| theme.search.editor.input.clone())), cx, ); + editor.set_placeholder_text("Text search all files", cx); editor.set_text(query_text, cx); editor }); @@ -1230,31 +1231,37 @@ impl View for ProjectSearchBar { .flex(1.0, true); let row_spacing = theme.workspace.toolbar.container.padding.bottom; + let query = ChildView::new(&search.query_editor, cx) + .aligned() + .left() + .flex(1., true); + let matches = search.active_match_index.map(|match_ix| { + Label::new( + format!( + "{}/{}", + match_ix + 1, + search.model.read(cx).match_ranges.len() + ), + theme.search.match_index.text.clone(), + ) + .contained() + .with_style(theme.search.match_index.container) + .aligned() + .left() + }); + let case_button = self.render_option_button("Case", SearchOptions::CASE_SENSITIVE, cx); + let word_button = self.render_option_button("Word", SearchOptions::WHOLE_WORD, cx); + let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); + let semantic_index = + SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); Flex::column() .with_child( Flex::row() + .with_children(matches) .with_child( Flex::row() - .with_child( - ChildView::new(&search.query_editor, cx) - .aligned() - .left() - .flex(1., true), - ) - .with_children(search.active_match_index.map(|match_ix| { - Label::new( - format!( - "{}/{}", - match_ix + 1, - search.model.read(cx).match_ranges.len() - ), - theme.search.match_index.text.clone(), - ) - .contained() - .with_style(theme.search.match_index.container) - .aligned() - })) + .with_child(query) .contained() .with_style(query_container_style) .aligned() @@ -1269,35 +1276,17 @@ impl View for ProjectSearchBar { .with_child(self.render_nav_button(">", Direction::Next, cx)) .aligned(), ) - .with_child({ - let row = if SemanticIndex::enabled(cx) { - Flex::row().with_child(self.render_semantic_search_button(cx)) - } else { - Flex::row() - }; - - let row = row - .with_child(self.render_option_button( - "Case", - SearchOptions::CASE_SENSITIVE, - cx, - )) - .with_child(self.render_option_button( - "Word", - SearchOptions::WHOLE_WORD, - cx, - )) - .with_child(self.render_option_button( - "Regex", - SearchOptions::REGEX, - cx, - )) + .with_child( + Flex::row() + .with_children(semantic_index) + .with_child(case_button) + .with_child(word_button) + .with_child(regex_button) .contained() .with_style(theme.search.option_button_group) - .aligned(); - - row - }) + .aligned() + .right(), + ) .contained() .with_margin_bottom(row_spacing), ) @@ -1328,8 +1317,7 @@ impl View for ProjectSearchBar { ) .contained() .with_style(theme.search.container) - .aligned() - .left() + .flex_float() .into_any_named("project search") } else { Empty::new().into_any() From f2a35a7e1dd75dd0f12a90130ce76c52b7a870a5 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:18:19 +0200 Subject: [PATCH 002/119] Use a three-way layout --- crates/search/src/project_search.rs | 129 +++++++++++++++++----------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 56c1d6f1d0..1485a9359f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1254,69 +1254,100 @@ impl View for ProjectSearchBar { let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); - - Flex::column() + Flex::row() + .with_child(Flex::row().flex(1., true)) .with_child( - Flex::row() - .with_children(matches) + Flex::column() .with_child( Flex::row() - .with_child(query) + .with_children(matches) + .with_child( + Flex::row() + .with_child(query) + .contained() + .with_style(query_container_style) + .aligned() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .flex(1., false), + ) + .with_child( + Flex::row() + .with_child(self.render_nav_button( + "<", + Direction::Prev, + cx, + )) + .with_child(self.render_nav_button( + ">", + Direction::Next, + cx, + )) + .aligned(), + ) + .with_child( + Flex::row() + .with_children(semantic_index) + .with_child(case_button) + .with_child(word_button) + .with_child(regex_button) + .contained() + .with_style(theme.search.option_button_group) + .aligned() + .right(), + ) .contained() - .with_style(query_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .flex(1., false), + .with_margin_bottom(row_spacing), ) .with_child( Flex::row() - .with_child(self.render_nav_button("<", Direction::Prev, cx)) - .with_child(self.render_nav_button(">", Direction::Next, cx)) - .aligned(), - ) - .with_child( - Flex::row() - .with_children(semantic_index) - .with_child(case_button) - .with_child(word_button) - .with_child(regex_button) - .contained() - .with_style(theme.search.option_button_group) - .aligned() - .right(), + .with_child( + Flex::row() + .with_child(included_files_view) + .contained() + .with_style(include_container_style) + .aligned() + .constrained() + .with_min_width( + theme.search.include_exclude_editor.min_width, + ) + .with_max_width( + theme.search.include_exclude_editor.max_width, + ) + .flex(1., false), + ) + .with_child( + Flex::row() + .with_child(excluded_files_view) + .contained() + .with_style(exclude_container_style) + .aligned() + .constrained() + .with_min_width( + theme.search.include_exclude_editor.min_width, + ) + .with_max_width( + theme.search.include_exclude_editor.max_width, + ) + .flex(1., false), + ), ) .contained() - .with_margin_bottom(row_spacing), + .with_style(theme.search.container) + .flex(2., true), ) .with_child( Flex::row() - .with_child( - Flex::row() - .with_child(included_files_view) - .contained() - .with_style(include_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.include_exclude_editor.min_width) - .with_max_width(theme.search.include_exclude_editor.max_width) - .flex(1., false), - ) - .with_child( - Flex::row() - .with_child(excluded_files_view) - .contained() - .with_style(exclude_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.include_exclude_editor.min_width) - .with_max_width(theme.search.include_exclude_editor.max_width) - .flex(1., false), - ), + .with_child(Label::new( + "Here be dragons", + theme.search.match_index.text.clone(), + )) + .flex(1., true) + .aligned() + .right(), ) .contained() - .with_style(theme.search.container) .flex_float() .into_any_named("project search") } else { From 4761197d621d0d0d92574b74dc1eacf71645cdd8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:08:31 +0200 Subject: [PATCH 003/119] Add filter button. Move semantic & regex buttons to the right hand side. Add default tab name for the new project search. --- crates/search/src/project_search.rs | 135 ++++++++++++++++++---------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1485a9359f..435b5542a9 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -102,6 +102,7 @@ pub struct ProjectSearchView { query_editor_was_focused: bool, included_files_editor: ViewHandle, excluded_files_editor: ViewHandle, + filters_enabled: bool, } struct SemanticSearchState { @@ -365,11 +366,19 @@ impl Item for ProjectSearchView { .contained() .with_margin_right(tab_theme.spacing), ) - .with_children(self.model.read(cx).active_query.as_ref().map(|query| { - let query_text = util::truncate_and_trailoff(query.as_str(), MAX_TAB_TITLE_LEN); - - Label::new(query_text, tab_theme.label.clone()).aligned() - })) + .with_child({ + let tab_name: Option> = + self.model.read(cx).active_query.as_ref().map(|query| { + let query_text = + util::truncate_and_trailoff(query.as_str(), MAX_TAB_TITLE_LEN); + query_text.into() + }); + Label::new( + tab_name.unwrap_or("Project search".into()), + tab_theme.label.clone(), + ) + .aligned() + }) .into_any() } @@ -565,7 +574,7 @@ impl ProjectSearchView { cx.emit(ViewEvent::EditorEvent(event.clone())) }) .detach(); - + let filters_enabled = false; let mut this = ProjectSearchView { search_id: model.read(cx).search_id, model, @@ -578,6 +587,7 @@ impl ProjectSearchView { query_editor_was_focused: false, included_files_editor, excluded_files_editor, + filters_enabled, }; this.model_changed(cx); this @@ -1012,6 +1022,19 @@ impl ProjectSearchBar { false } } + fn toggle_filters(&mut self, cx: &mut ViewContext) -> bool { + if let Some(search_view) = self.active_project_search.as_ref() { + search_view.update(cx, |search_view, cx| { + search_view.filters_enabled = !search_view.filters_enabled; + search_view.semantic = None; + search_view.search(cx); + }); + cx.notify(); + true + } else { + false + } + } fn toggle_semantic_search(&mut self, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { @@ -1249,9 +1272,56 @@ impl View for ProjectSearchBar { .aligned() .left() }); + let filters = search.filters_enabled.then(|| { + Flex::row() + .with_child( + Flex::row() + .with_child(included_files_view) + .contained() + .with_style(include_container_style) + .aligned() + .constrained() + .with_min_width(theme.search.include_exclude_editor.min_width) + .with_max_width(theme.search.include_exclude_editor.max_width) + .flex(1., false), + ) + .with_child( + Flex::row() + .with_child(excluded_files_view) + .contained() + .with_style(exclude_container_style) + .aligned() + .constrained() + .with_min_width(theme.search.include_exclude_editor.min_width) + .with_max_width(theme.search.include_exclude_editor.max_width) + .flex(1., false), + ) + }); + let filter_button = { + let tooltip_style = theme::current(cx).tooltip.clone(); + let is_active = search.filters_enabled; + MouseEventHandler::::new(0, cx, |state, cx| { + let theme = theme::current(cx); + let style = theme + .search + .option_button + .in_state(is_active) + .style_for(state); + Label::new("Filter", style.text.clone()) + .contained() + .with_style(style.container) + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_filters(cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) + .into_any() + }; let case_button = self.render_option_button("Case", SearchOptions::CASE_SENSITIVE, cx); let word_button = self.render_option_button("Word", SearchOptions::WHOLE_WORD, cx); let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); + let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); Flex::row() @@ -1288,10 +1358,9 @@ impl View for ProjectSearchBar { ) .with_child( Flex::row() - .with_children(semantic_index) .with_child(case_button) .with_child(word_button) - .with_child(regex_button) + .with_child(filter_button) .contained() .with_style(theme.search.option_button_group) .aligned() @@ -1300,52 +1369,20 @@ impl View for ProjectSearchBar { .contained() .with_margin_bottom(row_spacing), ) - .with_child( - Flex::row() - .with_child( - Flex::row() - .with_child(included_files_view) - .contained() - .with_style(include_container_style) - .aligned() - .constrained() - .with_min_width( - theme.search.include_exclude_editor.min_width, - ) - .with_max_width( - theme.search.include_exclude_editor.max_width, - ) - .flex(1., false), - ) - .with_child( - Flex::row() - .with_child(excluded_files_view) - .contained() - .with_style(exclude_container_style) - .aligned() - .constrained() - .with_min_width( - theme.search.include_exclude_editor.min_width, - ) - .with_max_width( - theme.search.include_exclude_editor.max_width, - ) - .flex(1., false), - ), - ) + .with_children(filters) .contained() .with_style(theme.search.container) .flex(2., true), ) .with_child( - Flex::row() - .with_child(Label::new( - "Here be dragons", - theme.search.match_index.text.clone(), - )) - .flex(1., true) - .aligned() - .right(), + Flex::column().with_child( + Flex::row() + .with_children(semantic_index) + .with_child(regex_button) + .flex(1., true) + .aligned() + .right(), + ), ) .contained() .flex_float() From 8ca1e0b15b16217940627a10e9c5d43dd49aae97 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:09:19 +0200 Subject: [PATCH 004/119] Add dummy filter icon --- assets/icons/filter_12.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 assets/icons/filter_12.svg diff --git a/assets/icons/filter_12.svg b/assets/icons/filter_12.svg new file mode 100644 index 0000000000..9c1ad5ba5c --- /dev/null +++ b/assets/icons/filter_12.svg @@ -0,0 +1,3 @@ + + + From dff9bf7d7eb4ccea9504b73f7bf14235b0391a16 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 16:03:26 +0200 Subject: [PATCH 005/119] Make row_count of toolbaritem dynamic (WIP). Move result count to the left hand side. --- crates/search/src/project_search.rs | 23 ++++++++++++++++++----- crates/workspace/src/toolbar.rs | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 435b5542a9..9b6b8da320 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -374,7 +374,9 @@ impl Item for ProjectSearchView { query_text.into() }); Label::new( - tab_name.unwrap_or("Project search".into()), + tab_name + .filter(|name| !name.is_empty()) + .unwrap_or("Project search".into()), tab_theme.label.clone(), ) .aligned() @@ -425,6 +427,7 @@ impl Item for ProjectSearchView { project: ModelHandle, cx: &mut ViewContext, ) -> Task> { + self.results_editor .update(cx, |editor, cx| editor.reload(project, cx)) } @@ -825,6 +828,7 @@ impl ProjectSearchView { } fn model_changed(&mut self, cx: &mut ViewContext) { + let match_ranges = self.model.read(cx).match_ranges.clone(); if match_ranges.is_empty() { self.active_match_index = None; @@ -1026,8 +1030,11 @@ impl ProjectSearchBar { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { search_view.filters_enabled = !search_view.filters_enabled; + search_view.included_files_editor.update(cx, |_, cx| {cx.notify()}); + search_view.excluded_files_editor.update(cx, |_, cx| {cx.notify()}); search_view.semantic = None; search_view.search(cx); + cx.notify(); }); cx.notify(); true @@ -1325,12 +1332,12 @@ impl View for ProjectSearchBar { let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); Flex::row() - .with_child(Flex::row().flex(1., true)) + .with_child(Flex::column().with_child(Flex::row().with_children(matches).aligned() + .left()).flex(1., true)) .with_child( Flex::column() .with_child( Flex::row() - .with_children(matches) .with_child( Flex::row() .with_child(query) @@ -1413,8 +1420,14 @@ impl ToolbarItemView for ProjectSearchBar { } } - fn row_count(&self) -> usize { - 2 + fn row_count(&self, cx: &ViewContext) -> usize { + self.active_project_search + .as_ref() + .map(|search| { + let offset = search.read(cx).filters_enabled as usize; + 1 + offset + }) + .unwrap_or_else(|| 1) } } diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 69394b8421..3fa37f3666 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -25,7 +25,7 @@ pub trait ToolbarItemView: View { /// Number of times toolbar's height will be repeated to get the effective height. /// Useful when multiple rows one under each other are needed. /// The rows have the same width and act as a whole when reacting to resizes and similar events. - fn row_count(&self) -> usize { + fn row_count(&self, _cx: &ViewContext) -> usize { 1 } } @@ -362,7 +362,7 @@ impl ToolbarItemViewHandle for ViewHandle { } fn row_count(&self, cx: &WindowContext) -> usize { - self.read(cx).row_count() + self.read_with(cx, |this, cx| this.row_count(cx)) } } From b9cdb851d2f448f72bf57b322daf1b0797141a30 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 27 Jul 2023 16:31:24 +0200 Subject: [PATCH 006/119] Update results text --- crates/search/src/project_search.rs | 31 +++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 9b6b8da320..1321aadb4f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -278,17 +278,23 @@ impl View for ProjectSearchView { Cow::Borrowed("Indexing complete") } } else if self.query_editor.read(cx).text(cx).is_empty() { - Cow::Borrowed("") + Cow::Borrowed("Text search all files and folders") } else { Cow::Borrowed("No results") }; MouseEventHandler::::new(0, cx, |_, _| { - Label::new(text, theme.search.results_status.clone()) - .aligned() + Flex::column() + .with_child(Flex::column().contained().flex(1., true)) + .with_child( + Label::new(text, theme.search.results_status.clone()) + .aligned() + .top() + .contained() + .flex(7., true), + ) .contained() .with_background_color(theme.editor.background) - .flex(1., true) }) .on_down(MouseButton::Left, |_, _, cx| { cx.focus_parent(); @@ -427,7 +433,6 @@ impl Item for ProjectSearchView { project: ModelHandle, cx: &mut ViewContext, ) -> Task> { - self.results_editor .update(cx, |editor, cx| editor.reload(project, cx)) } @@ -828,7 +833,6 @@ impl ProjectSearchView { } fn model_changed(&mut self, cx: &mut ViewContext) { - let match_ranges = self.model.read(cx).match_ranges.clone(); if match_ranges.is_empty() { self.active_match_index = None; @@ -1030,8 +1034,12 @@ impl ProjectSearchBar { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { search_view.filters_enabled = !search_view.filters_enabled; - search_view.included_files_editor.update(cx, |_, cx| {cx.notify()}); - search_view.excluded_files_editor.update(cx, |_, cx| {cx.notify()}); + search_view + .included_files_editor + .update(cx, |_, cx| cx.notify()); + search_view + .excluded_files_editor + .update(cx, |_, cx| cx.notify()); search_view.semantic = None; search_view.search(cx); cx.notify(); @@ -1332,8 +1340,11 @@ impl View for ProjectSearchBar { let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); Flex::row() - .with_child(Flex::column().with_child(Flex::row().with_children(matches).aligned() - .left()).flex(1., true)) + .with_child( + Flex::column() + .with_child(Flex::row().with_children(matches).aligned().left()) + .flex(1., true), + ) .with_child( Flex::column() .with_child( From 52a48de9ca94e7b766f40c97c67791982e1a24b2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 00:44:52 +0200 Subject: [PATCH 007/119] Add WIP Normal button (resuses parts of semantic button, gotta wire it proper) --- crates/search/src/project_search.rs | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1321aadb4f..1951e2c086 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1218,7 +1218,41 @@ impl ProjectSearchBar { ) .into_any() } + fn render_text_search_button(&self, cx: &mut ViewContext) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + let is_active = if let Some(search) = self.active_project_search.as_ref() { + let search = search.read(cx); + search.semantic.is_none() && !self.is_option_enabled(SearchOptions::REGEX, cx) + } else { + false + }; + let region_id = 4; + enum NormalSearchTag {} + MouseEventHandler::::new(region_id, cx, |state, cx| { + let theme = theme::current(cx); + let style = theme + .search + .option_button + .in_state(is_active) + .style_for(state); + Label::new("Text", style.text.clone()) + .contained() + .with_style(style.container) + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_semantic_search(cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + region_id, + format!("Toggle Normal Search"), + None, + tooltip_style, + cx, + ) + .into_any() + } fn is_option_enabled(&self, option: SearchOptions, cx: &AppContext) -> bool { if let Some(search) = self.active_project_search.as_ref() { search.read(cx).search_options.contains(option) @@ -1339,6 +1373,7 @@ impl View for ProjectSearchBar { let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); + let normal_search = self.render_text_search_button(cx); Flex::row() .with_child( Flex::column() @@ -1395,6 +1430,7 @@ impl View for ProjectSearchBar { .with_child( Flex::column().with_child( Flex::row() + .with_child(normal_search) .with_children(semantic_index) .with_child(regex_button) .flex(1., true) From a33d8519f230fe903c05fa66604d1c56382dfa9d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 02:06:40 +0200 Subject: [PATCH 008/119] Move buttons inside of query editor (WIP) --- assets/icons/word_search_14.svg | 6 ++ crates/search/src/project_search.rs | 116 +++++++++++++++++++--------- styles/src/style_tree/search.ts | 12 +-- 3 files changed, 90 insertions(+), 44 deletions(-) create mode 100644 assets/icons/word_search_14.svg diff --git a/assets/icons/word_search_14.svg b/assets/icons/word_search_14.svg new file mode 100644 index 0000000000..adb4976bcc --- /dev/null +++ b/assets/icons/word_search_14.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1951e2c086..35e953a592 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1149,6 +1149,38 @@ impl ProjectSearchBar { ) .into_any() } + fn render_option_button_icon( + &self, + icon: &'static str, + option: SearchOptions, + cx: &mut ViewContext, + ) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + let is_active = self.is_option_enabled(option, cx); + MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { + let theme = theme::current(cx); + let style = theme + .search + .option_button + .in_state(is_active) + .style_for(state); + Svg::new(icon).with_color(style.text.color.clone()) + .contained() + .with_style(style.container) + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_search_option(option, cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + option.bits as usize, + format!("Toggle {}", option.label()), + Some(option.to_toggle_action()), + tooltip_style, + cx, + ) + .into_any() + } fn render_option_button( &self, @@ -1272,8 +1304,8 @@ impl View for ProjectSearchBar { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - if let Some(search) = self.active_project_search.as_ref() { - let search = search.read(cx); + if let Some(_search) = self.active_project_search.as_ref() { + let search = _search.read(cx); let theme = theme::current(cx).clone(); let query_container_style = if search.panels_with_errors.contains(&InputPanel::Query) { theme.search.invalid_editor @@ -1301,12 +1333,53 @@ impl View for ProjectSearchBar { .aligned() .right() .flex(1.0, true); - + let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); let row_spacing = theme.workspace.toolbar.container.padding.bottom; - let query = ChildView::new(&search.query_editor, cx) +let search = _search.read(cx); + let filter_button = { + let tooltip_style = theme::current(cx).tooltip.clone(); + let is_active = search.filters_enabled; + MouseEventHandler::::new(0, cx, |state, cx| { + let theme = theme::current(cx); + let style = theme + .search + .option_button + .in_state(is_active) + .style_for(state); + Svg::new("icons/filter_12.svg") + .with_color(style.text.color.clone()) + .contained() + .with_style(style.container) + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_filters(cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) + .into_any() + }; let search = _search.read(cx); + let query = Flex::row() + .with_child( + ChildView::new(&search.query_editor, cx) + .constrained() + .flex(1., true) + .into_any(), + ) + .with_child( + Flex::row() + .with_children([ + filter_button, + self.render_option_button("Case", SearchOptions::CASE_SENSITIVE, cx), + self.render_option_button_icon("icons/word_search_14.svg", SearchOptions::WHOLE_WORD, cx), + + ]) + .flex(1., true) + .contained(), + ) .aligned() .left() .flex(1., true); + let search = _search.read(cx); let matches = search.active_match_index.map(|match_ix| { Label::new( format!( @@ -1321,6 +1394,7 @@ impl View for ProjectSearchBar { .aligned() .left() }); + let filters = search.filters_enabled.then(|| { Flex::row() .with_child( @@ -1346,30 +1420,6 @@ impl View for ProjectSearchBar { .flex(1., false), ) }); - let filter_button = { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = search.filters_enabled; - MouseEventHandler::::new(0, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme - .search - .option_button - .in_state(is_active) - .style_for(state); - Label::new("Filter", style.text.clone()) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_filters(cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) - .into_any() - }; - let case_button = self.render_option_button("Case", SearchOptions::CASE_SENSITIVE, cx); - let word_button = self.render_option_button("Word", SearchOptions::WHOLE_WORD, cx); - let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); let semantic_index = SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); @@ -1409,16 +1459,6 @@ impl View for ProjectSearchBar { )) .aligned(), ) - .with_child( - Flex::row() - .with_child(case_button) - .with_child(word_button) - .with_child(filter_button) - .contained() - .with_style(theme.search.option_button_group) - .aligned() - .right(), - ) .contained() .with_margin_bottom(row_spacing), ) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 28940a8367..43689c01bc 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -44,17 +44,17 @@ export default function search(): any { base: { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: 6, + corner_radius: 2, border: border(theme.highest, "on"), margin: { right: 4, }, padding: { - bottom: 2, - left: 10, - right: 10, - top: 2, - }, + bottom: 6, + left: 6, + right: 6, + top: 6, + }, }, state: { hovered: { From b29a535f0451108b16846d77ad69f484c2003c1e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:20:23 +0200 Subject: [PATCH 009/119] Use icons instead of text in toggles --- assets/icons/filter_14.svg | 6 ++++++ assets/icons/word_search_12.svg | 8 ++++++++ crates/search/src/project_search.rs | 29 +++++++++++++++++++++++------ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 assets/icons/filter_14.svg create mode 100644 assets/icons/word_search_12.svg diff --git a/assets/icons/filter_14.svg b/assets/icons/filter_14.svg new file mode 100644 index 0000000000..379be15b51 --- /dev/null +++ b/assets/icons/filter_14.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/word_search_12.svg b/assets/icons/word_search_12.svg new file mode 100644 index 0000000000..4cf6401fd2 --- /dev/null +++ b/assets/icons/word_search_12.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 35e953a592..443ccbd567 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1164,9 +1164,11 @@ impl ProjectSearchBar { .option_button .in_state(is_active) .style_for(state); - Svg::new(icon).with_color(style.text.color.clone()) + Svg::new(icon) + .with_color(style.text.color.clone()) .contained() .with_style(style.container) + .constrained() }) .on_click(MouseButton::Left, move |_, this, cx| { this.toggle_search_option(option, cx); @@ -1335,7 +1337,7 @@ impl View for ProjectSearchBar { .flex(1.0, true); let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); let row_spacing = theme.workspace.toolbar.container.padding.bottom; -let search = _search.read(cx); + let search = _search.read(cx); let filter_button = { let tooltip_style = theme::current(cx).tooltip.clone(); let is_active = search.filters_enabled; @@ -1357,8 +1359,15 @@ let search = _search.read(cx); .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) .into_any() - }; let search = _search.read(cx); + }; + let search = _search.read(cx); let query = Flex::row() + .with_child( + Svg::new("icons/magnifying_glass_12.svg").with_color(gpui::color::Color::white()) + //.with_color(tab_theme.label.text.color) + .contained().constrained() + //.with_margin_right(tab_theme.spacing), + ) .with_child( ChildView::new(&search.query_editor, cx) .constrained() @@ -1369,13 +1378,21 @@ let search = _search.read(cx); Flex::row() .with_children([ filter_button, - self.render_option_button("Case", SearchOptions::CASE_SENSITIVE, cx), - self.render_option_button_icon("icons/word_search_14.svg", SearchOptions::WHOLE_WORD, cx), - + self.render_option_button_icon( + "icons/word_search_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + ), + self.render_option_button_icon( + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + ), ]) .flex(1., true) .contained(), ) + .align_children_center() .aligned() .left() .flex(1., true); From 2e2d0a3884e16846329a2b31db11e2f5aca5e5ab Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:07:21 +0200 Subject: [PATCH 010/119] Hide whole word/case sensitive options under semantic search --- crates/search/src/project_search.rs | 46 +++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 443ccbd567..497085f75e 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1360,13 +1360,37 @@ impl View for ProjectSearchBar { .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) .into_any() }; + let search = _search.read(cx); + let is_semantic_disabled = search.semantic.is_none(); + + let case_sensitive = if is_semantic_disabled { + Some(self.render_option_button_icon( + "icons/word_search_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + )) + } else { + None + }; + + let whole_word = if is_semantic_disabled { + Some(self.render_option_button_icon( + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + )) + } else { + None + }; + let search = _search.read(cx); let query = Flex::row() .with_child( - Svg::new("icons/magnifying_glass_12.svg").with_color(gpui::color::Color::white()) + Svg::new("icons/magnifying_glass_12.svg") + .with_color(gpui::color::Color::white()) //.with_color(tab_theme.label.text.color) - .contained().constrained() - //.with_margin_right(tab_theme.spacing), + .contained() + .constrained(), //.with_margin_right(tab_theme.spacing), ) .with_child( ChildView::new(&search.query_editor, cx) @@ -1376,19 +1400,9 @@ impl View for ProjectSearchBar { ) .with_child( Flex::row() - .with_children([ - filter_button, - self.render_option_button_icon( - "icons/word_search_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - ), - self.render_option_button_icon( - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - ), - ]) + .with_child(filter_button) + .with_children(whole_word) + .with_children(case_sensitive) .flex(1., true) .contained(), ) From c28ba3a11ac34af1b1074813a7853f29f3e3dfb4 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 1 Aug 2023 11:47:30 -0400 Subject: [PATCH 011/119] add cycle mode to project search Co-authored-by: Piotrek --- crates/search/src/project_search.rs | 54 +++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 497085f75e..546bbeea69 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -40,7 +40,13 @@ use workspace::{ actions!( project_search, - [SearchInNew, ToggleFocus, NextField, ToggleSemanticSearch] + [ + SearchInNew, + ToggleFocus, + NextField, + ToggleSemanticSearch, + CycleMode + ] ); #[derive(Default)] @@ -54,6 +60,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(ProjectSearchBar::search_in_new); cx.add_action(ProjectSearchBar::select_next_match); cx.add_action(ProjectSearchBar::select_prev_match); + cx.add_action(ProjectSearchBar::cycle_mode); cx.capture_action(ProjectSearchBar::tab); cx.capture_action(ProjectSearchBar::tab_previous); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); @@ -103,6 +110,7 @@ pub struct ProjectSearchView { included_files_editor: ViewHandle, excluded_files_editor: ViewHandle, filters_enabled: bool, + current_mode: SearchMode, } struct SemanticSearchState { @@ -111,6 +119,15 @@ struct SemanticSearchState { _progress_task: Task<()>, } +// TODO: Update the default search mode to get from config +#[derive(Clone, Default, PartialEq)] +enum SearchMode { + #[default] + Text, + Semantic, + Regex, +} + pub struct ProjectSearchBar { active_project_search: Option>, subscription: Option, @@ -596,6 +613,7 @@ impl ProjectSearchView { included_files_editor, excluded_files_editor, filters_enabled, + current_mode: Default::default(), }; this.model_changed(cx); this @@ -910,7 +928,28 @@ impl ProjectSearchBar { subscription: Default::default(), } } + fn cycle_mode(workspace: &mut Workspace, _: &CycleMode, cx: &mut ViewContext) { + if let Some(search_view) = workspace + .active_item(cx) + .and_then(|item| item.downcast::()) + { + search_view.update(cx, |this, cx| { + let mode = &this.current_mode; + let next_text_state = if SemanticIndex::enabled(cx) { + SearchMode::Semantic + } else { + SearchMode::Regex + }; + this.current_mode = match mode { + &SearchMode::Text => next_text_state, + &SearchMode::Semantic => SearchMode::Regex, + SearchMode::Regex => SearchMode::Text, + }; + cx.notify(); + }) + } + } fn search(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| search_view.search(cx)); @@ -1184,14 +1223,15 @@ impl ProjectSearchBar { .into_any() } - fn render_option_button( + fn render_regex_button( &self, icon: &'static str, - option: SearchOptions, + current_mode: SearchMode, cx: &mut ViewContext, ) -> AnyElement { let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = self.is_option_enabled(option, cx); + let is_active = current_mode == SearchMode::Regex; + let option = SearchOptions::REGEX; MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { let theme = theme::current(cx); let style = theme @@ -1221,7 +1261,7 @@ impl ProjectSearchBar { let tooltip_style = theme::current(cx).tooltip.clone(); let is_active = if let Some(search) = self.active_project_search.as_ref() { let search = search.read(cx); - search.semantic.is_some() + search.current_mode == SearchMode::Semantic } else { false }; @@ -1256,7 +1296,7 @@ impl ProjectSearchBar { let tooltip_style = theme::current(cx).tooltip.clone(); let is_active = if let Some(search) = self.active_project_search.as_ref() { let search = search.read(cx); - search.semantic.is_none() && !self.is_option_enabled(SearchOptions::REGEX, cx) + search.current_mode == SearchMode::Text } else { false }; @@ -1335,7 +1375,7 @@ impl View for ProjectSearchBar { .aligned() .right() .flex(1.0, true); - let regex_button = self.render_option_button("Regex", SearchOptions::REGEX, cx); + let regex_button = self.render_regex_button("Regex", search.current_mode.clone(), cx); let row_spacing = theme.workspace.toolbar.container.padding.bottom; let search = _search.read(cx); let filter_button = { From 444b98e32fc5408770bd02684dfb21a603e5d9e8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:56:09 +0200 Subject: [PATCH 012/119] Update case insensitive button. Co-authored-by: Kyle --- assets/icons/case_insensitive_14.svg | 6 ++++++ crates/search/src/project_search.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 assets/icons/case_insensitive_14.svg diff --git a/assets/icons/case_insensitive_14.svg b/assets/icons/case_insensitive_14.svg new file mode 100644 index 0000000000..79698872bc --- /dev/null +++ b/assets/icons/case_insensitive_14.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 546bbeea69..992f9b143b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1405,7 +1405,7 @@ impl View for ProjectSearchBar { let case_sensitive = if is_semantic_disabled { Some(self.render_option_button_icon( - "icons/word_search_12.svg", + "icons/case_insensitive_14.svg", SearchOptions::CASE_SENSITIVE, cx, )) From 00a9672eca595ee1fbf9578c43fd29b56edabe49 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Aug 2023 18:02:17 +0200 Subject: [PATCH 013/119] Scale down the case insensitive icon Co-authored-by: Kyle --- assets/icons/case_insensitive_12.svg | 8 ++++++++ assets/icons/case_insensitive_14.svg | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 assets/icons/case_insensitive_12.svg delete mode 100644 assets/icons/case_insensitive_14.svg diff --git a/assets/icons/case_insensitive_12.svg b/assets/icons/case_insensitive_12.svg new file mode 100644 index 0000000000..8c943e7509 --- /dev/null +++ b/assets/icons/case_insensitive_12.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/case_insensitive_14.svg b/assets/icons/case_insensitive_14.svg deleted file mode 100644 index 79698872bc..0000000000 --- a/assets/icons/case_insensitive_14.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - From 7d5ff60ff01c846adfaf98027cf03a9a898447c3 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 1 Aug 2023 12:34:02 -0400 Subject: [PATCH 014/119] added svg right margin in search bar Co-authored-by: Piotr --- crates/search/src/project_search.rs | 13 +++++++------ crates/theme/src/theme.rs | 1 + styles/src/style_tree/search.ts | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 992f9b143b..7a4f1ad1a7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1230,7 +1230,7 @@ impl ProjectSearchBar { cx: &mut ViewContext, ) -> AnyElement { let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = current_mode == SearchMode::Regex; + let is_active = current_mode == SearchMode::Regex; //self.is_option_enabled(option, cx); let option = SearchOptions::REGEX; MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { let theme = theme::current(cx); @@ -1405,7 +1405,7 @@ impl View for ProjectSearchBar { let case_sensitive = if is_semantic_disabled { Some(self.render_option_button_icon( - "icons/case_insensitive_14.svg", + "icons/case_insensitive_12.svg", SearchOptions::CASE_SENSITIVE, cx, )) @@ -1424,13 +1424,14 @@ impl View for ProjectSearchBar { }; let search = _search.read(cx); + let icon_style = theme.search.editor_icon.clone(); + // " let query = Flex::row() .with_child( - Svg::new("icons/magnifying_glass_12.svg") - .with_color(gpui::color::Color::white()) - //.with_color(tab_theme.label.text.color) + Svg::for_style(icon_style.icon) .contained() - .constrained(), //.with_margin_right(tab_theme.spacing), + .with_style(icon_style.container) + .constrained(), ) .with_child( ChildView::new(&search.query_editor, cx) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4766f636f3..db6785c6fd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -385,6 +385,7 @@ pub struct Search { pub match_index: ContainedText, pub results_status: TextStyle, pub dismiss_button: Interactive, + pub editor_icon: IconStyle, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 43689c01bc..c2357f53b6 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -54,7 +54,7 @@ export default function search(): any { left: 6, right: 6, top: 6, - }, + }, }, state: { hovered: { @@ -163,5 +163,18 @@ export default function search(): any { }, }, }), + editor_icon: { + icon: { + color: foreground(theme.highest, "variant"), + asset: "icons/magnifying_glass_12.svg", + dimensions: { + width: 12, + height: 12, + } + }, + container: { + padding: { right: 6 } + } + } } } From cf060f0011464c3d071f41faf5e0a7ab62de5d33 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 1 Aug 2023 13:28:21 -0400 Subject: [PATCH 015/119] added major and minor display text to project search, and fixed icon padding Co-authored-by: Piotr --- crates/search/src/project_search.rs | 73 ++++++++++++++++++++++++----- crates/theme/src/theme.rs | 3 +- styles/src/style_tree/search.ts | 8 +++- 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 7a4f1ad1a7..44586b6102 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -120,7 +120,7 @@ struct SemanticSearchState { } // TODO: Update the default search mode to get from config -#[derive(Clone, Default, PartialEq)] +#[derive(Copy, Clone, Default, PartialEq)] enum SearchMode { #[default] Text, @@ -282,29 +282,67 @@ impl View for ProjectSearchView { enum Status {} let theme = theme::current(cx).clone(); - let text = if model.pending_search.is_some() { + + // If Search is Active -> Major: Searching..., Minor: None + // If Semantic -> Major: "Search using Natural Language", Minor: {Status}/n{ex...}/n{ex...} + // If Regex -> Major: "Search using Regex", Minor: {ex...} + // If Text -> Major: "Text search all files and folders", Minor: {...} + + let current_mode = self.current_mode; + let major_text = if model.pending_search.is_some() { Cow::Borrowed("Searching...") - } else if let Some(semantic) = &self.semantic { + } else { + match current_mode { + SearchMode::Text => Cow::Borrowed("Text search all files and folders"), + SearchMode::Semantic => { + Cow::Borrowed("Search all files and folders using Natural Language") + } + SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), + } + }; + + let semantic_status = if let Some(semantic) = &self.semantic { if semantic.outstanding_file_count > 0 { - Cow::Owned(format!( - "Indexing. {} of {}...", + let dots_count = semantic.outstanding_file_count % 3 + 1; + let dots: String = std::iter::repeat('.').take(dots_count).collect(); + format!( + "Indexing. {} of {}{dots}", semantic.file_count - semantic.outstanding_file_count, semantic.file_count - )) + ) } else { - Cow::Borrowed("Indexing complete") + "Indexing complete".to_string() } - } else if self.query_editor.read(cx).text(cx).is_empty() { - Cow::Borrowed("Text search all files and folders") } else { - Cow::Borrowed("No results") + "This is an invalid state".to_string() + }; + + let minor_text = match current_mode { + SearchMode::Semantic => [ + semantic_status, + "ex. list all available languages".to_owned(), + ], + _ => [ + "Include/exclude specific paths with the filter option.".to_owned(), + "Matching exact word and/or casing is available too.".to_owned(), + ], }; MouseEventHandler::::new(0, cx, |_, _| { Flex::column() .with_child(Flex::column().contained().flex(1., true)) .with_child( - Label::new(text, theme.search.results_status.clone()) + Flex::column() + .align_children_center() + .with_child(Label::new( + major_text, + theme.search.major_results_status.clone(), + )) + .with_children( + minor_text.into_iter().map(|x| { + Label::new(x, theme.search.minor_results_status.clone()) + }), + ) .aligned() .top() .contained() @@ -1060,6 +1098,9 @@ impl ProjectSearchBar { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { search_view.search_options.toggle(option); + if option.contains(SearchOptions::REGEX) { + search_view.current_mode = SearchMode::Regex; + } search_view.semantic = None; search_view.search(cx); }); @@ -1096,6 +1137,7 @@ impl ProjectSearchBar { if search_view.semantic.is_some() { search_view.semantic = None; } else if let Some(semantic_index) = SemanticIndex::global(cx) { + search_view.current_mode = SearchMode::Semantic; // TODO: confirm that it's ok to send this project search_view.search_options = SearchOptions::none(); @@ -1315,7 +1357,13 @@ impl ProjectSearchBar { .with_style(style.container) }) .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_semantic_search(cx); + if let Some(search) = this.active_project_search.as_mut() { + search.update(cx, |this, cx| { + this.semantic = None; + this.current_mode = SearchMode::Text; + cx.notify(); + }); + } }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( @@ -1425,7 +1473,6 @@ impl View for ProjectSearchBar { let search = _search.read(cx); let icon_style = theme.search.editor_icon.clone(); - // " let query = Flex::row() .with_child( Svg::for_style(icon_style.icon) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index db6785c6fd..b3e65d9fda 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -383,7 +383,8 @@ pub struct Search { pub action_button: Interactive, pub match_background: Color, pub match_index: ContainedText, - pub results_status: TextStyle, + pub major_results_status: TextStyle, + pub minor_results_status: TextStyle, pub dismiss_button: Interactive, pub editor_icon: IconStyle, } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index c2357f53b6..9f72cfc424 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -140,9 +140,13 @@ export default function search(): any { right: 6, }, }, - results_status: { + major_results_status: { ...text(theme.highest, "mono", "on"), - size: 18, + size: 15, + }, + minor_results_status: { + ...text(theme.highest, "mono", "variant"), + size: 13, }, dismiss_button: interactive({ base: { From e4871afaf3e2b1ae8113d6e23bfbb8db9484d1dc Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 00:36:46 +0200 Subject: [PATCH 016/119] Improve styling of mglass icon & search switches --- styles/src/style_tree/search.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 9f72cfc424..94b52aa664 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -22,8 +22,8 @@ export default function search(): any { padding: { top: 3, bottom: 3, - left: 12, - right: 8, + left: 10, + right: 4, }, } @@ -47,7 +47,7 @@ export default function search(): any { corner_radius: 2, border: border(theme.highest, "on"), margin: { - right: 4, + right: 2, }, padding: { bottom: 6, @@ -177,7 +177,8 @@ export default function search(): any { } }, container: { - padding: { right: 6 } + margin: { right: 6 }, + padding: { left: 4 } } } } From 4ef59899d1234c5156bf1efe3f058adfd373383d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 13:05:29 +0200 Subject: [PATCH 017/119] WIP: Add ButtonSide element --- crates/search/Cargo.toml | 4 +- crates/search/src/project_search.rs | 172 ++++++++++++++++++++++++++-- styles/src/style_tree/search.ts | 6 +- 3 files changed, 165 insertions(+), 17 deletions(-) diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 3a59f9a5cd..64421f5431 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -30,11 +30,11 @@ serde_derive.workspace = true smallvec.workspace = true smol.workspace = true globset.workspace = true - +serde_json.workspace = true [dev-dependencies] client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } -serde_json.workspace = true + workspace = { path = "../workspace", features = ["test-support"] } unindent.workspace = true diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 44586b6102..4d86134a1f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -10,6 +10,10 @@ use editor::{ }; use futures::StreamExt; use globset::{Glob, GlobMatcher}; +use gpui::color::Color; +use gpui::geometry::rect::RectF; +use gpui::json::{self, ToJson}; +use gpui::SceneBuilder; use gpui::{ actions, elements::*, @@ -17,6 +21,7 @@ use gpui::{ Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; +use gpui::{scene::Path, Border, LayoutContext}; use menu::Confirm; use postage::stream::Stream; use project::{search::SearchQuery, Entry, Project}; @@ -958,6 +963,113 @@ impl Default for ProjectSearchBar { Self::new() } } +type CreatePath = fn(RectF, Color) -> Path; + +pub struct ButtonSide { + color: Color, + factory: CreatePath, +} + +impl ButtonSide { + fn new(color: Color, factory: CreatePath) -> Self { + Self { color, factory } + } + pub fn left(color: Color) -> Self { + Self::new(color, left_button_side) + } + pub fn right(color: Color) -> Self { + Self::new(color, right_button_side) + } +} + +fn left_button_side(bounds: RectF, color: Color) -> Path { + use gpui::geometry::PathBuilder; + let mut path = PathBuilder::new(); + path.reset(bounds.lower_right()); + path.line_to(bounds.upper_right()); + let mut middle_point = bounds.origin(); + let distance_to_line = (middle_point.y() - bounds.lower_left().y()) / 4.; + middle_point.set_y(middle_point.y() - distance_to_line); + path.curve_to(middle_point, bounds.origin()); + let mut target = bounds.lower_left(); + target.set_y(target.y() + distance_to_line); + path.line_to(target); + //path.curve_to(bounds.lower_right(), bounds.upper_right()); + path.curve_to(bounds.lower_right(), bounds.lower_left()); + path.build(color, None) +} + +fn right_button_side(bounds: RectF, color: Color) -> Path { + use gpui::geometry::PathBuilder; + let mut path = PathBuilder::new(); + path.reset(bounds.lower_left()); + path.line_to(bounds.origin()); + let mut middle_point = bounds.upper_right(); + let distance_to_line = (middle_point.y() - bounds.lower_right().y()) / 4.; + middle_point.set_y(middle_point.y() - distance_to_line); + path.curve_to(middle_point, bounds.upper_right()); + let mut target = bounds.lower_right(); + target.set_y(target.y() + distance_to_line); + path.line_to(target); + //path.curve_to(bounds.lower_right(), bounds.upper_right()); + path.curve_to(bounds.lower_left(), bounds.lower_right()); + path.build(color, None) +} + +impl Element for ButtonSide { + type LayoutState = (); + + type PaintState = (); + + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + _: &mut ProjectSearchBar, + _: &mut LayoutContext, + ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { + (constraint.max, ()) + } + + fn paint( + &mut self, + scene: &mut SceneBuilder, + bounds: RectF, + _: RectF, + _: &mut Self::LayoutState, + _: &mut ProjectSearchBar, + _: &mut ViewContext, + ) -> Self::PaintState { + scene.push_path((self.factory)(bounds, self.color)); + } + + fn rect_for_text_range( + &self, + _: Range, + _: RectF, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &ProjectSearchBar, + _: &ViewContext, + ) -> Option { + None + } + + fn debug( + &self, + bounds: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &ProjectSearchBar, + _: &ViewContext, + ) -> gpui::json::Value { + json::json!({ + "type": "ButtonSide", + "bounds": bounds.to_json(), + "color": self.color.to_json(), + }) + } +} impl ProjectSearchBar { pub fn new() -> Self { @@ -1276,14 +1388,32 @@ impl ProjectSearchBar { let option = SearchOptions::REGEX; MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { let theme = theme::current(cx); - let style = theme + let mut style = theme .search .option_button .in_state(is_active) - .style_for(state); - Label::new(icon, style.text.clone()) - .contained() - .with_style(style.container) + .style_for(state) + .clone(); + style.container.border.right = false; + Flex::row() + .with_child( + Label::new(icon, style.text.clone()) + .contained() + .with_style(style.container), + ) + .with_child( + ButtonSide::right( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .contained() + .constrained() + .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .aligned() + .bottom(), + ) }) .on_click(MouseButton::Left, move |_, this, cx| { this.toggle_search_option(option, cx); @@ -1347,14 +1477,32 @@ impl ProjectSearchBar { enum NormalSearchTag {} MouseEventHandler::::new(region_id, cx, |state, cx| { let theme = theme::current(cx); - let style = theme + let mut style = theme .search .option_button .in_state(is_active) - .style_for(state); - Label::new("Text", style.text.clone()) - .contained() - .with_style(style.container) + .style_for(state) + .clone(); + style.container.border.left = false; + Flex::row() + .with_child( + ButtonSide::left( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .contained() + .constrained() + .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .aligned() + .bottom(), + ) + .with_child( + Label::new("Text", style.text.clone()) + .contained() + .with_style(style.container), + ) }) .on_click(MouseButton::Left, move |_, this, cx| { if let Some(search) = this.active_project_search.as_mut() { @@ -1592,7 +1740,9 @@ impl View for ProjectSearchBar { .with_child(normal_search) .with_children(semantic_index) .with_child(regex_button) - .flex(1., true) + .constrained() + .with_height(theme.workspace.toolbar.height) + .contained() .aligned() .right(), ), diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 94b52aa664..356cd77f97 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -44,11 +44,9 @@ export default function search(): any { base: { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: 2, + border: border(theme.highest, "on"), - margin: { - right: 2, - }, + padding: { bottom: 6, left: 6, From 0253ff304371a6737747a1183bcfcc6f69f5b5c9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 13:52:02 +0200 Subject: [PATCH 018/119] Touch up rounding on the sides of mode switcher --- crates/search/src/project_search.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 4d86134a1f..9550ca6e89 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1395,6 +1395,7 @@ impl ProjectSearchBar { .style_for(state) .clone(); style.container.border.right = false; + Flex::row() .with_child( Label::new(icon, style.text.clone()) @@ -1410,7 +1411,7 @@ impl ProjectSearchBar { ) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .with_max_width(theme.titlebar.avatar_ribbon.width / 3.) .aligned() .bottom(), ) @@ -1494,7 +1495,7 @@ impl ProjectSearchBar { ) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .with_max_width(theme.titlebar.avatar_ribbon.width / 3.) .aligned() .bottom(), ) From d39585b24040a457df95884782a743135ec85299 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:05:09 +0200 Subject: [PATCH 019/119] Fix query editor 'floating' when filters are enabled --- crates/search/src/project_search.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 9550ca6e89..49780e53ef 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1733,6 +1733,8 @@ impl View for ProjectSearchBar { .with_children(filters) .contained() .with_style(theme.search.container) + .aligned() + .top() .flex(2., true), ) .with_child( From e0eaf23c28a928428bda7243fb92413803e612fb Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:33:35 +0200 Subject: [PATCH 020/119] Add borders to button sides --- crates/search/src/project_search.rs | 51 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 49780e53ef..95d335ea31 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -12,6 +12,7 @@ use futures::StreamExt; use globset::{Glob, GlobMatcher}; use gpui::color::Color; use gpui::geometry::rect::RectF; +use gpui::geometry::vector::IntoVector2F; use gpui::json::{self, ToJson}; use gpui::SceneBuilder; use gpui::{ @@ -964,24 +965,46 @@ impl Default for ProjectSearchBar { } } type CreatePath = fn(RectF, Color) -> Path; - +type AdjustBorder = fn(RectF, f32) -> RectF; pub struct ButtonSide { color: Color, factory: CreatePath, + border_adjustment: AdjustBorder, + border: Option<(f32, Color)>, } impl ButtonSide { - fn new(color: Color, factory: CreatePath) -> Self { - Self { color, factory } + fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { + Self { + color, + factory, + border_adjustment, + border: None, + } + } + pub fn with_border(mut self, width: f32, color: Color) -> Self { + self.border = Some((width, color)); + self } pub fn left(color: Color) -> Self { - Self::new(color, left_button_side) + Self::new(color, left_button_side, left_button_border_adjust) } pub fn right(color: Color) -> Self { - Self::new(color, right_button_side) + Self::new(color, right_button_side, right_button_border_adjust) } } - +fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut lower_right = bounds.clone().lower_right(); + lower_right.set_x(lower_right.x() + width.x()); + RectF::from_points(bounds.origin() + width, lower_right) +} +fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut origin = bounds.clone().origin(); + origin.set_x(origin.x() - width.x()); + RectF::from_points(origin, bounds.lower_right() - width) +} fn left_button_side(bounds: RectF, color: Color) -> Path { use gpui::geometry::PathBuilder; let mut path = PathBuilder::new(); @@ -994,7 +1017,6 @@ fn left_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_left(); target.set_y(target.y() + distance_to_line); path.line_to(target); - //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_right(), bounds.lower_left()); path.build(color, None) } @@ -1011,7 +1033,6 @@ fn right_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_right(); target.set_y(target.y() + distance_to_line); path.line_to(target); - //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_left(), bounds.lower_right()); path.build(color, None) } @@ -1039,6 +1060,11 @@ impl Element for ButtonSide { _: &mut ProjectSearchBar, _: &mut ViewContext, ) -> Self::PaintState { + let mut bounds = bounds; + if let Some((border_width, border_color)) = self.border.as_ref() { + scene.push_path((self.factory)(bounds, border_color.clone())); + bounds = (self.border_adjustment)(bounds, *border_width); + }; scene.push_path((self.factory)(bounds, self.color)); } @@ -1409,9 +1435,10 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) + .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 3.) + .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) .aligned() .bottom(), ) @@ -1493,9 +1520,10 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) + .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 3.) + .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) .aligned() .bottom(), ) @@ -1747,7 +1775,8 @@ impl View for ProjectSearchBar { .with_height(theme.workspace.toolbar.height) .contained() .aligned() - .right(), + .right() + .flex(1., true), ), ) .contained() From caaa4b1618a9936253b687aadcbeaa9a71843b64 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 2 Aug 2023 11:23:55 -0400 Subject: [PATCH 021/119] add theme for search mode option buttons, and adjust padding Co-authored-by: Piotr --- crates/search/src/project_search.rs | 74 +++++++++++------------------ crates/theme/src/theme.rs | 2 + styles/src/style_tree/search.ts | 46 +++++++++++++++++- 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 95d335ea31..62ad328e02 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -12,7 +12,6 @@ use futures::StreamExt; use globset::{Glob, GlobMatcher}; use gpui::color::Color; use gpui::geometry::rect::RectF; -use gpui::geometry::vector::IntoVector2F; use gpui::json::{self, ToJson}; use gpui::SceneBuilder; use gpui::{ @@ -965,46 +964,24 @@ impl Default for ProjectSearchBar { } } type CreatePath = fn(RectF, Color) -> Path; -type AdjustBorder = fn(RectF, f32) -> RectF; + pub struct ButtonSide { color: Color, factory: CreatePath, - border_adjustment: AdjustBorder, - border: Option<(f32, Color)>, } impl ButtonSide { - fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { - Self { - color, - factory, - border_adjustment, - border: None, - } - } - pub fn with_border(mut self, width: f32, color: Color) -> Self { - self.border = Some((width, color)); - self + fn new(color: Color, factory: CreatePath) -> Self { + Self { color, factory } } pub fn left(color: Color) -> Self { - Self::new(color, left_button_side, left_button_border_adjust) + Self::new(color, left_button_side) } pub fn right(color: Color) -> Self { - Self::new(color, right_button_side, right_button_border_adjust) + Self::new(color, right_button_side) } } -fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut lower_right = bounds.clone().lower_right(); - lower_right.set_x(lower_right.x() + width.x()); - RectF::from_points(bounds.origin() + width, lower_right) -} -fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut origin = bounds.clone().origin(); - origin.set_x(origin.x() - width.x()); - RectF::from_points(origin, bounds.lower_right() - width) -} + fn left_button_side(bounds: RectF, color: Color) -> Path { use gpui::geometry::PathBuilder; let mut path = PathBuilder::new(); @@ -1017,6 +994,7 @@ fn left_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_left(); target.set_y(target.y() + distance_to_line); path.line_to(target); + //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_right(), bounds.lower_left()); path.build(color, None) } @@ -1033,6 +1011,7 @@ fn right_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_right(); target.set_y(target.y() + distance_to_line); path.line_to(target); + //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_left(), bounds.lower_right()); path.build(color, None) } @@ -1060,11 +1039,6 @@ impl Element for ButtonSide { _: &mut ProjectSearchBar, _: &mut ViewContext, ) -> Self::PaintState { - let mut bounds = bounds; - if let Some((border_width, border_color)) = self.border.as_ref() { - scene.push_path((self.factory)(bounds, border_color.clone())); - bounds = (self.border_adjustment)(bounds, *border_width); - }; scene.push_path((self.factory)(bounds, self.color)); } @@ -1416,12 +1390,14 @@ impl ProjectSearchBar { let theme = theme::current(cx); let mut style = theme .search - .option_button + .mode_button .in_state(is_active) .style_for(state) .clone(); style.container.border.right = false; - + style.container.padding.right -= theme.search.mode_filling_width; + style.container.corner_radius = 0.; + debug_assert!(style.container.padding.right >= 0.); Flex::row() .with_child( Label::new(icon, style.text.clone()) @@ -1435,10 +1411,9 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) - .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .with_max_width(theme.search.mode_filling_width) .aligned() .bottom(), ) @@ -1470,11 +1445,15 @@ impl ProjectSearchBar { MouseEventHandler::::new(region_id, cx, |state, cx| { let theme = theme::current(cx); - let style = theme + let mut style = theme .search - .option_button + .mode_button .in_state(is_active) - .style_for(state); + .style_for(state) + .clone(); + + style.container.corner_radius = 0.; + Label::new("Semantic", style.text.clone()) .contained() .with_style(style.container) @@ -1507,11 +1486,14 @@ impl ProjectSearchBar { let theme = theme::current(cx); let mut style = theme .search - .option_button + .mode_button .in_state(is_active) .style_for(state) .clone(); style.container.border.left = false; + style.container.padding.left -= theme.search.mode_filling_width; + debug_assert!(style.container.padding.left >= 0.); + style.container.corner_radius = 0.; Flex::row() .with_child( ButtonSide::left( @@ -1520,10 +1502,9 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) - .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.titlebar.avatar_ribbon.width / 2.) + .with_max_width(theme.search.mode_filling_width) .aligned() .bottom(), ) @@ -1768,15 +1749,16 @@ impl View for ProjectSearchBar { .with_child( Flex::column().with_child( Flex::row() + .align_children_center() .with_child(normal_search) .with_children(semantic_index) .with_child(regex_button) .constrained() .with_height(theme.workspace.toolbar.height) .contained() + .with_style(theme.search.container) .aligned() - .right() - .flex(1., true), + .right(), ), ) .contained() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b3e65d9fda..c3b3502770 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -387,6 +387,8 @@ pub struct Search { pub minor_results_status: TextStyle, pub dismiss_button: Interactive, pub editor_icon: IconStyle, + pub mode_button: Toggleable>, + pub mode_filling_width: f32, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 356cd77f97..370d12c6f0 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -178,6 +178,50 @@ export default function search(): any { margin: { right: 6 }, padding: { left: 4 } } - } + }, + mode_button: toggleable({ + base: interactive({ + base: { + ...text(theme.highest, "mono", "on"), + background: background(theme.highest, "on"), + + border: border(theme.highest, "on"), + + padding: { + bottom: 6, + left: 10, + right: 10, + top: 6, + }, + corner_radius: 2, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "on", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: border(theme.highest, "on", "hovered"), + }, + clicked: { + ...text(theme.highest, "mono", "on", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: border(theme.highest, "on", "pressed"), + }, + }, + }), + state: { + active: { + default: { + ...text(theme.highest, "mono", "accent"), + }, + hovered: { + ...text(theme.highest, "mono", "accent", "hovered"), + }, + clicked: { + ...text(theme.highest, "mono", "accent", "pressed"), + }, + }, + }, + }), + mode_filling_width: 4.0, } } From 5b30caa33301974e14aafd61b64e0786343ff476 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:33:35 +0200 Subject: [PATCH 022/119] Add borders to button sides --- crates/search/src/project_search.rs | 47 +++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 62ad328e02..b248de412d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -12,6 +12,7 @@ use futures::StreamExt; use globset::{Glob, GlobMatcher}; use gpui::color::Color; use gpui::geometry::rect::RectF; +use gpui::geometry::vector::IntoVector2F; use gpui::json::{self, ToJson}; use gpui::SceneBuilder; use gpui::{ @@ -964,24 +965,46 @@ impl Default for ProjectSearchBar { } } type CreatePath = fn(RectF, Color) -> Path; - +type AdjustBorder = fn(RectF, f32) -> RectF; pub struct ButtonSide { color: Color, factory: CreatePath, + border_adjustment: AdjustBorder, + border: Option<(f32, Color)>, } impl ButtonSide { - fn new(color: Color, factory: CreatePath) -> Self { - Self { color, factory } + fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { + Self { + color, + factory, + border_adjustment, + border: None, + } + } + pub fn with_border(mut self, width: f32, color: Color) -> Self { + self.border = Some((width, color)); + self } pub fn left(color: Color) -> Self { - Self::new(color, left_button_side) + Self::new(color, left_button_side, left_button_border_adjust) } pub fn right(color: Color) -> Self { - Self::new(color, right_button_side) + Self::new(color, right_button_side, right_button_border_adjust) } } - +fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut lower_right = bounds.clone().lower_right(); + lower_right.set_x(lower_right.x() + width.x()); + RectF::from_points(bounds.origin() + width, lower_right) +} +fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut origin = bounds.clone().origin(); + origin.set_x(origin.x() - width.x()); + RectF::from_points(origin, bounds.lower_right() - width) +} fn left_button_side(bounds: RectF, color: Color) -> Path { use gpui::geometry::PathBuilder; let mut path = PathBuilder::new(); @@ -994,7 +1017,6 @@ fn left_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_left(); target.set_y(target.y() + distance_to_line); path.line_to(target); - //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_right(), bounds.lower_left()); path.build(color, None) } @@ -1011,7 +1033,6 @@ fn right_button_side(bounds: RectF, color: Color) -> Path { let mut target = bounds.lower_right(); target.set_y(target.y() + distance_to_line); path.line_to(target); - //path.curve_to(bounds.lower_right(), bounds.upper_right()); path.curve_to(bounds.lower_left(), bounds.lower_right()); path.build(color, None) } @@ -1039,6 +1060,11 @@ impl Element for ButtonSide { _: &mut ProjectSearchBar, _: &mut ViewContext, ) -> Self::PaintState { + let mut bounds = bounds; + if let Some((border_width, border_color)) = self.border.as_ref() { + scene.push_path((self.factory)(bounds, border_color.clone())); + bounds = (self.border_adjustment)(bounds, *border_width); + }; scene.push_path((self.factory)(bounds, self.color)); } @@ -1411,6 +1437,7 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) + .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() .with_max_width(theme.search.mode_filling_width) @@ -1502,6 +1529,7 @@ impl ProjectSearchBar { .background_color .unwrap_or_else(gpui::color::Color::transparent_black), ) + .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() .with_max_width(theme.search.mode_filling_width) @@ -1758,7 +1786,8 @@ impl View for ProjectSearchBar { .contained() .with_style(theme.search.container) .aligned() - .right(), + .right() + .flex(1., true), ), ) .contained() From 7b43b0d4f117b3430b3302e784e4f0050854e141 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 2 Aug 2023 12:29:19 -0400 Subject: [PATCH 023/119] refactored search mode to ensure state is consistent Co-authored-by: Piotr --- crates/search/src/project_search.rs | 289 +++++++++++++++++++++++++--- 1 file changed, 257 insertions(+), 32 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b248de412d..b25defe4fe 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -51,7 +51,10 @@ actions!( ToggleFocus, NextField, ToggleSemanticSearch, - CycleMode + CycleMode, + ActivateTextMode, + ActivateSemanticMode, + ActivateRegexMode ] ); @@ -134,6 +137,68 @@ enum SearchMode { Regex, } +#[derive(Copy, Clone, Debug, PartialEq)] +enum Side { + Left, + Right, +} + +impl SearchMode { + fn label(&self) -> &'static str { + match self { + SearchMode::Text => "Text", + SearchMode::Semantic => "Semantic", + SearchMode::Regex => "Regex", + } + } + + fn region_id(&self) -> usize { + match self { + SearchMode::Text => 3, + SearchMode::Semantic => 4, + SearchMode::Regex => 5, + } + } + + fn tooltip_text(&self) -> &'static str { + match self { + SearchMode::Text => "Activate Text Search", + SearchMode::Semantic => "Activate Semantic Search", + SearchMode::Regex => "Activate Regex Search", + } + } + + fn activate_action(&self) -> Box { + match self { + SearchMode::Text => Box::new(ActivateTextMode), + SearchMode::Semantic => Box::new(ActivateSemanticMode), + SearchMode::Regex => Box::new(ActivateRegexMode), + } + } + + fn border_left(&self) -> bool { + match self { + SearchMode::Text => false, + _ => true, + } + } + + fn border_right(&self) -> bool { + match self { + SearchMode::Regex => false, + _ => true, + } + } + + fn button_side(&self) -> Option { + match self { + SearchMode::Text => Some(Side::Left), + SearchMode::Semantic => None, + SearchMode::Regex => Some(Side::Right), + } + } +} + pub struct ProjectSearchBar { active_project_search: Option>, subscription: Option, @@ -560,6 +625,68 @@ impl Item for ProjectSearchView { } impl ProjectSearchView { + fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { + self.current_mode = mode; + + match mode { + SearchMode::Semantic => { + if let Some(semantic_index) = SemanticIndex::global(cx) { + // Semantic search uses no options + self.search_options = SearchOptions::none(); + + let project = self.model.read(cx).project.clone(); + let index_task = semantic_index.update(cx, |semantic_index, cx| { + semantic_index.index_project(project, cx) + }); + + cx.spawn(|search_view, mut cx| async move { + let (files_to_index, mut files_remaining_rx) = index_task.await?; + + search_view.update(&mut cx, |search_view, cx| { + cx.notify(); + search_view.semantic = Some(SemanticSearchState { + file_count: files_to_index, + outstanding_file_count: files_to_index, + _progress_task: cx.spawn(|search_view, mut cx| async move { + while let Some(count) = files_remaining_rx.recv().await { + search_view + .update(&mut cx, |search_view, cx| { + if let Some(semantic_search_state) = + &mut search_view.semantic + { + semantic_search_state.outstanding_file_count = + count; + cx.notify(); + if count == 0 { + return; + } + } + }) + .ok(); + } + }), + }); + })?; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + } + SearchMode::Regex => { + if !self.is_option_enabled(SearchOptions::REGEX) { + self.toggle_search_option(SearchOptions::REGEX, cx); + } + self.semantic = None; + } + SearchMode::Text => { + if self.is_option_enabled(SearchOptions::REGEX) { + self.toggle_search_option(SearchOptions::REGEX, cx); + } + self.semantic = None; + } + } + cx.notify(); + } fn new(model: ModelHandle, cx: &mut ViewContext) -> Self { let project; let excerpts; @@ -739,24 +866,29 @@ impl ProjectSearchView { } fn search(&mut self, cx: &mut ViewContext) { - if let Some(semantic) = &mut self.semantic { - if semantic.outstanding_file_count > 0 { - return; - } + let mode = self.current_mode; + match mode { + SearchMode::Semantic => { + if let Some(semantic) = &mut self.semantic { + if semantic.outstanding_file_count > 0 { + return; + } - let query = self.query_editor.read(cx).text(cx); - if let Some((included_files, exclude_files)) = - self.get_included_and_excluded_globsets(cx) - { - self.model.update(cx, |model, cx| { - model.semantic_search(query, included_files, exclude_files, cx) - }); + let query = self.query_editor.read(cx).text(cx); + if let Some((included_files, exclude_files)) = + self.get_included_and_excluded_globsets(cx) + { + self.model.update(cx, |model, cx| { + model.semantic_search(query, included_files, exclude_files, cx) + }); + } + } + } + _ => { + if let Some(query) = self.build_search_query(cx) { + self.model.update(cx, |model, cx| model.search(query, cx)); + } } - return; - } - - if let Some(query) = self.build_search_query(cx) { - self.model.update(cx, |model, cx| model.search(query, cx)); } } @@ -791,7 +923,10 @@ impl ProjectSearchView { Some((included_files, excluded_files)) } - + fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) { + self.search_options.toggle(option); + self.semantic = None; + } fn build_search_query(&mut self, cx: &mut ViewContext) -> Option { let text = self.query_editor.read(cx).text(cx); let included_files = @@ -957,6 +1092,9 @@ impl ProjectSearchView { cx.propagate_action(); } + fn is_option_enabled(&self, option: SearchOptions) -> bool { + self.search_options.contains(option) + } } impl Default for ProjectSearchBar { @@ -1117,12 +1255,12 @@ impl ProjectSearchBar { SearchMode::Regex }; - this.current_mode = match mode { + let new_mode = match mode { &SearchMode::Text => next_text_state, &SearchMode::Semantic => SearchMode::Regex, SearchMode::Regex => SearchMode::Text, }; - cx.notify(); + this.activate_search_mode(new_mode, cx); }) } } @@ -1235,12 +1373,7 @@ impl ProjectSearchBar { fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { - search_view.search_options.toggle(option); - if option.contains(SearchOptions::REGEX) { - search_view.current_mode = SearchMode::Regex; - } - search_view.semantic = None; - search_view.search(cx); + search_view.toggle_search_option(option, cx); }); cx.notify(); true @@ -1248,6 +1381,7 @@ impl ProjectSearchBar { false } } + fn toggle_filters(&mut self, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { @@ -1403,6 +1537,98 @@ impl ProjectSearchBar { .into_any() } + fn render_search_mode_button( + &self, + mode: SearchMode, + cx: &mut ViewContext, + ) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + let is_active = if let Some(search) = self.active_project_search.as_ref() { + let search = search.read(cx); + search.current_mode == mode + } else { + false + }; + + enum SearchModeButton {} + MouseEventHandler::::new(mode.region_id(), cx, |state, cx| { + let theme = theme::current(cx); + let mut style = theme + .search + .mode_button + .in_state(is_active) + .style_for(state) + .clone(); + + let label = Label::new(mode.label(), style.text.clone()) + .contained() + .with_style(style.container); + + if let Some(button_side) = mode.button_side() { + style.container.border.left = mode.border_left(); + style.container.border.right = mode.border_right(); + + if button_side == Side::Left { + Flex::row() + .with_child( + ButtonSide::left( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .with_child(label) + .into_any() + } else { + Flex::row() + .with_child(label) + .with_child( + ButtonSide::right( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .into_any() + } + } else { + label.into_any() + } + }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.activate_search_mode(mode, cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + mode.region_id(), + mode.tooltip_text().to_owned(), + Some(mode.activate_action()), + tooltip_style, + cx, + ) + .into_any() + } + + fn activate_search_mode(&self, mode: SearchMode, cx: &mut ViewContext) { + // Update Current Mode + if let Some(search_view) = self.active_project_search.as_ref() { + search_view.update(cx, |search_view, cx| { + search_view.activate_search_mode(mode, cx); + }); + cx.notify(); + } + } + fn render_regex_button( &self, icon: &'static str, @@ -1561,6 +1787,7 @@ impl ProjectSearchBar { ) .into_any() } + fn is_option_enabled(&self, option: SearchOptions, cx: &AppContext) -> bool { if let Some(search) = self.active_project_search.as_ref() { search.read(cx).search_options.contains(option) @@ -1609,7 +1836,6 @@ impl View for ProjectSearchBar { .aligned() .right() .flex(1.0, true); - let regex_button = self.render_regex_button("Regex", search.current_mode.clone(), cx); let row_spacing = theme.workspace.toolbar.container.padding.bottom; let search = _search.read(cx); let filter_button = { @@ -1726,9 +1952,8 @@ impl View for ProjectSearchBar { ) }); - let semantic_index = - SemanticIndex::enabled(cx).then(|| self.render_semantic_search_button(cx)); - let normal_search = self.render_text_search_button(cx); + let semantic_index = SemanticIndex::enabled(cx) + .then(|| self.render_search_mode_button(SearchMode::Semantic, cx)); Flex::row() .with_child( Flex::column() @@ -1778,9 +2003,9 @@ impl View for ProjectSearchBar { Flex::column().with_child( Flex::row() .align_children_center() - .with_child(normal_search) + .with_child(self.render_search_mode_button(SearchMode::Text, cx)) .with_children(semantic_index) - .with_child(regex_button) + .with_child(self.render_search_mode_button(SearchMode::Regex, cx)) .constrained() .with_height(theme.workspace.toolbar.height) .contained() From acf78f5fb4c2ba5a53dc508d86950fa49d98dc08 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 2 Aug 2023 14:18:28 -0400 Subject: [PATCH 024/119] add kill_search function to stop searching on mode change --- crates/search/src/project_search.rs | 237 ++-------------------------- 1 file changed, 13 insertions(+), 224 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b25defe4fe..ee12038e1c 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,6 +1,5 @@ use crate::{ - SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, - ToggleWholeWord, + SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use anyhow::Result; use collections::HashMap; @@ -22,7 +21,7 @@ use gpui::{ Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; -use gpui::{scene::Path, Border, LayoutContext}; +use gpui::{scene::Path, LayoutContext}; use menu::Confirm; use postage::stream::Stream; use project::{search::SearchQuery, Entry, Project}; @@ -50,7 +49,6 @@ actions!( SearchInNew, ToggleFocus, NextField, - ToggleSemanticSearch, CycleMode, ActivateTextMode, ActivateSemanticMode, @@ -74,7 +72,6 @@ pub fn init(cx: &mut AppContext) { cx.capture_action(ProjectSearchBar::tab_previous); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); - add_toggle_option_action::(SearchOptions::REGEX, cx); } fn add_toggle_option_action(option: SearchOptions, cx: &mut AppContext) { @@ -234,6 +231,12 @@ impl ProjectSearch { }) } + fn kill_search(&mut self) { + self.active_query = None; + self.match_ranges.clear(); + self.pending_search = None; + } + fn search(&mut self, query: SearchQuery, cx: &mut ModelContext) { let search = self .project @@ -626,6 +629,7 @@ impl Item for ProjectSearchView { impl ProjectSearchView { fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { + self.model.update(cx, |model, _| model.kill_search()); self.current_mode = mode; match mode { @@ -674,13 +678,13 @@ impl ProjectSearchView { } SearchMode::Regex => { if !self.is_option_enabled(SearchOptions::REGEX) { - self.toggle_search_option(SearchOptions::REGEX, cx); + self.toggle_search_option(SearchOptions::REGEX); } self.semantic = None; } SearchMode::Text => { if self.is_option_enabled(SearchOptions::REGEX) { - self.toggle_search_option(SearchOptions::REGEX, cx); + self.toggle_search_option(SearchOptions::REGEX); } self.semantic = None; } @@ -923,7 +927,7 @@ impl ProjectSearchView { Some((included_files, excluded_files)) } - fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) { + fn toggle_search_option(&mut self, option: SearchOptions) { self.search_options.toggle(option); self.semantic = None; } @@ -1373,7 +1377,7 @@ impl ProjectSearchBar { fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { - search_view.toggle_search_option(option, cx); + search_view.toggle_search_option(option); }); cx.notify(); true @@ -1403,62 +1407,6 @@ impl ProjectSearchBar { } } - fn toggle_semantic_search(&mut self, cx: &mut ViewContext) -> bool { - if let Some(search_view) = self.active_project_search.as_ref() { - search_view.update(cx, |search_view, cx| { - if search_view.semantic.is_some() { - search_view.semantic = None; - } else if let Some(semantic_index) = SemanticIndex::global(cx) { - search_view.current_mode = SearchMode::Semantic; - // TODO: confirm that it's ok to send this project - search_view.search_options = SearchOptions::none(); - - let project = search_view.model.read(cx).project.clone(); - let index_task = semantic_index.update(cx, |semantic_index, cx| { - semantic_index.index_project(project, cx) - }); - - cx.spawn(|search_view, mut cx| async move { - let (files_to_index, mut files_remaining_rx) = index_task.await?; - - search_view.update(&mut cx, |search_view, cx| { - cx.notify(); - search_view.semantic = Some(SemanticSearchState { - file_count: files_to_index, - outstanding_file_count: files_to_index, - _progress_task: cx.spawn(|search_view, mut cx| async move { - while let Some(count) = files_remaining_rx.recv().await { - search_view - .update(&mut cx, |search_view, cx| { - if let Some(semantic_search_state) = - &mut search_view.semantic - { - semantic_search_state.outstanding_file_count = - count; - cx.notify(); - if count == 0 { - return; - } - } - }) - .ok(); - } - }), - }); - })?; - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - } - cx.notify(); - }); - cx.notify(); - true - } else { - false - } - } - fn render_nav_button( &self, icon: &'static str, @@ -1629,165 +1577,6 @@ impl ProjectSearchBar { } } - fn render_regex_button( - &self, - icon: &'static str, - current_mode: SearchMode, - cx: &mut ViewContext, - ) -> AnyElement { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = current_mode == SearchMode::Regex; //self.is_option_enabled(option, cx); - let option = SearchOptions::REGEX; - MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { - let theme = theme::current(cx); - let mut style = theme - .search - .mode_button - .in_state(is_active) - .style_for(state) - .clone(); - style.container.border.right = false; - style.container.padding.right -= theme.search.mode_filling_width; - style.container.corner_radius = 0.; - debug_assert!(style.container.padding.right >= 0.); - Flex::row() - .with_child( - Label::new(icon, style.text.clone()) - .contained() - .with_style(style.container), - ) - .with_child( - ButtonSide::right( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width) - .aligned() - .bottom(), - ) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_search_option(option, cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - option.bits as usize, - format!("Toggle {}", option.label()), - Some(option.to_toggle_action()), - tooltip_style, - cx, - ) - .into_any() - } - - fn render_semantic_search_button(&self, cx: &mut ViewContext) -> AnyElement { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = if let Some(search) = self.active_project_search.as_ref() { - let search = search.read(cx); - search.current_mode == SearchMode::Semantic - } else { - false - }; - - let region_id = 3; - - MouseEventHandler::::new(region_id, cx, |state, cx| { - let theme = theme::current(cx); - let mut style = theme - .search - .mode_button - .in_state(is_active) - .style_for(state) - .clone(); - - style.container.corner_radius = 0.; - - Label::new("Semantic", style.text.clone()) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_semantic_search(cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - region_id, - format!("Toggle Semantic Search"), - Some(Box::new(ToggleSemanticSearch)), - tooltip_style, - cx, - ) - .into_any() - } - fn render_text_search_button(&self, cx: &mut ViewContext) -> AnyElement { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = if let Some(search) = self.active_project_search.as_ref() { - let search = search.read(cx); - search.current_mode == SearchMode::Text - } else { - false - }; - - let region_id = 4; - enum NormalSearchTag {} - MouseEventHandler::::new(region_id, cx, |state, cx| { - let theme = theme::current(cx); - let mut style = theme - .search - .mode_button - .in_state(is_active) - .style_for(state) - .clone(); - style.container.border.left = false; - style.container.padding.left -= theme.search.mode_filling_width; - debug_assert!(style.container.padding.left >= 0.); - style.container.corner_radius = 0.; - Flex::row() - .with_child( - ButtonSide::left( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width) - .aligned() - .bottom(), - ) - .with_child( - Label::new("Text", style.text.clone()) - .contained() - .with_style(style.container), - ) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - if let Some(search) = this.active_project_search.as_mut() { - search.update(cx, |this, cx| { - this.semantic = None; - this.current_mode = SearchMode::Text; - cx.notify(); - }); - } - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - region_id, - format!("Toggle Normal Search"), - None, - tooltip_style, - cx, - ) - .into_any() - } - fn is_option_enabled(&self, option: SearchOptions, cx: &AppContext) -> bool { if let Some(search) = self.active_project_search.as_ref() { search.read(cx).search_options.contains(option) From 71bbd5f2f6d86a4319d66699f763324d2693a106 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 2 Aug 2023 15:08:54 -0400 Subject: [PATCH 025/119] update keymaps for cycle search mode and toggle filters --- assets/keymaps/default.json | 9 ++++++--- crates/search/src/project_search.rs | 31 +++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index a2be44cbce..590e0d6234 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -229,13 +229,15 @@ { "context": "ProjectSearchBar", "bindings": { - "escape": "project_search::ToggleFocus" + "escape": "project_search::ToggleFocus", + "alt-tab": "project_search::CycleMode", } }, { "context": "ProjectSearchView", "bindings": { - "escape": "project_search::ToggleFocus" + "escape": "project_search::ToggleFocus", + "alt-tab": "project_search::CycleMode" } }, { @@ -247,7 +249,8 @@ "alt-enter": "search::SelectAllMatches", "alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-w": "search::ToggleWholeWord", - "alt-cmd-r": "search::ToggleRegex" + "alt-cmd-r": "search::ToggleRegex", + "alt-cmd-f": "project_search::ToggleFilters" } }, // Bindings from VS Code diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index ee12038e1c..d3cefa1af1 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -50,6 +50,7 @@ actions!( ToggleFocus, NextField, CycleMode, + ToggleFilters, ActivateTextMode, ActivateSemanticMode, ActivateRegexMode @@ -72,6 +73,18 @@ pub fn init(cx: &mut AppContext) { cx.capture_action(ProjectSearchBar::tab_previous); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); + add_toggle_filters_action::(cx); +} + +fn add_toggle_filters_action(cx: &mut AppContext) { + 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_filters(cx)) { + return; + } + } + cx.propagate_action(); + }); } fn add_toggle_option_action(option: SearchOptions, cx: &mut AppContext) { @@ -380,7 +393,7 @@ impl View for ProjectSearchView { let dots_count = semantic.outstanding_file_count % 3 + 1; let dots: String = std::iter::repeat('.').take(dots_count).collect(); format!( - "Indexing. {} of {}{dots}", + "Indexing: {} of {}{dots}", semantic.file_count - semantic.outstanding_file_count, semantic.file_count ) @@ -388,13 +401,13 @@ impl View for ProjectSearchView { "Indexing complete".to_string() } } else { - "This is an invalid state".to_string() + "Indexing: ...".to_string() }; let minor_text = match current_mode { SearchMode::Semantic => [ semantic_status, - "ex. list all available languages".to_owned(), + "ex. 'list all available languages'".to_owned(), ], _ => [ "Include/exclude specific paths with the filter option.".to_owned(), @@ -1396,8 +1409,6 @@ impl ProjectSearchBar { search_view .excluded_files_editor .update(cx, |_, cx| cx.notify()); - search_view.semantic = None; - search_view.search(cx); cx.notify(); }); cx.notify(); @@ -1518,6 +1529,7 @@ impl ProjectSearchBar { if button_side == Side::Left { Flex::row() + .align_children_center() .with_child( ButtonSide::left( style @@ -1534,6 +1546,7 @@ impl ProjectSearchBar { .into_any() } else { Flex::row() + .align_children_center() .with_child(label) .with_child( ButtonSide::right( @@ -1646,7 +1659,13 @@ impl View for ProjectSearchBar { this.toggle_filters(cx); }) .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::(0, "Toggle filters".into(), None, tooltip_style, cx) + .with_tooltip::( + 0, + "Toggle filters".into(), + Some(Box::new(ToggleFilters)), + tooltip_style, + cx, + ) .into_any() }; let search = _search.read(cx); From b4f6d6eadc8a051f841a1e1801d72c23750d1253 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 2 Aug 2023 17:14:15 -0400 Subject: [PATCH 026/119] update search text for no results --- crates/search/src/project_search.rs | 36 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 32a301d4f3..03e9f47d8d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -113,6 +113,7 @@ struct ProjectSearch { active_query: Option, search_id: usize, search_history: SearchHistory, + no_results: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -235,6 +236,7 @@ impl ProjectSearch { active_query: None, search_id: 0, search_history: SearchHistory::default(), + no_results: None, } } @@ -249,6 +251,7 @@ impl ProjectSearch { active_query: self.active_query.clone(), search_id: self.search_id, search_history: self.search_history.clone(), + no_results: self.no_results.clone(), }) } @@ -256,6 +259,7 @@ impl ProjectSearch { self.active_query = None; self.match_ranges.clear(); self.pending_search = None; + self.no_results = None; } fn search(&mut self, query: SearchQuery, cx: &mut ModelContext) { @@ -272,6 +276,7 @@ impl ProjectSearch { let mut matches = matches.into_iter().collect::>(); let (_task, mut match_ranges) = this.update(&mut cx, |this, cx| { this.match_ranges.clear(); + this.no_results = Some(true); matches.sort_by_key(|(buffer, _)| buffer.read(cx).file().map(|file| file.path())); this.excerpts.update(cx, |excerpts, cx| { excerpts.clear(cx); @@ -285,6 +290,7 @@ impl ProjectSearch { while let Ok(Some(match_range)) = match_ranges.try_next() { this.match_ranges.push(match_range); } + this.no_results = Some(false); cx.notify(); }); } @@ -315,6 +321,7 @@ impl ProjectSearch { self.search_id += 1; self.match_ranges.clear(); self.search_history.add(query.as_str().to_string()); + self.no_results = Some(true); self.pending_search = Some(cx.spawn(|this, mut cx| async move { let results = search?.await.log_err()?; @@ -337,6 +344,7 @@ impl ProjectSearch { while let Ok(Some(match_range)) = match_ranges.try_next() { this.match_ranges.push(match_range); } + this.no_results = Some(false); cx.notify(); }); } @@ -382,6 +390,8 @@ impl View for ProjectSearchView { let current_mode = self.current_mode; let major_text = if model.pending_search.is_some() { Cow::Borrowed("Searching...") + } else if model.no_results.is_some_and(|v| v) { + Cow::Borrowed("No Results...") } else { match current_mode { SearchMode::Text => Cow::Borrowed("Text search all files and folders"), @@ -408,15 +418,23 @@ impl View for ProjectSearchView { "Indexing: ...".to_string() }; - let minor_text = match current_mode { - SearchMode::Semantic => [ - semantic_status, - "ex. 'list all available languages'".to_owned(), - ], - _ => [ - "Include/exclude specific paths with the filter option.".to_owned(), - "Matching exact word and/or casing is available too.".to_owned(), - ], + let minor_text = if let Some(no_results) = model.no_results { + if no_results { + vec!["No results found in this project for the provided query".to_owned()] + } else { + vec![] + } + } else { + match current_mode { + SearchMode::Semantic => vec![ + semantic_status, + "ex. 'list all available languages'".to_owned(), + ], + _ => vec![ + "Include/exclude specific paths with the filter option.".to_owned(), + "Matching exact word and/or casing is available too.".to_owned(), + ], + } }; let previous_query_keystrokes = From 7d83d15bf340d5b730df9c4c2f2fbd677531774f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:05:24 +0200 Subject: [PATCH 027/119] Move navigation buttons to the tab bar. Co-authored-by: Kyle --- crates/theme/src/theme.rs | 2 +- crates/workspace/src/pane.rs | 108 ++++++++++++++++++++++++++++++- crates/workspace/src/toolbar.rs | 66 ------------------- styles/src/style_tree/tab_bar.ts | 19 +++++- 4 files changed, 126 insertions(+), 69 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c3b3502770..da6a188527 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -295,6 +295,7 @@ pub struct TabBar { pub inactive_pane: TabStyles, pub dragged_tab: Tab, pub height: f32, + pub nav_button: Interactive, } impl TabBar { @@ -359,7 +360,6 @@ pub struct Toolbar { pub container: ContainerStyle, pub height: f32, pub item_spacing: f32, - pub nav_button: Interactive, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ee658c9cc9..be1460f200 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -222,6 +222,56 @@ impl TabBarContextMenu { } } +#[allow(clippy::too_many_arguments)] +fn nav_button)>( + svg_path: &'static str, + style: theme::Interactive, + nav_button_height: f32, + tooltip_style: TooltipStyle, + enabled: bool, + on_click: F, + tooltip_action: A, + action_name: &str, + cx: &mut ViewContext, +) -> AnyElement { + MouseEventHandler::::new(0, cx, |state, _| { + let style = if enabled { + style.style_for(state) + } else { + style.disabled_style() + }; + Svg::new(svg_path) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(nav_button_height) + .aligned() + .top() + }) + .with_cursor_style(if enabled { + CursorStyle::PointingHand + } else { + CursorStyle::default() + }) + .on_click(MouseButton::Left, move |_, toolbar, cx| { + on_click(toolbar, cx) + }) + .with_tooltip::( + 0, + action_name.to_string(), + Some(Box::new(tooltip_action)), + tooltip_style, + cx, + ) + .contained() + .into_any_named("nav button") +} + impl Pane { pub fn new( workspace: WeakViewHandle, @@ -236,6 +286,11 @@ impl Pane { context_menu.update(cx, |menu, _| { menu.set_position_mode(OverlayPositionMode::Local) }); + let theme = theme::current(cx).workspace.tab_bar.clone(); + let mut border_for_nav_buttons = theme.tab_style(false, false).container.border.clone(); + border_for_nav_buttons.left = false; + let nav_button_height = theme.height; + let button_style = theme.nav_button; Self { items: Vec::new(), @@ -265,8 +320,59 @@ impl Pane { has_focus: false, can_drop: Rc::new(|_, _| true), can_split: true, - render_tab_bar_buttons: Rc::new(|pane, cx| { + render_tab_bar_buttons: Rc::new(move |pane, cx| { + let tooltip_style = theme::current(cx).tooltip.clone(); Flex::row() + .with_child(nav_button( + "icons/arrow_left_16.svg", + button_style.clone(), + nav_button_height, + tooltip_style.clone(), + pane.can_navigate_backward(), + { + move |pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + workspace.go_back(pane, cx).detach_and_log_err(cx) + }) + }) + } + } + }, + super::GoBack, + "Go Back", + cx, + )) + .with_child( + nav_button( + "icons/arrow_right_16.svg", + button_style.clone(), + nav_button_height, + tooltip_style, + pane.can_navigate_forward(), + { + move |pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + workspace + .go_forward(pane, cx) + .detach_and_log_err(cx) + }) + }) + } + } + }, + super::GoForward, + "Go Forward", + cx, + ) + .contained() + .with_border(border_for_nav_buttons), + ) // New menu .with_child(Self::render_tab_bar_button( 0, diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 3fa37f3666..1d6e8b7e4b 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -118,76 +118,10 @@ impl View for Toolbar { } } - let pane = self.pane.clone(); - let mut enable_go_backward = false; - let mut enable_go_forward = false; - if let Some(pane) = pane.and_then(|pane| pane.upgrade(cx)) { - let pane = pane.read(cx); - enable_go_backward = pane.can_navigate_backward(); - enable_go_forward = pane.can_navigate_forward(); - } - let container_style = theme.container; let height = theme.height * primary_items_row_count as f32; - let nav_button_height = theme.height; - let button_style = theme.nav_button; - let tooltip_style = theme::current(cx).tooltip.clone(); let mut primary_items = Flex::row(); - if self.can_navigate { - primary_items.add_child(nav_button( - "icons/arrow_left_16.svg", - button_style, - nav_button_height, - tooltip_style.clone(), - enable_go_backward, - spacing, - { - move |toolbar, cx| { - if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx)) - { - if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) { - let pane = pane.downgrade(); - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace.go_back(pane, cx).detach_and_log_err(cx); - }); - }) - } - } - } - }, - super::GoBack, - "Go Back", - cx, - )); - primary_items.add_child(nav_button( - "icons/arrow_right_16.svg", - button_style, - nav_button_height, - tooltip_style, - enable_go_forward, - spacing, - { - move |toolbar, cx| { - if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx)) - { - if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) { - let pane = pane.downgrade(); - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace.go_forward(pane, cx).detach_and_log_err(cx); - }); - }) - } - } - } - }, - super::GoForward, - "Go Forward", - cx, - )); - } primary_items.extend(primary_left_items); primary_items.extend(primary_right_items); diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index e7b04246c4..f27ae4b2e6 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -84,7 +84,23 @@ export default function tab_bar(): any { bottom: false, }, } - + const nav_button = interactive({ + base: { + color: foreground(theme.highest, "on"), + icon_width: 12, + button_width: 24, + corner_radius: 6, + }, + state: { + hovered: { + color: foreground(theme.highest, "on", "hovered"), + background: background(theme.highest, "on", "hovered"), + }, + disabled: { + color: foreground(theme.highest, "on", "disabled"), + }, + }, + }); const dragged_tab = { ...active_pane_active_tab, background: with_opacity(tab.background, 0.9), @@ -141,5 +157,6 @@ export default function tab_bar(): any { right: false, }, }, + nav_button: nav_button } } From 8831e03ebaf3eddec26f37abc074aade7cc6d829 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:09:26 +0200 Subject: [PATCH 028/119] Remove reference to pane from a toolbar. Co-authored-by: Kyle --- assets/keymaps/default.json | 2 +- crates/ai/src/assistant.rs | 2 +- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/toolbar.rs | 56 +----------------------------- styles/src/style_tree/tab_bar.ts | 2 +- styles/src/style_tree/workspace.ts | 17 --------- 6 files changed, 5 insertions(+), 76 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 9fbe89975f..2f13ee6d03 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -238,7 +238,7 @@ "context": "ProjectSearchBar", "bindings": { "escape": "project_search::ToggleFocus", - "alt-tab": "project_search::CycleMode", + "alt-tab": "project_search::CycleMode" } }, { diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 957c5e1c06..fa81d6a400 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -158,7 +158,7 @@ impl AssistantPanel { }); let toolbar = cx.add_view(|cx| { - let mut toolbar = Toolbar::new(None); + let mut toolbar = Toolbar::new(); toolbar.set_can_navigate(false, cx); toolbar.add_item(cx.add_view(|cx| BufferSearchBar::new(cx)), cx); toolbar diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index be1460f200..3a4c85b24c 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -308,7 +308,7 @@ impl Pane { pane: handle.clone(), next_timestamp, }))), - toolbar: cx.add_view(|_| Toolbar::new(Some(handle))), + toolbar: cx.add_view(|_| Toolbar::new()), tab_bar_context_menu: TabBarContextMenu { kind: TabBarContextMenuKind::New, handle: context_menu, diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 1d6e8b7e4b..a7c00cbe0f 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -54,7 +54,6 @@ pub struct Toolbar { active_item: Option>, hidden: bool, can_navigate: bool, - pane: Option>, items: Vec<(Box, ToolbarItemLocation)>, } @@ -144,63 +143,10 @@ impl View for Toolbar { } } -#[allow(clippy::too_many_arguments)] -fn nav_button)>( - svg_path: &'static str, - style: theme::Interactive, - nav_button_height: f32, - tooltip_style: TooltipStyle, - enabled: bool, - spacing: f32, - on_click: F, - tooltip_action: A, - action_name: &str, - cx: &mut ViewContext, -) -> AnyElement { - MouseEventHandler::::new(0, cx, |state, _| { - let style = if enabled { - style.style_for(state) - } else { - style.disabled_style() - }; - Svg::new(svg_path) - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(nav_button_height) - .aligned() - .top() - }) - .with_cursor_style(if enabled { - CursorStyle::PointingHand - } else { - CursorStyle::default() - }) - .on_click(MouseButton::Left, move |_, toolbar, cx| { - on_click(toolbar, cx) - }) - .with_tooltip::( - 0, - action_name.to_string(), - Some(Box::new(tooltip_action)), - tooltip_style, - cx, - ) - .contained() - .with_margin_right(spacing) - .into_any_named("nav button") -} - impl Toolbar { - pub fn new(pane: Option>) -> Self { + pub fn new() -> Self { Self { active_item: None, - pane, items: Default::default(), hidden: false, can_navigate: true, diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index f27ae4b2e6..73b9e82d0c 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -100,7 +100,7 @@ export default function tab_bar(): any { color: foreground(theme.highest, "on", "disabled"), }, }, - }); + }) const dragged_tab = { ...active_pane_active_tab, background: with_opacity(tab.background, 0.9), diff --git a/styles/src/style_tree/workspace.ts b/styles/src/style_tree/workspace.ts index 5aee3c987d..c78b9b2909 100644 --- a/styles/src/style_tree/workspace.ts +++ b/styles/src/style_tree/workspace.ts @@ -132,23 +132,6 @@ export default function workspace(): any { background: background(theme.highest), border: border(theme.highest, { bottom: true }), item_spacing: 8, - nav_button: interactive({ - base: { - color: foreground(theme.highest, "on"), - icon_width: 12, - button_width: 24, - corner_radius: 6, - }, - state: { - hovered: { - color: foreground(theme.highest, "on", "hovered"), - background: background(theme.highest, "on", "hovered"), - }, - disabled: { - color: foreground(theme.highest, "on", "disabled"), - }, - }, - }), padding: { left: 8, right: 8, top: 4, bottom: 4 }, }, breadcrumb_height: 24, From 822b1ec002dd2449672f3fa71b6e417276bdd9e7 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:12:08 +0200 Subject: [PATCH 029/119] Clean up compiler warnings Co-authored-by: Kyle --- crates/search/src/project_search.rs | 2 +- crates/workspace/src/toolbar.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 03e9f47d8d..264ca0bd46 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1426,7 +1426,7 @@ impl ProjectSearchBar { fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) -> bool { 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| { search_view.toggle_search_option(option); }); cx.notify(); diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index a7c00cbe0f..e8c1240d43 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -1,7 +1,7 @@ -use crate::{ItemHandle, Pane}; +use crate::ItemHandle; use gpui::{ - elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyElement, AnyViewHandle, - AppContext, Entity, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + elements::*, AnyElement, AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle, + WindowContext, }; pub trait ToolbarItemView: View { From 358e4e5ccf626f28456d8901a112353bfcdf0eab Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 3 Aug 2023 10:22:31 -0400 Subject: [PATCH 030/119] update minor text when actively searching --- crates/search/src/project_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 264ca0bd46..b8ff3d94c5 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -419,7 +419,7 @@ impl View for ProjectSearchView { }; let minor_text = if let Some(no_results) = model.no_results { - if no_results { + if model.pending_search.is_none() && no_results { vec!["No results found in this project for the provided query".to_owned()] } else { vec![] From 8dd330a160586a199e51d18f889542d6d552cc0a Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 3 Aug 2023 12:52:20 -0400 Subject: [PATCH 031/119] add semantic search prompt for the indexing permission on first search Co-authored-by: Piotr --- crates/search/src/project_search.rs | 132 +++++++++++++------- crates/semantic_index/src/semantic_index.rs | 7 +- 2 files changed, 93 insertions(+), 46 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b8ff3d94c5..054eb5bee7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,7 +2,7 @@ use crate::{ NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; -use anyhow::Context; +use anyhow::{Context, Result}; use collections::HashMap; use editor::{ items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, @@ -13,6 +13,7 @@ use gpui::color::Color; use gpui::geometry::rect::RectF; use gpui::geometry::vector::IntoVector2F; use gpui::json::{self, ToJson}; +use gpui::platform::PromptLevel; use gpui::SceneBuilder; use gpui::{ actions, @@ -127,7 +128,8 @@ pub struct ProjectSearchView { model: ModelHandle, query_editor: ViewHandle, results_editor: ViewHandle, - semantic: Option, + semantic_state: Option, + semantic_permissioned: bool, search_options: SearchOptions, panels_with_errors: HashSet, active_match_index: Option, @@ -402,7 +404,7 @@ impl View for ProjectSearchView { } }; - let semantic_status = if let Some(semantic) = &self.semantic { + let semantic_status = if let Some(semantic) = &self.semantic_state { if semantic.outstanding_file_count > 0 { let dots_count = semantic.outstanding_file_count % 3 + 1; let dots: String = std::iter::repeat('.').take(dots_count).collect(); @@ -709,65 +711,108 @@ impl ProjectSearchView { fn toggle_search_option(&mut self, option: SearchOptions) { self.search_options.toggle(option); } + + fn index_project(&mut self, cx: &mut ViewContext) { + if let Some(semantic_index) = SemanticIndex::global(cx) { + // Semantic search uses no options + self.search_options = SearchOptions::none(); + + let project = self.model.read(cx).project.clone(); + let index_task = semantic_index.update(cx, |semantic_index, cx| { + semantic_index.index_project(project, cx) + }); + + cx.spawn(|search_view, mut cx| async move { + let (files_to_index, mut files_remaining_rx) = index_task.await?; + + search_view.update(&mut cx, |search_view, cx| { + cx.notify(); + search_view.semantic_state = Some(SemanticSearchState { + file_count: files_to_index, + outstanding_file_count: files_to_index, + _progress_task: cx.spawn(|search_view, mut cx| async move { + while let Some(count) = files_remaining_rx.recv().await { + search_view + .update(&mut cx, |search_view, cx| { + if let Some(semantic_search_state) = + &mut search_view.semantic_state + { + semantic_search_state.outstanding_file_count = count; + cx.notify(); + if count == 0 { + return; + } + } + }) + .ok(); + } + }), + }); + })?; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + } + fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { self.model.update(cx, |model, _| model.kill_search()); self.current_mode = mode; match mode { SearchMode::Semantic => { - if let Some(semantic_index) = SemanticIndex::global(cx) { - // Semantic search uses no options - self.search_options = SearchOptions::none(); - + // let semantic_permissioned = self.semantic_permissioned.await; + // if semantic_permissioned.is_ok_and(|permission| !permission) { + if !self.semantic_permissioned { + // TODO: Change this to read from the project name let project = self.model.read(cx).project.clone(); - let index_task = semantic_index.update(cx, |semantic_index, cx| { - semantic_index.index_project(project, cx) - }); + let project_name = project + .read(cx) + .worktree_root_names(cx) + .collect::>() + .join("/"); + let is_plural = + project_name.chars().filter(|letter| *letter == '/').count() > 0; + let prompt_text = format!("Would you like to index the '{}' project{} for semantic search? This requires sending code to the OpenAI API", project_name, + if is_plural { + "s" + } else {""}); + let mut answer = cx.prompt( + PromptLevel::Info, + prompt_text.as_str(), + &["Continue", "Cancel"], + ); cx.spawn(|search_view, mut cx| async move { - let (files_to_index, mut files_remaining_rx) = index_task.await?; - - search_view.update(&mut cx, |search_view, cx| { - cx.notify(); - search_view.semantic = Some(SemanticSearchState { - file_count: files_to_index, - outstanding_file_count: files_to_index, - _progress_task: cx.spawn(|search_view, mut cx| async move { - while let Some(count) = files_remaining_rx.recv().await { - search_view - .update(&mut cx, |search_view, cx| { - if let Some(semantic_search_state) = - &mut search_view.semantic - { - semantic_search_state.outstanding_file_count = - count; - cx.notify(); - if count == 0 { - return; - } - } - }) - .ok(); - } - }), + if answer.next().await == Some(0) { + search_view.update(&mut cx, |search_view, cx| { + search_view.semantic_permissioned = true; + search_view.index_project(cx); + })?; + anyhow::Ok(()) + } else { + search_view.update(&mut cx, |search_view, cx| { + search_view.activate_search_mode(SearchMode::Regex, cx); }); - })?; - anyhow::Ok(()) + anyhow::Ok(()) + } }) .detach_and_log_err(cx); + } else { + self.index_project(cx); } } SearchMode::Regex => { if !self.is_option_enabled(SearchOptions::REGEX) { self.toggle_search_option(SearchOptions::REGEX); } - self.semantic = None; + self.semantic_state = None; } SearchMode::Text => { if self.is_option_enabled(SearchOptions::REGEX) { self.toggle_search_option(SearchOptions::REGEX); } - self.semantic = None; + self.semantic_state = None; } } cx.notify(); @@ -856,12 +901,15 @@ impl ProjectSearchView { }) .detach(); let filters_enabled = false; + + // Check if Worktrees have all been previously indexed let mut this = ProjectSearchView { search_id: model.read(cx).search_id, model, query_editor, results_editor, - semantic: None, + semantic_state: None, + semantic_permissioned: false, search_options: options, panels_with_errors: HashSet::new(), active_match_index: None, @@ -953,7 +1001,7 @@ impl ProjectSearchView { let mode = self.current_mode; match mode { SearchMode::Semantic => { - if let Some(semantic) = &mut self.semantic { + if let Some(semantic) = &mut self.semantic_state { if semantic.outstanding_file_count > 0 { return; } @@ -1747,7 +1795,7 @@ impl View for ProjectSearchBar { .into_any() }; let search = _search.read(cx); - let is_semantic_disabled = search.semantic.is_none(); + let is_semantic_disabled = search.semantic_state.is_none(); let case_sensitive = if is_semantic_disabled { Some(self.render_option_button_icon( diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 851c656d9a..772dff9af7 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -16,7 +16,7 @@ use language::{Anchor, Buffer, Language, LanguageRegistry}; use parking_lot::Mutex; use parsing::{CodeContextRetriever, Document, PARSEABLE_ENTIRE_FILE_TYPES}; use postage::watch; -use project::{search::PathMatcher, Fs, Project, WorktreeId}; +use project::{project_settings, search::PathMatcher, Fs, Project, WorktreeId}; use smol::channel; use std::{ cmp::Ordering, @@ -49,9 +49,8 @@ pub fn init( .join(Path::new(RELEASE_CHANNEL_NAME.as_str())) .join("embeddings_db"); - if *RELEASE_CHANNEL == ReleaseChannel::Stable - || !settings::get::(cx).enabled - { + // This needs to be removed at some point before stable. + if *RELEASE_CHANNEL == ReleaseChannel::Stable { return; } From 799adf6c25e8a4dd938c4f6d0252b1d54a9751a3 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:18:14 +0200 Subject: [PATCH 032/119] Query Semantic Index Engine for permission state. Co-authored-by: Kyle --- crates/search/src/project_search.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 054eb5bee7..1a2320bda3 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,7 +2,7 @@ use crate::{ NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; -use anyhow::{Context, Result}; +use anyhow::Context; use collections::HashMap; use editor::{ items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, @@ -793,7 +793,7 @@ impl ProjectSearchView { } else { search_view.update(&mut cx, |search_view, cx| { search_view.activate_search_mode(SearchMode::Regex, cx); - }); + })?; anyhow::Ok(()) } }) @@ -851,7 +851,7 @@ impl ProjectSearchView { .detach(); let results_editor = cx.add_view(|cx| { - let mut editor = Editor::for_multibuffer(excerpts, Some(project), cx); + let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), cx); editor.set_searchable(false); editor }); @@ -901,6 +901,14 @@ impl ProjectSearchView { }) .detach(); let filters_enabled = false; + let semantic_permissioned = SemanticIndex::global(cx) + .and_then(|semantic| { + smol::block_on( + semantic.update(cx, |this, cx| this.project_previously_indexed(project, cx)), + ) + .ok() + }) + .unwrap_or_default(); // Check if Worktrees have all been previously indexed let mut this = ProjectSearchView { @@ -909,7 +917,7 @@ impl ProjectSearchView { query_editor, results_editor, semantic_state: None, - semantic_permissioned: false, + semantic_permissioned, search_options: options, panels_with_errors: HashSet::new(), active_match_index: None, From d157e3598d2431b6a6aadd4fdd16ffc0897b51ad Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:27:27 +0200 Subject: [PATCH 033/119] Query semantic_permissioned on demand. Co-authored-by: Kyle --- crates/search/src/project_search.rs | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 1a2320bda3..014ec13197 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -129,7 +129,7 @@ pub struct ProjectSearchView { query_editor: ViewHandle, results_editor: ViewHandle, semantic_state: Option, - semantic_permissioned: bool, + semantic_permissioned: Option, search_options: SearchOptions, panels_with_errors: HashSet, active_match_index: Option, @@ -763,7 +763,7 @@ impl ProjectSearchView { SearchMode::Semantic => { // let semantic_permissioned = self.semantic_permissioned.await; // if semantic_permissioned.is_ok_and(|permission| !permission) { - if !self.semantic_permissioned { + if !self.semantic_permissioned(cx) { // TODO: Change this to read from the project name let project = self.model.read(cx).project.clone(); let project_name = project @@ -786,7 +786,7 @@ impl ProjectSearchView { cx.spawn(|search_view, mut cx| async move { if answer.next().await == Some(0) { search_view.update(&mut cx, |search_view, cx| { - search_view.semantic_permissioned = true; + search_view.semantic_permissioned = Some(true); search_view.index_project(cx); })?; anyhow::Ok(()) @@ -901,14 +901,6 @@ impl ProjectSearchView { }) .detach(); let filters_enabled = false; - let semantic_permissioned = SemanticIndex::global(cx) - .and_then(|semantic| { - smol::block_on( - semantic.update(cx, |this, cx| this.project_previously_indexed(project, cx)), - ) - .ok() - }) - .unwrap_or_default(); // Check if Worktrees have all been previously indexed let mut this = ProjectSearchView { @@ -917,7 +909,7 @@ impl ProjectSearchView { query_editor, results_editor, semantic_state: None, - semantic_permissioned, + semantic_permissioned: None, search_options: options, panels_with_errors: HashSet::new(), active_match_index: None, @@ -930,7 +922,20 @@ impl ProjectSearchView { this.model_changed(cx); this } - + fn semantic_permissioned(&mut self, cx: &mut ViewContext) -> bool { + *self.semantic_permissioned.get_or_insert_with(|| { + SemanticIndex::global(cx) + .and_then(|semantic| { + let project = self.model.read(cx).project.clone(); + smol::block_on( + semantic + .update(cx, |this, cx| this.project_previously_indexed(project, cx)), + ) + .ok() + }) + .unwrap_or_default() + }) + } pub fn new_search_in_directory( workspace: &mut Workspace, dir_entry: &Entry, From 31fb5034180242d6036d377cab8fc670656a7cf6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:26:56 +0200 Subject: [PATCH 034/119] Rewrite permission queries (it no longer blocks) Co-authored-by: Kyle Co-authored-by: Max --- crates/search/src/project_search.rs | 99 +++++++++++---------- crates/semantic_index/src/semantic_index.rs | 14 --- 2 files changed, 51 insertions(+), 62 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 014ec13197..0fc13d992c 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,7 +2,7 @@ use crate::{ NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; -use anyhow::Context; +use anyhow::{Context, Result}; use collections::HashMap; use editor::{ items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, @@ -761,46 +761,50 @@ impl ProjectSearchView { match mode { SearchMode::Semantic => { - // let semantic_permissioned = self.semantic_permissioned.await; - // if semantic_permissioned.is_ok_and(|permission| !permission) { - if !self.semantic_permissioned(cx) { - // TODO: Change this to read from the project name - let project = self.model.read(cx).project.clone(); - let project_name = project - .read(cx) - .worktree_root_names(cx) - .collect::>() - .join("/"); - let is_plural = - project_name.chars().filter(|letter| *letter == '/').count() > 0; - let prompt_text = format!("Would you like to index the '{}' project{} for semantic search? This requires sending code to the OpenAI API", project_name, - if is_plural { - "s" - } else {""}); - let mut answer = cx.prompt( - PromptLevel::Info, - prompt_text.as_str(), - &["Continue", "Cancel"], - ); + let has_permission = self.semantic_permissioned(cx); + cx.spawn(|this, mut cx| async move { + let has_permission = has_permission.await?; + + if !has_permission { + let mut answer = this.update(&mut cx, |this, cx| { + let project = this.model.read(cx).project.clone(); + let project_name = project + .read(cx) + .worktree_root_names(cx) + .collect::>() + .join("/"); + let is_plural = + project_name.chars().filter(|letter| *letter == '/').count() > 0; + let prompt_text = format!("Would you like to index the '{}' project{} for semantic search? This requires sending code to the OpenAI API", project_name, + if is_plural { + "s" + } else {""}); + cx.prompt( + PromptLevel::Info, + prompt_text.as_str(), + &["Continue", "Cancel"], + ) + })?; - cx.spawn(|search_view, mut cx| async move { if answer.next().await == Some(0) { - search_view.update(&mut cx, |search_view, cx| { - search_view.semantic_permissioned = Some(true); - search_view.index_project(cx); + this.update(&mut cx, |this, cx| { + this.semantic_permissioned = Some(true); })?; - anyhow::Ok(()) } else { - search_view.update(&mut cx, |search_view, cx| { - search_view.activate_search_mode(SearchMode::Regex, cx); + this.update(&mut cx, |this, cx| { + this.semantic_permissioned = Some(false); + this.activate_search_mode(SearchMode::Regex, cx); })?; - anyhow::Ok(()) + return anyhow::Ok(()); } - }) - .detach_and_log_err(cx); - } else { - self.index_project(cx); - } + } + + this.update(&mut cx, |this, cx| { + this.index_project(cx); + })?; + + anyhow::Ok(()) + }).detach_and_log_err(cx); } SearchMode::Regex => { if !self.is_option_enabled(SearchOptions::REGEX) { @@ -922,19 +926,18 @@ impl ProjectSearchView { this.model_changed(cx); this } - fn semantic_permissioned(&mut self, cx: &mut ViewContext) -> bool { - *self.semantic_permissioned.get_or_insert_with(|| { - SemanticIndex::global(cx) - .and_then(|semantic| { - let project = self.model.read(cx).project.clone(); - smol::block_on( - semantic - .update(cx, |this, cx| this.project_previously_indexed(project, cx)), - ) - .ok() - }) - .unwrap_or_default() - }) + + fn semantic_permissioned(&mut self, cx: &mut ViewContext) -> Task> { + if let Some(value) = self.semantic_permissioned { + return Task::ready(Ok(value)); + } + + SemanticIndex::global(cx) + .map(|semantic| { + let project = self.model.read(cx).project.clone(); + semantic.update(cx, |this, cx| this.project_previously_indexed(project, cx)) + }) + .unwrap_or(Task::ready(Ok(false))) } pub fn new_search_in_directory( workspace: &mut Workspace, diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 772dff9af7..50b871d454 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -500,26 +500,12 @@ impl SemanticIndex { project: ModelHandle, cx: &mut ModelContext, ) -> Task> { - let worktree_scans_complete = project - .read(cx) - .worktrees(cx) - .map(|worktree| { - let scan_complete = worktree.read(cx).as_local().unwrap().scan_complete(); - async move { - scan_complete.await; - } - }) - .collect::>(); - let worktrees_indexed_previously = project .read(cx) .worktrees(cx) .map(|worktree| self.worktree_previously_indexed(worktree.read(cx).abs_path())) .collect::>(); - cx.spawn(|_, _cx| async move { - futures::future::join_all(worktree_scans_complete).await; - let worktree_indexed_previously = futures::future::join_all(worktrees_indexed_previously).await; From 4658bc610c1bc6fe31ea12ef0e9ecbe87d5f3e4c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:35:10 +0200 Subject: [PATCH 035/119] Update styling of nav buttons (width, corner_radius) Co-authored-by: Kyle --- styles/src/style_tree/tab_bar.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index 73b9e82d0c..81e1f7adf3 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -88,8 +88,8 @@ export default function tab_bar(): any { base: { color: foreground(theme.highest, "on"), icon_width: 12, - button_width: 24, - corner_radius: 6, + + button_width: active_pane_active_tab.height, }, state: { hovered: { From c14a99d8fa14aaea960c38bc46d622d2cee9056b Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 3 Aug 2023 17:02:46 -0400 Subject: [PATCH 036/119] updated project_search text --- crates/search/src/project_search.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0fc13d992c..74635bf53c 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -398,7 +398,7 @@ impl View for ProjectSearchView { match current_mode { SearchMode::Text => Cow::Borrowed("Text search all files and folders"), SearchMode::Semantic => { - Cow::Borrowed("Search all files and folders using Natural Language") + Cow::Borrowed("Search all code objects using Natural Language") } SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), } @@ -429,10 +429,13 @@ impl View for ProjectSearchView { } else { match current_mode { SearchMode::Semantic => vec![ + "".to_owned(), semantic_status, - "ex. 'list all available languages'".to_owned(), + "Simply explain the code you are looking to find.".to_owned(), + "ex. 'prompt user for permissions to index their project'".to_owned(), ], _ => vec![ + "".to_owned(), "Include/exclude specific paths with the filter option.".to_owned(), "Matching exact word and/or casing is available too.".to_owned(), ], From 13a6b65a35b886b9ac380e56354acb7e562bcfab Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 02:33:29 +0200 Subject: [PATCH 037/119] Focus filters when enabled --- crates/search/src/project_search.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 74635bf53c..97edb1b1aa 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1507,6 +1507,11 @@ impl ProjectSearchBar { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { search_view.filters_enabled = !search_view.filters_enabled; + if search_view.filters_enabled { + cx.focus(&search_view.included_files_editor); + } else { + cx.focus(&search_view.query_editor); + } search_view .included_files_editor .update(cx, |_, cx| cx.notify()); From 50d03ffc8c3bd179845ea04d63acf53983bd8f8f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:24:57 +0200 Subject: [PATCH 038/119] Revert "Focus filters when enabled" This reverts commit 13a6b65a35b886b9ac380e56354acb7e562bcfab. --- crates/search/src/project_search.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 97edb1b1aa..74635bf53c 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1507,11 +1507,6 @@ impl ProjectSearchBar { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { search_view.filters_enabled = !search_view.filters_enabled; - if search_view.filters_enabled { - cx.focus(&search_view.included_files_editor); - } else { - cx.focus(&search_view.query_editor); - } search_view .included_files_editor .update(cx, |_, cx| cx.notify()); From 792f29e288597e3432a26cae6895d8c57767bbcb Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:25:18 +0200 Subject: [PATCH 039/119] Refresh windows on toggle_filters. --- crates/search/src/project_search.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 74635bf53c..651ec3a537 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1513,6 +1513,7 @@ impl ProjectSearchBar { search_view .excluded_files_editor .update(cx, |_, cx| cx.notify()); + cx.refresh_windows(); cx.notify(); }); cx.notify(); From fcefb37ca07635ae99023c8105fe36b927ae1cdd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:54:50 +0200 Subject: [PATCH 040/119] Prevent modes from being vertically centered when row_count changes --- crates/search/src/project_search.rs | 35 +++++++++++++++++------------ styles/src/style_tree/search.ts | 4 +++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 651ec3a537..d7ca0b6b46 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1954,20 +1954,27 @@ impl View for ProjectSearchBar { .flex(2., true), ) .with_child( - Flex::column().with_child( - Flex::row() - .align_children_center() - .with_child(self.render_search_mode_button(SearchMode::Text, cx)) - .with_children(semantic_index) - .with_child(self.render_search_mode_button(SearchMode::Regex, cx)) - .constrained() - .with_height(theme.workspace.toolbar.height) - .contained() - .with_style(theme.search.container) - .aligned() - .right() - .flex(1., true), - ), + Flex::column() + .with_child( + Flex::row() + .align_children_center() + .with_child(self.render_search_mode_button(SearchMode::Text, cx)) + .with_children(semantic_index) + .with_child(self.render_search_mode_button(SearchMode::Regex, cx)) + .constrained() + .with_height(theme.workspace.toolbar.height) + .contained() + .with_style(theme.search.container) + .aligned() + .right() + .flex(1., true), + ) + .with_children( + _search + .read(cx) + .filters_enabled + .then(|| Flex::row().flex(1., true)), + ), ) .contained() .flex_float() diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 370d12c6f0..3565db1f9f 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -44,7 +44,7 @@ export default function search(): any { base: { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - + corner_radius: 2, border: border(theme.highest, "on"), padding: { @@ -130,6 +130,8 @@ export default function search(): any { padding: { left: 12, right: 12, + top: 3, + bottom: 3, }, }, include_exclude_inputs: { From 2c0e3886a5d42e38360e5b82b1a63c16b061cad0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:38:53 +0200 Subject: [PATCH 041/119] Align search bar in the middle --- crates/search/src/project_search.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index d7ca0b6b46..2f19c91868 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1950,8 +1950,9 @@ impl View for ProjectSearchBar { .contained() .with_style(theme.search.container) .aligned() + .left() .top() - .flex(2., true), + .flex(1., true), ) .with_child( Flex::column() From 82eb6d8bc3ff327a6b598155f2601b569ce28073 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 17:35:59 +0200 Subject: [PATCH 042/119] Change styling of match nav buttons Co-authored-by: Kyle --- crates/search/src/project_search.rs | 83 ++++++++++++++++++++++------- crates/theme/src/theme.rs | 1 + styles/src/style_tree/search.ts | 28 ++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 2f19c91868..6a000091e4 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1531,6 +1531,7 @@ impl ProjectSearchBar { ) -> AnyElement { let action: Box; let tooltip; + match direction { Direction::Prev => { action = Box::new(SelectPrevMatch); @@ -1546,10 +1547,51 @@ impl ProjectSearchBar { enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); - let style = theme.search.option_button.inactive_state().style_for(state); - Label::new(icon, style.text.clone()) + let mut style = theme.search.nav_button.style_for(state).clone(); + + match direction { + Direction::Prev => style.container.border.left = false, + Direction::Next => style.container.border.right = false, + }; + let mut label = Label::new(icon, style.label.clone()) .contained() - .with_style(style.container) + .with_style(style.container.clone()); + match direction { + Direction::Prev => Flex::row() + .with_child( + ButtonSide::left( + style + .clone() + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .with_child(label) + .constrained() + .with_height(theme.workspace.toolbar.height), + Direction::Next => Flex::row() + .with_child(label) + .with_child( + ButtonSide::right( + style + .clone() + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .constrained() + .with_height(theme.workspace.toolbar.height), + } }) .on_click(MouseButton::Left, move |_, this, cx| { if let Some(search) = this.active_project_search.as_ref() { @@ -1911,7 +1953,26 @@ impl View for ProjectSearchBar { Flex::row() .with_child( Flex::column() - .with_child(Flex::row().with_children(matches).aligned().left()) + .with_child( + Flex::row() + .with_child( + Flex::row() + .with_child(self.render_nav_button( + "<", + Direction::Prev, + cx, + )) + .with_child(self.render_nav_button( + ">", + Direction::Next, + cx, + )) + .aligned(), + ) + .with_children(matches) + .aligned() + .left(), + ) .flex(1., true), ) .with_child( @@ -1929,20 +1990,6 @@ impl View for ProjectSearchBar { .with_max_width(theme.search.editor.max_width) .flex(1., false), ) - .with_child( - Flex::row() - .with_child(self.render_nav_button( - "<", - Direction::Prev, - cx, - )) - .with_child(self.render_nav_button( - ">", - Direction::Next, - cx, - )) - .aligned(), - ) .contained() .with_margin_bottom(row_spacing), ) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index da6a188527..1107906ca7 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -389,6 +389,7 @@ pub struct Search { pub editor_icon: IconStyle, pub mode_button: Toggleable>, pub mode_filling_width: f32, + pub nav_button: Interactive, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 3565db1f9f..fb9b356591 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -225,5 +225,33 @@ export default function search(): any { }, }), mode_filling_width: 4.0, + nav_button: interactive({ + base: { + text: text(theme.highest, "mono", "on"), + background: background(theme.highest, "on"), + corner_radius: 2, + border: border(theme.highest, "on"), + + padding: { + bottom: 6, + left: 6, + right: 6, + top: 6, + }, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "on", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: border(theme.highest, "on", "hovered"), + }, + clicked: { + ...text(theme.highest, "mono", "on", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: border(theme.highest, "on", "pressed"), + }, + }, + }), + } } From 8fa082c28b3d7c8da6beef1f2e524e1523419fe6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 17:49:54 +0200 Subject: [PATCH 043/119] Center the query editor (for real now) Co-authored-by: Kyle --- crates/search/src/project_search.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6a000091e4..6b303d2ce8 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1973,10 +1973,12 @@ impl View for ProjectSearchBar { .aligned() .left(), ) + .contained() .flex(1., true), ) .with_child( Flex::column() + .align_children_center() .with_child( Flex::row() .with_child( @@ -1997,9 +1999,8 @@ impl View for ProjectSearchBar { .contained() .with_style(theme.search.container) .aligned() - .left() .top() - .flex(1., true), + .flex(1., false), ) .with_child( Flex::column() @@ -2022,7 +2023,9 @@ impl View for ProjectSearchBar { .read(cx) .filters_enabled .then(|| Flex::row().flex(1., true)), - ), + ) + .contained() + .flex(1., true), ) .contained() .flex_float() From de8e1852a8eef049ea1c51b52159d33f1f489692 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:21:46 +0200 Subject: [PATCH 044/119] Align match count Co-authored-by: Kyle --- crates/search/src/project_search.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6b303d2ce8..e746d75cdd 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1918,8 +1918,6 @@ impl View for ProjectSearchBar { ) .contained() .with_style(theme.search.match_index.container) - .aligned() - .left() }); let filters = search.filters_enabled.then(|| { @@ -1955,6 +1953,7 @@ impl View for ProjectSearchBar { Flex::column() .with_child( Flex::row() + .align_children_center() .with_child( Flex::row() .with_child(self.render_nav_button( From 9889449a81f2f552c807ed687d4312acc34eb0f0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:54:27 +0200 Subject: [PATCH 045/119] Adjust row count for project search. Instead of using the same row count as for the breadcrumbs, we double the height so that there's some space for padding. Co-authored-by: Kyle --- crates/search/src/project_search.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e746d75cdd..e2ad89117f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1970,6 +1970,7 @@ impl View for ProjectSearchBar { ) .with_children(matches) .aligned() + .top() .left(), ) .contained() @@ -2015,6 +2016,7 @@ impl View for ProjectSearchBar { .with_style(theme.search.container) .aligned() .right() + .top() .flex(1., true), ) .with_children( @@ -2027,6 +2029,7 @@ impl View for ProjectSearchBar { .flex(1., true), ) .contained() + .with_uniform_padding(theme.workspace.toolbar.height / 3.) .flex_float() .into_any_named("project search") } else { @@ -2060,9 +2063,9 @@ impl ToolbarItemView for ProjectSearchBar { .as_ref() .map(|search| { let offset = search.read(cx).filters_enabled as usize; - 1 + offset + 2 + offset }) - .unwrap_or_else(|| 1) + .unwrap_or_else(|| 2) } } From 7a1f40405a9785a234f69da5d17661a9db455e46 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:22:10 +0200 Subject: [PATCH 046/119] Add dismiss button to project search --- crates/search/src/buffer_search.rs | 42 +++++------------------------ crates/search/src/project_search.rs | 17 +++++++++++- crates/search/src/search.rs | 1 + crates/search/src/search_bar.rs | 34 +++++++++++++++++++++++ 4 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 crates/search/src/search_bar.rs diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 45842aa561..baba45e0f6 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -222,7 +222,12 @@ impl View for BufferSearchBar { ) .flex(1., true), ) - .with_child(self.render_close_button(&theme.search, cx)) + .with_child(super::search_bar::render_close_button( + &theme.search, + cx, + |_, this, cx| this.dismiss(&Default::default(), cx), + Some(Box::new(Dismiss)), + )) .contained() .with_style(theme.search.container) .into_any_named("search bar") @@ -518,41 +523,6 @@ impl BufferSearchBar { .into_any() } - fn render_close_button( - &self, - theme: &theme::Search, - cx: &mut ViewContext, - ) -> AnyElement { - let tooltip = "Dismiss Buffer Search"; - let tooltip_style = theme::current(cx).tooltip.clone(); - - enum CloseButton {} - MouseEventHandler::::new(0, cx, |state, _| { - let style = theme.dismiss_button.style_for(state); - Svg::new("icons/x_mark_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .constrained() - .with_width(style.button_width) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.dismiss(&Default::default(), cx) - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - 0, - tooltip.to_string(), - Some(Box::new(Dismiss)), - tooltip_style, - cx, - ) - .into_any() - } - fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { let mut propagate_action = true; if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e2ad89117f..53b54192da 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -362,10 +362,12 @@ impl ProjectSearch { } } +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ViewEvent { UpdateTab, Activate, EditorEvent(editor::Event), + Dismiss, } impl Entity for ProjectSearchView { @@ -547,7 +549,9 @@ impl Item for ProjectSearchView { .then(|| query_text.into()) .or_else(|| Some("Project Search".into())) } - + fn should_close_item_on_event(event: &Self::Event) -> bool { + event == &Self::Event::Dismiss + } fn act_as_type<'a>( &'a self, type_id: TypeId, @@ -679,6 +683,7 @@ impl Item for ProjectSearchView { smallvec::smallvec![ItemEvent::UpdateBreadcrumbs, ItemEvent::UpdateTab] } ViewEvent::EditorEvent(editor_event) => Editor::to_item_events(editor_event), + ViewEvent::Dismiss => smallvec::smallvec![ItemEvent::CloseItem], _ => SmallVec::new(), } } @@ -2010,6 +2015,16 @@ impl View for ProjectSearchBar { .with_child(self.render_search_mode_button(SearchMode::Text, cx)) .with_children(semantic_index) .with_child(self.render_search_mode_button(SearchMode::Regex, cx)) + .with_child(super::search_bar::render_close_button( + &theme.search, + cx, + |_, this, cx| { + if let Some(search) = this.active_project_search.as_mut() { + search.update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) + } + }, + None, + )) .constrained() .with_height(theme.workspace.toolbar.height) .contained() diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index f1711afec2..7940490de9 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -7,6 +7,7 @@ use smallvec::SmallVec; pub mod buffer_search; pub mod project_search; +pub(crate) mod search_bar; pub fn init(cx: &mut AppContext) { buffer_search::init(cx); diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs new file mode 100644 index 0000000000..5119e7866e --- /dev/null +++ b/crates/search/src/search_bar.rs @@ -0,0 +1,34 @@ +use gpui::{ + elements::{MouseEventHandler, Svg}, + platform::{CursorStyle, MouseButton}, + scene::MouseClick, + Action, AnyElement, Element, EventContext, View, ViewContext, +}; + +pub(super) fn render_close_button( + theme: &theme::Search, + cx: &mut ViewContext, + on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, + dismiss_action: Option>, +) -> AnyElement { + let tooltip = "Dismiss Buffer Search"; + let tooltip_style = theme::current(cx).tooltip.clone(); + + enum CloseButton {} + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.dismiss_button.style_for(state); + Svg::new("icons/x_mark_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .contained() + .with_style(style.container) + }) + .on_click(MouseButton::Left, on_click) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::(0, tooltip.to_string(), dismiss_action, tooltip_style, cx) + .into_any() +} From 95891d28d8ab51c8f7da4b33da104e869654e81c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:14:39 +0200 Subject: [PATCH 047/119] Move SearchMode and SearchHistory to separate modules --- crates/search/src/buffer_search.rs | 5 +- crates/search/src/history.rs | 184 ++++++++++++++++++++++++++ crates/search/src/mode.rs | 73 +++++++++++ crates/search/src/project_search.rs | 86 +----------- crates/search/src/search.rs | 194 +--------------------------- 5 files changed, 269 insertions(+), 273 deletions(-) create mode 100644 crates/search/src/history.rs create mode 100644 crates/search/src/mode.rs diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index baba45e0f6..52dd943625 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1,6 +1,6 @@ use crate::{ - NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectAllMatches, - SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, ToggleWholeWord, + history::SearchHistory, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, + SelectAllMatches, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use collections::HashMap; use editor::Editor; @@ -50,7 +50,6 @@ pub fn init(cx: &mut AppContext) { cx.add_action(BufferSearchBar::previous_history_query); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); - add_toggle_option_action::(SearchOptions::REGEX, cx); } fn add_toggle_option_action(option: SearchOptions, cx: &mut AppContext) { diff --git a/crates/search/src/history.rs b/crates/search/src/history.rs new file mode 100644 index 0000000000..6b06c60293 --- /dev/null +++ b/crates/search/src/history.rs @@ -0,0 +1,184 @@ +use smallvec::SmallVec; +const SEARCH_HISTORY_LIMIT: usize = 20; + +#[derive(Default, Debug, Clone)] +pub struct SearchHistory { + history: SmallVec<[String; SEARCH_HISTORY_LIMIT]>, + selected: Option, +} + +impl SearchHistory { + pub fn add(&mut self, search_string: String) { + if let Some(i) = self.selected { + if search_string == self.history[i] { + return; + } + } + + if let Some(previously_searched) = self.history.last_mut() { + if search_string.find(previously_searched.as_str()).is_some() { + *previously_searched = search_string; + self.selected = Some(self.history.len() - 1); + return; + } + } + + self.history.push(search_string); + if self.history.len() > SEARCH_HISTORY_LIMIT { + self.history.remove(0); + } + self.selected = Some(self.history.len() - 1); + } + + pub fn next(&mut self) -> Option<&str> { + let history_size = self.history.len(); + if history_size == 0 { + return None; + } + + let selected = self.selected?; + if selected == history_size - 1 { + return None; + } + let next_index = selected + 1; + self.selected = Some(next_index); + Some(&self.history[next_index]) + } + + pub fn current(&self) -> Option<&str> { + Some(&self.history[self.selected?]) + } + + pub fn previous(&mut self) -> Option<&str> { + let history_size = self.history.len(); + if history_size == 0 { + return None; + } + + let prev_index = match self.selected { + Some(selected_index) => { + if selected_index == 0 { + return None; + } else { + selected_index - 1 + } + } + None => history_size - 1, + }; + + self.selected = Some(prev_index); + Some(&self.history[prev_index]) + } + + pub fn reset_selection(&mut self) { + self.selected = None; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add() { + let mut search_history = SearchHistory::default(); + assert_eq!( + search_history.current(), + None, + "No current selection should be set fo the default search history" + ); + + search_history.add("rust".to_string()); + assert_eq!( + search_history.current(), + Some("rust"), + "Newly added item should be selected" + ); + + // check if duplicates are not added + search_history.add("rust".to_string()); + assert_eq!( + search_history.history.len(), + 1, + "Should not add a duplicate" + ); + assert_eq!(search_history.current(), Some("rust")); + + // check if new string containing the previous string replaces it + search_history.add("rustlang".to_string()); + assert_eq!( + search_history.history.len(), + 1, + "Should replace previous item if it's a substring" + ); + assert_eq!(search_history.current(), Some("rustlang")); + + // push enough items to test SEARCH_HISTORY_LIMIT + for i in 0..SEARCH_HISTORY_LIMIT * 2 { + search_history.add(format!("item{i}")); + } + assert!(search_history.history.len() <= SEARCH_HISTORY_LIMIT); + } + + #[test] + fn test_next_and_previous() { + let mut search_history = SearchHistory::default(); + assert_eq!( + search_history.next(), + None, + "Default search history should not have a next item" + ); + + search_history.add("Rust".to_string()); + assert_eq!(search_history.next(), None); + search_history.add("JavaScript".to_string()); + assert_eq!(search_history.next(), None); + search_history.add("TypeScript".to_string()); + assert_eq!(search_history.next(), None); + + assert_eq!(search_history.current(), Some("TypeScript")); + + assert_eq!(search_history.previous(), Some("JavaScript")); + assert_eq!(search_history.current(), Some("JavaScript")); + + assert_eq!(search_history.previous(), Some("Rust")); + assert_eq!(search_history.current(), Some("Rust")); + + assert_eq!(search_history.previous(), None); + assert_eq!(search_history.current(), Some("Rust")); + + assert_eq!(search_history.next(), Some("JavaScript")); + assert_eq!(search_history.current(), Some("JavaScript")); + + assert_eq!(search_history.next(), Some("TypeScript")); + assert_eq!(search_history.current(), Some("TypeScript")); + + assert_eq!(search_history.next(), None); + assert_eq!(search_history.current(), Some("TypeScript")); + } + + #[test] + fn test_reset_selection() { + let mut search_history = SearchHistory::default(); + search_history.add("Rust".to_string()); + search_history.add("JavaScript".to_string()); + search_history.add("TypeScript".to_string()); + + assert_eq!(search_history.current(), Some("TypeScript")); + search_history.reset_selection(); + assert_eq!(search_history.current(), None); + assert_eq!( + search_history.previous(), + Some("TypeScript"), + "Should start from the end after reset on previous item query" + ); + + search_history.previous(); + assert_eq!(search_history.current(), Some("JavaScript")); + search_history.previous(); + assert_eq!(search_history.current(), Some("Rust")); + + search_history.reset_selection(); + assert_eq!(search_history.current(), None); + } +} diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs new file mode 100644 index 0000000000..b27365b8ed --- /dev/null +++ b/crates/search/src/mode.rs @@ -0,0 +1,73 @@ +use gpui::Action; + +use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode}; +// TODO: Update the default search mode to get from config +#[derive(Copy, Clone, Default, PartialEq)] +pub(crate) enum SearchMode { + #[default] + Text, + Semantic, + Regex, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum Side { + Left, + Right, +} + +impl SearchMode { + pub(crate) fn label(&self) -> &'static str { + match self { + SearchMode::Text => "Text", + SearchMode::Semantic => "Semantic", + SearchMode::Regex => "Regex", + } + } + + pub(crate) fn region_id(&self) -> usize { + match self { + SearchMode::Text => 3, + SearchMode::Semantic => 4, + SearchMode::Regex => 5, + } + } + + pub(crate) fn tooltip_text(&self) -> &'static str { + match self { + SearchMode::Text => "Activate Text Search", + SearchMode::Semantic => "Activate Semantic Search", + SearchMode::Regex => "Activate Regex Search", + } + } + + pub(crate) fn activate_action(&self) -> Box { + match self { + SearchMode::Text => Box::new(ActivateTextMode), + SearchMode::Semantic => Box::new(ActivateSemanticMode), + SearchMode::Regex => Box::new(ActivateRegexMode), + } + } + + pub(crate) fn border_left(&self) -> bool { + match self { + SearchMode::Text => false, + _ => true, + } + } + + pub(crate) fn border_right(&self) -> bool { + match self { + SearchMode::Regex => false, + _ => true, + } + } + + pub(crate) fn button_side(&self) -> Option { + match self { + SearchMode::Text => Some(Side::Left), + SearchMode::Semantic => None, + SearchMode::Regex => Some(Side::Right), + } + } +} diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 53b54192da..c3e1a44afa 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,5 +1,7 @@ use crate::{ - NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectNextMatch, + history::SearchHistory, + mode::{SearchMode, Side}, + CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use anyhow::{Context, Result}; @@ -49,16 +51,7 @@ use workspace::{ actions!( project_search, - [ - SearchInNew, - ToggleFocus, - NextField, - CycleMode, - ToggleFilters, - ActivateTextMode, - ActivateSemanticMode, - ActivateRegexMode - ] + [SearchInNew, ToggleFocus, NextField, ToggleFilters,] ); #[derive(Default)] @@ -147,77 +140,6 @@ struct SemanticSearchState { _progress_task: Task<()>, } -// TODO: Update the default search mode to get from config -#[derive(Copy, Clone, Default, PartialEq)] -enum SearchMode { - #[default] - Text, - Semantic, - Regex, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum Side { - Left, - Right, -} - -impl SearchMode { - fn label(&self) -> &'static str { - match self { - SearchMode::Text => "Text", - SearchMode::Semantic => "Semantic", - SearchMode::Regex => "Regex", - } - } - - fn region_id(&self) -> usize { - match self { - SearchMode::Text => 3, - SearchMode::Semantic => 4, - SearchMode::Regex => 5, - } - } - - fn tooltip_text(&self) -> &'static str { - match self { - SearchMode::Text => "Activate Text Search", - SearchMode::Semantic => "Activate Semantic Search", - SearchMode::Regex => "Activate Regex Search", - } - } - - fn activate_action(&self) -> Box { - match self { - SearchMode::Text => Box::new(ActivateTextMode), - SearchMode::Semantic => Box::new(ActivateSemanticMode), - SearchMode::Regex => Box::new(ActivateRegexMode), - } - } - - fn border_left(&self) -> bool { - match self { - SearchMode::Text => false, - _ => true, - } - } - - fn border_right(&self) -> bool { - match self { - SearchMode::Regex => false, - _ => true, - } - } - - fn button_side(&self) -> Option { - match self { - SearchMode::Text => Some(Side::Left), - SearchMode::Semantic => None, - SearchMode::Regex => Some(Side::Right), - } - } -} - pub struct ProjectSearchBar { active_project_search: Option>, subscription: Option, diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 7940490de9..8aa03bdc35 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -3,9 +3,10 @@ pub use buffer_search::BufferSearchBar; use gpui::{actions, Action, AppContext}; use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; -use smallvec::SmallVec; pub mod buffer_search; +mod history; +mod mode; pub mod project_search; pub(crate) mod search_bar; @@ -17,14 +18,17 @@ pub fn init(cx: &mut AppContext) { actions!( search, [ + CycleMode, ToggleWholeWord, ToggleCaseSensitive, - ToggleRegex, SelectNextMatch, SelectPrevMatch, SelectAllMatches, NextHistoryQuery, PreviousHistoryQuery, + ActivateTextMode, + ActivateSemanticMode, + ActivateRegexMode ] ); @@ -43,7 +47,6 @@ impl SearchOptions { match *self { SearchOptions::WHOLE_WORD => "Match Whole Word", SearchOptions::CASE_SENSITIVE => "Match Case", - SearchOptions::REGEX => "Use Regular Expression", _ => panic!("{:?} is not a named SearchOption", self), } } @@ -52,7 +55,6 @@ impl SearchOptions { match *self { SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord), SearchOptions::CASE_SENSITIVE => Box::new(ToggleCaseSensitive), - SearchOptions::REGEX => Box::new(ToggleRegex), _ => panic!("{:?} is not a named SearchOption", self), } } @@ -69,187 +71,3 @@ impl SearchOptions { options } } - -const SEARCH_HISTORY_LIMIT: usize = 20; - -#[derive(Default, Debug, Clone)] -pub struct SearchHistory { - history: SmallVec<[String; SEARCH_HISTORY_LIMIT]>, - selected: Option, -} - -impl SearchHistory { - pub fn add(&mut self, search_string: String) { - if let Some(i) = self.selected { - if search_string == self.history[i] { - return; - } - } - - if let Some(previously_searched) = self.history.last_mut() { - if search_string.find(previously_searched.as_str()).is_some() { - *previously_searched = search_string; - self.selected = Some(self.history.len() - 1); - return; - } - } - - self.history.push(search_string); - if self.history.len() > SEARCH_HISTORY_LIMIT { - self.history.remove(0); - } - self.selected = Some(self.history.len() - 1); - } - - pub fn next(&mut self) -> Option<&str> { - let history_size = self.history.len(); - if history_size == 0 { - return None; - } - - let selected = self.selected?; - if selected == history_size - 1 { - return None; - } - let next_index = selected + 1; - self.selected = Some(next_index); - Some(&self.history[next_index]) - } - - pub fn current(&self) -> Option<&str> { - Some(&self.history[self.selected?]) - } - - pub fn previous(&mut self) -> Option<&str> { - let history_size = self.history.len(); - if history_size == 0 { - return None; - } - - let prev_index = match self.selected { - Some(selected_index) => { - if selected_index == 0 { - return None; - } else { - selected_index - 1 - } - } - None => history_size - 1, - }; - - self.selected = Some(prev_index); - Some(&self.history[prev_index]) - } - - pub fn reset_selection(&mut self) { - self.selected = None; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_add() { - let mut search_history = SearchHistory::default(); - assert_eq!( - search_history.current(), - None, - "No current selection should be set fo the default search history" - ); - - search_history.add("rust".to_string()); - assert_eq!( - search_history.current(), - Some("rust"), - "Newly added item should be selected" - ); - - // check if duplicates are not added - search_history.add("rust".to_string()); - assert_eq!( - search_history.history.len(), - 1, - "Should not add a duplicate" - ); - assert_eq!(search_history.current(), Some("rust")); - - // check if new string containing the previous string replaces it - search_history.add("rustlang".to_string()); - assert_eq!( - search_history.history.len(), - 1, - "Should replace previous item if it's a substring" - ); - assert_eq!(search_history.current(), Some("rustlang")); - - // push enough items to test SEARCH_HISTORY_LIMIT - for i in 0..SEARCH_HISTORY_LIMIT * 2 { - search_history.add(format!("item{i}")); - } - assert!(search_history.history.len() <= SEARCH_HISTORY_LIMIT); - } - - #[test] - fn test_next_and_previous() { - let mut search_history = SearchHistory::default(); - assert_eq!( - search_history.next(), - None, - "Default search history should not have a next item" - ); - - search_history.add("Rust".to_string()); - assert_eq!(search_history.next(), None); - search_history.add("JavaScript".to_string()); - assert_eq!(search_history.next(), None); - search_history.add("TypeScript".to_string()); - assert_eq!(search_history.next(), None); - - assert_eq!(search_history.current(), Some("TypeScript")); - - assert_eq!(search_history.previous(), Some("JavaScript")); - assert_eq!(search_history.current(), Some("JavaScript")); - - assert_eq!(search_history.previous(), Some("Rust")); - assert_eq!(search_history.current(), Some("Rust")); - - assert_eq!(search_history.previous(), None); - assert_eq!(search_history.current(), Some("Rust")); - - assert_eq!(search_history.next(), Some("JavaScript")); - assert_eq!(search_history.current(), Some("JavaScript")); - - assert_eq!(search_history.next(), Some("TypeScript")); - assert_eq!(search_history.current(), Some("TypeScript")); - - assert_eq!(search_history.next(), None); - assert_eq!(search_history.current(), Some("TypeScript")); - } - - #[test] - fn test_reset_selection() { - let mut search_history = SearchHistory::default(); - search_history.add("Rust".to_string()); - search_history.add("JavaScript".to_string()); - search_history.add("TypeScript".to_string()); - - assert_eq!(search_history.current(), Some("TypeScript")); - search_history.reset_selection(); - assert_eq!(search_history.current(), None); - assert_eq!( - search_history.previous(), - Some("TypeScript"), - "Should start from the end after reset on previous item query" - ); - - search_history.previous(); - assert_eq!(search_history.current(), Some("JavaScript")); - search_history.previous(); - assert_eq!(search_history.current(), Some("Rust")); - - search_history.reset_selection(); - assert_eq!(search_history.current(), None); - } -} From d497f279f09e6bb84dd1c3d516bb4da65a877061 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:24:26 +0200 Subject: [PATCH 048/119] Move ButtonSide to new module --- crates/search/src/elements.rs | 146 ++++++++++++++++++++++++++++ crates/search/src/project_search.rs | 146 ++-------------------------- crates/search/src/search.rs | 1 + 3 files changed, 153 insertions(+), 140 deletions(-) create mode 100644 crates/search/src/elements.rs diff --git a/crates/search/src/elements.rs b/crates/search/src/elements.rs new file mode 100644 index 0000000000..cdc203983f --- /dev/null +++ b/crates/search/src/elements.rs @@ -0,0 +1,146 @@ +use std::ops::Range; + +use gpui::color::Color; +use gpui::geometry::rect::RectF; +use gpui::geometry::vector::IntoVector2F; +use gpui::json::{self, ToJson}; +use gpui::{scene::Path, LayoutContext}; +use gpui::{Element, SceneBuilder, View, ViewContext}; + +type CreatePath = fn(RectF, Color) -> Path; +type AdjustBorder = fn(RectF, f32) -> RectF; +type BorderThickness = f32; + +pub(crate) struct ButtonSide { + color: Color, + factory: CreatePath, + /// After the outline is drawn with border color, + /// the drawing bounds have to be adjusted by different factors in different dimensions. + border_adjustment: AdjustBorder, + border: Option<(BorderThickness, Color)>, +} + +impl ButtonSide { + fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { + Self { + color, + factory, + border_adjustment, + border: None, + } + } + pub fn with_border(mut self, width: f32, color: Color) -> Self { + self.border = Some((width, color)); + self + } + pub fn left(color: Color) -> Self { + Self::new(color, left_button_side, left_button_border_adjust) + } + pub fn right(color: Color) -> Self { + Self::new(color, right_button_side, right_button_border_adjust) + } +} + +fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut lower_right = bounds.clone().lower_right(); + lower_right.set_x(lower_right.x() + width.x()); + RectF::from_points(bounds.origin() + width, lower_right) +} +fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { + let width = width.into_vector_2f(); + let mut origin = bounds.clone().origin(); + origin.set_x(origin.x() - width.x()); + RectF::from_points(origin, bounds.lower_right() - width) +} +fn left_button_side(bounds: RectF, color: Color) -> Path { + use gpui::geometry::PathBuilder; + let mut path = PathBuilder::new(); + path.reset(bounds.lower_right()); + path.line_to(bounds.upper_right()); + let mut middle_point = bounds.origin(); + let distance_to_line = (middle_point.y() - bounds.lower_left().y()) / 4.; + middle_point.set_y(middle_point.y() - distance_to_line); + path.curve_to(middle_point, bounds.origin()); + let mut target = bounds.lower_left(); + target.set_y(target.y() + distance_to_line); + path.line_to(target); + path.curve_to(bounds.lower_right(), bounds.lower_left()); + path.build(color, None) +} + +fn right_button_side(bounds: RectF, color: Color) -> Path { + use gpui::geometry::PathBuilder; + let mut path = PathBuilder::new(); + path.reset(bounds.lower_left()); + path.line_to(bounds.origin()); + let mut middle_point = bounds.upper_right(); + let distance_to_line = (middle_point.y() - bounds.lower_right().y()) / 4.; + middle_point.set_y(middle_point.y() - distance_to_line); + path.curve_to(middle_point, bounds.upper_right()); + let mut target = bounds.lower_right(); + target.set_y(target.y() + distance_to_line); + path.line_to(target); + path.curve_to(bounds.lower_left(), bounds.lower_right()); + path.build(color, None) +} + +impl Element for ButtonSide { + type LayoutState = (); + + type PaintState = (); + + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + _: &mut V, + _: &mut LayoutContext, + ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { + (constraint.max, ()) + } + + fn paint( + &mut self, + scene: &mut SceneBuilder, + bounds: RectF, + _: RectF, + _: &mut Self::LayoutState, + _: &mut V, + _: &mut ViewContext, + ) -> Self::PaintState { + let mut bounds = bounds; + if let Some((border_width, border_color)) = self.border.as_ref() { + scene.push_path((self.factory)(bounds, border_color.clone())); + bounds = (self.border_adjustment)(bounds, *border_width); + }; + scene.push_path((self.factory)(bounds, self.color)); + } + + fn rect_for_text_range( + &self, + _: Range, + _: RectF, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &V, + _: &ViewContext, + ) -> Option { + None + } + + fn debug( + &self, + bounds: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &V, + _: &ViewContext, + ) -> gpui::json::Value { + json::json!({ + "type": "ButtonSide", + "bounds": bounds.to_json(), + "color": self.color.to_json(), + }) + } +} diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index c3e1a44afa..fa2630f969 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,4 +1,5 @@ use crate::{ + elements::ButtonSide, history::SearchHistory, mode::{SearchMode, Side}, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, @@ -11,12 +12,9 @@ use editor::{ SelectAll, MAX_TAB_TITLE_LEN, }; use futures::StreamExt; -use gpui::color::Color; -use gpui::geometry::rect::RectF; -use gpui::geometry::vector::IntoVector2F; -use gpui::json::{self, ToJson}; + use gpui::platform::PromptLevel; -use gpui::SceneBuilder; + use gpui::{ actions, elements::*, @@ -24,7 +22,7 @@ use gpui::{ Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; -use gpui::{scene::Path, LayoutContext}; + use menu::Confirm; use postage::stream::Stream; use project::{ @@ -717,7 +715,7 @@ impl ProjectSearchView { })?; if answer.next().await == Some(0) { - this.update(&mut cx, |this, cx| { + this.update(&mut cx, |this, _| { this.semantic_permissioned = Some(true); })?; } else { @@ -1150,138 +1148,6 @@ impl Default for ProjectSearchBar { Self::new() } } -type CreatePath = fn(RectF, Color) -> Path; -type AdjustBorder = fn(RectF, f32) -> RectF; -pub struct ButtonSide { - color: Color, - factory: CreatePath, - border_adjustment: AdjustBorder, - border: Option<(f32, Color)>, -} - -impl ButtonSide { - fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { - Self { - color, - factory, - border_adjustment, - border: None, - } - } - pub fn with_border(mut self, width: f32, color: Color) -> Self { - self.border = Some((width, color)); - self - } - pub fn left(color: Color) -> Self { - Self::new(color, left_button_side, left_button_border_adjust) - } - pub fn right(color: Color) -> Self { - Self::new(color, right_button_side, right_button_border_adjust) - } -} -fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut lower_right = bounds.clone().lower_right(); - lower_right.set_x(lower_right.x() + width.x()); - RectF::from_points(bounds.origin() + width, lower_right) -} -fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut origin = bounds.clone().origin(); - origin.set_x(origin.x() - width.x()); - RectF::from_points(origin, bounds.lower_right() - width) -} -fn left_button_side(bounds: RectF, color: Color) -> Path { - use gpui::geometry::PathBuilder; - let mut path = PathBuilder::new(); - path.reset(bounds.lower_right()); - path.line_to(bounds.upper_right()); - let mut middle_point = bounds.origin(); - let distance_to_line = (middle_point.y() - bounds.lower_left().y()) / 4.; - middle_point.set_y(middle_point.y() - distance_to_line); - path.curve_to(middle_point, bounds.origin()); - let mut target = bounds.lower_left(); - target.set_y(target.y() + distance_to_line); - path.line_to(target); - path.curve_to(bounds.lower_right(), bounds.lower_left()); - path.build(color, None) -} - -fn right_button_side(bounds: RectF, color: Color) -> Path { - use gpui::geometry::PathBuilder; - let mut path = PathBuilder::new(); - path.reset(bounds.lower_left()); - path.line_to(bounds.origin()); - let mut middle_point = bounds.upper_right(); - let distance_to_line = (middle_point.y() - bounds.lower_right().y()) / 4.; - middle_point.set_y(middle_point.y() - distance_to_line); - path.curve_to(middle_point, bounds.upper_right()); - let mut target = bounds.lower_right(); - target.set_y(target.y() + distance_to_line); - path.line_to(target); - path.curve_to(bounds.lower_left(), bounds.lower_right()); - path.build(color, None) -} - -impl Element for ButtonSide { - type LayoutState = (); - - type PaintState = (); - - fn layout( - &mut self, - constraint: gpui::SizeConstraint, - _: &mut ProjectSearchBar, - _: &mut LayoutContext, - ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - (constraint.max, ()) - } - - fn paint( - &mut self, - scene: &mut SceneBuilder, - bounds: RectF, - _: RectF, - _: &mut Self::LayoutState, - _: &mut ProjectSearchBar, - _: &mut ViewContext, - ) -> Self::PaintState { - let mut bounds = bounds; - if let Some((border_width, border_color)) = self.border.as_ref() { - scene.push_path((self.factory)(bounds, border_color.clone())); - bounds = (self.border_adjustment)(bounds, *border_width); - }; - scene.push_path((self.factory)(bounds, self.color)); - } - - fn rect_for_text_range( - &self, - _: Range, - _: RectF, - _: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &ProjectSearchBar, - _: &ViewContext, - ) -> Option { - None - } - - fn debug( - &self, - bounds: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &ProjectSearchBar, - _: &ViewContext, - ) -> gpui::json::Value { - json::json!({ - "type": "ButtonSide", - "bounds": bounds.to_json(), - "color": self.color.to_json(), - }) - } -} impl ProjectSearchBar { pub fn new() -> Self { @@ -1480,7 +1346,7 @@ impl ProjectSearchBar { Direction::Prev => style.container.border.left = false, Direction::Next => style.container.border.right = false, }; - let mut label = Label::new(icon, style.label.clone()) + let label = Label::new(icon, style.label.clone()) .contained() .with_style(style.container.clone()); match direction { diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 8aa03bdc35..3a985bbe8d 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -5,6 +5,7 @@ use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; pub mod buffer_search; +mod elements; mod history; mod mode; pub mod project_search; From 0ca29e56c26152c84d2efe16b8c8c6ae7fc03825 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:33:51 +0200 Subject: [PATCH 049/119] Update keybinds to use new names --- assets/keymaps/default.json | 6 +++--- crates/search/src/project_search.rs | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 2f13ee6d03..97dad7a831 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -238,7 +238,7 @@ "context": "ProjectSearchBar", "bindings": { "escape": "project_search::ToggleFocus", - "alt-tab": "project_search::CycleMode" + "alt-tab": "search::CycleMode" } }, { @@ -252,7 +252,7 @@ "context": "ProjectSearchView", "bindings": { "escape": "project_search::ToggleFocus", - "alt-tab": "project_search::CycleMode" + "alt-tab": "search::CycleMode" } }, { @@ -264,7 +264,7 @@ "alt-enter": "search::SelectAllMatches", "alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-w": "search::ToggleWholeWord", - "alt-cmd-r": "search::ToggleRegex", + "alt-cmd-r": "search::ActivateRegexMode", "alt-cmd-f": "project_search::ToggleFilters" } }, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index fa2630f969..5fed36994a 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,8 +2,8 @@ use crate::{ elements::ButtonSide, history::SearchHistory, mode::{SearchMode, Side}, - CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, - SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, + ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, + SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use anyhow::{Context, Result}; use collections::HashMap; @@ -66,6 +66,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(ProjectSearchBar::cycle_mode); cx.add_action(ProjectSearchBar::next_history_query); cx.add_action(ProjectSearchBar::previous_history_query); + cx.add_action(ProjectSearchBar::activate_regex_mode); cx.capture_action(ProjectSearchBar::tab); cx.capture_action(ProjectSearchBar::tab_previous); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); @@ -1296,6 +1297,19 @@ impl ProjectSearchBar { } } + fn activate_regex_mode(pane: &mut Pane, _: &ActivateRegexMode, cx: &mut ViewContext) { + if let Some(search_view) = pane + .active_item() + .and_then(|item| item.downcast::()) + { + search_view.update(cx, |view, cx| { + view.activate_search_mode(SearchMode::Regex, cx) + }); + } else { + cx.propagate_action(); + } + } + fn toggle_filters(&mut self, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { From c53554ead3cd8843ac316e1f5fb4c1c201330dd8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:42:11 +0200 Subject: [PATCH 050/119] Remove SearchOptions::REGEX. A bit WIP as it awaits migration of buffer search to modes --- crates/search/src/buffer_search.rs | 6 +++--- crates/search/src/mode.rs | 2 +- crates/search/src/project_search.rs | 12 ++---------- crates/search/src/search.rs | 2 -- crates/vim/src/normal/search.rs | 2 +- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 52dd943625..d7b8ab1410 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -209,12 +209,12 @@ impl View for BufferSearchBar { SearchOptions::WHOLE_WORD, cx, )) - .with_children(self.render_search_option( + /*.with_children(self.render_search_option( supported_options.regex, "Regex", SearchOptions::REGEX, cx, - )) + ))*/ .contained() .with_style(theme.search.option_button_group) .aligned(), @@ -697,7 +697,7 @@ impl BufferSearchBar { active_searchable_item.clear_matches(cx); let _ = done_tx.send(()); } else { - let query = if self.search_options.contains(SearchOptions::REGEX) { + let query = if true { match SearchQuery::regex( query, self.search_options.contains(SearchOptions::WHOLE_WORD), diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index b27365b8ed..aaaebc6e86 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -2,7 +2,7 @@ use gpui::Action; use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode}; // TODO: Update the default search mode to get from config -#[derive(Copy, Clone, Default, PartialEq)] +#[derive(Copy, Clone, Debug, Default, PartialEq)] pub(crate) enum SearchMode { #[default] Text, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 5fed36994a..ef905fa8e7 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -736,15 +736,9 @@ impl ProjectSearchView { }).detach_and_log_err(cx); } SearchMode::Regex => { - if !self.is_option_enabled(SearchOptions::REGEX) { - self.toggle_search_option(SearchOptions::REGEX); - } self.semantic_state = None; } SearchMode::Text => { - if self.is_option_enabled(SearchOptions::REGEX) { - self.toggle_search_option(SearchOptions::REGEX); - } self.semantic_state = None; } } @@ -992,7 +986,7 @@ impl ProjectSearchView { return None; } }; - if self.search_options.contains(SearchOptions::REGEX) { + if self.current_mode == SearchMode::Regex { match SearchQuery::regex( text, self.search_options.contains(SearchOptions::WHOLE_WORD), @@ -1011,6 +1005,7 @@ impl ProjectSearchView { } } } else { + debug_assert_ne!(self.current_mode, SearchMode::Semantic); Some(SearchQuery::text( text, self.search_options.contains(SearchOptions::WHOLE_WORD), @@ -1139,9 +1134,6 @@ impl ProjectSearchView { cx.propagate_action(); } - fn is_option_enabled(&self, option: SearchOptions) -> bool { - self.search_options.contains(option) - } } impl Default for ProjectSearchBar { diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 3a985bbe8d..b2cc43c7c1 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -39,7 +39,6 @@ bitflags! { const NONE = 0b000; const WHOLE_WORD = 0b001; const CASE_SENSITIVE = 0b010; - const REGEX = 0b100; } } @@ -68,7 +67,6 @@ impl 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 } } diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index 9375c4e78d..2ec4162e1e 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -66,7 +66,7 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext Date: Tue, 8 Aug 2023 14:56:21 +0200 Subject: [PATCH 051/119] Move nav buttons generation to search_bar --- crates/search/src/project_search.rs | 110 ++++------------------------ crates/search/src/search_bar.rs | 93 ++++++++++++++++++++++- 2 files changed, 107 insertions(+), 96 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index ef905fa8e7..30bb76197b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,6 +2,7 @@ use crate::{ elements::ButtonSide, history::SearchHistory, mode::{SearchMode, Side}, + search_bar::render_nav_button, ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; @@ -1322,91 +1323,6 @@ impl ProjectSearchBar { } } - fn render_nav_button( - &self, - icon: &'static str, - direction: Direction, - cx: &mut ViewContext, - ) -> AnyElement { - 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 = theme::current(cx).tooltip.clone(); - - enum NavButton {} - MouseEventHandler::::new(direction as usize, cx, |state, cx| { - let theme = theme::current(cx); - let mut style = theme.search.nav_button.style_for(state).clone(); - - match direction { - Direction::Prev => style.container.border.left = false, - Direction::Next => style.container.border.right = false, - }; - let label = Label::new(icon, style.label.clone()) - .contained() - .with_style(style.container.clone()); - match direction { - Direction::Prev => Flex::row() - .with_child( - ButtonSide::left( - style - .clone() - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width), - ) - .with_child(label) - .constrained() - .with_height(theme.workspace.toolbar.height), - Direction::Next => Flex::row() - .with_child(label) - .with_child( - ButtonSide::right( - style - .clone() - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width), - ) - .constrained() - .with_height(theme.workspace.toolbar.height), - } - }) - .on_click(MouseButton::Left, move |_, this, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |search, cx| search.select_match(direction, cx)); - } - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - direction as usize, - tooltip.to_string(), - Some(action), - tooltip_style, - cx, - ) - .into_any() - } fn render_option_button_icon( &self, icon: &'static str, @@ -1747,6 +1663,18 @@ impl View for ProjectSearchBar { let semantic_index = SemanticIndex::enabled(cx) .then(|| self.render_search_mode_button(SearchMode::Semantic, cx)); + let mut nav_button_for_direction = |label, direction| { + render_nav_button( + label, + direction, + move |_, this, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |search, cx| search.select_match(direction, cx)); + } + }, + cx, + ) + }; Flex::row() .with_child( Flex::column() @@ -1755,16 +1683,8 @@ impl View for ProjectSearchBar { .align_children_center() .with_child( Flex::row() - .with_child(self.render_nav_button( - "<", - Direction::Prev, - cx, - )) - .with_child(self.render_nav_button( - ">", - Direction::Next, - cx, - )) + .with_child(nav_button_for_direction("<", Direction::Prev)) + .with_child(nav_button_for_direction(">", Direction::Next)) .aligned(), ) .with_children(matches) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 5119e7866e..236509b86f 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -1,9 +1,12 @@ use gpui::{ - elements::{MouseEventHandler, Svg}, + elements::{Flex, Label, MouseEventHandler, ParentElement, Svg}, platform::{CursorStyle, MouseButton}, scene::MouseClick, Action, AnyElement, Element, EventContext, View, ViewContext, }; +use workspace::searchable::Direction; + +use crate::{elements::ButtonSide, SelectNextMatch, SelectPrevMatch}; pub(super) fn render_close_button( theme: &theme::Search, @@ -32,3 +35,91 @@ pub(super) fn render_close_button( .with_tooltip::(0, tooltip.to_string(), dismiss_action, tooltip_style, cx) .into_any() } + +pub(super) fn render_nav_button( + icon: &'static str, + direction: Direction, + on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, + cx: &mut ViewContext, +) -> AnyElement { + 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 = theme::current(cx).tooltip.clone(); + + enum NavButton {} + MouseEventHandler::::new(direction as usize, cx, |state, cx| { + let theme = theme::current(cx); + let mut style = theme.search.nav_button.style_for(state).clone(); + + match direction { + Direction::Prev => style.container.border.left = false, + Direction::Next => style.container.border.right = false, + }; + let label = Label::new(icon, style.label.clone()) + .contained() + .with_style(style.container.clone()); + match direction { + Direction::Prev => Flex::row() + .with_child( + ButtonSide::left( + style + .clone() + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .with_child(label) + .constrained() + .with_height(theme.workspace.toolbar.height), + Direction::Next => Flex::row() + .with_child(label) + .with_child( + ButtonSide::right( + style + .clone() + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .constrained() + .with_height(theme.workspace.toolbar.height), + } + }) + .on_click( + MouseButton::Left, + on_click, /*move |_, this, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |search, cx| search.select_match(direction, cx)); + }*/ + ) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + direction as usize, + tooltip.to_string(), + Some(action), + tooltip_style, + cx, + ) + .into_any() +} From 7547fa2679673e7e5d3c5387f98fe99037194f3d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:11:32 +0200 Subject: [PATCH 052/119] Move mode rendering to a search_bar module --- crates/search/src/project_search.rs | 142 ++++++++-------------------- crates/search/src/search_bar.rs | 89 ++++++++++++++++- 2 files changed, 129 insertions(+), 102 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 30bb76197b..4852e77004 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,8 +1,7 @@ use crate::{ - elements::ButtonSide, history::SearchHistory, - mode::{SearchMode, Side}, - search_bar::render_nav_button, + mode::SearchMode, + search_bar::{render_nav_button, render_search_mode_button}, ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; @@ -1358,90 +1357,6 @@ impl ProjectSearchBar { .into_any() } - fn render_search_mode_button( - &self, - mode: SearchMode, - cx: &mut ViewContext, - ) -> AnyElement { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = if let Some(search) = self.active_project_search.as_ref() { - let search = search.read(cx); - search.current_mode == mode - } else { - false - }; - - enum SearchModeButton {} - MouseEventHandler::::new(mode.region_id(), cx, |state, cx| { - let theme = theme::current(cx); - let mut style = theme - .search - .mode_button - .in_state(is_active) - .style_for(state) - .clone(); - - let label = Label::new(mode.label(), style.text.clone()) - .contained() - .with_style(style.container); - - if let Some(button_side) = mode.button_side() { - style.container.border.left = mode.border_left(); - style.container.border.right = mode.border_right(); - - if button_side == Side::Left { - Flex::row() - .align_children_center() - .with_child( - ButtonSide::left( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width), - ) - .with_child(label) - .into_any() - } else { - Flex::row() - .align_children_center() - .with_child(label) - .with_child( - ButtonSide::right( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(theme.search.mode_filling_width), - ) - .into_any() - } - } else { - label.into_any() - } - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.activate_search_mode(mode, cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - mode.region_id(), - mode.tooltip_text().to_owned(), - Some(mode.activate_action()), - tooltip_style, - cx, - ) - .into_any() - } - fn activate_search_mode(&self, mode: SearchMode, cx: &mut ViewContext) { // Update Current Mode if let Some(search_view) = self.active_project_search.as_ref() { @@ -1660,21 +1575,38 @@ impl View for ProjectSearchBar { .flex(1., false), ) }); - - let semantic_index = SemanticIndex::enabled(cx) - .then(|| self.render_search_mode_button(SearchMode::Semantic, cx)); - let mut nav_button_for_direction = |label, direction| { - render_nav_button( - label, - direction, + let search_button_for_mode = |mode, cx: &mut ViewContext| { + let is_active = if let Some(search) = self.active_project_search.as_ref() { + let search = search.read(cx); + search.current_mode == mode + } else { + false + }; + render_search_mode_button( + mode, + is_active, move |_, this, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |search, cx| search.select_match(direction, cx)); - } + this.activate_search_mode(mode, cx); }, cx, ) }; + let semantic_index = SemanticIndex::enabled(cx) + .then(|| search_button_for_mode(SearchMode::Semantic, cx)); + let nav_button_for_direction = + |label, direction, cx: &mut ViewContext| { + render_nav_button( + label, + direction, + move |_, this, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |search, cx| search.select_match(direction, cx)); + } + }, + cx, + ) + }; + Flex::row() .with_child( Flex::column() @@ -1683,8 +1615,16 @@ impl View for ProjectSearchBar { .align_children_center() .with_child( Flex::row() - .with_child(nav_button_for_direction("<", Direction::Prev)) - .with_child(nav_button_for_direction(">", Direction::Next)) + .with_child(nav_button_for_direction( + "<", + Direction::Prev, + cx, + )) + .with_child(nav_button_for_direction( + ">", + Direction::Next, + cx, + )) .aligned(), ) .with_children(matches) @@ -1726,9 +1666,9 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .align_children_center() - .with_child(self.render_search_mode_button(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Text, cx)) .with_children(semantic_index) - .with_child(self.render_search_mode_button(SearchMode::Regex, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) .with_child(super::search_bar::render_close_button( &theme.search, cx, diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 236509b86f..cd640ee4a8 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -6,7 +6,11 @@ use gpui::{ }; use workspace::searchable::Direction; -use crate::{elements::ButtonSide, SelectNextMatch, SelectPrevMatch}; +use crate::{ + elements::ButtonSide, + mode::{SearchMode, Side}, + SelectNextMatch, SelectPrevMatch, +}; pub(super) fn render_close_button( theme: &theme::Search, @@ -123,3 +127,86 @@ pub(super) fn render_nav_button( ) .into_any() } + +pub(crate) fn render_search_mode_button( + mode: SearchMode, + is_active: bool, + on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, + cx: &mut ViewContext, +) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + // let is_active = if let Some(search) = self.active_project_search.as_ref() { + // let search = search.read(cx); + // search.current_mode == mode + // } else { + // false + // }; + + enum SearchModeButton {} + MouseEventHandler::::new(mode.region_id(), cx, |state, cx| { + let theme = theme::current(cx); + let mut style = theme + .search + .mode_button + .in_state(is_active) + .style_for(state) + .clone(); + + let label = Label::new(mode.label(), style.text.clone()) + .contained() + .with_style(style.container); + + if let Some(button_side) = mode.button_side() { + style.container.border.left = mode.border_left(); + style.container.border.right = mode.border_right(); + + if button_side == Side::Left { + Flex::row() + .align_children_center() + .with_child( + ButtonSide::left( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .with_child(label) + .into_any() + } else { + Flex::row() + .align_children_center() + .with_child(label) + .with_child( + ButtonSide::right( + style + .container + .background_color + .unwrap_or_else(gpui::color::Color::transparent_black), + ) + .with_border(style.container.border.width, style.container.border.color) + .contained() + .constrained() + .with_max_width(theme.search.mode_filling_width), + ) + .into_any() + } + } else { + label.into_any() + } + }) + .on_click(MouseButton::Left, on_click) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + mode.region_id(), + mode.tooltip_text().to_owned(), + Some(mode.activate_action()), + tooltip_style, + cx, + ) + .into_any() +} From 0374fdfd062708a8edf1c2e42692381d6ac79cbb Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:20:34 +0200 Subject: [PATCH 053/119] Move mode cycling to mode module --- crates/search/src/mode.rs | 14 ++++++++++++++ crates/search/src/project_search.rs | 14 ++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index aaaebc6e86..072b9ffd80 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -71,3 +71,17 @@ impl SearchMode { } } } + +pub(crate) fn next_mode(mode: &SearchMode, semantic_enabled: bool) -> SearchMode { + let next_text_state = if semantic_enabled { + SearchMode::Semantic + } else { + SearchMode::Regex + }; + + match mode { + SearchMode::Text => next_text_state, + SearchMode::Semantic => SearchMode::Regex, + SearchMode::Regex => SearchMode::Text, + } +} diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 4852e77004..97b9f7b75b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1155,18 +1155,8 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |this, cx| { - let mode = &this.current_mode; - let next_text_state = if SemanticIndex::enabled(cx) { - SearchMode::Semantic - } else { - SearchMode::Regex - }; - - let new_mode = match mode { - &SearchMode::Text => next_text_state, - &SearchMode::Semantic => SearchMode::Regex, - SearchMode::Regex => SearchMode::Text, - }; + let new_mode = + crate::mode::next_mode(&this.current_mode, SemanticIndex::enabled(cx)); this.activate_search_mode(new_mode, cx); }) } From fecea03c90a1160c883834b59ac7625185d9cf65 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:25:32 +0200 Subject: [PATCH 054/119] Add modes to buffer search --- crates/search/src/buffer_search.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index d7b8ab1410..8823f8236e 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1,6 +1,7 @@ use crate::{ - history::SearchHistory, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, - SelectAllMatches, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, + history::SearchHistory, mode::SearchMode, search_bar::render_search_mode_button, + NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectAllMatches, SelectNextMatch, + SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use collections::HashMap; use editor::Editor; @@ -78,6 +79,7 @@ pub struct BufferSearchBar { query_contains_error: bool, dismissed: bool, search_history: SearchHistory, + current_mode: SearchMode, } impl Entity for BufferSearchBar { @@ -149,7 +151,18 @@ impl View for BufferSearchBar { self.query_editor.update(cx, |editor, cx| { editor.set_placeholder_text(new_placeholder_text, cx); }); + let search_button_for_mode = |mode, cx: &mut ViewContext| { + let is_active = self.current_mode == mode; + render_search_mode_button( + mode, + is_active, + move |_, this, cx| { + //this.activate_search_mode(mode, cx); + }, + cx, + ) + }; Flex::row() .with_child( Flex::row() @@ -221,6 +234,8 @@ impl View for BufferSearchBar { ) .flex(1., true), ) + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) .with_child(super::search_bar::render_close_button( &theme.search, cx, @@ -308,6 +323,7 @@ impl BufferSearchBar { query_contains_error: false, dismissed: true, search_history: SearchHistory::default(), + current_mode: SearchMode::default(), } } From 343e9d080ed1b35bd156ec11ca4f352aaf12dcdd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:57:07 +0200 Subject: [PATCH 055/119] Finish migration to modes for buffer search --- crates/search/src/buffer_search.rs | 24 +++++++++++++++--------- crates/search/src/mode.rs | 2 +- crates/search/src/search.rs | 1 + crates/vim/src/normal/search.rs | 8 +++----- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 8823f8236e..758a0b17cf 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -158,7 +158,7 @@ impl View for BufferSearchBar { mode, is_active, move |_, this, cx| { - //this.activate_search_mode(mode, cx); + this.activate_search_mode(mode, cx); }, cx, ) @@ -222,12 +222,6 @@ impl View for BufferSearchBar { SearchOptions::WHOLE_WORD, cx, )) - /*.with_children(self.render_search_option( - supported_options.regex, - "Regex", - SearchOptions::REGEX, - cx, - ))*/ .contained() .with_style(theme.search.option_button_group) .aligned(), @@ -537,7 +531,19 @@ impl BufferSearchBar { ) .into_any() } - + pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { + assert_ne!( + mode, + SearchMode::Semantic, + "Semantic search is not supported in buffer search" + ); + if mode == self.current_mode { + return; + } + self.current_mode = mode; + let _ = self.update_matches(cx); + cx.notify(); + } fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { let mut propagate_action = true; if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { @@ -713,7 +719,7 @@ impl BufferSearchBar { active_searchable_item.clear_matches(cx); let _ = done_tx.send(()); } else { - let query = if true { + let query = if self.current_mode == SearchMode::Regex { match SearchQuery::regex( query, self.search_options.contains(SearchOptions::WHOLE_WORD), diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index 072b9ffd80..bb620f0670 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -3,7 +3,7 @@ use gpui::Action; use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode}; // TODO: Update the default search mode to get from config #[derive(Copy, Clone, Debug, Default, PartialEq)] -pub(crate) enum SearchMode { +pub enum SearchMode { #[default] Text, Semantic, diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index b2cc43c7c1..33905f83ec 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -1,6 +1,7 @@ use bitflags::bitflags; pub use buffer_search::BufferSearchBar; use gpui::{actions, Action, AppContext}; +pub use mode::SearchMode; use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index 2ec4162e1e..5f1a68cfe9 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -1,5 +1,5 @@ use gpui::{actions, impl_actions, AppContext, ViewContext}; -use search::{buffer_search, BufferSearchBar, SearchOptions}; +use search::{buffer_search, BufferSearchBar, SearchMode, SearchOptions}; use serde_derive::Deserialize; use workspace::{searchable::Direction, Pane, Workspace}; @@ -65,10 +65,8 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext Date: Tue, 8 Aug 2023 16:26:19 +0200 Subject: [PATCH 056/119] Move option button rendering to search_bar --- crates/search/src/project_search.rs | 69 ++++++++--------------------- crates/search/src/search_bar.rs | 35 ++++++++++++++- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 97b9f7b75b..611fd02846 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1312,41 +1312,6 @@ impl ProjectSearchBar { } } - fn render_option_button_icon( - &self, - icon: &'static str, - option: SearchOptions, - cx: &mut ViewContext, - ) -> AnyElement { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = self.is_option_enabled(option, cx); - MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme - .search - .option_button - .in_state(is_active) - .style_for(state); - Svg::new(icon) - .with_color(style.text.color.clone()) - .contained() - .with_style(style.container) - .constrained() - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_search_option(option, cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - option.bits as usize, - format!("Toggle {}", option.label()), - Some(option.to_toggle_action()), - tooltip_style, - cx, - ) - .into_any() - } - fn activate_search_mode(&self, mode: SearchMode, cx: &mut ViewContext) { // Update Current Mode if let Some(search_view) = self.active_project_search.as_ref() { @@ -1478,26 +1443,28 @@ impl View for ProjectSearchBar { }; let search = _search.read(cx); let is_semantic_disabled = search.semantic_state.is_none(); - - let case_sensitive = if is_semantic_disabled { - Some(self.render_option_button_icon( + let render_option_button_icon = |path, option, cx: &mut ViewContext| { + crate::search_bar::render_option_button_icon( + self.is_option_enabled(option, cx), + path, + option, + move |_, this, cx| { + this.toggle_search_option(option, cx); + }, + cx, + ) + }; + let case_sensitive = is_semantic_disabled.then(|| { + render_option_button_icon( "icons/case_insensitive_12.svg", SearchOptions::CASE_SENSITIVE, cx, - )) - } else { - None - }; + ) + }); - let whole_word = if is_semantic_disabled { - Some(self.render_option_button_icon( - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - )) - } else { - None - }; + let whole_word = is_semantic_disabled.then(|| { + render_option_button_icon("icons/word_search_12.svg", SearchOptions::WHOLE_WORD, cx) + }); let search = _search.read(cx); let icon_style = theme.search.editor_icon.clone(); diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index cd640ee4a8..446039e960 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -9,7 +9,7 @@ use workspace::searchable::Direction; use crate::{ elements::ButtonSide, mode::{SearchMode, Side}, - SelectNextMatch, SelectPrevMatch, + SearchOptions, SelectNextMatch, SelectPrevMatch, }; pub(super) fn render_close_button( @@ -210,3 +210,36 @@ pub(crate) fn render_search_mode_button( ) .into_any() } + +pub(crate) fn render_option_button_icon( + is_active: bool, + icon: &'static str, + option: SearchOptions, + on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, + cx: &mut ViewContext, +) -> AnyElement { + let tooltip_style = theme::current(cx).tooltip.clone(); + MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { + let theme = theme::current(cx); + let style = theme + .search + .option_button + .in_state(is_active) + .style_for(state); + Svg::new(icon) + .with_color(style.text.color.clone()) + .contained() + .with_style(style.container) + .constrained() + }) + .on_click(MouseButton::Left, on_click) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + option.bits as usize, + format!("Toggle {}", option.label()), + Some(option.to_toggle_action()), + tooltip_style, + cx, + ) + .into_any() +} From a7bd05ec0ac1b19a0336edf86dd3b2411d436c1c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 00:16:12 +0200 Subject: [PATCH 057/119] buffer: use icons for search options --- crates/search/src/buffer_search.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 758a0b17cf..e02720712a 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -163,6 +163,21 @@ impl View for BufferSearchBar { cx, ) }; + let render_search_option = + |options: bool, icon, option, cx: &mut ViewContext| { + options.then(|| { + let is_active = self.search_options.contains(option); + crate::search_bar::render_option_button_icon::( + is_active, + icon, + option, + move |_, this, cx| { + this.toggle_search_option(option, cx); + }, + cx, + ) + }) + }; Flex::row() .with_child( Flex::row() @@ -210,15 +225,15 @@ impl View for BufferSearchBar { ) .with_child( Flex::row() - .with_children(self.render_search_option( + .with_children(render_search_option( supported_options.case, - "Case", + "icons/case_insensitive_12.svg", SearchOptions::CASE_SENSITIVE, cx, )) - .with_children(self.render_search_option( + .with_children(render_search_option( supported_options.word, - "Word", + "icons/word_search_12.svg", SearchOptions::WHOLE_WORD, cx, )) From e1724daf35285c19ff5147f066bfd12911f2851f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 00:38:50 +0200 Subject: [PATCH 058/119] buffer: WIP style sides of buffer search --- crates/search/src/buffer_search.rs | 78 +++++++++++++++++++----------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index e02720712a..a4658c9428 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -180,14 +180,15 @@ impl View for BufferSearchBar { }; Flex::row() .with_child( - Flex::row() + Flex::column() .with_child( Flex::row() + .align_children_center() .with_child( - ChildView::new(&self.query_editor, cx) - .aligned() - .left() - .flex(1., true), + Flex::row() + .with_child(self.render_nav_button("<", Direction::Prev, cx)) + .with_child(self.render_nav_button(">", Direction::Next, cx)) + .aligned(), ) .with_children(self.active_searchable_item.as_ref().and_then( |searchable_item| { @@ -208,23 +209,24 @@ impl View for BufferSearchBar { ) }, )) - .contained() - .with_style(editor_container) .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .flex(1., false), - ) - .with_child( - Flex::row() - .with_child(self.render_nav_button("<", Direction::Prev, cx)) - .with_child(self.render_nav_button(">", Direction::Next, cx)) - .with_child(self.render_action_button("Select All", cx)) - .aligned(), + .left() + .top(), ) + .contained() + .flex(1., true), + ) + .with_child( + Flex::row() + .align_children_center() .with_child( Flex::row() + .with_child( + ChildView::new(&self.query_editor, cx) + .aligned() + .left() + .flex(1., true), + ) .with_children(render_search_option( supported_options.case, "icons/case_insensitive_12.svg", @@ -238,19 +240,39 @@ impl View for BufferSearchBar { cx, )) .contained() - .with_style(theme.search.option_button_group) + .with_style(editor_container) + .aligned() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .flex(1., false), + ) + .with_child( + Flex::row() + .with_child(self.render_action_button("Select All", cx)) .aligned(), ) - .flex(1., true), + .flex(1., false), + ) + .with_child( + Flex::column().with_child( + Flex::row() + .align_children_center() + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .with_child(super::search_bar::render_close_button( + &theme.search, + cx, + |_, this, cx| this.dismiss(&Default::default(), cx), + Some(Box::new(Dismiss)), + )) + .contained() + .aligned() + .right() + .top() + .flex(1., true), + ), ) - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .with_child(super::search_bar::render_close_button( - &theme.search, - cx, - |_, this, cx| this.dismiss(&Default::default(), cx), - Some(Box::new(Dismiss)), - )) .contained() .with_style(theme.search.container) .into_any_named("search bar") From b282bdfe5a3e02bceba2464b9c9d22ed1094f5cd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:09:07 +0200 Subject: [PATCH 059/119] buffer: finish up moving things around --- crates/search/src/buffer_search.rs | 70 +++++++++++++++++------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index a4658c9428..915ede6cb5 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -211,47 +211,56 @@ impl View for BufferSearchBar { )) .aligned() .left() - .top(), + .top() + .flex(1., true) + .constrained(), ) - .contained() - .flex(1., true), + .contained(), ) .with_child( - Flex::row() + Flex::column() .align_children_center() .with_child( Flex::row() .with_child( - ChildView::new(&self.query_editor, cx) + Flex::row() + .with_child( + ChildView::new(&self.query_editor, cx) + .aligned() + .left() + .flex(1., true), + ) + .with_children(render_search_option( + supported_options.case, + "icons/case_insensitive_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + )) + .with_children(render_search_option( + supported_options.word, + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + )) + .contained() + .with_style(editor_container) .aligned() - .left() - .flex(1., true), + .top() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .flex(1., false), + ) + .with_child( + Flex::row() + .with_child(self.render_action_button("Select All", cx)) + .aligned(), ) - .with_children(render_search_option( - supported_options.case, - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - )) - .with_children(render_search_option( - supported_options.word, - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - )) - .contained() - .with_style(editor_container) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) .flex(1., false), ) - .with_child( - Flex::row() - .with_child(self.render_action_button("Select All", cx)) - .aligned(), - ) + .contained() + .aligned() + .top() .flex(1., false), ) .with_child( @@ -275,6 +284,7 @@ impl View for BufferSearchBar { ) .contained() .with_style(theme.search.container) + .flex_float() .into_any_named("search bar") } } From a583d1c6ccfa8ccaf16bc8bf4d13e275150cc50c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:15:17 +0200 Subject: [PATCH 060/119] Do not display match count when query is empty --- crates/search/src/buffer_search.rs | 44 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 915ede6cb5..b9b09523cc 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -178,6 +178,29 @@ impl View for BufferSearchBar { ) }) }; + let match_count = self + .active_searchable_item + .as_ref() + .and_then(|searchable_item| { + if self.query(cx).is_empty() { + return None; + } + let matches = self + .searchable_items_with_matches + .get(&searchable_item.downgrade())?; + let message = if let Some(match_ix) = self.active_match_index { + format!("{}/{}", match_ix + 1, matches.len()) + } else { + "No matches".to_string() + }; + + Some( + Label::new(message, theme.search.match_index.text.clone()) + .contained() + .with_style(theme.search.match_index.container) + .aligned(), + ) + }); Flex::row() .with_child( Flex::column() @@ -190,25 +213,7 @@ impl View for BufferSearchBar { .with_child(self.render_nav_button(">", Direction::Next, cx)) .aligned(), ) - .with_children(self.active_searchable_item.as_ref().and_then( - |searchable_item| { - let matches = self - .searchable_items_with_matches - .get(&searchable_item.downgrade())?; - let message = if let Some(match_ix) = self.active_match_index { - format!("{}/{}", match_ix + 1, matches.len()) - } else { - "No matches".to_string() - }; - - Some( - Label::new(message, theme.search.match_index.text.clone()) - .contained() - .with_style(theme.search.match_index.container) - .aligned(), - ) - }, - )) + .with_children(match_count) .aligned() .left() .top() @@ -765,6 +770,7 @@ impl BufferSearchBar { self.active_match_index.take(); active_searchable_item.clear_matches(cx); let _ = done_tx.send(()); + cx.notify(); } else { let query = if self.current_mode == SearchMode::Regex { match SearchQuery::regex( From e2b8e6ce63ad5a2ac70cd0661860c1e9664b4b88 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:16:24 +0200 Subject: [PATCH 061/119] chore: fix compiler warnings --- crates/search/src/buffer_search.rs | 40 --------------------- crates/semantic_index/src/semantic_index.rs | 2 +- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index b9b09523cc..d7add91599 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -468,46 +468,6 @@ impl BufferSearchBar { self.update_matches(cx) } - fn render_search_option( - &self, - option_supported: bool, - icon: &'static str, - option: SearchOptions, - cx: &mut ViewContext, - ) -> Option> { - if !option_supported { - return None; - } - - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = self.search_options.contains(option); - Some( - MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme - .search - .option_button - .in_state(is_active) - .style_for(state); - Label::new(icon, style.text.clone()) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - this.toggle_search_option(option, cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - option.bits as usize, - format!("Toggle {}", option.label()), - Some(option.to_toggle_action()), - tooltip_style, - cx, - ) - .into_any(), - ) - } - fn render_nav_button( &self, icon: &'static str, diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 50b871d454..8c9877b9d3 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -16,7 +16,7 @@ use language::{Anchor, Buffer, Language, LanguageRegistry}; use parking_lot::Mutex; use parsing::{CodeContextRetriever, Document, PARSEABLE_ENTIRE_FILE_TYPES}; use postage::watch; -use project::{project_settings, search::PathMatcher, Fs, Project, WorktreeId}; +use project::{search::PathMatcher, Fs, Project, WorktreeId}; use smol::channel; use std::{ cmp::Ordering, From f978acdd34085bb0238c6e3307edd1651f3808b8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:29:22 +0200 Subject: [PATCH 062/119] buffer: use shared func for nav button rendering --- crates/search/src/buffer_search.rs | 19 ++++++++++++++++--- crates/search/src/project_search.rs | 25 ++++++++++++------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index d7add91599..25149012c7 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1,5 +1,7 @@ use crate::{ - history::SearchHistory, mode::SearchMode, search_bar::render_search_mode_button, + history::SearchHistory, + mode::SearchMode, + search_bar::{render_nav_button, render_search_mode_button}, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectAllMatches, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; @@ -201,6 +203,17 @@ impl View for BufferSearchBar { .aligned(), ) }); + let nav_button_for_direction = |label, direction, cx: &mut ViewContext| { + render_nav_button( + label, + direction, + move |_, this, cx| match direction { + Direction::Prev => this.select_prev_match(&Default::default(), cx), + Direction::Next => this.select_next_match(&Default::default(), cx), + }, + cx, + ) + }; Flex::row() .with_child( Flex::column() @@ -209,8 +222,8 @@ impl View for BufferSearchBar { .align_children_center() .with_child( Flex::row() - .with_child(self.render_nav_button("<", Direction::Prev, cx)) - .with_child(self.render_nav_button(">", Direction::Next, cx)) + .with_child(nav_button_for_direction("<", Direction::Prev, cx)) + .with_child(nav_button_for_direction(">", Direction::Next, cx)) .aligned(), ) .with_children(match_count) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 611fd02846..b607848e21 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1550,19 +1550,18 @@ impl View for ProjectSearchBar { }; let semantic_index = SemanticIndex::enabled(cx) .then(|| search_button_for_mode(SearchMode::Semantic, cx)); - let nav_button_for_direction = - |label, direction, cx: &mut ViewContext| { - render_nav_button( - label, - direction, - move |_, this, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |search, cx| search.select_match(direction, cx)); - } - }, - cx, - ) - }; + let nav_button_for_direction = |label, direction, cx: &mut ViewContext| { + render_nav_button( + label, + direction, + move |_, this, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |search, cx| search.select_match(direction, cx)); + } + }, + cx, + ) + }; Flex::row() .with_child( From 22f630e985a5b10827de1c61c53bf1671062f376 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:30:45 +0200 Subject: [PATCH 063/119] chore: remove unused function --- crates/search/src/buffer_search.rs | 45 ------------------------------ 1 file changed, 45 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 25149012c7..845d943f4e 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -481,51 +481,6 @@ impl BufferSearchBar { self.update_matches(cx) } - fn render_nav_button( - &self, - icon: &'static str, - direction: Direction, - cx: &mut ViewContext, - ) -> AnyElement { - 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 = theme::current(cx).tooltip.clone(); - - enum NavButton {} - MouseEventHandler::::new(direction as usize, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme.search.option_button.inactive_state().style_for(state); - Label::new(icon, style.text.clone()) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, { - move |_, this, cx| match direction { - Direction::Prev => this.select_prev_match(&Default::default(), cx), - Direction::Next => this.select_next_match(&Default::default(), cx), - } - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - direction as usize, - tooltip.to_string(), - Some(action), - tooltip_style, - cx, - ) - .into_any() - } - fn render_action_button( &self, icon: &'static str, From d34da2db697baeb6044530ca4ee8a5c32141e8d4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:37:17 +0200 Subject: [PATCH 064/119] fix dismiss tooltip for project search --- crates/search/src/buffer_search.rs | 1 + crates/search/src/project_search.rs | 1 + crates/search/src/search_bar.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 845d943f4e..36dbe77bb4 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -288,6 +288,7 @@ impl View for BufferSearchBar { .with_child(search_button_for_mode(SearchMode::Text, cx)) .with_child(search_button_for_mode(SearchMode::Regex, cx)) .with_child(super::search_bar::render_close_button( + "Dismiss Buffer Search", &theme.search, cx, |_, this, cx| this.dismiss(&Default::default(), cx), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b607848e21..8939279f3b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1626,6 +1626,7 @@ impl View for ProjectSearchBar { .with_children(semantic_index) .with_child(search_button_for_mode(SearchMode::Regex, cx)) .with_child(super::search_bar::render_close_button( + "Dismiss Project Search", &theme.search, cx, |_, this, cx| { diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 446039e960..19d8fa6b98 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -13,12 +13,12 @@ use crate::{ }; pub(super) fn render_close_button( + tooltip: &'static str, theme: &theme::Search, cx: &mut ViewContext, on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, dismiss_action: Option>, ) -> AnyElement { - let tooltip = "Dismiss Buffer Search"; let tooltip_style = theme::current(cx).tooltip.clone(); enum CloseButton {} From 8b033223fb407de5b3a96fea77d76e62de9bc54a Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:28:15 +0200 Subject: [PATCH 065/119] Go back to previous mode on rejection of semantic indexing --- crates/search/src/project_search.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 8939279f3b..be40f6ca15 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -685,6 +685,10 @@ impl ProjectSearchView { } fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { + let previous_mode = self.current_mode; + if previous_mode == mode { + return; + } self.model.update(cx, |model, _| model.kill_search()); self.current_mode = mode; @@ -722,7 +726,8 @@ impl ProjectSearchView { } else { this.update(&mut cx, |this, cx| { this.semantic_permissioned = Some(false); - this.activate_search_mode(SearchMode::Regex, cx); + debug_assert_ne!(previous_mode, SearchMode::Semantic, "Tried to re-enable semantic search mode after user modal was rejected"); + this.activate_search_mode(previous_mode, cx); })?; return anyhow::Ok(()); } From 6fbef9f3c7ee1972e8c2b269b0c048fd635c737a Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:33:14 +0200 Subject: [PATCH 066/119] chore: update fn paint --- crates/search/src/elements.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/search/src/elements.rs b/crates/search/src/elements.rs index cdc203983f..bd81f4f6a2 100644 --- a/crates/search/src/elements.rs +++ b/crates/search/src/elements.rs @@ -5,7 +5,7 @@ use gpui::geometry::rect::RectF; use gpui::geometry::vector::IntoVector2F; use gpui::json::{self, ToJson}; use gpui::{scene::Path, LayoutContext}; -use gpui::{Element, SceneBuilder, View, ViewContext}; +use gpui::{Element, PaintContext, SceneBuilder, View, ViewContext}; type CreatePath = fn(RectF, Color) -> Path; type AdjustBorder = fn(RectF, f32) -> RectF; @@ -106,7 +106,7 @@ impl Element for ButtonSide { _: RectF, _: &mut Self::LayoutState, _: &mut V, - _: &mut ViewContext, + _: &mut PaintContext, ) -> Self::PaintState { let mut bounds = bounds; if let Some((border_width, border_color)) = self.border.as_ref() { From 28a57662af44989e7e9761ee4bb73eb7f00bfc75 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:47:48 +0200 Subject: [PATCH 067/119] buffer: Add magnifying glass icon --- crates/search/src/buffer_search.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 29cd32c6e5..cffbe91fbe 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -214,6 +214,7 @@ impl View for BufferSearchBar { cx, ) }; + let icon_style = theme.search.editor_icon.clone(); Flex::row() .with_child( Flex::column() @@ -242,6 +243,13 @@ impl View for BufferSearchBar { Flex::row() .with_child( Flex::row() + .align_children_center() + .with_child( + Svg::for_style(icon_style.icon) + .contained() + .with_style(icon_style.container) + .constrained(), + ) .with_child( ChildView::new(&self.query_editor, cx) .aligned() From 17d505bb7b0dcce4147b4015eba48590a96f518a Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:25:56 +0200 Subject: [PATCH 068/119] Reset search index on mode change --- crates/search/src/project_search.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 45b50dbe34..52797aa8db 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -695,6 +695,7 @@ impl ProjectSearchView { match mode { SearchMode::Semantic => { let has_permission = self.semantic_permissioned(cx); + self.active_match_index = None; cx.spawn(|this, mut cx| async move { let has_permission = has_permission.await?; @@ -740,11 +741,9 @@ impl ProjectSearchView { anyhow::Ok(()) }).detach_and_log_err(cx); } - SearchMode::Regex => { - self.semantic_state = None; - } - SearchMode::Text => { + SearchMode::Regex | SearchMode::Text => { self.semantic_state = None; + self.active_match_index = None; } } cx.notify(); From c58cf396d3c1b9fe58ace18a66051a54d2ee3115 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Aug 2023 18:02:21 +0200 Subject: [PATCH 069/119] Add cycle mode command for buffer search --- assets/keymaps/default.json | 4 +++- crates/search/src/buffer_search.rs | 28 +++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 97dad7a831..3b894de723 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -224,7 +224,8 @@ "tab": "buffer_search::FocusEditor", "enter": "search::SelectNextMatch", "shift-enter": "search::SelectPrevMatch", - "alt-enter": "search::SelectAllMatches" + "alt-enter": "search::SelectAllMatches", + "alt-tab": "search::CycleMode" } }, { @@ -265,6 +266,7 @@ "alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-w": "search::ToggleWholeWord", "alt-cmd-r": "search::ActivateRegexMode", + "alt-tab": "search::CycleMode", "alt-cmd-f": "project_search::ToggleFilters" } }, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index cffbe91fbe..ec2f1c6855 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1,9 +1,9 @@ use crate::{ history::SearchHistory, - mode::SearchMode, + mode::{next_mode, SearchMode}, search_bar::{render_nav_button, render_search_mode_button}, - NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectAllMatches, SelectNextMatch, - SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, + CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectAllMatches, + SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; use collections::HashMap; use editor::Editor; @@ -51,6 +51,8 @@ pub fn init(cx: &mut AppContext) { cx.add_action(BufferSearchBar::handle_editor_cancel); cx.add_action(BufferSearchBar::next_history_query); cx.add_action(BufferSearchBar::previous_history_query); + cx.add_action(BufferSearchBar::cycle_mode); + cx.add_action(BufferSearchBar::cycle_mode_on_pane); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); } @@ -804,6 +806,26 @@ impl BufferSearchBar { let _ = self.search(&new_query, Some(self.search_options), cx); } } + fn cycle_mode(&mut self, _: &CycleMode, cx: &mut ViewContext) { + self.activate_search_mode(next_mode(&self.current_mode, false), cx); + } + fn cycle_mode_on_pane(pane: &mut Pane, action: &CycleMode, cx: &mut ViewContext) { + let mut should_propagate = true; + if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + search_bar.update(cx, |bar, cx| { + if bar.show(cx) { + should_propagate = false; + bar.cycle_mode(action, cx); + false + } else { + true + } + }); + } + if should_propagate { + cx.propagate_action(); + } + } } #[cfg(test)] From b2f773e91dd391c543d176b246d047bd510bb931 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:57:12 +0200 Subject: [PATCH 070/119] Increase padding on search --- crates/search/src/project_search.rs | 6 ++---- crates/search/src/search_bar.rs | 13 +------------ styles/src/style_tree/search.ts | 13 +++++++++++-- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 52797aa8db..a3ae61f0a8 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1616,7 +1616,6 @@ impl View for ProjectSearchBar { ) .with_children(filters) .contained() - .with_style(theme.search.container) .aligned() .top() .flex(1., false), @@ -1643,7 +1642,6 @@ impl View for ProjectSearchBar { .constrained() .with_height(theme.workspace.toolbar.height) .contained() - .with_style(theme.search.container) .aligned() .right() .top() @@ -1659,7 +1657,7 @@ impl View for ProjectSearchBar { .flex(1., true), ) .contained() - .with_uniform_padding(theme.workspace.toolbar.height / 3.) + .with_style(theme.search.container) .flex_float() .into_any_named("project search") } else { @@ -1693,7 +1691,7 @@ impl ToolbarItemView for ProjectSearchBar { .as_ref() .map(|search| { let offset = search.read(cx).filters_enabled as usize; - 2 + offset + 3 + offset }) .unwrap_or_else(|| 2) } diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 19d8fa6b98..19feb10c95 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -65,11 +65,6 @@ pub(super) fn render_nav_button( MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); let mut style = theme.search.nav_button.style_for(state).clone(); - - match direction { - Direction::Prev => style.container.border.left = false, - Direction::Next => style.container.border.right = false, - }; let label = Label::new(icon, style.label.clone()) .contained() .with_style(style.container.clone()); @@ -110,13 +105,7 @@ pub(super) fn render_nav_button( .with_height(theme.workspace.toolbar.height), } }) - .on_click( - MouseButton::Left, - on_click, /*move |_, this, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |search, cx| search.select_match(direction, cx)); - }*/ - ) + .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( direction as usize, diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index fb9b356591..3de76f1d7e 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -34,6 +34,7 @@ export default function search(): any { } return { + padding: { top: 32, bottom: 32, left: 32, right: 32 }, // TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive match_background: with_opacity( foreground(theme.highest, "accent"), @@ -187,7 +188,11 @@ export default function search(): any { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - border: border(theme.highest, "on"), + border: { + ...border(theme.highest, "on"), + left: false, + right: false + }, padding: { bottom: 6, @@ -230,7 +235,11 @@ export default function search(): any { text: text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), corner_radius: 2, - border: border(theme.highest, "on"), + border: { + ...border(theme.highest, "on"), + left: false, + right: false, + }, padding: { bottom: 6, From da3a4174ce6d4fd824fc50c02b822371eda96190 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:01:20 +0200 Subject: [PATCH 071/119] Bump row_count for buffer search --- crates/search/src/buffer_search.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index ec2f1c6855..e55d91ac1c 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -367,6 +367,9 @@ impl ToolbarItemView for BufferSearchBar { ToolbarItemLocation::Hidden } } + fn row_count(&self, _: &ViewContext) -> usize { + 3 + } } impl BufferSearchBar { From 9ad308f2f081ae5e11e4f2d4962f973459db685f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:53:25 +0200 Subject: [PATCH 072/119] Fix padding inside search input. Adjust borders for toggle buttons --- styles/src/style_tree/search.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 3de76f1d7e..35e7042ceb 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -20,9 +20,9 @@ export default function search(): any { right: 12, }, padding: { - top: 3, - bottom: 3, - left: 10, + top: 4, + bottom: 4, + left: 4, right: 4, }, } @@ -46,8 +46,8 @@ export default function search(): any { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), corner_radius: 2, - border: border(theme.highest, "on"), - + margin: { right: 2 }, + border: { width: 0, color: foreground(theme.highest, "variant") }, padding: { bottom: 6, left: 6, @@ -59,25 +59,26 @@ export default function search(): any { hovered: { ...text(theme.highest, "mono", "on", "hovered"), background: background(theme.highest, "on", "hovered"), - border: border(theme.highest, "on", "hovered"), }, clicked: { ...text(theme.highest, "mono", "on", "pressed"), background: background(theme.highest, "on", "pressed"), - border: border(theme.highest, "on", "pressed"), }, }, }), state: { active: { default: { - ...text(theme.highest, "mono", "accent"), + background: background(theme.highest, "accent", "hovered"), + border: border(theme.highest, "on"), }, hovered: { ...text(theme.highest, "mono", "accent", "hovered"), + border: border(theme.highest, "on", "hovered"), }, clicked: { ...text(theme.highest, "mono", "accent", "pressed"), + border: border(theme.highest, "on", "pressed"), }, }, }, From 096e293c179865f5f3e651fe9fdb36f18fcc098d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 10 Aug 2023 15:26:05 +0200 Subject: [PATCH 073/119] Fix rounding artifacts of nav buttons --- crates/search/src/search_bar.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 19feb10c95..9993fa530a 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -65,6 +65,7 @@ pub(super) fn render_nav_button( MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); let mut style = theme.search.nav_button.style_for(state).clone(); + style.container.corner_radius = 0.; let label = Label::new(icon, style.label.clone()) .contained() .with_style(style.container.clone()); @@ -124,13 +125,6 @@ pub(crate) fn render_search_mode_button( cx: &mut ViewContext, ) -> AnyElement { let tooltip_style = theme::current(cx).tooltip.clone(); - // let is_active = if let Some(search) = self.active_project_search.as_ref() { - // let search = search.read(cx); - // search.current_mode == mode - // } else { - // false - // }; - enum SearchModeButton {} MouseEventHandler::::new(mode.region_id(), cx, |state, cx| { let theme = theme::current(cx); From a9a2d281c3455957f57d02b56ae6fa7c7d341c9c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:10:56 +0200 Subject: [PATCH 074/119] Make ButtonSide scale with corner_radius --- crates/search/src/search_bar.rs | 12 +++++++----- crates/theme/src/theme.rs | 1 - styles/src/style_tree/search.ts | 5 ++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 9993fa530a..7b2ec33187 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -65,6 +65,7 @@ pub(super) fn render_nav_button( MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); let mut style = theme.search.nav_button.style_for(state).clone(); + let button_side_width = style.container.corner_radius; style.container.corner_radius = 0.; let label = Label::new(icon, style.label.clone()) .contained() @@ -82,7 +83,7 @@ pub(super) fn render_nav_button( .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.search.mode_filling_width), + .with_max_width(button_side_width), ) .with_child(label) .constrained() @@ -100,7 +101,7 @@ pub(super) fn render_nav_button( .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.search.mode_filling_width), + .with_max_width(button_side_width), ) .constrained() .with_height(theme.workspace.toolbar.height), @@ -134,7 +135,8 @@ pub(crate) fn render_search_mode_button( .in_state(is_active) .style_for(state) .clone(); - + let side_width = style.container.corner_radius; + style.container.corner_radius = 0.; let label = Label::new(mode.label(), style.text.clone()) .contained() .with_style(style.container); @@ -156,7 +158,7 @@ pub(crate) fn render_search_mode_button( .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.search.mode_filling_width), + .with_max_width(side_width), ) .with_child(label) .into_any() @@ -174,7 +176,7 @@ pub(crate) fn render_search_mode_button( .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(theme.search.mode_filling_width), + .with_max_width(side_width), ) .into_any() } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 1107906ca7..e950845b49 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -388,7 +388,6 @@ pub struct Search { pub dismiss_button: Interactive, pub editor_icon: IconStyle, pub mode_button: Toggleable>, - pub mode_filling_width: f32, pub nav_button: Interactive, } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 35e7042ceb..8da7e21eb6 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -201,7 +201,7 @@ export default function search(): any { right: 10, top: 6, }, - corner_radius: 2, + corner_radius: 6, }, state: { hovered: { @@ -230,12 +230,11 @@ export default function search(): any { }, }, }), - mode_filling_width: 4.0, nav_button: interactive({ base: { text: text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: 2, + corner_radius: 6, border: { ...border(theme.highest, "on"), left: false, From b8df26b19405749bba898c4482562fa20c2e5d01 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:26:22 +0200 Subject: [PATCH 075/119] mode_button: get rid of borders before creating a label --- crates/search/src/search_bar.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 7b2ec33187..94d1407b9b 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -137,14 +137,15 @@ pub(crate) fn render_search_mode_button( .clone(); let side_width = style.container.corner_radius; style.container.corner_radius = 0.; + if let Some(button_side) = mode.button_side() { + style.container.border.left = mode.border_left(); + style.container.border.right = mode.border_right(); + } let label = Label::new(mode.label(), style.text.clone()) .contained() .with_style(style.container); if let Some(button_side) = mode.button_side() { - style.container.border.left = mode.border_left(); - style.container.border.right = mode.border_right(); - if button_side == Side::Left { Flex::row() .align_children_center() From 3d103546bc2169ed51640a8114e4735d89496880 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 14:44:22 +0200 Subject: [PATCH 076/119] Make ButtonSide respect corner_radius --- crates/search/src/elements.rs | 41 +++++++++++++++++++++++---------- crates/search/src/search_bar.rs | 10 ++++++-- styles/src/style_tree/search.ts | 4 ++-- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/crates/search/src/elements.rs b/crates/search/src/elements.rs index bd81f4f6a2..0a7990c24a 100644 --- a/crates/search/src/elements.rs +++ b/crates/search/src/elements.rs @@ -7,7 +7,7 @@ use gpui::json::{self, ToJson}; use gpui::{scene::Path, LayoutContext}; use gpui::{Element, PaintContext, SceneBuilder, View, ViewContext}; -type CreatePath = fn(RectF, Color) -> Path; +type CreatePath = fn(RectF, Color, f32) -> Path; type AdjustBorder = fn(RectF, f32) -> RectF; type BorderThickness = f32; @@ -18,26 +18,43 @@ pub(crate) struct ButtonSide { /// the drawing bounds have to be adjusted by different factors in different dimensions. border_adjustment: AdjustBorder, border: Option<(BorderThickness, Color)>, + radius: f32, } impl ButtonSide { - fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self { + fn new( + color: Color, + factory: CreatePath, + border_adjustment: AdjustBorder, + radius: f32, + ) -> Self { Self { color, factory, border_adjustment, border: None, + radius, } } pub fn with_border(mut self, width: f32, color: Color) -> Self { self.border = Some((width, color)); self } - pub fn left(color: Color) -> Self { - Self::new(color, left_button_side, left_button_border_adjust) + pub fn left(color: Color, corner_radius: f32) -> Self { + Self::new( + color, + left_button_side, + left_button_border_adjust, + corner_radius, + ) } - pub fn right(color: Color) -> Self { - Self::new(color, right_button_side, right_button_border_adjust) + pub fn right(color: Color, corner_radius: f32) -> Self { + Self::new( + color, + right_button_side, + right_button_border_adjust, + corner_radius, + ) } } @@ -53,13 +70,13 @@ fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { origin.set_x(origin.x() - width.x()); RectF::from_points(origin, bounds.lower_right() - width) } -fn left_button_side(bounds: RectF, color: Color) -> Path { +fn left_button_side(bounds: RectF, color: Color, radius: f32) -> Path { use gpui::geometry::PathBuilder; let mut path = PathBuilder::new(); path.reset(bounds.lower_right()); path.line_to(bounds.upper_right()); let mut middle_point = bounds.origin(); - let distance_to_line = (middle_point.y() - bounds.lower_left().y()) / 4.; + let distance_to_line = (middle_point.y() - bounds.lower_left().y()).min(-radius.abs()); middle_point.set_y(middle_point.y() - distance_to_line); path.curve_to(middle_point, bounds.origin()); let mut target = bounds.lower_left(); @@ -69,13 +86,13 @@ fn left_button_side(bounds: RectF, color: Color) -> Path { path.build(color, None) } -fn right_button_side(bounds: RectF, color: Color) -> Path { +fn right_button_side(bounds: RectF, color: Color, radius: f32) -> Path { use gpui::geometry::PathBuilder; let mut path = PathBuilder::new(); path.reset(bounds.lower_left()); path.line_to(bounds.origin()); let mut middle_point = bounds.upper_right(); - let distance_to_line = (middle_point.y() - bounds.lower_right().y()) / 4.; + let distance_to_line = (middle_point.y() - bounds.lower_right().y()).min(-radius.abs()); middle_point.set_y(middle_point.y() - distance_to_line); path.curve_to(middle_point, bounds.upper_right()); let mut target = bounds.lower_right(); @@ -110,10 +127,10 @@ impl Element for ButtonSide { ) -> Self::PaintState { let mut bounds = bounds; if let Some((border_width, border_color)) = self.border.as_ref() { - scene.push_path((self.factory)(bounds, border_color.clone())); + scene.push_path((self.factory)(bounds, border_color.clone(), self.radius)); bounds = (self.border_adjustment)(bounds, *border_width); }; - scene.push_path((self.factory)(bounds, self.color)); + scene.push_path((self.factory)(bounds, self.color, self.radius)); } fn rect_for_text_range( diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 94d1407b9b..fa463d4e0d 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -79,6 +79,7 @@ pub(super) fn render_nav_button( .container .background_color .unwrap_or_else(gpui::color::Color::transparent_black), + button_side_width, ) .with_border(style.container.border.width, style.container.border.color) .contained() @@ -97,6 +98,7 @@ pub(super) fn render_nav_button( .container .background_color .unwrap_or_else(gpui::color::Color::transparent_black), + button_side_width, ) .with_border(style.container.border.width, style.container.border.color) .contained() @@ -155,11 +157,13 @@ pub(crate) fn render_search_mode_button( .container .background_color .unwrap_or_else(gpui::color::Color::transparent_black), + side_width, ) .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(side_width), + .with_max_width(side_width) + .with_height(32.), ) .with_child(label) .into_any() @@ -173,11 +177,13 @@ pub(crate) fn render_search_mode_button( .container .background_color .unwrap_or_else(gpui::color::Color::transparent_black), + side_width, ) .with_border(style.container.border.width, style.container.border.color) .contained() .constrained() - .with_max_width(side_width), + .with_max_width(side_width) + .with_height(32.), ) .into_any() } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 8da7e21eb6..52bffa6cad 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -196,10 +196,10 @@ export default function search(): any { }, padding: { - bottom: 6, + bottom: 4, left: 10, right: 10, - top: 6, + top: 5, }, corner_radius: 6, }, From 9e9b3093a9a91b55e49e041898b4dc1cab53f444 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 16:32:15 +0200 Subject: [PATCH 077/119] Style mode/option buttons --- crates/search/src/search_bar.rs | 1 + styles/src/style_tree/search.ts | 39 +++++++++++++++++++++------------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index fa463d4e0d..6cdb36acc3 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -223,6 +223,7 @@ pub(crate) fn render_option_button_icon( .contained() .with_style(style.container) .constrained() + .with_height(22.) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 52bffa6cad..ff36e3532d 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -47,22 +47,30 @@ export default function search(): any { background: background(theme.highest, "on"), corner_radius: 2, margin: { right: 2 }, - border: { width: 0, color: foreground(theme.highest, "variant") }, + border: { + width: 1., color: background(theme.highest, "on") + }, padding: { - bottom: 6, - left: 6, - right: 6, - top: 6, + bottom: 4, + left: 4, + right: 4, + top: 4, }, }, state: { hovered: { ...text(theme.highest, "mono", "on", "hovered"), background: background(theme.highest, "on", "hovered"), + border: { + width: 1., color: background(theme.highest, "on", "hovered") + }, }, clicked: { ...text(theme.highest, "mono", "on", "pressed"), background: background(theme.highest, "on", "pressed"), + border: { + width: 1., color: background(theme.highest, "on", "pressed") + }, }, }, }), @@ -186,8 +194,8 @@ export default function search(): any { mode_button: toggleable({ base: interactive({ base: { - ...text(theme.highest, "mono", "on"), - background: background(theme.highest, "on"), + ...text(theme.highest, "mono", "variant"), + background: background(theme.highest, "variant"), border: { ...border(theme.highest, "on"), @@ -205,13 +213,13 @@ export default function search(): any { }, state: { hovered: { - ...text(theme.highest, "mono", "on", "hovered"), - background: background(theme.highest, "on", "hovered"), + ...text(theme.highest, "mono", "variant", "hovered"), + background: background(theme.highest, "variant", "hovered"), border: border(theme.highest, "on", "hovered"), }, clicked: { - ...text(theme.highest, "mono", "on", "pressed"), - background: background(theme.highest, "on", "pressed"), + ...text(theme.highest, "mono", "variant", "pressed"), + background: background(theme.highest, "variant", "pressed"), border: border(theme.highest, "on", "pressed"), }, }, @@ -219,13 +227,16 @@ export default function search(): any { state: { active: { default: { - ...text(theme.highest, "mono", "accent"), + ...text(theme.highest, "mono", "on"), + background: background(theme.highest, "on") }, hovered: { - ...text(theme.highest, "mono", "accent", "hovered"), + ...text(theme.highest, "mono", "on", "hovered"), + background: background(theme.highest, "on", "hovered") }, clicked: { - ...text(theme.highest, "mono", "accent", "pressed"), + ...text(theme.highest, "mono", "on", "pressed"), + background: background(theme.highest, "on", "pressed") }, }, }, From f6ecf83f33d2c95e014eaa5b8951dae7434986c9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 16:43:07 +0200 Subject: [PATCH 078/119] Increase editor's left hand side padding to 10 --- styles/src/style_tree/search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index ff36e3532d..e612c8daf5 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -22,7 +22,7 @@ export default function search(): any { padding: { top: 4, bottom: 4, - left: 4, + left: 10, right: 4, }, } From 100a8961b5dfd4f7f7e28bbf3afec25a9a100fbd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 16:51:18 +0200 Subject: [PATCH 079/119] Add accent colors to search option buttons --- styles/src/style_tree/search.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index e612c8daf5..252e759a6d 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -77,16 +77,16 @@ export default function search(): any { state: { active: { default: { - background: background(theme.highest, "accent", "hovered"), - border: border(theme.highest, "on"), + background: background(theme.highest, "accent"), + border: border(theme.highest, "accent"), }, hovered: { - ...text(theme.highest, "mono", "accent", "hovered"), - border: border(theme.highest, "on", "hovered"), + background: background(theme.highest, "accent", "hovered"), + border: border(theme.highest, "accent", "hovered"), }, clicked: { - ...text(theme.highest, "mono", "accent", "pressed"), - border: border(theme.highest, "on", "pressed"), + background: background(theme.highest, "accent", "pressed"), + border: border(theme.highest, "accent", "pressed"), }, }, }, From 799278e2966c50711fd76dc2def85abaac2790d2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:02:25 +0200 Subject: [PATCH 080/119] Add row_height --- crates/search/src/project_search.rs | 4 +++- crates/search/src/search_bar.rs | 4 ++-- crates/theme/src/theme.rs | 1 + styles/src/style_tree/search.ts | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index a3ae61f0a8..aa30d47243 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1592,7 +1592,8 @@ impl View for ProjectSearchBar { .top() .left(), ) - .contained() + .constrained() + .with_max_height(theme.search.search_bar_row_height) .flex(1., true), ) .with_child( @@ -1609,6 +1610,7 @@ impl View for ProjectSearchBar { .constrained() .with_min_width(theme.search.editor.min_width) .with_max_width(theme.search.editor.max_width) + .with_max_height(theme.search.search_bar_row_height) .flex(1., false), ) .contained() diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 6cdb36acc3..593e0aeb69 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -163,7 +163,7 @@ pub(crate) fn render_search_mode_button( .contained() .constrained() .with_max_width(side_width) - .with_height(32.), + .with_height(theme.search.search_bar_row_height), ) .with_child(label) .into_any() @@ -183,7 +183,7 @@ pub(crate) fn render_search_mode_button( .contained() .constrained() .with_max_width(side_width) - .with_height(32.), + .with_height(theme.search.search_bar_row_height), ) .into_any() } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index e950845b49..dbcffe0382 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -389,6 +389,7 @@ pub struct Search { pub editor_icon: IconStyle, pub mode_button: Toggleable>, pub nav_button: Interactive, + pub search_bar_row_height: f32, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 252e759a6d..c8e347a57d 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -272,6 +272,7 @@ export default function search(): any { }, }, }), + search_bar_row_height: 32, } } From e4c593c2fb09c7f97bea75a07c5fa8f39afa49bd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:09:26 +0200 Subject: [PATCH 081/119] chore: fix compiler warning --- crates/search/src/search_bar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 593e0aeb69..1569174c92 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -139,7 +139,7 @@ pub(crate) fn render_search_mode_button( .clone(); let side_width = style.container.corner_radius; style.container.corner_radius = 0.; - if let Some(button_side) = mode.button_side() { + if mode.button_side().is_some() { style.container.border.left = mode.border_left(); style.container.border.right = mode.border_right(); } From 0a0314eec92472e840de4753f883baeb3092fbb3 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:19:45 +0200 Subject: [PATCH 082/119] Shrink project search + decrease padding --- crates/search/src/project_search.rs | 2 +- styles/src/style_tree/search.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index aa30d47243..f0363ee5d9 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1693,7 +1693,7 @@ impl ToolbarItemView for ProjectSearchBar { .as_ref() .map(|search| { let offset = search.read(cx).filters_enabled as usize; - 3 + offset + 2 + offset }) .unwrap_or_else(|| 2) } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index c8e347a57d..6a6b2cc3ac 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -34,7 +34,7 @@ export default function search(): any { } return { - padding: { top: 32, bottom: 32, left: 32, right: 32 }, + padding: { top: 16, bottom: 16, left: 16, right: 16 }, // TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive match_background: with_opacity( foreground(theme.highest, "accent"), From c0356fdf16320347c18c95acbab326c5b505d482 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Fri, 11 Aug 2023 23:47:16 +0200 Subject: [PATCH 083/119] Decrease row count for buffer search bar --- crates/search/src/buffer_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index e55d91ac1c..505e871af7 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -368,7 +368,7 @@ impl ToolbarItemView for BufferSearchBar { } } fn row_count(&self, _: &ViewContext) -> usize { - 3 + 2 } } From 94f1d8281ab326b19e72dd7cdd80f6f7782f89ee Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:18:27 +0200 Subject: [PATCH 084/119] Remove buttonside, use new corner_radii instead --- crates/search/src/elements.rs | 163 ---------------------------- crates/search/src/project_search.rs | 4 +- crates/search/src/search.rs | 1 - crates/search/src/search_bar.rs | 117 +++++++------------- styles/src/style_tree/search.ts | 2 +- 5 files changed, 42 insertions(+), 245 deletions(-) delete mode 100644 crates/search/src/elements.rs diff --git a/crates/search/src/elements.rs b/crates/search/src/elements.rs deleted file mode 100644 index 0a7990c24a..0000000000 --- a/crates/search/src/elements.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::ops::Range; - -use gpui::color::Color; -use gpui::geometry::rect::RectF; -use gpui::geometry::vector::IntoVector2F; -use gpui::json::{self, ToJson}; -use gpui::{scene::Path, LayoutContext}; -use gpui::{Element, PaintContext, SceneBuilder, View, ViewContext}; - -type CreatePath = fn(RectF, Color, f32) -> Path; -type AdjustBorder = fn(RectF, f32) -> RectF; -type BorderThickness = f32; - -pub(crate) struct ButtonSide { - color: Color, - factory: CreatePath, - /// After the outline is drawn with border color, - /// the drawing bounds have to be adjusted by different factors in different dimensions. - border_adjustment: AdjustBorder, - border: Option<(BorderThickness, Color)>, - radius: f32, -} - -impl ButtonSide { - fn new( - color: Color, - factory: CreatePath, - border_adjustment: AdjustBorder, - radius: f32, - ) -> Self { - Self { - color, - factory, - border_adjustment, - border: None, - radius, - } - } - pub fn with_border(mut self, width: f32, color: Color) -> Self { - self.border = Some((width, color)); - self - } - pub fn left(color: Color, corner_radius: f32) -> Self { - Self::new( - color, - left_button_side, - left_button_border_adjust, - corner_radius, - ) - } - pub fn right(color: Color, corner_radius: f32) -> Self { - Self::new( - color, - right_button_side, - right_button_border_adjust, - corner_radius, - ) - } -} - -fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut lower_right = bounds.clone().lower_right(); - lower_right.set_x(lower_right.x() + width.x()); - RectF::from_points(bounds.origin() + width, lower_right) -} -fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF { - let width = width.into_vector_2f(); - let mut origin = bounds.clone().origin(); - origin.set_x(origin.x() - width.x()); - RectF::from_points(origin, bounds.lower_right() - width) -} -fn left_button_side(bounds: RectF, color: Color, radius: f32) -> Path { - use gpui::geometry::PathBuilder; - let mut path = PathBuilder::new(); - path.reset(bounds.lower_right()); - path.line_to(bounds.upper_right()); - let mut middle_point = bounds.origin(); - let distance_to_line = (middle_point.y() - bounds.lower_left().y()).min(-radius.abs()); - middle_point.set_y(middle_point.y() - distance_to_line); - path.curve_to(middle_point, bounds.origin()); - let mut target = bounds.lower_left(); - target.set_y(target.y() + distance_to_line); - path.line_to(target); - path.curve_to(bounds.lower_right(), bounds.lower_left()); - path.build(color, None) -} - -fn right_button_side(bounds: RectF, color: Color, radius: f32) -> Path { - use gpui::geometry::PathBuilder; - let mut path = PathBuilder::new(); - path.reset(bounds.lower_left()); - path.line_to(bounds.origin()); - let mut middle_point = bounds.upper_right(); - let distance_to_line = (middle_point.y() - bounds.lower_right().y()).min(-radius.abs()); - middle_point.set_y(middle_point.y() - distance_to_line); - path.curve_to(middle_point, bounds.upper_right()); - let mut target = bounds.lower_right(); - target.set_y(target.y() + distance_to_line); - path.line_to(target); - path.curve_to(bounds.lower_left(), bounds.lower_right()); - path.build(color, None) -} - -impl Element for ButtonSide { - type LayoutState = (); - - type PaintState = (); - - fn layout( - &mut self, - constraint: gpui::SizeConstraint, - _: &mut V, - _: &mut LayoutContext, - ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - (constraint.max, ()) - } - - fn paint( - &mut self, - scene: &mut SceneBuilder, - bounds: RectF, - _: RectF, - _: &mut Self::LayoutState, - _: &mut V, - _: &mut PaintContext, - ) -> Self::PaintState { - let mut bounds = bounds; - if let Some((border_width, border_color)) = self.border.as_ref() { - scene.push_path((self.factory)(bounds, border_color.clone(), self.radius)); - bounds = (self.border_adjustment)(bounds, *border_width); - }; - scene.push_path((self.factory)(bounds, self.color, self.radius)); - } - - fn rect_for_text_range( - &self, - _: Range, - _: RectF, - _: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &V, - _: &ViewContext, - ) -> Option { - None - } - - fn debug( - &self, - bounds: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &V, - _: &ViewContext, - ) -> gpui::json::Value { - json::json!({ - "type": "ButtonSide", - "bounds": bounds.to_json(), - "color": self.color.to_json(), - }) - } -} diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index db48fa14c3..5589688368 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1593,7 +1593,7 @@ impl View for ProjectSearchBar { .left(), ) .constrained() - .with_max_height(theme.search.search_bar_row_height) + .with_height(theme.search.search_bar_row_height) .flex(1., true), ) .with_child( @@ -1642,7 +1642,7 @@ impl View for ProjectSearchBar { None, )) .constrained() - .with_height(theme.workspace.toolbar.height) + .with_height(theme.search.search_bar_row_height) .contained() .aligned() .right() diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 33905f83ec..079a8965eb 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -6,7 +6,6 @@ use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; pub mod buffer_search; -mod elements; mod history; mod mode; pub mod project_search; diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index ecfb51b1c6..afc0d109d6 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -1,13 +1,12 @@ use gpui::{ elements::{Flex, Label, MouseEventHandler, ParentElement, Svg}, platform::{CursorStyle, MouseButton}, - scene::MouseClick, + scene::{CornerRadii, MouseClick}, Action, AnyElement, Element, EventContext, View, ViewContext, }; use workspace::searchable::Direction; use crate::{ - elements::ButtonSide, mode::{SearchMode, Side}, SearchOptions, SelectNextMatch, SelectPrevMatch, }; @@ -65,48 +64,31 @@ pub(super) fn render_nav_button( MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); let mut style = theme.search.nav_button.style_for(state).clone(); - let button_side_width = style.container.corner_radii.top_left; - style.container.corner_radii = (0.).into(); - let label = Label::new(icon, style.label.clone()) - .contained() - .with_style(style.container.clone()); + let mut container_style = style.container.clone(); + let label = Label::new(icon, style.label.clone()).contained(); match direction { - Direction::Prev => Flex::row() - .with_child( - ButtonSide::left( - style - .clone() - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - button_side_width, - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() + Direction::Prev => { + container_style.corner_radii = CornerRadii { + bottom_right: 0., + top_right: 0., + ..container_style.corner_radii + }; + Flex::row() + .with_child(label.with_style(container_style)) .constrained() - .with_max_width(button_side_width), - ) - .with_child(label) - .constrained() - .with_height(theme.workspace.toolbar.height), - Direction::Next => Flex::row() - .with_child(label) - .with_child( - ButtonSide::right( - style - .clone() - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - button_side_width, - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() + //.with_height(theme.workspace.toolbar.height) + } + Direction::Next => { + container_style.corner_radii = CornerRadii { + bottom_left: 0., + top_left: 0., + ..container_style.corner_radii + }; + Flex::row() + .with_child(label.with_style(container_style)) .constrained() - .with_max_width(button_side_width), - ) - .constrained() - .with_height(theme.workspace.toolbar.height), + // .with_height(theme.workspace.toolbar.height) + } } }) .on_click(MouseButton::Left, on_click) @@ -137,58 +119,37 @@ pub(crate) fn render_search_mode_button( .in_state(is_active) .style_for(state) .clone(); - let side_width = style.container.corner_radii.top_left; - style.container.corner_radii = (0.).into(); if mode.button_side().is_some() { style.container.border.left = mode.border_left(); style.container.border.right = mode.border_right(); } - let label = Label::new(mode.label(), style.text.clone()) - .contained() - .with_style(style.container); - + let label = Label::new(mode.label(), style.text.clone()).contained(); + let mut container_style = style.container.clone(); if let Some(button_side) = mode.button_side() { if button_side == Side::Left { + container_style.corner_radii = CornerRadii { + bottom_right: 0., + top_right: 0., + ..container_style.corner_radii + }; Flex::row() .align_children_center() - .with_child( - ButtonSide::left( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - side_width, - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(side_width) - .with_height(theme.search.search_bar_row_height), - ) - .with_child(label) + .with_child(label.with_style(container_style)) .into_any() } else { + container_style.corner_radii = CornerRadii { + bottom_left: 0., + top_left: 0., + ..container_style.corner_radii + }; Flex::row() .align_children_center() - .with_child(label) - .with_child( - ButtonSide::right( - style - .container - .background_color - .unwrap_or_else(gpui::color::Color::transparent_black), - side_width, - ) - .with_border(style.container.border.width, style.container.border.color) - .contained() - .constrained() - .with_max_width(side_width) - .with_height(theme.search.search_bar_row_height), - ) + .with_child(label.with_style(container_style)) .into_any() } } else { - label.into_any() + container_style.corner_radii = CornerRadii::default(); + label.with_style(container_style).into_any() } }) .on_click(MouseButton::Left, on_click) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 6a6b2cc3ac..e853beacdd 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -245,7 +245,7 @@ export default function search(): any { base: { text: text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: 6, + corner_radius: { top_left: 6, top_right: 6, bottom_right: 6, bottom_left: 6 }, border: { ...border(theme.highest, "on"), left: false, From c6425b36dadf442fecd55194c0198c30b2f6bbf7 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:20:22 +0200 Subject: [PATCH 085/119] chore: remove redundant mut --- crates/search/src/search_bar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index afc0d109d6..8bc444ed67 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -63,7 +63,7 @@ pub(super) fn render_nav_button( enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); - let mut style = theme.search.nav_button.style_for(state).clone(); + let style = theme.search.nav_button.style_for(state).clone(); let mut container_style = style.container.clone(); let label = Label::new(icon, style.label.clone()).contained(); match direction { From e151555542a28505706808288a2bdff3c60d6935 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 13 Aug 2023 01:06:35 +0200 Subject: [PATCH 086/119] Style dismiss button. Fix clipping in nav buttons and mode buttons. Add missing borders to outskirts of mode buttons. --- crates/search/src/mode.rs | 4 +-- crates/search/src/project_search.rs | 49 ++++++++++++++++---------- crates/search/src/search_bar.rs | 53 ++++++++++------------------- styles/src/style_tree/search.ts | 27 ++++++++++----- 4 files changed, 69 insertions(+), 64 deletions(-) diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index bb620f0670..ecaa00f6ac 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -49,14 +49,14 @@ impl SearchMode { } } - pub(crate) fn border_left(&self) -> bool { + pub(crate) fn border_right(&self) -> bool { match self { SearchMode::Text => false, _ => true, } } - pub(crate) fn border_right(&self) -> bool { + pub(crate) fn border_left(&self) -> bool { match self { SearchMode::Regex => false, _ => true, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 5589688368..a462327ec0 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1590,10 +1590,10 @@ impl View for ProjectSearchBar { .with_children(matches) .aligned() .top() - .left(), + .left() + .constrained() + .with_height(theme.search.search_bar_row_height), ) - .constrained() - .with_height(theme.search.search_bar_row_height) .flex(1., true), ) .with_child( @@ -1627,23 +1627,36 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .align_children_center() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_children(semantic_index) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .with_child(super::search_bar::render_close_button( - "Dismiss Project Search", - &theme.search, - cx, - |_, this, cx| { - if let Some(search) = this.active_project_search.as_mut() { - search.update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) - } - }, - None, - )) + .with_child( + Flex::row() + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_children(semantic_index) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .aligned() + .left() + .contained() + .with_margin_right(3.), + ) + .with_child( + super::search_bar::render_close_button( + "Dismiss Project Search", + &theme.search, + cx, + |_, this, cx| { + if let Some(search) = + this.active_project_search.as_mut() + { + search + .update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) + } + }, + None, + ) + .aligned() + .right(), + ) .constrained() .with_height(theme.search.search_bar_row_height) - .contained() .aligned() .right() .top() diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 8bc444ed67..f0ab9a99fb 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -28,10 +28,10 @@ pub(super) fn render_close_button( .constrained() .with_width(style.icon_width) .aligned() - .constrained() - .with_width(style.button_width) .contained() .with_style(style.container) + .constrained() + .with_height(theme.search_bar_row_height) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) @@ -66,30 +66,19 @@ pub(super) fn render_nav_button( let style = theme.search.nav_button.style_for(state).clone(); let mut container_style = style.container.clone(); let label = Label::new(icon, style.label.clone()).contained(); - match direction { - Direction::Prev => { - container_style.corner_radii = CornerRadii { - bottom_right: 0., - top_right: 0., - ..container_style.corner_radii - }; - Flex::row() - .with_child(label.with_style(container_style)) - .constrained() - //.with_height(theme.workspace.toolbar.height) - } - Direction::Next => { - container_style.corner_radii = CornerRadii { - bottom_left: 0., - top_left: 0., - ..container_style.corner_radii - }; - Flex::row() - .with_child(label.with_style(container_style)) - .constrained() - // .with_height(theme.workspace.toolbar.height) - } - } + container_style.corner_radii = match direction { + Direction::Prev => CornerRadii { + bottom_right: 0., + top_right: 0., + ..container_style.corner_radii + }, + Direction::Next => CornerRadii { + bottom_left: 0., + top_left: 0., + ..container_style.corner_radii + }, + }; + label.with_style(container_style) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) @@ -132,24 +121,18 @@ pub(crate) fn render_search_mode_button( top_right: 0., ..container_style.corner_radii }; - Flex::row() - .align_children_center() - .with_child(label.with_style(container_style)) - .into_any() + label.with_style(container_style) } else { container_style.corner_radii = CornerRadii { bottom_left: 0., top_left: 0., ..container_style.corner_radii }; - Flex::row() - .align_children_center() - .with_child(label.with_style(container_style)) - .into_any() + label.with_style(container_style) } } else { container_style.corner_radii = CornerRadii::default(); - label.with_style(container_style).into_any() + label.with_style(container_style) } }) .on_click(MouseButton::Left, on_click) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index e853beacdd..a4428fe4a8 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -161,19 +161,28 @@ export default function search(): any { dismiss_button: interactive({ base: { color: foreground(theme.highest, "variant"), - icon_width: 12, - button_width: 14, + icon_width: 14, + button_width: 32, + corner_radius: 6, padding: { - left: 10, - right: 10, + top: 8, + bottom: 8, + left: 8, + right: 8, }, + + background: background(theme.highest, "variant"), + + border: border(theme.highest, "on"), }, state: { hovered: { color: foreground(theme.highest, "hovered"), + background: background(theme.highest, "variant", "hovered") }, clicked: { color: foreground(theme.highest, "pressed"), + background: background(theme.highest, "variant", "pressed") }, }, }), @@ -245,7 +254,7 @@ export default function search(): any { base: { text: text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: { top_left: 6, top_right: 6, bottom_right: 6, bottom_left: 6 }, + corner_radius: 6, border: { ...border(theme.highest, "on"), left: false, @@ -253,10 +262,10 @@ export default function search(): any { }, padding: { - bottom: 6, - left: 6, - right: 6, - top: 6, + bottom: 3, + left: 10, + right: 10, + top: 3, }, }, state: { From b586601cab24255466b7b593d7d509a334855217 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 13 Aug 2023 01:29:35 +0200 Subject: [PATCH 087/119] Style nav buttons --- crates/search/src/buffer_search.rs | 1 + crates/search/src/project_search.rs | 2 + crates/search/src/search_bar.rs | 18 +++++-- crates/theme/src/theme.rs | 2 +- styles/src/style_tree/search.ts | 84 +++++++++++++++++++---------- 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 505e871af7..5c02874f97 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -209,6 +209,7 @@ impl View for BufferSearchBar { render_nav_button( label, direction, + self.active_match_index.is_some(), move |_, this, cx| match direction { Direction::Prev => this.select_prev_match(&Default::default(), cx), Direction::Next => this.select_next_match(&Default::default(), cx), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index a462327ec0..7bc7741882 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1552,12 +1552,14 @@ impl View for ProjectSearchBar { cx, ) }; + let is_active = search.active_match_index.is_some(); let semantic_index = SemanticIndex::enabled(cx) .then(|| search_button_for_mode(SearchMode::Semantic, cx)); let nav_button_for_direction = |label, direction, cx: &mut ViewContext| { render_nav_button( label, direction, + is_active, move |_, this, cx| { if let Some(search) = this.active_project_search.as_ref() { search.update(cx, |search, cx| search.select_match(direction, cx)); diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index f0ab9a99fb..19a40a4a78 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -1,5 +1,5 @@ use gpui::{ - elements::{Flex, Label, MouseEventHandler, ParentElement, Svg}, + elements::{Label, MouseEventHandler, Svg}, platform::{CursorStyle, MouseButton}, scene::{CornerRadii, MouseClick}, Action, AnyElement, Element, EventContext, View, ViewContext, @@ -42,6 +42,7 @@ pub(super) fn render_close_button( pub(super) fn render_nav_button( icon: &'static str, direction: Direction, + active: bool, on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, cx: &mut ViewContext, ) -> AnyElement { @@ -59,11 +60,20 @@ pub(super) fn render_nav_button( } }; let tooltip_style = theme::current(cx).tooltip.clone(); - + let cursor_style = if active { + CursorStyle::PointingHand + } else { + CursorStyle::default() + }; enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { let theme = theme::current(cx); - let style = theme.search.nav_button.style_for(state).clone(); + let style = theme + .search + .nav_button + .in_state(active) + .style_for(state) + .clone(); let mut container_style = style.container.clone(); let label = Label::new(icon, style.label.clone()).contained(); container_style.corner_radii = match direction { @@ -81,7 +91,7 @@ pub(super) fn render_nav_button( label.with_style(container_style) }) .on_click(MouseButton::Left, on_click) - .with_cursor_style(CursorStyle::PointingHand) + .with_cursor_style(cursor_style) .with_tooltip::( direction as usize, tooltip.to_string(), diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index dbcffe0382..41a9eaaba5 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -388,7 +388,7 @@ pub struct Search { pub dismiss_button: Interactive, pub editor_icon: IconStyle, pub mode_button: Toggleable>, - pub nav_button: Interactive, + pub nav_button: Toggleable>, pub search_bar_row_height: f32, } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index a4428fe4a8..16de861310 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -250,36 +250,62 @@ export default function search(): any { }, }, }), - nav_button: interactive({ - base: { - text: text(theme.highest, "mono", "on"), - background: background(theme.highest, "on"), - corner_radius: 6, - border: { - ...border(theme.highest, "on"), - left: false, - right: false, - }, - - padding: { - bottom: 3, - left: 10, - right: 10, - top: 3, - }, - }, + nav_button: toggleable({ state: { - hovered: { - ...text(theme.highest, "mono", "on", "hovered"), - background: background(theme.highest, "on", "hovered"), - border: border(theme.highest, "on", "hovered"), - }, - clicked: { - ...text(theme.highest, "mono", "on", "pressed"), - background: background(theme.highest, "on", "pressed"), - border: border(theme.highest, "on", "pressed"), - }, - }, + inactive: interactive({ + base: { + background: background(theme.highest, "disabled"), + text: text(theme.highest, "mono", "disabled"), + corner_radius: 6, + border: { + ...border(theme.highest, "disabled"), + left: false, + right: false, + }, + + padding: { + bottom: 3, + left: 10, + right: 10, + top: 3, + }, + }, + state: { + hovered: {} + } + }), + active: interactive({ + base: { + text: text(theme.highest, "mono", "on"), + background: background(theme.highest, "on"), + corner_radius: 6, + border: { + ...border(theme.highest, "on"), + left: false, + right: false, + }, + + padding: { + bottom: 3, + left: 10, + right: 10, + top: 3, + }, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "on", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: border(theme.highest, "on", "hovered"), + }, + clicked: { + ...text(theme.highest, "mono", "on", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: border(theme.highest, "on", "pressed"), + }, + }, + }) + } }), search_bar_row_height: 32, From 9121178ba0154b101e587de6e2214cd8bbe2db9f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:59:42 +0200 Subject: [PATCH 088/119] project_search: Swap places of case-sens/whole-word --- crates/search/src/project_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 7bc7741882..7389faf031 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1488,8 +1488,8 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .with_child(filter_button) - .with_children(whole_word) .with_children(case_sensitive) + .with_children(whole_word) .flex(1., true) .contained(), ) From d37ebe7841f5d92d428d8ee6e945bca842234658 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:10:37 +0200 Subject: [PATCH 089/119] Add option_button_height style --- crates/search/src/search_bar.rs | 2 +- crates/theme/src/theme.rs | 1 + styles/src/style_tree/search.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 19a40a4a78..10f1a984b5 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -177,7 +177,7 @@ pub(crate) fn render_option_button_icon( .contained() .with_style(style.container) .constrained() - .with_height(22.) + .with_height(theme.search.option_button_height) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 41a9eaaba5..100b2c676b 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -390,6 +390,7 @@ pub struct Search { pub mode_button: Toggleable>, pub nav_button: Toggleable>, pub search_bar_row_height: f32, + pub option_button_height: f32, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 16de861310..edf9a46e42 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -308,6 +308,7 @@ export default function search(): any { } }), search_bar_row_height: 32, + option_button_height: 22, } } From 1c35db7e97997dea0f0c59201ebbab1ff63c91a2 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:20:59 +0200 Subject: [PATCH 090/119] project_search: style filters button like the rest of the buttons --- crates/search/src/buffer_search.rs | 4 ++- crates/search/src/project_search.rs | 51 ++++++++++------------------- crates/search/src/search_bar.rs | 18 +++++----- 3 files changed, 28 insertions(+), 45 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 5c02874f97..585672c45b 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -174,7 +174,9 @@ impl View for BufferSearchBar { crate::search_bar::render_option_button_icon::( is_active, icon, - option, + option.bits as usize, + format!("Toggle {}", option.label()), + option.to_toggle_action(), move |_, this, cx| { this.toggle_search_option(option, cx); }, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 7389faf031..de0c424e7b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1,7 +1,7 @@ use crate::{ history::SearchHistory, mode::SearchMode, - search_bar::{render_nav_button, render_search_mode_button}, + search_bar::{render_nav_button, render_option_button_icon, render_search_mode_button}, ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; @@ -16,11 +16,9 @@ use futures::StreamExt; use gpui::platform::PromptLevel; use gpui::{ - actions, - elements::*, - platform::{CursorStyle, MouseButton}, - Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, - Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, + actions, elements::*, platform::MouseButton, Action, AnyElement, AnyViewHandle, AppContext, + Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, + WeakModelHandle, WeakViewHandle, }; use menu::Confirm; @@ -1417,41 +1415,26 @@ impl View for ProjectSearchBar { .flex(1.0, true); let row_spacing = theme.workspace.toolbar.container.padding.bottom; let search = _search.read(cx); - let filter_button = { - let tooltip_style = theme::current(cx).tooltip.clone(); - let is_active = search.filters_enabled; - MouseEventHandler::::new(0, cx, |state, cx| { - let theme = theme::current(cx); - let style = theme - .search - .option_button - .in_state(is_active) - .style_for(state); - Svg::new("icons/filter_12.svg") - .with_color(style.text.color.clone()) - .contained() - .with_style(style.container) - }) - .on_click(MouseButton::Left, move |_, this, cx| { + let filter_button = render_option_button_icon( + search.filters_enabled, + "icons/filter_12.svg", + 0, + "Toggle filters", + Box::new(ToggleFilters), + move |_, this, cx| { this.toggle_filters(cx); - }) - .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - 0, - "Toggle filters", - Some(Box::new(ToggleFilters)), - tooltip_style, - cx, - ) - .into_any() - }; + }, + cx, + ); let search = _search.read(cx); let is_semantic_disabled = search.semantic_state.is_none(); let render_option_button_icon = |path, option, cx: &mut ViewContext| { crate::search_bar::render_option_button_icon( self.is_option_enabled(option, cx), path, - option, + option.bits as usize, + format!("Toggle {}", option.label()), + option.to_toggle_action(), move |_, this, cx| { this.toggle_search_option(option, cx); }, diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 10f1a984b5..0cd63922ef 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use gpui::{ elements::{Label, MouseEventHandler, Svg}, platform::{CursorStyle, MouseButton}, @@ -8,7 +10,7 @@ use workspace::searchable::Direction; use crate::{ mode::{SearchMode, Side}, - SearchOptions, SelectNextMatch, SelectPrevMatch, + SelectNextMatch, SelectPrevMatch, }; pub(super) fn render_close_button( @@ -160,12 +162,14 @@ pub(crate) fn render_search_mode_button( pub(crate) fn render_option_button_icon( is_active: bool, icon: &'static str, - option: SearchOptions, + id: usize, + label: impl Into>, + action: Box, on_click: impl Fn(MouseClick, &mut V, &mut EventContext) + 'static, cx: &mut ViewContext, ) -> AnyElement { let tooltip_style = theme::current(cx).tooltip.clone(); - MouseEventHandler::::new(option.bits as usize, cx, |state, cx| { + MouseEventHandler::::new(id, cx, |state, cx| { let theme = theme::current(cx); let style = theme .search @@ -181,12 +185,6 @@ pub(crate) fn render_option_button_icon( }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::( - option.bits as usize, - format!("Toggle {}", option.label()), - Some(option.to_toggle_action()), - tooltip_style, - cx, - ) + .with_tooltip::(id, label, Some(action), tooltip_style, cx) .into_any() } From 8bb3bfa6a87d720e5cb7fee0a434d78915ec98ee Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:25:59 +0200 Subject: [PATCH 091/119] Style buffer modes like project modes --- crates/search/src/buffer_search.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 585672c45b..681c098a37 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -237,7 +237,8 @@ impl View for BufferSearchBar { .left() .top() .flex(1., true) - .constrained(), + .constrained() + .with_max_height(theme.search.search_bar_row_height), ) .contained(), ) @@ -280,6 +281,7 @@ impl View for BufferSearchBar { .constrained() .with_min_width(theme.search.editor.min_width) .with_max_width(theme.search.editor.max_width) + .with_max_height(theme.search.search_bar_row_height) .flex(1., false), ) .with_child( @@ -307,6 +309,8 @@ impl View for BufferSearchBar { |_, this, cx| this.dismiss(&Default::default(), cx), Some(Box::new(Dismiss)), )) + .constrained() + .with_height(theme.search.search_bar_row_height) .contained() .aligned() .right() From 94ac84a908eb74bce91bd24e5e0b7fde187f8167 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:28:35 +0200 Subject: [PATCH 092/119] Fix borders being cut off in buffer search --- crates/search/src/buffer_search.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 681c098a37..33c6c1ec85 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -300,18 +300,28 @@ impl View for BufferSearchBar { Flex::column().with_child( Flex::row() .align_children_center() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .with_child(super::search_bar::render_close_button( - "Dismiss Buffer Search", - &theme.search, - cx, - |_, this, cx| this.dismiss(&Default::default(), cx), - Some(Box::new(Dismiss)), - )) + .with_child( + Flex::row() + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .aligned() + .left() + .contained() + .with_margin_right(3.), + ) + .with_child( + super::search_bar::render_close_button( + "Dismiss Buffer Search", + &theme.search, + cx, + |_, this, cx| this.dismiss(&Default::default(), cx), + Some(Box::new(Dismiss)), + ) + .aligned() + .right(), + ) .constrained() .with_height(theme.search.search_bar_row_height) - .contained() .aligned() .right() .top() From ae229d4c524cdb62734b00afd495e08c90f578ed Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:31:11 +0200 Subject: [PATCH 093/119] Align "Select all" to the center --- crates/search/src/buffer_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 33c6c1ec85..c7186065d6 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -247,9 +247,9 @@ impl View for BufferSearchBar { .align_children_center() .with_child( Flex::row() + .align_children_center() .with_child( Flex::row() - .align_children_center() .with_child( Svg::for_style(icon_style.icon) .contained() From 9ae28f81c14146bfb1bf1a22aae2949706d4e692 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:35:33 +0200 Subject: [PATCH 094/119] Update option buttons text color --- styles/src/style_tree/search.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index edf9a46e42..7d9600771d 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -43,7 +43,7 @@ export default function search(): any { option_button: toggleable({ base: interactive({ base: { - ...text(theme.highest, "mono", "on"), + ...text(theme.highest, "mono", "variant"), background: background(theme.highest, "on"), corner_radius: 2, margin: { right: 2 }, @@ -59,14 +59,14 @@ export default function search(): any { }, state: { hovered: { - ...text(theme.highest, "mono", "on", "hovered"), + ...text(theme.highest, "mono", "variant", "hovered"), background: background(theme.highest, "on", "hovered"), border: { width: 1., color: background(theme.highest, "on", "hovered") }, }, clicked: { - ...text(theme.highest, "mono", "on", "pressed"), + ...text(theme.highest, "mono", "variant", "pressed"), background: background(theme.highest, "on", "pressed"), border: { width: 1., color: background(theme.highest, "on", "pressed") From b72a42ec2916f3e65f62eb59f77f6943cbb64d05 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:31:14 +0200 Subject: [PATCH 095/119] buffer: fix alignment of editor icon. Co-authored-by: Kyle --- crates/search/src/buffer_search.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index c7186065d6..d77f8ac51c 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -250,6 +250,7 @@ impl View for BufferSearchBar { .align_children_center() .with_child( Flex::row() + .align_children_center() .with_child( Svg::for_style(icon_style.icon) .contained() From 7314456685a618382ee1ff0a176cb18d12178eea Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:33:40 +0200 Subject: [PATCH 096/119] nav_button: Fix double border between nav buttons. Co-authored-by: Kyle --- crates/search/src/search_bar.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 0cd63922ef..00eff11f8a 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -90,6 +90,11 @@ pub(super) fn render_nav_button( ..container_style.corner_radii }, }; + if direction == Direction::Prev { + // Remove right border so that when both Next and Prev buttons are + // next to one another, there's no double border between them. + container_style.border.right = false; + } label.with_style(container_style) }) .on_click(MouseButton::Left, on_click) From d17aba4fd34a30812e938baaecbd7c450eaebd14 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:43:06 +0200 Subject: [PATCH 097/119] Fix double borders in mode buttons. Co-authored-by: Kyle --- crates/search/src/mode.rs | 9 +++++---- crates/search/src/search_bar.rs | 7 +++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index ecaa00f6ac..2c180be761 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -51,15 +51,16 @@ impl SearchMode { pub(crate) fn border_right(&self) -> bool { match self { - SearchMode::Text => false, - _ => true, + SearchMode::Regex => true, + SearchMode::Text => true, + SearchMode::Semantic => true, } } pub(crate) fn border_left(&self) -> bool { match self { - SearchMode::Regex => false, - _ => true, + SearchMode::Text => true, + _ => false, } } diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 00eff11f8a..088a96b279 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -125,10 +125,9 @@ pub(crate) fn render_search_mode_button( .in_state(is_active) .style_for(state) .clone(); - if mode.button_side().is_some() { - style.container.border.left = mode.border_left(); - style.container.border.right = mode.border_right(); - } + style.container.border.left = mode.border_left(); + style.container.border.right = mode.border_right(); + let label = Label::new(mode.label(), style.text.clone()).contained(); let mut container_style = style.container.clone(); if let Some(button_side) = mode.button_side() { From f4121b42da9dd6d2c72a4e5bc920a6337d9eeb41 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:01:01 +0200 Subject: [PATCH 098/119] Add more padding to dismiss button. Add a style for mode buttons group margin Co-authored-by: Kyle --- crates/search/src/buffer_search.rs | 2 +- crates/search/src/project_search.rs | 4 +++- crates/theme/src/theme.rs | 1 + styles/src/style_tree/search.ts | 13 +++++++++---- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index d77f8ac51c..408c696a91 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -308,7 +308,7 @@ impl View for BufferSearchBar { .aligned() .left() .contained() - .with_margin_right(3.), + .with_style(theme.search.modes_container), ) .with_child( super::search_bar::render_close_button( diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index de0c424e7b..4e265a8c65 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1617,10 +1617,12 @@ impl View for ProjectSearchBar { .with_child(search_button_for_mode(SearchMode::Text, cx)) .with_children(semantic_index) .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .constrained() + .with_height(theme.search.search_bar_row_height) .aligned() .left() .contained() - .with_margin_right(3.), + .with_style(theme.search.modes_container), ) .with_child( super::search_bar::render_close_button( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 100b2c676b..91d7eeb5dc 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -391,6 +391,7 @@ pub struct Search { pub nav_button: Toggleable>, pub search_bar_row_height: f32, pub option_button_height: f32, + pub modes_container: ContainerStyle, } #[derive(Clone, Deserialize, Default, JsonSchema)] diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 7d9600771d..76452ddec3 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -165,10 +165,10 @@ export default function search(): any { button_width: 32, corner_radius: 6, padding: { - top: 8, - bottom: 8, - left: 8, - right: 8, + top: 10, + bottom: 10, + left: 10, + right: 10, }, background: background(theme.highest, "variant"), @@ -309,6 +309,11 @@ export default function search(): any { }), search_bar_row_height: 32, option_button_height: 22, + modes_container: { + margin: { + right: 9 + } + } } } From 4aa5df4cda9180a437b30f42a46c26d945cfe3b7 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:13:17 +0200 Subject: [PATCH 099/119] Extract columns into separate objects. Co-authored-by: Kyle --- crates/search/src/buffer_search.rs | 66 ++++++----- crates/search/src/project_search.rs | 165 +++++++++++++--------------- 2 files changed, 110 insertions(+), 121 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 408c696a91..0234784952 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -219,29 +219,30 @@ impl View for BufferSearchBar { cx, ) }; - let icon_style = theme.search.editor_icon.clone(); - Flex::row() + + let nav_column = Flex::column() .with_child( - Flex::column() + Flex::row() + .align_children_center() .with_child( Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(nav_button_for_direction("<", Direction::Prev, cx)) - .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .aligned(), - ) - .with_children(match_count) - .aligned() - .left() - .top() - .flex(1., true) - .constrained() - .with_max_height(theme.search.search_bar_row_height), + .with_child(nav_button_for_direction("<", Direction::Prev, cx)) + .with_child(nav_button_for_direction(">", Direction::Next, cx)) + .aligned(), ) - .contained(), + .with_children(match_count) + .aligned() + .left() + .top() + .flex(1., true) + .constrained() + .with_max_height(theme.search.search_bar_row_height), ) + .contained(); + + let icon_style = theme.search.editor_icon.clone(); + Flex::row() + .with_child(nav_column) .with_child( Flex::column() .align_children_center() @@ -263,18 +264,23 @@ impl View for BufferSearchBar { .left() .flex(1., true), ) - .with_children(render_search_option( - supported_options.case, - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - )) - .with_children(render_search_option( - supported_options.word, - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - )) + .with_child( + Flex::row() + .with_children(render_search_option( + supported_options.case, + "icons/case_insensitive_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + )) + .with_children(render_search_option( + supported_options.word, + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + )) + .flex(1., true) + .contained(), + ) .contained() .with_style(editor_container) .aligned() diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 4e265a8c65..f216efb46b 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1551,113 +1551,96 @@ impl View for ProjectSearchBar { cx, ) }; - - Flex::row() + let nav_column = Flex::column() .with_child( - Flex::column() - .with_child( - Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(nav_button_for_direction( - "<", - Direction::Prev, - cx, - )) - .with_child(nav_button_for_direction( - ">", - Direction::Next, - cx, - )) - .aligned(), - ) - .with_children(matches) - .aligned() - .top() - .left() - .constrained() - .with_height(theme.search.search_bar_row_height), - ) - .flex(1., true), - ) - .with_child( - Flex::column() + Flex::row() .align_children_center() .with_child( Flex::row() - .with_child( - Flex::row() - .with_child(query) - .contained() - .with_style(query_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .with_max_height(theme.search.search_bar_row_height) - .flex(1., false), - ) - .contained() - .with_margin_bottom(row_spacing), + .with_child(nav_button_for_direction("<", Direction::Prev, cx)) + .with_child(nav_button_for_direction(">", Direction::Next, cx)) + .aligned(), ) - .with_children(filters) - .contained() + .with_children(matches) .aligned() .top() - .flex(1., false), + .left() + .constrained() + .with_height(theme.search.search_bar_row_height), ) + .flex(1., true); + let editor_column = Flex::column() + .align_children_center() .with_child( - Flex::column() + Flex::row() .with_child( Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_children(semantic_index) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .constrained() - .with_height(theme.search.search_bar_row_height) - .aligned() - .left() - .contained() - .with_style(theme.search.modes_container), - ) - .with_child( - super::search_bar::render_close_button( - "Dismiss Project Search", - &theme.search, - cx, - |_, this, cx| { - if let Some(search) = - this.active_project_search.as_mut() - { - search - .update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) - } - }, - None, - ) - .aligned() - .right(), - ) - .constrained() - .with_height(theme.search.search_bar_row_height) + .with_child(query) + .contained() + .with_style(query_container_style) .aligned() - .right() - .top() - .flex(1., true), - ) - .with_children( - _search - .read(cx) - .filters_enabled - .then(|| Flex::row().flex(1., true)), + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .with_max_height(theme.search.search_bar_row_height) + .flex(1., false), ) .contained() + .with_margin_bottom(row_spacing), + ) + .with_children(filters) + .contained() + .aligned() + .top() + .flex(1., false); + let mode_column = Flex::column() + .with_child( + Flex::row() + .align_children_center() + .with_child( + Flex::row() + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_children(semantic_index) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .aligned() + .left() + .contained() + .with_style(theme.search.modes_container), + ) + .with_child( + super::search_bar::render_close_button( + "Dismiss Project Search", + &theme.search, + cx, + |_, this, cx| { + if let Some(search) = this.active_project_search.as_mut() { + search.update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) + } + }, + None, + ) + .aligned() + .right(), + ) + .constrained() + .with_height(theme.search.search_bar_row_height) + .aligned() + .right() + .top() .flex(1., true), ) + .with_children( + _search + .read(cx) + .filters_enabled + .then(|| Flex::row().flex(1., true)), + ) + .contained() + .flex(1., true); + Flex::row() + .with_child(nav_column) + .with_child(editor_column) + .with_child(mode_column) .contained() .with_style(theme.search.container) .flex_float() From ff4370f88ec74d9c66cf0b74416dfc692f5bbe8c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:14:57 +0200 Subject: [PATCH 100/119] buffer: Extract columns to separate objects. Co-authored-by: Kyle --- crates/search/src/buffer_search.rs | 152 ++++++++++++++--------------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 0234784952..4897d0b729 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -220,6 +220,7 @@ impl View for BufferSearchBar { ) }; + let icon_style = theme.search.editor_icon.clone(); let nav_column = Flex::column() .with_child( Flex::row() @@ -240,101 +241,98 @@ impl View for BufferSearchBar { ) .contained(); - let icon_style = theme.search.editor_icon.clone(); - Flex::row() - .with_child(nav_column) + let editor_column = Flex::column() + .align_children_center() .with_child( - Flex::column() + Flex::row() .align_children_center() .with_child( Flex::row() .align_children_center() .with_child( - Flex::row() - .align_children_center() - .with_child( - Svg::for_style(icon_style.icon) - .contained() - .with_style(icon_style.container) - .constrained(), - ) - .with_child( - ChildView::new(&self.query_editor, cx) - .aligned() - .left() - .flex(1., true), - ) - .with_child( - Flex::row() - .with_children(render_search_option( - supported_options.case, - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - )) - .with_children(render_search_option( - supported_options.word, - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - )) - .flex(1., true) - .contained(), - ) + Svg::for_style(icon_style.icon) .contained() - .with_style(editor_container) + .with_style(icon_style.container) + .constrained(), + ) + .with_child( + ChildView::new(&self.query_editor, cx) .aligned() - .top() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .with_max_height(theme.search.search_bar_row_height) - .flex(1., false), + .left() + .flex(1., true), ) .with_child( Flex::row() - .with_child(self.render_action_button("Select All", cx)) - .aligned(), + .with_children(render_search_option( + supported_options.case, + "icons/case_insensitive_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + )) + .with_children(render_search_option( + supported_options.word, + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + )) + .flex(1., true) + .contained(), ) + .contained() + .with_style(editor_container) + .aligned() + .top() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .with_max_height(theme.search.search_bar_row_height) .flex(1., false), ) - .contained() - .aligned() - .top() + .with_child( + Flex::row() + .with_child(self.render_action_button("Select All", cx)) + .aligned(), + ) .flex(1., false), ) - .with_child( - Flex::column().with_child( + .contained() + .aligned() + .top() + .flex(1., false); + let mode_column = Flex::column().with_child( + Flex::row() + .align_children_center() + .with_child( Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .aligned() - .left() - .contained() - .with_style(theme.search.modes_container), - ) - .with_child( - super::search_bar::render_close_button( - "Dismiss Buffer Search", - &theme.search, - cx, - |_, this, cx| this.dismiss(&Default::default(), cx), - Some(Box::new(Dismiss)), - ) - .aligned() - .right(), - ) - .constrained() - .with_height(theme.search.search_bar_row_height) + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) .aligned() - .right() - .top() - .flex(1., true), - ), - ) + .left() + .contained() + .with_style(theme.search.modes_container), + ) + .with_child( + super::search_bar::render_close_button( + "Dismiss Buffer Search", + &theme.search, + cx, + |_, this, cx| this.dismiss(&Default::default(), cx), + Some(Box::new(Dismiss)), + ) + .aligned() + .right(), + ) + .constrained() + .with_height(theme.search.search_bar_row_height) + .aligned() + .right() + .top() + .flex(1., true), + ); + Flex::row() + .with_child(nav_column) + .with_child(editor_column) + .with_child(mode_column) .contained() .with_style(theme.search.container) .flex_float() From db36a5fe2d04d9c81c8c252921a7356f1885fefe Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:27:57 +0200 Subject: [PATCH 101/119] Refactor buffer search UI rendering in a quest to find the meaning of life. Co-authored-by: Kyle --- crates/search/src/buffer_search.rs | 73 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 4897d0b729..702bafe975 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -103,7 +103,7 @@ impl View for BufferSearchBar { fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let theme = theme::current(cx).clone(); - let editor_container = if self.query_contains_error { + let query_container_style = if self.query_contains_error { theme.search.invalid_editor } else { theme.search.editor.input.container @@ -241,47 +241,50 @@ impl View for BufferSearchBar { ) .contained(); + let query = Flex::row() + .with_child( + Svg::for_style(icon_style.icon) + .contained() + .with_style(icon_style.container) + .constrained(), + ) + .with_child( + ChildView::new(&self.query_editor, cx) + .constrained() + .flex(1., true), + ) + .with_child( + Flex::row() + .with_children(render_search_option( + supported_options.case, + "icons/case_insensitive_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + )) + .with_children(render_search_option( + supported_options.word, + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + )) + .flex(1., true) + .contained(), + ) + .align_children_center() + .aligned() + .left() + .flex(1., true); + let row_spacing = theme.workspace.toolbar.container.padding.bottom; let editor_column = Flex::column() .align_children_center() .with_child( Flex::row() - .align_children_center() .with_child( Flex::row() - .align_children_center() - .with_child( - Svg::for_style(icon_style.icon) - .contained() - .with_style(icon_style.container) - .constrained(), - ) - .with_child( - ChildView::new(&self.query_editor, cx) - .aligned() - .left() - .flex(1., true), - ) - .with_child( - Flex::row() - .with_children(render_search_option( - supported_options.case, - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - )) - .with_children(render_search_option( - supported_options.word, - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - cx, - )) - .flex(1., true) - .contained(), - ) + .with_child(query) .contained() - .with_style(editor_container) + .with_style(query_container_style) .aligned() - .top() .constrained() .with_min_width(theme.search.editor.min_width) .with_max_width(theme.search.editor.max_width) @@ -293,6 +296,8 @@ impl View for BufferSearchBar { .with_child(self.render_action_button("Select All", cx)) .aligned(), ) + .contained() + .with_margin_bottom(row_spacing) .flex(1., false), ) .contained() From 695e6d2f2e547c50f534e062cb57bf8dd7e16b73 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 15 Aug 2023 15:05:39 +0100 Subject: [PATCH 102/119] fix spacing on editor magnifying glass icon --- styles/src/style_tree/search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 76452ddec3..11c69490a4 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -197,7 +197,7 @@ export default function search(): any { }, container: { margin: { right: 6 }, - padding: { left: 4 } + padding: { left: 2, right: 2 }, } }, mode_button: toggleable({ From 1e8a7c7caa0928011d7ce036db913327a6f9f7df Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 15 Aug 2023 20:04:14 +0100 Subject: [PATCH 103/119] refactor buffer_search to reduce redundancy and simplying height management Co-authored-by: maxbrunsfeld --- crates/search/src/buffer_search.rs | 123 ++++++++++------------------ crates/search/src/project_search.rs | 6 +- crates/search/src/search_bar.rs | 8 +- styles/src/style_tree/search.ts | 31 +++---- 4 files changed, 63 insertions(+), 105 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 702bafe975..c634a71297 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -221,38 +221,23 @@ impl View for BufferSearchBar { }; let icon_style = theme.search.editor_icon.clone(); - let nav_column = Flex::column() - .with_child( - Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(nav_button_for_direction("<", Direction::Prev, cx)) - .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .aligned(), - ) - .with_children(match_count) - .aligned() - .left() - .top() - .flex(1., true) - .constrained() - .with_max_height(theme.search.search_bar_row_height), - ) - .contained(); + let side_column_min_width = 165.; + let button_height = 32.; + let nav_column = Flex::row() + .with_child(nav_button_for_direction("<", Direction::Prev, cx)) + .with_child(nav_button_for_direction(">", Direction::Next, cx)) + .with_children(match_count) + .constrained() + .with_height(theme.search.search_bar_row_height) + .with_min_width(side_column_min_width); let query = Flex::row() .with_child( Svg::for_style(icon_style.icon) .contained() - .with_style(icon_style.container) - .constrained(), - ) - .with_child( - ChildView::new(&self.query_editor, cx) - .constrained() - .flex(1., true), + .with_style(icon_style.container), ) + .with_child(ChildView::new(&self.query_editor, cx).flex(1., true)) .with_child( Flex::row() .with_children(render_search_option( @@ -271,76 +256,53 @@ impl View for BufferSearchBar { .contained(), ) .align_children_center() - .aligned() - .left() .flex(1., true); let row_spacing = theme.workspace.toolbar.container.padding.bottom; - let editor_column = Flex::column() - .align_children_center() + let editor_column = Flex::row() .with_child( - Flex::row() - .with_child( - Flex::row() - .with_child(query) - .contained() - .with_style(query_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .with_max_height(theme.search.search_bar_row_height) - .flex(1., false), - ) - .with_child( - Flex::row() - .with_child(self.render_action_button("Select All", cx)) - .aligned(), - ) + query .contained() - .with_margin_bottom(row_spacing) + .with_style(query_container_style) + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) .flex(1., false), ) + .with_child(self.render_action_button("Select All", cx)) .contained() + .constrained() + .with_height(theme.search.search_bar_row_height) .aligned() .top() .flex(1., false); - let mode_column = Flex::column().with_child( - Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .aligned() - .left() - .contained() - .with_style(theme.search.modes_container), - ) - .with_child( - super::search_bar::render_close_button( - "Dismiss Buffer Search", - &theme.search, - cx, - |_, this, cx| this.dismiss(&Default::default(), cx), - Some(Box::new(Dismiss)), - ) - .aligned() - .right(), - ) - .constrained() - .with_height(theme.search.search_bar_row_height) - .aligned() - .right() - .top() - .flex(1., true), - ); + let mode_column = Flex::row() + .with_child( + Flex::row() + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) + .contained() + .with_style(theme.search.modes_container), + ) + .with_child(super::search_bar::render_close_button( + "Dismiss Buffer Search", + &theme.search, + cx, + |_, this, cx| this.dismiss(&Default::default(), cx), + Some(Box::new(Dismiss)), + )) + .constrained() + .with_height(theme.search.search_bar_row_height) + .aligned() + .right() + .constrained() + .with_min_width(side_column_min_width) + .flex_float(); Flex::row() .with_child(nav_column) .with_child(editor_column) .with_child(mode_column) .contained() .with_style(theme.search.container) - .flex_float() .into_any_named("search bar") } } @@ -536,6 +498,7 @@ impl BufferSearchBar { let theme = theme::current(cx); let style = theme.search.action_button.style_for(state); Label::new(icon, style.text.clone()) + .aligned() .contained() .with_style(style.container) }) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index f216efb46b..338e52eedd 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1556,17 +1556,13 @@ impl View for ProjectSearchBar { Flex::row() .align_children_center() .with_child( - Flex::row() + Flex::row().align_children_center() .with_child(nav_button_for_direction("<", Direction::Prev, cx)) .with_child(nav_button_for_direction(">", Direction::Next, cx)) .aligned(), ) .with_children(matches) .aligned() - .top() - .left() - .constrained() - .with_height(theme.search.search_bar_row_height), ) .flex(1., true); let editor_column = Flex::column() diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 088a96b279..47892c0ca6 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -77,7 +77,7 @@ pub(super) fn render_nav_button( .style_for(state) .clone(); let mut container_style = style.container.clone(); - let label = Label::new(icon, style.label.clone()).contained(); + let label = Label::new(icon, style.label.clone()).aligned().contained(); container_style.corner_radii = match direction { Direction::Prev => CornerRadii { bottom_right: 0., @@ -128,7 +128,9 @@ pub(crate) fn render_search_mode_button( style.container.border.left = mode.border_left(); style.container.border.right = mode.border_right(); - let label = Label::new(mode.label(), style.text.clone()).contained(); + let label = Label::new(mode.label(), style.text.clone()) + .aligned() + .contained(); let mut container_style = style.container.clone(); if let Some(button_side) = mode.button_side() { if button_side == Side::Left { @@ -150,6 +152,8 @@ pub(crate) fn render_search_mode_button( container_style.corner_radii = CornerRadii::default(); label.with_style(container_style) } + .constrained() + .with_height(theme.search.search_bar_row_height) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 11c69490a4..24d0930863 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -17,7 +17,7 @@ export default function search(): any { text: text(theme.highest, "mono", "default"), border: border(theme.highest), margin: { - right: 12, + right: 9, }, padding: { top: 4, @@ -51,10 +51,8 @@ export default function search(): any { width: 1., color: background(theme.highest, "on") }, padding: { - bottom: 4, left: 4, right: 4, - top: 4, }, }, state: { @@ -97,14 +95,11 @@ export default function search(): any { background: background(theme.highest, "on"), corner_radius: 6, border: border(theme.highest, "on"), - margin: { - right: 4, - }, padding: { - bottom: 2, + // bottom: 2, left: 10, right: 10, - top: 2, + // top: 2, }, }, state: { @@ -140,8 +135,8 @@ export default function search(): any { padding: { left: 12, right: 12, - top: 3, - bottom: 3, + // top: 3, + // bottom: 3, }, }, include_exclude_inputs: { @@ -165,8 +160,8 @@ export default function search(): any { button_width: 32, corner_radius: 6, padding: { - top: 10, - bottom: 10, + // // top: 10, + // bottom: 10, left: 10, right: 10, }, @@ -213,10 +208,10 @@ export default function search(): any { }, padding: { - bottom: 4, + // bottom: 4, left: 10, right: 10, - top: 5, + // top: 5, }, corner_radius: 6, }, @@ -264,10 +259,10 @@ export default function search(): any { }, padding: { - bottom: 3, + bottom: 0, left: 10, right: 10, - top: 3, + top: 0, }, }, state: { @@ -286,10 +281,10 @@ export default function search(): any { }, padding: { - bottom: 3, + bottom: 0, left: 10, right: 10, - top: 3, + top: 0, }, }, state: { From 32bec2e401762ea3a1c7003855b8a9c449404b38 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Tue, 15 Aug 2023 23:18:03 +0100 Subject: [PATCH 104/119] left align buffer search with new structure --- crates/search/src/buffer_search.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index c634a71297..cbb68ba99f 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -224,12 +224,17 @@ impl View for BufferSearchBar { let side_column_min_width = 165.; let button_height = 32.; let nav_column = Flex::row() + .with_child( + Flex::row() + .with_children(match_count) + .constrained() + .with_min_width(100.), + ) .with_child(nav_button_for_direction("<", Direction::Prev, cx)) .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .with_children(match_count) + .with_child(self.render_action_button("Select All", cx)) .constrained() - .with_height(theme.search.search_bar_row_height) - .with_min_width(side_column_min_width); + .with_height(theme.search.search_bar_row_height); let query = Flex::row() .with_child( @@ -257,7 +262,6 @@ impl View for BufferSearchBar { ) .align_children_center() .flex(1., true); - let row_spacing = theme.workspace.toolbar.container.padding.bottom; let editor_column = Flex::row() .with_child( query @@ -268,12 +272,9 @@ impl View for BufferSearchBar { .with_max_width(theme.search.editor.max_width) .flex(1., false), ) - .with_child(self.render_action_button("Select All", cx)) .contained() .constrained() .with_height(theme.search.search_bar_row_height) - .aligned() - .top() .flex(1., false); let mode_column = Flex::row() .with_child( @@ -298,8 +299,8 @@ impl View for BufferSearchBar { .with_min_width(side_column_min_width) .flex_float(); Flex::row() - .with_child(nav_column) .with_child(editor_column) + .with_child(nav_column) .with_child(mode_column) .contained() .with_style(theme.search.container) From 11ecb7b6048f6bc97f49870fb56a19c0d29021b9 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 11:35:09 +0100 Subject: [PATCH 105/119] reorganize search bar, enable filters and disable select all during invalid states Co-authored-by: Piotr --- crates/search/src/buffer_search.rs | 25 +-- crates/search/src/project_search.rs | 235 ++++++++++++---------------- crates/theme/src/theme.rs | 2 +- styles/src/style_tree/search.ts | 71 ++++++--- 4 files changed, 162 insertions(+), 171 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index cbb68ba99f..32e6c93f4a 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -222,17 +222,11 @@ impl View for BufferSearchBar { let icon_style = theme.search.editor_icon.clone(); let side_column_min_width = 165.; - let button_height = 32.; let nav_column = Flex::row() - .with_child( - Flex::row() - .with_children(match_count) - .constrained() - .with_min_width(100.), - ) + .with_child(self.render_action_button("Select All", cx)) .with_child(nav_button_for_direction("<", Direction::Prev, cx)) .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .with_child(self.render_action_button("Select All", cx)) + .with_child(Flex::row().with_children(match_count)) .constrained() .with_height(theme.search.search_bar_row_height); @@ -493,11 +487,20 @@ impl BufferSearchBar { let tooltip = "Select All Matches"; let tooltip_style = theme::current(cx).tooltip.clone(); let action_type_id = 0_usize; - + let has_matches = self.active_match_index.is_some(); + let cursor_style = if has_matches { + CursorStyle::PointingHand + } else { + CursorStyle::default() + }; enum ActionButton {} MouseEventHandler::::new(action_type_id, cx, |state, cx| { let theme = theme::current(cx); - let style = theme.search.action_button.style_for(state); + let style = theme + .search + .action_button + .in_state(has_matches) + .style_for(state); Label::new(icon, style.text.clone()) .aligned() .contained() @@ -506,7 +509,7 @@ impl BufferSearchBar { .on_click(MouseButton::Left, move |_, this, cx| { this.select_all_matches(&SelectAllMatches, cx) }) - .with_cursor_style(CursorStyle::PointingHand) + .with_cursor_style(cursor_style) .with_tooltip::( action_type_id, tooltip.to_string(), diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 338e52eedd..0b2c06f4b8 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1392,27 +1392,7 @@ impl View for ProjectSearchBar { } else { theme.search.editor.input.container }; - let include_container_style = - if search.panels_with_errors.contains(&InputPanel::Include) { - theme.search.invalid_include_exclude_editor - } else { - theme.search.include_exclude_editor.input.container - }; - let exclude_container_style = - if search.panels_with_errors.contains(&InputPanel::Exclude) { - theme.search.invalid_include_exclude_editor - } else { - theme.search.include_exclude_editor.input.container - }; - let included_files_view = ChildView::new(&search.included_files_editor, cx) - .aligned() - .left() - .flex(1.0, true); - let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx) - .aligned() - .right() - .flex(1.0, true); let row_spacing = theme.workspace.toolbar.container.padding.bottom; let search = _search.read(cx); let filter_button = render_option_button_icon( @@ -1455,19 +1435,15 @@ impl View for ProjectSearchBar { let search = _search.read(cx); let icon_style = theme.search.editor_icon.clone(); + + // Editor Functionality let query = Flex::row() .with_child( Svg::for_style(icon_style.icon) .contained() - .with_style(icon_style.container) - .constrained(), - ) - .with_child( - ChildView::new(&search.query_editor, cx) - .constrained() - .flex(1., true) - .into_any(), + .with_style(icon_style.container), ) + .with_child(ChildView::new(&search.query_editor, cx).flex(1., true)) .with_child( Flex::row() .with_child(filter_button) @@ -1477,10 +1453,70 @@ impl View for ProjectSearchBar { .contained(), ) .align_children_center() - .aligned() - .left() .flex(1., true); + let search = _search.read(cx); + + let include_container_style = + if search.panels_with_errors.contains(&InputPanel::Include) { + theme.search.invalid_include_exclude_editor + } else { + theme.search.include_exclude_editor.input.container + }; + + let exclude_container_style = + if search.panels_with_errors.contains(&InputPanel::Exclude) { + theme.search.invalid_include_exclude_editor + } else { + theme.search.include_exclude_editor.input.container + }; + + let included_files_view = ChildView::new(&search.included_files_editor, cx) + .contained() + .flex(1.0, true); + let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx) + .contained() + .flex(1.0, true); + let filters = search.filters_enabled.then(|| { + Flex::row() + .with_child( + included_files_view + .contained() + .with_style(include_container_style) + .constrained() + .with_height(theme.search.search_bar_row_height) + .with_min_width(theme.search.include_exclude_editor.min_width) + .with_max_width(theme.search.include_exclude_editor.max_width), + ) + .with_child( + excluded_files_view + .contained() + .with_style(exclude_container_style) + .constrained() + .with_height(theme.search.search_bar_row_height) + .with_min_width(theme.search.include_exclude_editor.min_width) + .with_max_width(theme.search.include_exclude_editor.max_width), + ) + .contained() + .with_padding_top(3.) + }); + + let editor_column = Flex::column() + .with_child( + query + .contained() + .with_style(query_container_style) + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .with_height(theme.search.search_bar_row_height) + .flex(1., false), + ) + .with_children(filters) + .contained() + .with_background_color(gpui::color::Color::blue()) + .flex(1., false); + let matches = search.active_match_index.map(|match_ix| { Label::new( format!( @@ -1492,33 +1528,9 @@ impl View for ProjectSearchBar { ) .contained() .with_style(theme.search.match_index.container) + .aligned() }); - let filters = search.filters_enabled.then(|| { - Flex::row() - .with_child( - Flex::row() - .with_child(included_files_view) - .contained() - .with_style(include_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.include_exclude_editor.min_width) - .with_max_width(theme.search.include_exclude_editor.max_width) - .flex(1., false), - ) - .with_child( - Flex::row() - .with_child(excluded_files_view) - .contained() - .with_style(exclude_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.include_exclude_editor.min_width) - .with_max_width(theme.search.include_exclude_editor.max_width) - .flex(1., false), - ) - }); let search_button_for_mode = |mode, cx: &mut ViewContext| { let is_active = if let Some(search) = self.active_project_search.as_ref() { let search = search.read(cx); @@ -1551,95 +1563,52 @@ impl View for ProjectSearchBar { cx, ) }; - let nav_column = Flex::column() + + let nav_column = Flex::row() + .with_child(nav_button_for_direction("<", Direction::Prev, cx)) + .with_child(nav_button_for_direction(">", Direction::Next, cx)) + .with_child(Flex::row().with_children(matches)) + .constrained() + .with_height(theme.search.search_bar_row_height) + .contained() + .with_background_color(gpui::color::Color::red()); + + let side_column_min_width = 200.; + let mode_column = Flex::row() .with_child( Flex::row() - .align_children_center() - .with_child( - Flex::row().align_children_center() - .with_child(nav_button_for_direction("<", Direction::Prev, cx)) - .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .aligned(), - ) - .with_children(matches) - .aligned() - ) - .flex(1., true); - let editor_column = Flex::column() - .align_children_center() - .with_child( - Flex::row() - .with_child( - Flex::row() - .with_child(query) - .contained() - .with_style(query_container_style) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .with_max_height(theme.search.search_bar_row_height) - .flex(1., false), - ) + .with_child(search_button_for_mode(SearchMode::Text, cx)) + .with_children(semantic_index) + .with_child(search_button_for_mode(SearchMode::Regex, cx)) .contained() - .with_margin_bottom(row_spacing), + .with_style(theme.search.modes_container), ) - .with_children(filters) - .contained() + .with_child(super::search_bar::render_close_button( + "Dismiss Project Search", + &theme.search, + cx, + |_, this, cx| { + if let Some(search) = this.active_project_search.as_mut() { + search.update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) + } + }, + None, + )) + .constrained() + .with_height(theme.search.search_bar_row_height) .aligned() + .right() .top() - .flex(1., false); - let mode_column = Flex::column() - .with_child( - Flex::row() - .align_children_center() - .with_child( - Flex::row() - .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_children(semantic_index) - .with_child(search_button_for_mode(SearchMode::Regex, cx)) - .aligned() - .left() - .contained() - .with_style(theme.search.modes_container), - ) - .with_child( - super::search_bar::render_close_button( - "Dismiss Project Search", - &theme.search, - cx, - |_, this, cx| { - if let Some(search) = this.active_project_search.as_mut() { - search.update(cx, |_, cx| cx.emit(ViewEvent::Dismiss)) - } - }, - None, - ) - .aligned() - .right(), - ) - .constrained() - .with_height(theme.search.search_bar_row_height) - .aligned() - .right() - .top() - .flex(1., true), - ) - .with_children( - _search - .read(cx) - .filters_enabled - .then(|| Flex::row().flex(1., true)), - ) - .contained() - .flex(1., true); + .constrained() + .with_min_width(side_column_min_width) + .flex_float(); + Flex::row() - .with_child(nav_column) .with_child(editor_column) + .with_child(nav_column) .with_child(mode_column) .contained() .with_style(theme.search.container) - .flex_float() .into_any_named("project search") } else { Empty::new().into_any() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 91d7eeb5dc..5ee11a4d94 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -380,7 +380,7 @@ pub struct Search { pub invalid_include_exclude_editor: ContainerStyle, pub include_exclude_inputs: ContainedText, pub option_button: Toggleable>, - pub action_button: Interactive, + pub action_button: Toggleable>, pub match_background: Color, pub match_index: ContainedText, pub major_results_status: TextStyle, diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 24d0930863..05392ce48d 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -89,31 +89,52 @@ export default function search(): any { }, }, }), - action_button: interactive({ - base: { - ...text(theme.highest, "mono", "on"), - background: background(theme.highest, "on"), - corner_radius: 6, - border: border(theme.highest, "on"), - padding: { - // bottom: 2, - left: 10, - right: 10, - // top: 2, + action_button: toggleable({ + base: interactive({ + base: { + ...text(theme.highest, "mono", "disabled"), + background: background(theme.highest, "disabled"), + corner_radius: 6, + border: border(theme.highest, "disabled"), + padding: { + // bottom: 2, + left: 10, + right: 10, + // top: 2, + }, }, - }, + state: { + hovered: {} + }, + }), state: { - hovered: { - ...text(theme.highest, "mono", "on", "hovered"), - background: background(theme.highest, "on", "hovered"), - border: border(theme.highest, "on", "hovered"), - }, - clicked: { - ...text(theme.highest, "mono", "on", "pressed"), - background: background(theme.highest, "on", "pressed"), - border: border(theme.highest, "on", "pressed"), - }, - }, + active: interactive({ + base: { + ...text(theme.highest, "mono", "on"), + background: background(theme.highest, "on"), + corner_radius: 6, + border: border(theme.highest, "on"), + padding: { + // bottom: 2, + left: 10, + right: 10, + // top: 2, + }, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "on", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: border(theme.highest, "on", "hovered"), + }, + clicked: { + ...text(theme.highest, "mono", "on", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: border(theme.highest, "on", "pressed"), + }, + }, + }) + } }), editor, invalid_editor: { @@ -128,7 +149,7 @@ export default function search(): any { match_index: { ...text(theme.highest, "mono", "variant"), padding: { - left: 6, + left: 9, }, }, option_button_group: { @@ -208,10 +229,8 @@ export default function search(): any { }, padding: { - // bottom: 4, left: 10, right: 10, - // top: 5, }, corner_radius: 6, }, From 9bf227b884a35098670e26b988b52b8a282dfa1e Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 12:17:59 +0100 Subject: [PATCH 106/119] remove regex keymap, and made spacing consistent between search objects Co-authored-by: Piotr --- crates/search/src/project_search.rs | 10 +++------- styles/src/style_tree/search.ts | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 0b2c06f4b8..fb0e71b1ed 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -64,7 +64,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(ProjectSearchBar::cycle_mode); cx.add_action(ProjectSearchBar::next_history_query); cx.add_action(ProjectSearchBar::previous_history_query); - cx.add_action(ProjectSearchBar::activate_regex_mode); + // cx.add_action(ProjectSearchBar::activate_regex_mode); cx.capture_action(ProjectSearchBar::tab); cx.capture_action(ProjectSearchBar::tab_previous); add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); @@ -1498,7 +1498,7 @@ impl View for ProjectSearchBar { .with_max_width(theme.search.include_exclude_editor.max_width), ) .contained() - .with_padding_top(3.) + .with_padding_top(theme.workspace.toolbar.container.padding.bottom) }); let editor_column = Flex::column() @@ -1513,8 +1513,6 @@ impl View for ProjectSearchBar { .flex(1., false), ) .with_children(filters) - .contained() - .with_background_color(gpui::color::Color::blue()) .flex(1., false); let matches = search.active_match_index.map(|match_ix| { @@ -1569,9 +1567,7 @@ impl View for ProjectSearchBar { .with_child(nav_button_for_direction(">", Direction::Next, cx)) .with_child(Flex::row().with_children(matches)) .constrained() - .with_height(theme.search.search_bar_row_height) - .contained() - .with_background_color(gpui::color::Color::red()); + .with_height(theme.search.search_bar_row_height); let side_column_min_width = 200.; let mode_column = Flex::row() diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 05392ce48d..abb27f39e8 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -102,6 +102,9 @@ export default function search(): any { right: 10, // top: 2, }, + margin: { + right: 9, + } }, state: { hovered: {} @@ -112,14 +115,7 @@ export default function search(): any { base: { ...text(theme.highest, "mono", "on"), background: background(theme.highest, "on"), - corner_radius: 6, border: border(theme.highest, "on"), - padding: { - // bottom: 2, - left: 10, - right: 10, - // top: 2, - }, }, state: { hovered: { From a59535efa126572330aa73b628dfc12c9e7c193a Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 12:55:32 +0100 Subject: [PATCH 107/119] remove redundant and unneeded styling Co-authored-by: Piotr --- crates/search/src/buffer_search.rs | 3 --- crates/search/src/project_search.rs | 11 +++-------- styles/src/style_tree/search.ts | 2 -- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index daf92151fc..6fe8712ee0 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -221,7 +221,6 @@ impl View for BufferSearchBar { }; let icon_style = theme.search.editor_icon.clone(); - let side_column_min_width = 165.; let nav_column = Flex::row() .with_child(self.render_action_button("Select All", cx)) .with_child(nav_button_for_direction("<", Direction::Prev, cx)) @@ -289,8 +288,6 @@ impl View for BufferSearchBar { .with_height(theme.search.search_bar_row_height) .aligned() .right() - .constrained() - .with_min_width(side_column_min_width) .flex_float(); Flex::row() .with_child(editor_column) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index d0a1aa95c1..884f56bda5 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -327,10 +327,8 @@ impl View for ProjectSearchView { let semantic_status = if let Some(semantic) = &self.semantic_state { if semantic.outstanding_file_count > 0 { - let dots_count = semantic.outstanding_file_count % 3 + 1; - let dots: String = std::iter::repeat('.').take(dots_count).collect(); format!( - "Indexing: {} of {}{dots}", + "Indexing: {} of {}...", semantic.file_count - semantic.outstanding_file_count, semantic.file_count ) @@ -1473,10 +1471,10 @@ impl View for ProjectSearchBar { let included_files_view = ChildView::new(&search.included_files_editor, cx) .contained() - .flex(1.0, true); + .flex(1., true); let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx) .contained() - .flex(1.0, true); + .flex(1., true); let filters = search.filters_enabled.then(|| { Flex::row() .with_child( @@ -1569,7 +1567,6 @@ impl View for ProjectSearchBar { .constrained() .with_height(theme.search.search_bar_row_height); - let side_column_min_width = 200.; let mode_column = Flex::row() .with_child( Flex::row() @@ -1595,8 +1592,6 @@ impl View for ProjectSearchBar { .aligned() .right() .top() - .constrained() - .with_min_width(side_column_min_width) .flex_float(); Flex::row() diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index abb27f39e8..4dfa9624b4 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -152,8 +152,6 @@ export default function search(): any { padding: { left: 12, right: 12, - // top: 3, - // bottom: 3, }, }, include_exclude_inputs: { From 6f78a1633dd0ceefc75f17bb1b7f4882431b8e33 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 13:51:41 +0100 Subject: [PATCH 108/119] fix editor height in buffer search, but the dancing is back Co-authored-by: Piotr --- crates/search/src/buffer_search.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 6fe8712ee0..0637191f97 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -263,6 +263,7 @@ impl View for BufferSearchBar { .constrained() .with_min_width(theme.search.editor.min_width) .with_max_width(theme.search.editor.max_width) + .with_height(theme.search.search_bar_row_height) .flex(1., false), ) .contained() @@ -295,6 +296,7 @@ impl View for BufferSearchBar { .with_child(mode_column) .contained() .with_style(theme.search.container) + .aligned() .into_any_named("search bar") } } From 2a7df106e124f614b5de68052d6b5014c47df8af Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 14:01:06 +0100 Subject: [PATCH 109/119] adjusted icon sizes downwards Co-authored-by: Piotr --- crates/search/src/buffer_search.rs | 2 +- crates/search/src/project_search.rs | 3 ++- crates/search/src/search_bar.rs | 6 +++++- crates/theme/src/theme.rs | 2 +- styles/src/style_tree/search.ts | 14 +++++++++----- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 0637191f97..3e13f49181 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -250,7 +250,7 @@ impl View for BufferSearchBar { SearchOptions::WHOLE_WORD, cx, )) - .flex(1., true) + .flex_float() .contained(), ) .align_children_center() diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 884f56bda5..323c33bc56 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1447,7 +1447,8 @@ impl View for ProjectSearchBar { .with_child(filter_button) .with_children(case_sensitive) .with_children(whole_word) - .flex(1., true) + .flex(1., false) + .constrained() .contained(), ) .align_children_center() diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index 88d4675102..f227ced0d4 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -185,11 +185,15 @@ pub(crate) fn render_option_button_icon( .in_state(is_active) .style_for(state); Svg::new(icon) - .with_color(style.text.color.clone()) + .with_color(style.color.clone()) + .constrained() + .with_width(style.icon_width) .contained() .with_style(style.container) + // .aligned() .constrained() .with_height(theme.search.option_button_height) + .with_width(style.button_width) }) .on_click(MouseButton::Left, on_click) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 5ee11a4d94..df6dbe9f55 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -379,7 +379,7 @@ pub struct Search { pub include_exclude_editor: FindEditor, pub invalid_include_exclude_editor: ContainerStyle, pub include_exclude_inputs: ContainedText, - pub option_button: Toggleable>, + pub option_button: Toggleable>, pub action_button: Toggleable>, pub match_background: Color, pub match_index: ContainedText, diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 4dfa9624b4..b5259886b9 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -2,6 +2,7 @@ import { with_opacity } from "../theme/color" import { background, border, foreground, text } from "./components" import { interactive, toggleable } from "../element" import { useTheme } from "../theme" +import { toggleable_icon_button } from "../component/icon_button" export default function search(): any { const theme = useTheme() @@ -43,7 +44,9 @@ export default function search(): any { option_button: toggleable({ base: interactive({ base: { - ...text(theme.highest, "mono", "variant"), + icon_width: 14, + button_width: 32, + color: foreground(theme.highest, "variant"), background: background(theme.highest, "on"), corner_radius: 2, margin: { right: 2 }, @@ -53,6 +56,8 @@ export default function search(): any { padding: { left: 4, right: 4, + top: 4, + bottom: 4, }, }, state: { @@ -75,6 +80,9 @@ export default function search(): any { state: { active: { default: { + icon_width: 14, + button_width: 32, + color: foreground(theme.highest, "variant"), background: background(theme.highest, "accent"), border: border(theme.highest, "accent"), }, @@ -272,10 +280,8 @@ export default function search(): any { }, padding: { - bottom: 0, left: 10, right: 10, - top: 0, }, }, state: { @@ -294,10 +300,8 @@ export default function search(): any { }, padding: { - bottom: 0, left: 10, right: 10, - top: 0, }, }, state: { From 6e3e61ec95607c8e83866f9e2594a931e7ff31ea Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 14:01:53 +0100 Subject: [PATCH 110/119] removed commented out code --- crates/search/src/search_bar.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/search/src/search_bar.rs b/crates/search/src/search_bar.rs index f227ced0d4..7d3c5261ea 100644 --- a/crates/search/src/search_bar.rs +++ b/crates/search/src/search_bar.rs @@ -190,7 +190,6 @@ pub(crate) fn render_option_button_icon( .with_width(style.icon_width) .contained() .with_style(style.container) - // .aligned() .constrained() .with_height(theme.search.option_button_height) .with_width(style.button_width) From aeda5d984246ef63c3f855247de9f36d7f933c35 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 15:50:54 +0100 Subject: [PATCH 111/119] fix semantic search panic which is created via incompatible build_search_query path Co-authored-by: Piotr --- crates/project/src/search.rs | 73 +++++++++++++---------- crates/search/src/project_search.rs | 59 +++++++++--------- crates/terminal_view/src/terminal_view.rs | 6 +- styles/src/style_tree/search.ts | 1 - 4 files changed, 75 insertions(+), 64 deletions(-) diff --git a/crates/project/src/search.rs b/crates/project/src/search.rs index 71a0b70b81..08ff803598 100644 --- a/crates/project/src/search.rs +++ b/crates/project/src/search.rs @@ -13,24 +13,39 @@ use std::{ sync::Arc, }; +#[derive(Clone, Debug)] +pub struct SearchInputs { + query: Arc, + files_to_include: Vec, + files_to_exclude: Vec, +} + +impl SearchInputs { + pub fn as_str(&self) -> &str { + self.query.as_ref() + } + pub fn files_to_include(&self) -> &[PathMatcher] { + &self.files_to_include + } + pub fn files_to_exclude(&self) -> &[PathMatcher] { + &self.files_to_exclude + } +} #[derive(Clone, Debug)] pub enum SearchQuery { Text { search: Arc>, - query: Arc, whole_word: bool, case_sensitive: bool, - files_to_include: Vec, - files_to_exclude: Vec, + inner: SearchInputs, }, Regex { regex: Regex, - query: Arc, + multiline: bool, whole_word: bool, case_sensitive: bool, - files_to_include: Vec, - files_to_exclude: Vec, + inner: SearchInputs, }, } @@ -72,13 +87,16 @@ impl SearchQuery { .auto_configure(&[&query]) .ascii_case_insensitive(!case_sensitive) .build(&[&query]); + let inner = SearchInputs { + query: query.into(), + files_to_exclude, + files_to_include, + }; Self::Text { search: Arc::new(search), - query: Arc::from(query), whole_word, case_sensitive, - files_to_include, - files_to_exclude, + inner, } } @@ -104,14 +122,17 @@ impl SearchQuery { .case_insensitive(!case_sensitive) .multi_line(multiline) .build()?; + let inner = SearchInputs { + query: initial_query, + files_to_exclude, + files_to_include, + }; Ok(Self::Regex { regex, - query: initial_query, multiline, whole_word, case_sensitive, - files_to_include, - files_to_exclude, + inner, }) } @@ -267,10 +288,7 @@ impl SearchQuery { } pub fn as_str(&self) -> &str { - match self { - Self::Text { query, .. } => query.as_ref(), - Self::Regex { query, .. } => query.as_ref(), - } + self.as_inner().as_str() } pub fn whole_word(&self) -> bool { @@ -292,25 +310,11 @@ impl SearchQuery { } pub fn files_to_include(&self) -> &[PathMatcher] { - match self { - Self::Text { - files_to_include, .. - } => files_to_include, - Self::Regex { - files_to_include, .. - } => files_to_include, - } + self.as_inner().files_to_include() } pub fn files_to_exclude(&self) -> &[PathMatcher] { - match self { - Self::Text { - files_to_exclude, .. - } => files_to_exclude, - Self::Regex { - files_to_exclude, .. - } => files_to_exclude, - } + self.as_inner().files_to_exclude() } pub fn file_matches(&self, file_path: Option<&Path>) -> bool { @@ -329,6 +333,11 @@ impl SearchQuery { None => self.files_to_include().is_empty(), } } + pub fn as_inner(&self) -> &SearchInputs { + match self { + Self::Regex { inner, .. } | Self::Text { inner, .. } => inner, + } + } } fn deserialize_path_matches(glob_set: &str) -> anyhow::Result> { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 323c33bc56..6535c257e2 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -24,7 +24,7 @@ use gpui::{ use menu::Confirm; use postage::stream::Stream; use project::{ - search::{PathMatcher, SearchQuery}, + search::{PathMatcher, SearchInputs, SearchQuery}, Entry, Project, }; use semantic_index::SemanticIndex; @@ -177,10 +177,12 @@ impl ProjectSearch { } fn kill_search(&mut self) { + dbg!("Killing search"); self.active_query = None; self.match_ranges.clear(); self.pending_search = None; self.no_results = None; + dbg!("Killed search"); } fn search(&mut self, query: SearchQuery, cx: &mut ModelContext) { @@ -226,22 +228,22 @@ impl ProjectSearch { cx.notify(); } - fn semantic_search(&mut self, query: SearchQuery, cx: &mut ModelContext) { + fn semantic_search(&mut self, inputs: &SearchInputs, cx: &mut ModelContext) { let search = SemanticIndex::global(cx).map(|index| { index.update(cx, |semantic_index, cx| { semantic_index.search_project( self.project.clone(), - query.as_str().to_owned(), + inputs.as_str().to_owned(), 10, - query.files_to_include().to_vec(), - query.files_to_exclude().to_vec(), + inputs.files_to_include().to_vec(), + inputs.files_to_exclude().to_vec(), cx, ) }) }); self.search_id += 1; self.match_ranges.clear(); - self.search_history.add(query.as_str().to_string()); + self.search_history.add(inputs.as_str().to_string()); self.no_results = Some(true); self.pending_search = Some(cx.spawn(|this, mut cx| async move { let results = search?.await.log_err()?; @@ -682,6 +684,7 @@ impl ProjectSearchView { fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { let previous_mode = self.current_mode; + log::error!("Going from {previous_mode:?} to {:?}", mode); if previous_mode == mode { return; } @@ -690,6 +693,7 @@ impl ProjectSearchView { match mode { SearchMode::Semantic => { + dbg!("Matched on Semantic"); let has_permission = self.semantic_permissioned(cx); self.active_match_index = None; cx.spawn(|this, mut cx| async move { @@ -947,7 +951,7 @@ impl ProjectSearchView { if let Some(query) = self.build_search_query(cx) { self.model - .update(cx, |model, cx| model.semantic_search(query, cx)); + .update(cx, |model, cx| model.semantic_search(query.as_inner(), cx)); } } } @@ -986,33 +990,34 @@ impl ProjectSearchView { return None; } }; - if self.current_mode == SearchMode::Regex { - match SearchQuery::regex( - text, - self.search_options.contains(SearchOptions::WHOLE_WORD), - self.search_options.contains(SearchOptions::CASE_SENSITIVE), - included_files, - excluded_files, - ) { - Ok(query) => { - self.panels_with_errors.remove(&InputPanel::Query); - Some(query) - } - Err(_e) => { - self.panels_with_errors.insert(InputPanel::Query); - cx.notify(); - None + let current_mode = self.current_mode; + match current_mode { + SearchMode::Regex => { + match SearchQuery::regex( + text, + self.search_options.contains(SearchOptions::WHOLE_WORD), + self.search_options.contains(SearchOptions::CASE_SENSITIVE), + included_files, + excluded_files, + ) { + Ok(query) => { + self.panels_with_errors.remove(&InputPanel::Query); + Some(query) + } + Err(_e) => { + self.panels_with_errors.insert(InputPanel::Query); + cx.notify(); + None + } } } - } else { - debug_assert_ne!(self.current_mode, SearchMode::Semantic); - Some(SearchQuery::text( + _ => Some(SearchQuery::text( text, self.search_options.contains(SearchOptions::WHOLE_WORD), self.search_options.contains(SearchOptions::CASE_SENSITIVE), included_files, excluded_files, - )) + )), } } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index b48597d901..4f891e678e 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -483,10 +483,8 @@ fn possible_open_targets( } pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option { - let searcher = match query { - project::search::SearchQuery::Text { query, .. } => RegexSearch::new(&query), - project::search::SearchQuery::Regex { query, .. } => RegexSearch::new(&query), - }; + let query = query.as_str(); + let searcher = RegexSearch::new(&query); searcher.ok() } diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index b5259886b9..4c0df69804 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -2,7 +2,6 @@ import { with_opacity } from "../theme/color" import { background, border, foreground, text } from "./components" import { interactive, toggleable } from "../element" import { useTheme } from "../theme" -import { toggleable_icon_button } from "../component/icon_button" export default function search(): any { const theme = useTheme() From c99b530968a81856508c8a442af976d2fbfe9c25 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Wed, 16 Aug 2023 16:13:21 +0100 Subject: [PATCH 112/119] remove kill search, and restart search immediately upon cycle mode Co-authored-by: Piotr --- crates/search/src/project_search.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6535c257e2..065b8d0880 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -176,15 +176,6 @@ impl ProjectSearch { }) } - fn kill_search(&mut self) { - dbg!("Killing search"); - self.active_query = None; - self.match_ranges.clear(); - self.pending_search = None; - self.no_results = None; - dbg!("Killed search"); - } - fn search(&mut self, query: SearchQuery, cx: &mut ModelContext) { let search = self .project @@ -688,7 +679,7 @@ impl ProjectSearchView { if previous_mode == mode { return; } - self.model.update(cx, |model, _| model.kill_search()); + self.current_mode = mode; match mode { @@ -746,6 +737,10 @@ impl ProjectSearchView { self.active_match_index = None; } } + + if let Some(query) = self.build_search_query(cx) { + self.search(cx); + }; cx.notify(); } fn new(model: ModelHandle, cx: &mut ViewContext) -> Self { From b7dd12e53e3a9774dfb9da378c1a9e9683c89a85 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 17 Aug 2023 11:11:09 +0100 Subject: [PATCH 113/119] ensured search results are cleared appropriately while cycling modes --- crates/search/src/project_search.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 065b8d0880..e15bd0e27a 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -673,9 +673,22 @@ impl ProjectSearchView { } } + fn clear_search(&mut self, cx: &mut ViewContext) { + self.model.update(cx, |model, cx| { + model.pending_search = None; + model.no_results = None; + model.match_ranges.clear(); + + model.excerpts.update(cx, |excerpts, cx| { + excerpts.clear(cx); + }); + }); + } + fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { + self.clear_search(cx); + let previous_mode = self.current_mode; - log::error!("Going from {previous_mode:?} to {:?}", mode); if previous_mode == mode { return; } @@ -684,7 +697,6 @@ impl ProjectSearchView { match mode { SearchMode::Semantic => { - dbg!("Matched on Semantic"); let has_permission = self.semantic_permissioned(cx); self.active_match_index = None; cx.spawn(|this, mut cx| async move { @@ -738,9 +750,6 @@ impl ProjectSearchView { } } - if let Some(query) = self.build_search_query(cx) { - self.search(cx); - }; cx.notify(); } fn new(model: ModelHandle, cx: &mut ViewContext) -> Self { From 1bd7d7077a68202b6851b4e507ebf99efb295d4f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:45:11 +0200 Subject: [PATCH 114/119] Move nav buttons to the left hand side of a tab bar. Co-authored-by: Nate --- crates/workspace/src/pane.rs | 115 +++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index dee6701564..3b295af802 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -287,8 +287,7 @@ impl Pane { menu.set_position_mode(OverlayPositionMode::Local) }); let theme = theme::current(cx).workspace.tab_bar.clone(); - let mut border_for_nav_buttons = theme.tab_style(false, false).container.border.clone(); - border_for_nav_buttons.left = false; + let nav_button_height = theme.height; let button_style = theme.nav_button; @@ -323,56 +322,6 @@ impl Pane { render_tab_bar_buttons: Rc::new(move |pane, cx| { let tooltip_style = theme::current(cx).tooltip.clone(); Flex::row() - .with_child(nav_button( - "icons/arrow_left_16.svg", - button_style.clone(), - nav_button_height, - tooltip_style.clone(), - pane.can_navigate_backward(), - { - move |pane, cx| { - if let Some(workspace) = pane.workspace.upgrade(cx) { - let pane = cx.weak_handle(); - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace.go_back(pane, cx).detach_and_log_err(cx) - }) - }) - } - } - }, - super::GoBack, - "Go Back", - cx, - )) - .with_child( - nav_button( - "icons/arrow_right_16.svg", - button_style.clone(), - nav_button_height, - tooltip_style, - pane.can_navigate_forward(), - { - move |pane, cx| { - if let Some(workspace) = pane.workspace.upgrade(cx) { - let pane = cx.weak_handle(); - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace - .go_forward(pane, cx) - .detach_and_log_err(cx) - }) - }) - } - } - }, - super::GoForward, - "Go Forward", - cx, - ) - .contained() - .with_border(border_for_nav_buttons), - ) // New menu .with_child(Self::render_tab_bar_button( 0, @@ -1677,8 +1626,70 @@ impl View for Pane { }, ), ); + let tooltip_style = theme.tooltip.clone(); + let tab_bar_theme = theme.workspace.tab_bar.clone(); + + let nav_button_height = tab_bar_theme.height; + let button_style = tab_bar_theme.nav_button; + let border_for_nav_buttons = tab_bar_theme + .tab_style(false, false) + .container + .border + .clone(); let mut tab_row = Flex::row() + .with_child(nav_button( + "icons/arrow_left_16.svg", + button_style.clone(), + nav_button_height, + tooltip_style.clone(), + self.can_navigate_backward(), + { + move |pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + workspace + .go_back(pane, cx) + .detach_and_log_err(cx) + }) + }) + } + } + }, + super::GoBack, + "Go Back", + cx, + )) + .with_child( + nav_button( + "icons/arrow_right_16.svg", + button_style.clone(), + nav_button_height, + tooltip_style, + self.can_navigate_forward(), + { + move |pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + workspace + .go_forward(pane, cx) + .detach_and_log_err(cx) + }) + }) + } + } + }, + super::GoForward, + "Go Forward", + cx, + ) + .contained() + .with_border(border_for_nav_buttons), + ) .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs")); if self.has_focus { From f451e3423d64c2fb3bd97da13653ddd888f78b69 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 17 Aug 2023 11:00:22 -0400 Subject: [PATCH 115/119] Fix missing border on tab bar navigation arrows --- styles/src/style_tree/tab_bar.ts | 7 ++++++- styles/src/theme/create_theme.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index 81e1f7adf3..129bd17869 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -90,6 +90,10 @@ export default function tab_bar(): any { icon_width: 12, button_width: active_pane_active_tab.height, + border: border(theme.lowest, "on", { + bottom: true, + overlay: true, + }) }, state: { hovered: { @@ -97,10 +101,11 @@ export default function tab_bar(): any { background: background(theme.highest, "on", "hovered"), }, disabled: { - color: foreground(theme.highest, "on", "disabled"), + color: foreground(theme.highest, "on", "disabled") }, }, }) + const dragged_tab = { ...active_pane_active_tab, background: with_opacity(tab.background, 0.9), diff --git a/styles/src/theme/create_theme.ts b/styles/src/theme/create_theme.ts index e0da345bc5..ab3c96f280 100644 --- a/styles/src/theme/create_theme.ts +++ b/styles/src/theme/create_theme.ts @@ -1,4 +1,4 @@ -import chroma, { Scale, Color } from "chroma-js" +import { Scale, Color } from "chroma-js" import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax" export { Syntax, ThemeSyntax, SyntaxHighlightStyle } import { From 8630557ece7e3ad0a29060f0c91ba5df68a2910d Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 17 Aug 2023 15:30:40 -0700 Subject: [PATCH 116/119] Add action button component for rendering the search options --- crates/gpui/src/app.rs | 29 +++++++++++++- crates/gpui/src/app/action.rs | 7 ++++ crates/gpui/src/elements.rs | 18 ++++++++- crates/gpui/src/elements/component.rs | 34 ++++++++++++++++ crates/gpui/src/elements/tooltip.rs | 22 ++++++++--- crates/search/src/buffer_search.rs | 31 ++++++--------- crates/search/src/search.rs | 26 +++++++++++- crates/theme/src/theme.rs | 29 ++++++++++++-- crates/theme/src/ui.rs | 4 +- crates/workspace/src/pane.rs | 1 - styles/src/style_tree/search.ts | 57 +++++++++++++++++++++++++++ 11 files changed, 223 insertions(+), 35 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index b08d9501f6..ca5c2fb8b5 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3313,11 +3313,20 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { &mut self, element_id: usize, initial: T, + ) -> ElementStateHandle { + self.element_state_dynamic(TypeTag::new::(), element_id, initial) + } + + pub fn element_state_dynamic( + &mut self, + tag: TypeTag, + element_id: usize, + initial: T, ) -> ElementStateHandle { let id = ElementStateId { view_id: self.view_id(), element_id, - tag: TypeId::of::(), + tag, }; self.element_states .entry(id) @@ -3331,11 +3340,20 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { ) -> ElementStateHandle { self.element_state::(element_id, T::default()) } + + pub fn default_element_state_dynamic( + &mut self, + tag: TypeTag, + element_id: usize, + ) -> ElementStateHandle { + self.element_state_dynamic::(tag, element_id, T::default()) + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TypeTag { tag: TypeId, + composed: Option, #[cfg(debug_assertions)] tag_type_name: &'static str, } @@ -3344,6 +3362,7 @@ impl TypeTag { pub fn new() -> Self { Self { tag: TypeId::of::(), + composed: None, #[cfg(debug_assertions)] tag_type_name: std::any::type_name::(), } @@ -3352,11 +3371,17 @@ impl TypeTag { pub fn dynamic(tag: TypeId, #[cfg(debug_assertions)] type_name: &'static str) -> Self { Self { tag, + composed: None, #[cfg(debug_assertions)] tag_type_name: type_name, } } + pub fn compose(mut self, other: TypeTag) -> Self { + self.composed = Some(other.tag); + self + } + #[cfg(debug_assertions)] pub(crate) fn type_name(&self) -> &'static str { self.tag_type_name @@ -4751,7 +4776,7 @@ impl Hash for AnyWeakViewHandle { pub struct ElementStateId { view_id: usize, element_id: usize, - tag: TypeId, + tag: TypeTag, } pub struct ElementStateHandle { diff --git a/crates/gpui/src/app/action.rs b/crates/gpui/src/app/action.rs index c6b43e489b..23eb4da730 100644 --- a/crates/gpui/src/app/action.rs +++ b/crates/gpui/src/app/action.rs @@ -1,10 +1,13 @@ use std::any::{Any, TypeId}; +use crate::TypeTag; + pub trait Action: 'static { fn id(&self) -> TypeId; fn namespace(&self) -> &'static str; fn name(&self) -> &'static str; fn as_any(&self) -> &dyn Any; + fn type_tag(&self) -> TypeTag; fn boxed_clone(&self) -> Box; fn eq(&self, other: &dyn Action) -> bool; @@ -107,6 +110,10 @@ macro_rules! __impl_action { } } + fn type_tag(&self) -> $crate::TypeTag { + $crate::TypeTag::new::() + } + $from_json_fn } }; diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index f1be9b34ae..03caae8dd9 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -34,8 +34,8 @@ use crate::{ rect::RectF, vector::{vec2f, Vector2F}, }, - json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext, - WeakViewHandle, WindowContext, + json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View, + ViewContext, WeakViewHandle, WindowContext, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -172,6 +172,20 @@ pub trait Element: 'static { FlexItem::new(self.into_any()).float() } + fn with_dynamic_tooltip( + self, + tag: TypeTag, + id: usize, + text: impl Into>, + action: Option>, + style: TooltipStyle, + cx: &mut ViewContext, + ) -> Tooltip + where + Self: 'static + Sized, + { + Tooltip::new_dynamic(tag, id, text, action, style, self.into_any(), cx) + } fn with_tooltip( self, id: usize, diff --git a/crates/gpui/src/elements/component.rs b/crates/gpui/src/elements/component.rs index 2f9cc6cce6..018dc644c6 100644 --- a/crates/gpui/src/elements/component.rs +++ b/crates/gpui/src/elements/component.rs @@ -7,6 +7,34 @@ use crate::{ ViewContext, }; +use super::Empty; + +pub trait GeneralComponent { + fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement; +} + +pub trait StyleableComponent { + type Style: Clone; + type Output: GeneralComponent; + + fn with_style(self, style: Self::Style) -> Self::Output; +} + +impl GeneralComponent for () { + fn render(self, _: &mut V, _: &mut ViewContext) -> AnyElement { + Empty::new().into_any() + } +} + +impl StyleableComponent for () { + type Style = (); + type Output = (); + + fn with_style(self, _: Self::Style) -> Self::Output { + () + } +} + pub trait Component { fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement; @@ -18,6 +46,12 @@ pub trait Component { } } +impl Component for C { + fn render(self, v: &mut V, cx: &mut ViewContext) -> AnyElement { + self.render(v, cx) + } +} + pub struct ComponentAdapter { component: Option, phantom: PhantomData, diff --git a/crates/gpui/src/elements/tooltip.rs b/crates/gpui/src/elements/tooltip.rs index 0ba0110303..0ce34fcc14 100644 --- a/crates/gpui/src/elements/tooltip.rs +++ b/crates/gpui/src/elements/tooltip.rs @@ -7,7 +7,7 @@ use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::json, Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, - Task, View, ViewContext, + Task, TypeTag, View, ViewContext, }; use schemars::JsonSchema; use serde::Deserialize; @@ -61,11 +61,23 @@ impl Tooltip { child: AnyElement, cx: &mut ViewContext, ) -> Self { - struct ElementState(Tag); - struct MouseEventHandlerState(Tag); + Self::new_dynamic(TypeTag::new::(), id, text, action, style, child, cx) + } + + pub fn new_dynamic( + mut tag: TypeTag, + id: usize, + text: impl Into>, + action: Option>, + style: TooltipStyle, + child: AnyElement, + cx: &mut ViewContext, + ) -> Self { + tag = tag.compose(TypeTag::new::()); + let focused_view_id = cx.focused_view_id(); - let state_handle = cx.default_element_state::, Rc>(id); + let state_handle = cx.default_element_state_dynamic::>(tag, id); let state = state_handle.read(cx).clone(); let text = text.into(); @@ -95,7 +107,7 @@ impl Tooltip { } else { None }; - let child = MouseEventHandler::new::, _>(id, cx, |_, _| child) + let child = MouseEventHandler::new_dynamic(tag, id, cx, |_, _| child) .on_hover(move |e, _, cx| { let position = e.position; if e.started { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index c31236023b..0c5b5717d6 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -19,6 +19,7 @@ use gpui::{ use project::search::SearchQuery; use serde::Deserialize; use std::{any::Any, sync::Arc}; + use util::ResultExt; use workspace::{ item::ItemHandle, @@ -167,23 +168,17 @@ impl View for BufferSearchBar { cx, ) }; - let render_search_option = - |options: bool, icon, option, cx: &mut ViewContext| { - options.then(|| { - let is_active = self.search_options.contains(option); - crate::search_bar::render_option_button_icon::( - is_active, - icon, - option.bits as usize, - format!("Toggle {}", option.label()), - option.to_toggle_action(), - move |_, this, cx| { - this.toggle_search_option(option, cx); - }, - cx, - ) - }) - }; + let render_search_option = |options: bool, icon, option| { + options.then(|| { + let is_active = self.search_options.contains(option); + option.as_button( + is_active, + icon, + theme.tooltip.clone(), + theme.search.option_button_component.clone(), + ) + }) + }; let match_count = self .active_searchable_item .as_ref() @@ -242,13 +237,11 @@ impl View for BufferSearchBar { supported_options.case, "icons/case_insensitive_12.svg", SearchOptions::CASE_SENSITIVE, - cx, )) .with_children(render_search_option( supported_options.word, "icons/word_search_12.svg", SearchOptions::WHOLE_WORD, - cx, )) .flex_float() .contained(), diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index 079a8965eb..ec6f97b04d 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -1,9 +1,14 @@ use bitflags::bitflags; pub use buffer_search::BufferSearchBar; -use gpui::{actions, Action, AppContext}; +use gpui::{ + actions, + elements::{Component, StyleableComponent, 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::ActionButton, ComponentExt, ToggleIconButtonStyle}; pub mod buffer_search; mod history; @@ -69,4 +74,23 @@ impl SearchOptions { options.set(SearchOptions::CASE_SENSITIVE, query.case_sensitive()); options } + + pub fn as_button( + &self, + active: bool, + icon: &str, + tooltip_style: TooltipStyle, + button_style: ToggleIconButtonStyle, + ) -> AnyElement { + ActionButton::new_dynamic( + self.to_toggle_action(), + format!("Toggle {}", self.label()), + tooltip_style, + ) + .with_contents(theme::components::svg::Svg::new(icon.to_owned())) + .toggleable(active) + .with_style(button_style) + .into_element() + .into_any() + } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4de0076825..80e823632a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -1,7 +1,9 @@ +pub mod components; mod theme_registry; mod theme_settings; pub mod ui; +use components::ToggleIconButtonStyle; use gpui::{ color::Color, elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle}, @@ -13,7 +15,7 @@ use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; use settings::SettingsStore; use std::{collections::HashMap, sync::Arc}; -use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle}; +use ui::{CheckboxStyle, CopilotCTAButton, IconStyle, ModalStyle}; pub use theme_registry::*; pub use theme_settings::*; @@ -182,7 +184,7 @@ pub struct CopilotAuth { pub prompting: CopilotAuthPrompting, pub not_authorized: CopilotAuthNotAuthorized, pub authorized: CopilotAuthAuthorized, - pub cta_button: ButtonStyle, + pub cta_button: CopilotCTAButton, pub header: IconStyle, } @@ -196,7 +198,7 @@ pub struct CopilotAuthPrompting { #[derive(Deserialize, Default, Clone, JsonSchema)] pub struct DeviceCode { pub text: TextStyle, - pub cta: ButtonStyle, + pub cta: CopilotCTAButton, pub left: f32, pub left_container: ContainerStyle, pub right: f32, @@ -420,6 +422,7 @@ pub struct Search { pub invalid_include_exclude_editor: ContainerStyle, pub include_exclude_inputs: ContainedText, pub option_button: Toggleable>, + pub option_button_component: ToggleIconButtonStyle, pub action_button: Toggleable>, pub match_background: Color, pub match_index: ContainedText, @@ -887,12 +890,32 @@ pub struct Interactive { pub disabled: Option, } +impl Interactive<()> { + pub fn new_blank() -> Self { + Self { + default: (), + hovered: None, + clicked: None, + disabled: None, + } + } +} + #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] pub struct Toggleable { active: T, inactive: T, } +impl Toggleable<()> { + pub fn new_blank() -> Self { + Self { + active: (), + inactive: (), + } + } +} + impl Toggleable { pub fn new(active: T, inactive: T) -> Self { Self { active, inactive } diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index f4a249e74e..7f0b05731e 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -145,12 +145,12 @@ pub fn keystroke_label( .with_style(label_style.container) } -pub type ButtonStyle = Interactive; +pub type CopilotCTAButton = Interactive; pub fn cta_button( label: L, max_width: f32, - style: &ButtonStyle, + style: &CopilotCTAButton, cx: &mut ViewContext, f: F, ) -> MouseEventHandler diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 3b295af802..528b1e2029 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -320,7 +320,6 @@ impl Pane { can_drop: Rc::new(|_, _| true), can_split: true, render_tab_bar_buttons: Rc::new(move |pane, cx| { - let tooltip_style = theme::current(cx).tooltip.clone(); Flex::row() // New menu .with_child(Self::render_tab_bar_button( diff --git a/styles/src/style_tree/search.ts b/styles/src/style_tree/search.ts index 4c0df69804..4493634a8e 100644 --- a/styles/src/style_tree/search.ts +++ b/styles/src/style_tree/search.ts @@ -96,6 +96,63 @@ export default function search(): any { }, }, }), + option_button_component: toggleable({ + base: interactive({ + base: { + icon_size: 14, + color: foreground(theme.highest, "variant"), + + button_width: 32, + background: background(theme.highest, "on"), + corner_radius: 2, + margin: { right: 2 }, + border: { + width: 1., color: background(theme.highest, "on") + }, + padding: { + left: 4, + right: 4, + top: 4, + bottom: 4, + }, + }, + state: { + hovered: { + ...text(theme.highest, "mono", "variant", "hovered"), + background: background(theme.highest, "on", "hovered"), + border: { + width: 1., color: background(theme.highest, "on", "hovered") + }, + }, + clicked: { + ...text(theme.highest, "mono", "variant", "pressed"), + background: background(theme.highest, "on", "pressed"), + border: { + width: 1., color: background(theme.highest, "on", "pressed") + }, + }, + }, + }), + state: { + active: { + default: { + icon_size: 14, + button_width: 32, + color: foreground(theme.highest, "variant"), + background: background(theme.highest, "accent"), + border: border(theme.highest, "accent"), + }, + hovered: { + background: background(theme.highest, "accent", "hovered"), + border: border(theme.highest, "accent", "hovered"), + }, + clicked: { + background: background(theme.highest, "accent", "pressed"), + border: border(theme.highest, "accent", "pressed"), + }, + }, + }, + }), action_button: toggleable({ base: interactive({ base: { From 21fa6090b8bc8d270b203b41544ad66d69727efb Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 17 Aug 2023 15:30:40 -0700 Subject: [PATCH 117/119] Add action button component for rendering the search options --- crates/search/src/buffer_search.rs | 37 ++-- crates/search/src/search.rs | 11 +- crates/theme/src/components.rs | 339 +++++++++++++++++++++++++++++ 3 files changed, 365 insertions(+), 22 deletions(-) create mode 100644 crates/theme/src/components.rs diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 0c5b5717d6..4078cb572d 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -168,16 +168,13 @@ impl View for BufferSearchBar { cx, ) }; - let render_search_option = |options: bool, icon, option| { - options.then(|| { - let is_active = self.search_options.contains(option); - option.as_button( - is_active, - icon, - theme.tooltip.clone(), - theme.search.option_button_component.clone(), - ) - }) + let search_option_button = |option| { + let is_active = self.search_options.contains(option); + option.as_button( + is_active, + theme.tooltip.clone(), + theme.search.option_button_component.clone(), + ) }; let match_count = self .active_searchable_item @@ -233,16 +230,16 @@ impl View for BufferSearchBar { .with_child(ChildView::new(&self.query_editor, cx).flex(1., true)) .with_child( Flex::row() - .with_children(render_search_option( - supported_options.case, - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - )) - .with_children(render_search_option( - supported_options.word, - "icons/word_search_12.svg", - SearchOptions::WHOLE_WORD, - )) + .with_children( + supported_options + .case + .then(|| search_option_button(SearchOptions::CASE_SENSITIVE)), + ) + .with_children( + supported_options + .word + .then(|| search_option_button(SearchOptions::WHOLE_WORD)), + ) .flex_float() .contained(), ) diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index ec6f97b04d..c31ea6f2f8 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -56,6 +56,14 @@ impl SearchOptions { } } + pub fn icon(&self) -> &'static str { + match *self { + SearchOptions::WHOLE_WORD => "icons/word_search_12.svg", + SearchOptions::CASE_SENSITIVE => "icons/case_insensitive_12.svg", + _ => panic!("{:?} is not a named SearchOption", self), + } + } + pub fn to_toggle_action(&self) -> Box { match *self { SearchOptions::WHOLE_WORD => Box::new(ToggleWholeWord), @@ -78,7 +86,6 @@ impl SearchOptions { pub fn as_button( &self, active: bool, - icon: &str, tooltip_style: TooltipStyle, button_style: ToggleIconButtonStyle, ) -> AnyElement { @@ -87,7 +94,7 @@ impl SearchOptions { format!("Toggle {}", self.label()), tooltip_style, ) - .with_contents(theme::components::svg::Svg::new(icon.to_owned())) + .with_contents(theme::components::svg::Svg::new(self.icon())) .toggleable(active) .with_style(button_style) .into_element() diff --git a/crates/theme/src/components.rs b/crates/theme/src/components.rs new file mode 100644 index 0000000000..fce7ad825c --- /dev/null +++ b/crates/theme/src/components.rs @@ -0,0 +1,339 @@ +use gpui::elements::StyleableComponent; + +use crate::{Interactive, Toggleable}; + +use self::{action_button::ButtonStyle, svg::SvgStyle, toggle::Toggle}; + +pub type ToggleIconButtonStyle = Toggleable>>; + +pub trait ComponentExt { + fn toggleable(self, active: bool) -> Toggle; +} + +impl ComponentExt for C { + fn toggleable(self, active: bool) -> Toggle { + Toggle::new(self, active) + } +} + +pub mod toggle { + use gpui::elements::{GeneralComponent, StyleableComponent}; + + use crate::Toggleable; + + pub struct Toggle { + style: S, + active: bool, + component: C, + } + + impl Toggle { + pub fn new(component: C, active: bool) -> Self { + Toggle { + active, + component, + style: (), + } + } + } + + impl StyleableComponent for Toggle { + type Style = Toggleable; + + type Output = Toggle; + + fn with_style(self, style: Self::Style) -> Self::Output { + Toggle { + active: self.active, + component: self.component, + style, + } + } + } + + impl GeneralComponent for Toggle> { + fn render( + self, + v: &mut V, + cx: &mut gpui::ViewContext, + ) -> gpui::AnyElement { + self.component + .with_style(self.style.in_state(self.active).clone()) + .render(v, cx) + } + } +} + +pub mod action_button { + use std::borrow::Cow; + + use gpui::{ + elements::{ + ContainerStyle, GeneralComponent, MouseEventHandler, StyleableComponent, TooltipStyle, + }, + platform::{CursorStyle, MouseButton}, + Action, Element, TypeTag, View, + }; + use schemars::JsonSchema; + use serde_derive::Deserialize; + + use crate::Interactive; + + pub struct ActionButton { + action: Box, + tooltip: Cow<'static, str>, + tooltip_style: TooltipStyle, + tag: TypeTag, + contents: C, + style: Interactive, + } + + #[derive(Clone, Deserialize, Default, JsonSchema)] + pub struct ButtonStyle { + #[serde(flatten)] + container: ContainerStyle, + button_width: Option, + button_height: Option, + #[serde(flatten)] + contents: C, + } + + impl ActionButton<(), ()> { + pub fn new_dynamic( + action: Box, + tooltip: impl Into>, + tooltip_style: TooltipStyle, + ) -> Self { + Self { + contents: (), + tag: action.type_tag(), + style: Interactive::new_blank(), + tooltip: tooltip.into(), + tooltip_style, + action, + } + } + + pub fn new( + action: A, + tooltip: impl Into>, + tooltip_style: TooltipStyle, + ) -> Self { + Self::new_dynamic(Box::new(action), tooltip, tooltip_style) + } + + pub fn with_contents(self, contents: C) -> ActionButton { + ActionButton { + action: self.action, + tag: self.tag, + style: self.style, + tooltip: self.tooltip, + tooltip_style: self.tooltip_style, + contents, + } + } + } + + impl StyleableComponent for ActionButton { + type Style = Interactive>; + type Output = ActionButton>; + + fn with_style(self, style: Self::Style) -> Self::Output { + ActionButton { + action: self.action, + tag: self.tag, + contents: self.contents, + tooltip: self.tooltip, + tooltip_style: self.tooltip_style, + style, + } + } + } + + impl GeneralComponent for ActionButton> { + fn render(self, v: &mut V, cx: &mut gpui::ViewContext) -> gpui::AnyElement { + MouseEventHandler::new_dynamic(self.tag, 0, cx, |state, cx| { + let style = self.style.style_for(state); + let mut contents = self + .contents + .with_style(style.contents.to_owned()) + .render(v, cx) + .contained() + .with_style(style.container) + .constrained(); + + if let Some(height) = style.button_height { + contents = contents.with_height(height); + } + + if let Some(width) = style.button_width { + contents = contents.with_width(width); + } + + contents.into_any() + }) + .on_click(MouseButton::Left, { + let action = self.action.boxed_clone(); + move |_, _, cx| { + cx.window() + .dispatch_action(cx.view_id(), action.as_ref(), cx); + } + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_dynamic_tooltip( + self.tag, + 0, + self.tooltip, + Some(self.action), + self.tooltip_style, + cx, + ) + .into_any() + } + } +} + +pub mod svg { + use std::borrow::Cow; + + use gpui::{ + elements::{GeneralComponent, StyleableComponent}, + Element, + }; + use schemars::JsonSchema; + use serde::Deserialize; + + #[derive(Clone, Default, JsonSchema)] + pub struct SvgStyle { + icon_width: f32, + icon_height: f32, + color: gpui::color::Color, + } + + impl<'de> Deserialize<'de> for SvgStyle { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + pub enum IconSize { + IconSize { icon_size: f32 }, + Dimensions { width: f32, height: f32 }, + } + + #[derive(Deserialize)] + struct SvgStyleHelper { + #[serde(flatten)] + size: IconSize, + color: gpui::color::Color, + } + + let json = SvgStyleHelper::deserialize(deserializer)?; + let color = json.color; + + let result = match json.size { + IconSize::IconSize { icon_size } => SvgStyle { + icon_width: icon_size, + icon_height: icon_size, + color, + }, + IconSize::Dimensions { width, height } => SvgStyle { + icon_width: width, + icon_height: height, + color, + }, + }; + + Ok(result) + } + } + + pub struct Svg { + path: Cow<'static, str>, + style: S, + } + + impl Svg<()> { + pub fn new(path: impl Into>) -> Self { + Self { + path: path.into(), + style: (), + } + } + } + + impl StyleableComponent for Svg<()> { + type Style = SvgStyle; + + type Output = Svg; + + fn with_style(self, style: Self::Style) -> Self::Output { + Svg { + path: self.path, + style, + } + } + } + + impl GeneralComponent for Svg { + fn render( + self, + _: &mut V, + _: &mut gpui::ViewContext, + ) -> gpui::AnyElement { + gpui::elements::Svg::new(self.path) + .with_color(self.style.color) + .constrained() + .with_width(self.style.icon_width) + .with_height(self.style.icon_height) + .into_any() + } + } +} + +pub mod label { + use std::borrow::Cow; + + use gpui::{ + elements::{GeneralComponent, LabelStyle, StyleableComponent}, + Element, + }; + + pub struct Label { + text: Cow<'static, str>, + style: S, + } + + impl Label<()> { + pub fn new(text: impl Into>) -> Self { + Self { + text: text.into(), + style: (), + } + } + } + + impl StyleableComponent for Label<()> { + type Style = LabelStyle; + + type Output = Label; + + fn with_style(self, style: Self::Style) -> Self::Output { + Label { + text: self.text, + style, + } + } + } + + impl GeneralComponent for Label { + fn render( + self, + _: &mut V, + _: &mut gpui::ViewContext, + ) -> gpui::AnyElement { + gpui::elements::Label::new(self.text, self.style).into_any() + } + } +} From c0f042b39a595da6f93de2268e5990a3c5604912 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 17 Aug 2023 17:25:50 -0700 Subject: [PATCH 118/119] Remove semantic search UI --- crates/gpui/src/elements/component.rs | 51 +++-- crates/search/src/buffer_search.rs | 7 +- crates/search/src/mode.rs | 20 +- crates/search/src/project_search.rs | 296 ++++---------------------- crates/search/src/search.rs | 7 +- crates/settings/src/settings.rs | 2 + crates/theme/src/components.rs | 9 +- crates/workspace/src/pane.rs | 4 - 8 files changed, 89 insertions(+), 307 deletions(-) diff --git a/crates/gpui/src/elements/component.rs b/crates/gpui/src/elements/component.rs index 018dc644c6..a26355a539 100644 --- a/crates/gpui/src/elements/component.rs +++ b/crates/gpui/src/elements/component.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use crate::{ @@ -52,22 +50,26 @@ impl Component for C { } } -pub struct ComponentAdapter { +pub struct ComponentAdapter { component: Option, - phantom: PhantomData, + element: Option>, + #[cfg(debug_assertions)] + _component_name: &'static str, } -impl ComponentAdapter { +impl ComponentAdapter { pub fn new(e: E) -> Self { Self { component: Some(e), - phantom: PhantomData, + element: None, + #[cfg(debug_assertions)] + _component_name: std::any::type_name::(), } } } impl + 'static> Element for ComponentAdapter { - type LayoutState = AnyElement; + type LayoutState = (); type PaintState = (); @@ -77,10 +79,12 @@ impl + 'static> Element for ComponentAdapter { view: &mut V, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { - let component = self.component.take().unwrap(); - let mut element = component.render(view, cx.view_context()); - let constraint = element.layout(constraint, view, cx); - (constraint, element) + if self.element.is_none() { + let component = self.component.take().unwrap(); + self.element = Some(component.render(view, cx.view_context())); + } + let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx); + (constraint, ()) } fn paint( @@ -88,11 +92,14 @@ impl + 'static> Element for ComponentAdapter { scene: &mut SceneBuilder, bounds: RectF, visible_bounds: RectF, - layout: &mut Self::LayoutState, + _: &mut Self::LayoutState, view: &mut V, cx: &mut PaintContext, ) -> Self::PaintState { - layout.paint(scene, bounds.origin(), visible_bounds, view, cx) + self.element + .as_mut() + .unwrap() + .paint(scene, bounds.origin(), visible_bounds, view, cx) } fn rect_for_text_range( @@ -100,25 +107,35 @@ impl + 'static> Element for ComponentAdapter { range_utf16: std::ops::Range, _: RectF, _: RectF, - element: &Self::LayoutState, + _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> Option { - element.rect_for_text_range(range_utf16, view, cx) + self.element + .as_ref() + .unwrap() + .rect_for_text_range(range_utf16, view, cx) } fn debug( &self, _: RectF, - element: &Self::LayoutState, + _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> serde_json::Value { + #[cfg(debug_assertions)] + let component_name = self._component_name; + + #[cfg(not(debug_assertions))] + let component_name = "Unknown"; + serde_json::json!({ "type": "ComponentAdapter", - "child": element.debug(view, cx), + "child": self.element.as_ref().unwrap().debug(view, cx), + "component_name": component_name }) } } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 4078cb572d..b65c7222a4 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -523,11 +523,6 @@ impl BufferSearchBar { } pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { - assert_ne!( - mode, - SearchMode::Semantic, - "Semantic search is not supported in buffer search" - ); if mode == self.current_mode { return; } @@ -802,7 +797,7 @@ impl BufferSearchBar { } } fn cycle_mode(&mut self, _: &CycleMode, cx: &mut ViewContext) { - self.activate_search_mode(next_mode(&self.current_mode, false), cx); + self.activate_search_mode(next_mode(&self.current_mode), cx); } fn cycle_mode_on_pane(pane: &mut Pane, action: &CycleMode, cx: &mut ViewContext) { let mut should_propagate = true; diff --git a/crates/search/src/mode.rs b/crates/search/src/mode.rs index 2c180be761..0163528951 100644 --- a/crates/search/src/mode.rs +++ b/crates/search/src/mode.rs @@ -1,12 +1,11 @@ use gpui::Action; -use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode}; +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, - Semantic, Regex, } @@ -20,7 +19,6 @@ impl SearchMode { pub(crate) fn label(&self) -> &'static str { match self { SearchMode::Text => "Text", - SearchMode::Semantic => "Semantic", SearchMode::Regex => "Regex", } } @@ -28,7 +26,6 @@ impl SearchMode { pub(crate) fn region_id(&self) -> usize { match self { SearchMode::Text => 3, - SearchMode::Semantic => 4, SearchMode::Regex => 5, } } @@ -36,7 +33,6 @@ impl SearchMode { pub(crate) fn tooltip_text(&self) -> &'static str { match self { SearchMode::Text => "Activate Text Search", - SearchMode::Semantic => "Activate Semantic Search", SearchMode::Regex => "Activate Regex Search", } } @@ -44,7 +40,6 @@ impl SearchMode { pub(crate) fn activate_action(&self) -> Box { match self { SearchMode::Text => Box::new(ActivateTextMode), - SearchMode::Semantic => Box::new(ActivateSemanticMode), SearchMode::Regex => Box::new(ActivateRegexMode), } } @@ -53,7 +48,6 @@ impl SearchMode { match self { SearchMode::Regex => true, SearchMode::Text => true, - SearchMode::Semantic => true, } } @@ -67,22 +61,14 @@ impl SearchMode { pub(crate) fn button_side(&self) -> Option { match self { SearchMode::Text => Some(Side::Left), - SearchMode::Semantic => None, SearchMode::Regex => Some(Side::Right), } } } -pub(crate) fn next_mode(mode: &SearchMode, semantic_enabled: bool) -> SearchMode { - let next_text_state = if semantic_enabled { - SearchMode::Semantic - } else { - SearchMode::Regex - }; - +pub(crate) fn next_mode(mode: &SearchMode) -> SearchMode { match mode { - SearchMode::Text => next_text_state, - SearchMode::Semantic => SearchMode::Regex, + SearchMode::Text => SearchMode::Regex, SearchMode::Regex => SearchMode::Text, } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e15bd0e27a..abb7395e50 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2,10 +2,10 @@ use crate::{ history::SearchHistory, mode::SearchMode, search_bar::{render_nav_button, render_option_button_icon, render_search_mode_button}, - ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, - SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, + CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, + SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, }; -use anyhow::{Context, Result}; +use anyhow::Context; use collections::HashMap; use editor::{ items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, @@ -13,8 +13,6 @@ use editor::{ }; use futures::StreamExt; -use gpui::platform::PromptLevel; - use gpui::{ actions, elements::*, platform::MouseButton, Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, @@ -22,12 +20,10 @@ use gpui::{ }; use menu::Confirm; -use postage::stream::Stream; use project::{ - search::{PathMatcher, SearchInputs, SearchQuery}, + search::{PathMatcher, SearchQuery}, Entry, Project, }; -use semantic_index::SemanticIndex; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -118,8 +114,6 @@ pub struct ProjectSearchView { model: ModelHandle, query_editor: ViewHandle, results_editor: ViewHandle, - semantic_state: Option, - semantic_permissioned: Option, search_options: SearchOptions, panels_with_errors: HashSet, active_match_index: Option, @@ -131,12 +125,6 @@ pub struct ProjectSearchView { current_mode: SearchMode, } -struct SemanticSearchState { - file_count: usize, - outstanding_file_count: usize, - _progress_task: Task<()>, -} - pub struct ProjectSearchBar { active_project_search: Option>, subscription: Option, @@ -218,60 +206,6 @@ impl ProjectSearch { })); cx.notify(); } - - fn semantic_search(&mut self, inputs: &SearchInputs, cx: &mut ModelContext) { - let search = SemanticIndex::global(cx).map(|index| { - index.update(cx, |semantic_index, cx| { - semantic_index.search_project( - self.project.clone(), - inputs.as_str().to_owned(), - 10, - inputs.files_to_include().to_vec(), - inputs.files_to_exclude().to_vec(), - cx, - ) - }) - }); - self.search_id += 1; - self.match_ranges.clear(); - self.search_history.add(inputs.as_str().to_string()); - self.no_results = Some(true); - self.pending_search = Some(cx.spawn(|this, mut cx| async move { - let results = search?.await.log_err()?; - - let (_task, mut match_ranges) = this.update(&mut cx, |this, cx| { - this.excerpts.update(cx, |excerpts, cx| { - excerpts.clear(cx); - - let matches = results - .into_iter() - .map(|result| (result.buffer, vec![result.range.start..result.range.start])) - .collect(); - - excerpts.stream_excerpts_with_context_lines(matches, 3, cx) - }) - }); - - while let Some(match_range) = match_ranges.next().await { - this.update(&mut cx, |this, cx| { - this.match_ranges.push(match_range); - while let Ok(Some(match_range)) = match_ranges.try_next() { - this.match_ranges.push(match_range); - } - this.no_results = Some(false); - cx.notify(); - }); - } - - this.update(&mut cx, |this, cx| { - this.pending_search.take(); - cx.notify(); - }); - - None - })); - cx.notify(); - } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -311,27 +245,10 @@ impl View for ProjectSearchView { } else { match current_mode { SearchMode::Text => Cow::Borrowed("Text search all files and folders"), - SearchMode::Semantic => { - Cow::Borrowed("Search all code objects using Natural Language") - } SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), } }; - let semantic_status = if let Some(semantic) = &self.semantic_state { - if semantic.outstanding_file_count > 0 { - format!( - "Indexing: {} of {}...", - semantic.file_count - semantic.outstanding_file_count, - semantic.file_count - ) - } else { - "Indexing complete".to_string() - } - } else { - "Indexing: ...".to_string() - }; - let minor_text = if let Some(no_results) = model.no_results { if model.pending_search.is_none() && no_results { vec!["No results found in this project for the provided query".to_owned()] @@ -339,19 +256,11 @@ impl View for ProjectSearchView { vec![] } } else { - match current_mode { - SearchMode::Semantic => vec![ - "".to_owned(), - semantic_status, - "Simply explain the code you are looking to find.".to_owned(), - "ex. 'prompt user for permissions to index their project'".to_owned(), - ], - _ => vec![ - "".to_owned(), - "Include/exclude specific paths with the filter option.".to_owned(), - "Matching exact word and/or casing is available too.".to_owned(), - ], - } + vec![ + "".to_owned(), + "Include/exclude specific paths with the filter option.".to_owned(), + "Matching exact word and/or casing is available too.".to_owned(), + ] }; let previous_query_keystrokes = @@ -630,49 +539,6 @@ impl ProjectSearchView { self.search_options.toggle(option); } - fn index_project(&mut self, cx: &mut ViewContext) { - if let Some(semantic_index) = SemanticIndex::global(cx) { - // Semantic search uses no options - self.search_options = SearchOptions::none(); - - let project = self.model.read(cx).project.clone(); - let index_task = semantic_index.update(cx, |semantic_index, cx| { - semantic_index.index_project(project, cx) - }); - - cx.spawn(|search_view, mut cx| async move { - let (files_to_index, mut files_remaining_rx) = index_task.await?; - - search_view.update(&mut cx, |search_view, cx| { - cx.notify(); - search_view.semantic_state = Some(SemanticSearchState { - file_count: files_to_index, - outstanding_file_count: files_to_index, - _progress_task: cx.spawn(|search_view, mut cx| async move { - while let Some(count) = files_remaining_rx.recv().await { - search_view - .update(&mut cx, |search_view, cx| { - if let Some(semantic_search_state) = - &mut search_view.semantic_state - { - semantic_search_state.outstanding_file_count = count; - cx.notify(); - if count == 0 { - return; - } - } - }) - .ok(); - } - }), - }); - })?; - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - } - } - fn clear_search(&mut self, cx: &mut ViewContext) { self.model.update(cx, |model, cx| { model.pending_search = None; @@ -696,56 +562,7 @@ impl ProjectSearchView { self.current_mode = mode; match mode { - SearchMode::Semantic => { - let has_permission = self.semantic_permissioned(cx); - self.active_match_index = None; - cx.spawn(|this, mut cx| async move { - let has_permission = has_permission.await?; - - if !has_permission { - let mut answer = this.update(&mut cx, |this, cx| { - let project = this.model.read(cx).project.clone(); - let project_name = project - .read(cx) - .worktree_root_names(cx) - .collect::>() - .join("/"); - let is_plural = - project_name.chars().filter(|letter| *letter == '/').count() > 0; - let prompt_text = format!("Would you like to index the '{}' project{} for semantic search? This requires sending code to the OpenAI API", project_name, - if is_plural { - "s" - } else {""}); - cx.prompt( - PromptLevel::Info, - prompt_text.as_str(), - &["Continue", "Cancel"], - ) - })?; - - if answer.next().await == Some(0) { - this.update(&mut cx, |this, _| { - this.semantic_permissioned = Some(true); - })?; - } else { - this.update(&mut cx, |this, cx| { - this.semantic_permissioned = Some(false); - debug_assert_ne!(previous_mode, SearchMode::Semantic, "Tried to re-enable semantic search mode after user modal was rejected"); - this.activate_search_mode(previous_mode, cx); - })?; - return anyhow::Ok(()); - } - } - - this.update(&mut cx, |this, cx| { - this.index_project(cx); - })?; - - anyhow::Ok(()) - }).detach_and_log_err(cx); - } SearchMode::Regex | SearchMode::Text => { - self.semantic_state = None; self.active_match_index = None; } } @@ -843,8 +660,6 @@ impl ProjectSearchView { model, query_editor, results_editor, - semantic_state: None, - semantic_permissioned: None, search_options: options, panels_with_errors: HashSet::new(), active_match_index: None, @@ -858,18 +673,6 @@ impl ProjectSearchView { this } - fn semantic_permissioned(&mut self, cx: &mut ViewContext) -> Task> { - if let Some(value) = self.semantic_permissioned { - return Task::ready(Ok(value)); - } - - SemanticIndex::global(cx) - .map(|semantic| { - let project = self.model.read(cx).project.clone(); - semantic.update(cx, |this, cx| this.project_previously_indexed(project, cx)) - }) - .unwrap_or(Task::ready(Ok(false))) - } pub fn new_search_in_directory( workspace: &mut Workspace, dir_entry: &Entry, @@ -945,26 +748,8 @@ impl ProjectSearchView { } fn search(&mut self, cx: &mut ViewContext) { - let mode = self.current_mode; - match mode { - SearchMode::Semantic => { - if let Some(semantic) = &mut self.semantic_state { - if semantic.outstanding_file_count > 0 { - return; - } - - if let Some(query) = self.build_search_query(cx) { - self.model - .update(cx, |model, cx| model.semantic_search(query.as_inner(), cx)); - } - } - } - - _ => { - if let Some(query) = self.build_search_query(cx) { - self.model.update(cx, |model, cx| model.search(query, cx)); - } - } + if let Some(query) = self.build_search_query(cx) { + self.model.update(cx, |model, cx| model.search(query, cx)); } } @@ -1164,8 +949,7 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |this, cx| { - let new_mode = - crate::mode::next_mode(&this.current_mode, SemanticIndex::enabled(cx)); + let new_mode = crate::mode::next_mode(&this.current_mode); this.activate_search_mode(new_mode, cx); }) } @@ -1288,18 +1072,18 @@ impl ProjectSearchBar { } } - fn activate_regex_mode(pane: &mut Pane, _: &ActivateRegexMode, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| { - view.activate_search_mode(SearchMode::Regex, cx) - }); - } else { - cx.propagate_action(); - } - } + // fn activate_regex_mode(pane: &mut Pane, _: &ActivateRegexMode, cx: &mut ViewContext) { + // if let Some(search_view) = pane + // .active_item() + // .and_then(|item| item.downcast::()) + // { + // search_view.update(cx, |view, cx| { + // view.activate_search_mode(SearchMode::Regex, cx) + // }); + // } else { + // cx.propagate_action(); + // } + // } fn toggle_filters(&mut self, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { @@ -1400,7 +1184,6 @@ impl View for ProjectSearchBar { theme.search.editor.input.container }; - let row_spacing = theme.workspace.toolbar.container.padding.bottom; let search = _search.read(cx); let filter_button = render_option_button_icon( search.filters_enabled, @@ -1413,8 +1196,7 @@ impl View for ProjectSearchBar { }, cx, ); - let search = _search.read(cx); - let is_semantic_disabled = search.semantic_state.is_none(); + let render_option_button_icon = |path, option, cx: &mut ViewContext| { crate::search_bar::render_option_button_icon( self.is_option_enabled(option, cx), @@ -1428,17 +1210,17 @@ impl View for ProjectSearchBar { cx, ) }; - let case_sensitive = is_semantic_disabled.then(|| { - render_option_button_icon( - "icons/case_insensitive_12.svg", - SearchOptions::CASE_SENSITIVE, - cx, - ) - }); + let case_sensitive = render_option_button_icon( + "icons/case_insensitive_12.svg", + SearchOptions::CASE_SENSITIVE, + cx, + ); - let whole_word = is_semantic_disabled.then(|| { - render_option_button_icon("icons/word_search_12.svg", SearchOptions::WHOLE_WORD, cx) - }); + let whole_word = render_option_button_icon( + "icons/word_search_12.svg", + SearchOptions::WHOLE_WORD, + cx, + ); let search = _search.read(cx); let icon_style = theme.search.editor_icon.clone(); @@ -1454,8 +1236,8 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .with_child(filter_button) - .with_children(case_sensitive) - .with_children(whole_word) + .with_child(case_sensitive) + .with_child(whole_word) .flex(1., false) .constrained() .contained(), @@ -1554,8 +1336,7 @@ impl View for ProjectSearchBar { ) }; let is_active = search.active_match_index.is_some(); - let semantic_index = SemanticIndex::enabled(cx) - .then(|| search_button_for_mode(SearchMode::Semantic, cx)); + let nav_button_for_direction = |label, direction, cx: &mut ViewContext| { render_nav_button( label, @@ -1581,7 +1362,6 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .with_child(search_button_for_mode(SearchMode::Text, cx)) - .with_children(semantic_index) .with_child(search_button_for_mode(SearchMode::Regex, cx)) .contained() .with_style(theme.search.modes_container), diff --git a/crates/search/src/search.rs b/crates/search/src/search.rs index c31ea6f2f8..7132efa5e3 100644 --- a/crates/search/src/search.rs +++ b/crates/search/src/search.rs @@ -8,7 +8,9 @@ use gpui::{ pub use mode::SearchMode; use project::search::SearchQuery; pub use project_search::{ProjectSearchBar, ProjectSearchView}; -use theme::components::{action_button::ActionButton, ComponentExt, ToggleIconButtonStyle}; +use theme::components::{ + action_button::ActionButton, svg::Svg, ComponentExt, ToggleIconButtonStyle, +}; pub mod buffer_search; mod history; @@ -33,7 +35,6 @@ actions!( NextHistoryQuery, PreviousHistoryQuery, ActivateTextMode, - ActivateSemanticMode, ActivateRegexMode ] ); @@ -94,7 +95,7 @@ impl SearchOptions { format!("Toggle {}", self.label()), tooltip_style, ) - .with_contents(theme::components::svg::Svg::new(self.icon())) + .with_contents(Svg::new(self.icon())) .toggleable(active) .with_style(button_style) .into_element() diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 8c3587d942..5287c999e8 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case, non_upper_case_globals)] + mod keymap_file; mod settings_file; mod settings_store; diff --git a/crates/theme/src/components.rs b/crates/theme/src/components.rs index fce7ad825c..a74b9ed4a4 100644 --- a/crates/theme/src/components.rs +++ b/crates/theme/src/components.rs @@ -175,8 +175,13 @@ pub mod action_button { .on_click(MouseButton::Left, { let action = self.action.boxed_clone(); move |_, _, cx| { - cx.window() - .dispatch_action(cx.view_id(), action.as_ref(), cx); + let window = cx.window(); + let view = cx.view_id(); + let action = action.boxed_clone(); + cx.spawn(|_, mut cx| async move { + window.dispatch_action(view, action.as_ref(), &mut cx); + }) + .detach(); } }) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 528b1e2029..60d22bbdae 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -286,10 +286,6 @@ impl Pane { context_menu.update(cx, |menu, _| { menu.set_position_mode(OverlayPositionMode::Local) }); - let theme = theme::current(cx).workspace.tab_bar.clone(); - - let nav_button_height = theme.height; - let button_style = theme.nav_button; Self { items: Vec::new(), From 66e94aa199fcfb1f9b0beb93c4c64254d51afec8 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Thu, 17 Aug 2023 17:53:21 -0700 Subject: [PATCH 119/119] Make search re-query eagerly when changing modes Fix a bug where focus could be lost when clearing the search results --- crates/search/src/project_search.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index abb7395e50..2cec9610f1 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -241,7 +241,7 @@ impl View for ProjectSearchView { let major_text = if model.pending_search.is_some() { Cow::Borrowed("Searching...") } else if model.no_results.is_some_and(|v| v) { - Cow::Borrowed("No Results...") + Cow::Borrowed("No Results") } else { match current_mode { SearchMode::Text => Cow::Borrowed("Text search all files and folders"), @@ -552,23 +552,20 @@ impl ProjectSearchView { } fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext) { - self.clear_search(cx); - let previous_mode = self.current_mode; if previous_mode == mode { return; } + self.clear_search(cx); self.current_mode = mode; + self.active_match_index = None; - match mode { - SearchMode::Regex | SearchMode::Text => { - self.active_match_index = None; - } - } + self.search(cx); cx.notify(); } + fn new(model: ModelHandle, cx: &mut ViewContext) -> Self { let project; let excerpts; @@ -951,6 +948,7 @@ impl ProjectSearchBar { search_view.update(cx, |this, cx| { let new_mode = crate::mode::next_mode(&this.current_mode); this.activate_search_mode(new_mode, cx); + cx.focus(&this.query_editor); }) } } @@ -1062,8 +1060,9 @@ impl ProjectSearchBar { fn toggle_search_option(&mut self, option: SearchOptions, cx: &mut ViewContext) -> bool { 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| { search_view.toggle_search_option(option); + search_view.search(cx); }); cx.notify(); true