From 0c23e6738b85cbb557223812286294791dbccef8 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:36:45 +0100 Subject: [PATCH 01/18] Barebones project search (no UI, but the crate compiles) --- Cargo.lock | 1 + crates/editor2/src/editor.rs | 12 +- crates/search2/Cargo.toml | 2 +- crates/search2/src/project_search.rs | 1489 +++++++++++++------------- crates/search2/src/search.rs | 5 +- 5 files changed, 759 insertions(+), 750 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f15c9c8e6..203dfe991a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8252,6 +8252,7 @@ dependencies = [ "menu2", "postage", "project2", + "semantic_index2", "serde", "serde_derive", "serde_json", diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 46d64fcf9d..9fffdbccf3 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -8682,13 +8682,13 @@ impl Editor { ); } - // pub fn set_searchable(&mut self, searchable: bool) { - // self.searchable = searchable; - // } + pub fn set_searchable(&mut self, searchable: bool) { + self.searchable = searchable; + } - // pub fn searchable(&self) -> bool { - // self.searchable - // } + pub fn searchable(&self) -> bool { + self.searchable + } fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext) { let buffer = self.buffer.read(cx); diff --git a/crates/search2/Cargo.toml b/crates/search2/Cargo.toml index 97cfdd6494..34158e7416 100644 --- a/crates/search2/Cargo.toml +++ b/crates/search2/Cargo.toml @@ -21,7 +21,7 @@ theme = { package = "theme2", path = "../theme2" } util = { path = "../util" } ui = {package = "ui2", path = "../ui2"} workspace = { package = "workspace2", path = "../workspace2" } -#semantic_index = { path = "../semantic_index" } +semantic_index = { package = "semantic_index2", path = "../semantic_index2" } anyhow.workspace = true futures.workspace = true log.workspace = true diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 41dd87d4d3..e75b4f26b1 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1,24 +1,21 @@ use crate::{ history::SearchHistory, - mode::{SearchMode, Side}, - search_bar::{render_nav_button, render_option_button_icon, render_search_mode_button}, + mode::SearchMode, + search_bar::{render_nav_button, render_search_mode_button}, ActivateRegexMode, ActivateSemanticMode, ActivateTextMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleReplace, ToggleWholeWord, }; -use anyhow::{Context, Result}; +use anyhow::{Context as _, Result}; use collections::HashMap; use editor::{ - items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, - SelectAll, MAX_TAB_TITLE_LEN, + items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, EditorEvent, + MultiBuffer, SelectAll, MAX_TAB_TITLE_LEN, }; -use futures::StreamExt; use gpui::{ - actions, - elements::*, - platform::{MouseButton, PromptLevel}, - Action, AnyElement, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, Subscription, - Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, + actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Div, Element, Entity, + EntityId, EventEmitter, FocusableView, Model, ModelContext, PromptLevel, Render, SharedString, + Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, }; use menu::Confirm; use project::{ @@ -27,6 +24,7 @@ use project::{ }; use semantic_index::{SemanticIndex, SemanticIndexStatus}; use smallvec::SmallVec; +use smol::stream::StreamExt; use std::{ any::{Any, TypeId}, borrow::Cow, @@ -41,81 +39,87 @@ use util::{paths::PathMatcher, ResultExt as _}; use workspace::{ item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, searchable::{Direction, SearchableItem, SearchableItemHandle}, - ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceId, + ItemNavHistory, Pane, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, + WorkspaceId, }; -actions!( - project_search, - [SearchInNew, ToggleFocus, NextField, ToggleFilters,] -); +actions!(SearchInNew, ToggleFocus, NextField, ToggleFilters,); #[derive(Default)] -struct ActiveSearches(HashMap, WeakViewHandle>); +struct ActiveSearches(HashMap, WeakView>); #[derive(Default)] -struct ActiveSettings(HashMap, ProjectSearchSettings>); +struct ActiveSettings(HashMap, ProjectSearchSettings>); pub fn init(cx: &mut AppContext) { + // todo!() po cx.set_global(ActiveSearches::default()); cx.set_global(ActiveSettings::default()); - cx.add_action(ProjectSearchView::deploy); - cx.add_action(ProjectSearchView::move_focus_to_results); - cx.add_action(ProjectSearchBar::confirm); - 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::replace_next); - cx.add_action(ProjectSearchBar::replace_all); - 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::toggle_replace); - cx.add_action(ProjectSearchBar::toggle_replace_on_a_pane); - cx.add_action(ProjectSearchBar::activate_text_mode); + cx.observe_new_views(|workspace: &mut Workspace, cx| { + workspace.register_action(ProjectSearchView::deploy); + }) + .detach(); - // This action should only be registered if the semantic index is enabled - // We are registering it all the time, as I dont want to introduce a dependency - // for Semantic Index Settings globally whenever search is tested. - cx.add_action(ProjectSearchBar::activate_semantic_mode); + // cx.add_action(ProjectSearchView::deploy); + // cx.add_action(ProjectSearchView::move_focus_to_results); + // cx.add_action(ProjectSearchBar::confirm); + // 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::replace_next); + // cx.add_action(ProjectSearchBar::replace_all); + // 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::toggle_replace); + // cx.add_action(ProjectSearchBar::toggle_replace_on_a_pane); + // cx.add_action(ProjectSearchBar::activate_text_mode); - cx.capture_action(ProjectSearchBar::tab); - cx.capture_action(ProjectSearchBar::tab_previous); - cx.capture_action(ProjectSearchView::replace_all); - cx.capture_action(ProjectSearchView::replace_next); - add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); - add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); - add_toggle_option_action::(SearchOptions::INCLUDE_IGNORED, cx); - add_toggle_filters_action::(cx); + // // This action should only be registered if the semantic index is enabled + // // We are registering it all the time, as I dont want to introduce a dependency + // // for Semantic Index Settings globally whenever search is tested. + // cx.add_action(ProjectSearchBar::activate_semantic_mode); + + // cx.capture_action(ProjectSearchBar::tab); + // cx.capture_action(ProjectSearchBar::tab_previous); + // cx.capture_action(ProjectSearchView::replace_all); + // cx.capture_action(ProjectSearchView::replace_next); + // add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); + // add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); + // add_toggle_option_action::(SearchOptions::INCLUDE_IGNORED, 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(); - }); + // todo!() po + // cx.register_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)) { + // cx.stop_propagation(); + // return; + // } + // } + // }); } fn add_toggle_option_action(option: SearchOptions, 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_search_option(option, cx) - }) { - return; - } - } - cx.propagate_action(); - }); + // todo!() po + // cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { + // if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { + // if search_bar.update(cx, |search_bar, cx| { + // search_bar.toggle_search_option(option, cx) + // }) { + // cx.stop_propagation(); + // return; + // } + // } + // }); } struct ProjectSearch { - project: ModelHandle, - excerpts: ModelHandle, + project: Model, + excerpts: Model, pending_search: Option>>, match_ranges: Vec>, active_query: Option, @@ -132,10 +136,10 @@ enum InputPanel { } pub struct ProjectSearchView { - model: ModelHandle, - query_editor: ViewHandle, - replacement_editor: ViewHandle, - results_editor: ViewHandle, + model: Model, + query_editor: View, + replacement_editor: View, + results_editor: View, semantic_state: Option, semantic_permissioned: Option, search_options: SearchOptions, @@ -143,8 +147,8 @@ pub struct ProjectSearchView { active_match_index: Option, search_id: usize, query_editor_was_focused: bool, - included_files_editor: ViewHandle, - excluded_files_editor: ViewHandle, + included_files_editor: View, + excluded_files_editor: View, filters_enabled: bool, replace_enabled: bool, current_mode: SearchMode, @@ -164,20 +168,16 @@ struct ProjectSearchSettings { } pub struct ProjectSearchBar { - active_project_search: Option>, + active_project_search: Option>, subscription: Option, } -impl Entity for ProjectSearch { - type Event = (); -} - impl ProjectSearch { - fn new(project: ModelHandle, cx: &mut ModelContext) -> Self { + fn new(project: Model, cx: &mut ModelContext) -> Self { let replica_id = project.read(cx).replica_id(); Self { project, - excerpts: cx.add_model(|_| MultiBuffer::new(replica_id)), + excerpts: cx.build_model(|_| MultiBuffer::new(replica_id)), pending_search: Default::default(), match_ranges: Default::default(), active_query: None, @@ -187,12 +187,12 @@ impl ProjectSearch { } } - fn clone(&self, cx: &mut ModelContext) -> ModelHandle { - cx.add_model(|cx| Self { + fn clone(&self, cx: &mut ModelContext) -> Model { + cx.build_model(|cx| Self { project: self.project.clone(), excerpts: self .excerpts - .update(cx, |excerpts, cx| cx.add_model(|cx| excerpts.clone(cx))), + .update(cx, |excerpts, cx| cx.build_model(|cx| excerpts.clone(cx))), pending_search: Default::default(), match_ranges: self.match_ranges.clone(), active_query: self.active_query.clone(), @@ -210,9 +210,9 @@ impl ProjectSearch { self.search_history.add(query.as_str().to_string()); self.active_query = Some(query); self.match_ranges.clear(); - self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move { + self.pending_search = Some(cx.spawn(|this, mut cx| async move { let mut matches = search; - let this = this.upgrade(&cx)?; + let this = this.upgrade()?; this.update(&mut cx, |this, cx| { this.match_ranges.clear(); this.excerpts.update(cx, |this, cx| this.clear(cx)); @@ -220,12 +220,14 @@ impl ProjectSearch { }); while let Some((buffer, anchors)) = matches.next().await { - let mut ranges = this.update(&mut cx, |this, cx| { - this.no_results = Some(false); - this.excerpts.update(cx, |excerpts, cx| { - excerpts.stream_excerpts_with_context_lines(buffer, anchors, 1, cx) + let mut ranges = this + .update(&mut cx, |this, cx| { + this.no_results = Some(false); + this.excerpts.update(cx, |excerpts, cx| { + excerpts.stream_excerpts_with_context_lines(buffer, anchors, 1, cx) + }) }) - }); + .ok()?; while let Some(range) = ranges.next().await { this.update(&mut cx, |this, _| this.match_ranges.push(range)); @@ -273,12 +275,14 @@ impl ProjectSearch { }); }); for (buffer, ranges) in matches { - let mut match_ranges = this.update(&mut cx, |this, cx| { - this.no_results = Some(false); - this.excerpts.update(cx, |excerpts, cx| { - excerpts.stream_excerpts_with_context_lines(buffer, ranges, 3, cx) + let mut match_ranges = this + .update(&mut cx, |this, cx| { + this.no_results = Some(false); + this.excerpts.update(cx, |excerpts, cx| { + excerpts.stream_excerpts_with_context_lines(buffer, ranges, 3, cx) + }) }) - }); + .ok()?; while let Some(match_range) = match_ranges.next().await { this.update(&mut cx, |this, cx| { this.match_ranges.push(match_range); @@ -305,221 +309,236 @@ impl ProjectSearch { pub enum ViewEvent { UpdateTab, Activate, - EditorEvent(editor::Event), + EditorEvent(editor::EditorEvent), Dismiss, } -impl Entity for ProjectSearchView { - type Event = ViewEvent; +impl EventEmitter for ProjectSearchView {} + +impl Render for ProjectSearchView { + type Element = Div; + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div() + } } +// impl Entity for ProjectSearchView { +// type Event = ViewEvent; +// } -impl View for ProjectSearchView { - fn ui_name() -> &'static str { - "ProjectSearchView" - } +// impl View for ProjectSearchView { +// fn ui_name() -> &'static str { +// "ProjectSearchView" +// } - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let model = &self.model.read(cx); - if model.match_ranges.is_empty() { - enum Status {} +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// let model = &self.model.read(cx); +// if model.match_ranges.is_empty() { +// enum Status {} - let theme = theme::current(cx).clone(); +// let theme = theme::current(cx).clone(); - // 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: {...} +// // 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 mut 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"), - SearchMode::Semantic => { - Cow::Borrowed("Search all code objects using Natural Language") - } - SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), - } - }; +// let current_mode = self.current_mode; +// let mut 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"), +// SearchMode::Semantic => { +// Cow::Borrowed("Search all code objects using Natural Language") +// } +// SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), +// } +// }; - let mut show_minor_text = true; - let semantic_status = self.semantic_state.as_ref().and_then(|semantic| { - let status = semantic.index_status; - match status { - SemanticIndexStatus::NotAuthenticated => { - major_text = Cow::Borrowed("Not Authenticated"); - show_minor_text = false; - Some(vec![ - "API Key Missing: Please set 'OPENAI_API_KEY' in Environment Variables." - .to_string(), "If you authenticated using the Assistant Panel, please restart Zed to Authenticate.".to_string()]) - } - SemanticIndexStatus::Indexed => Some(vec!["Indexing complete".to_string()]), - SemanticIndexStatus::Indexing { - remaining_files, - rate_limit_expiry, - } => { - if remaining_files == 0 { - Some(vec![format!("Indexing...")]) - } else { - if let Some(rate_limit_expiry) = rate_limit_expiry { - let remaining_seconds = - rate_limit_expiry.duration_since(Instant::now()); - if remaining_seconds > Duration::from_secs(0) { - Some(vec![format!( - "Remaining files to index (rate limit resets in {}s): {}", - remaining_seconds.as_secs(), - remaining_files - )]) - } else { - Some(vec![format!("Remaining files to index: {}", remaining_files)]) - } - } else { - Some(vec![format!("Remaining files to index: {}", remaining_files)]) - } - } - } - SemanticIndexStatus::NotIndexed => None, - } - }); +// let mut show_minor_text = true; +// let semantic_status = self.semantic_state.as_ref().and_then(|semantic| { +// let status = semantic.index_status; +// match status { +// SemanticIndexStatus::NotAuthenticated => { +// major_text = Cow::Borrowed("Not Authenticated"); +// show_minor_text = false; +// Some(vec![ +// "API Key Missing: Please set 'OPENAI_API_KEY' in Environment Variables." +// .to_string(), "If you authenticated using the Assistant Panel, please restart Zed to Authenticate.".to_string()]) +// } +// SemanticIndexStatus::Indexed => Some(vec!["Indexing complete".to_string()]), +// SemanticIndexStatus::Indexing { +// remaining_files, +// rate_limit_expiry, +// } => { +// if remaining_files == 0 { +// Some(vec![format!("Indexing...")]) +// } else { +// if let Some(rate_limit_expiry) = rate_limit_expiry { +// let remaining_seconds = +// rate_limit_expiry.duration_since(Instant::now()); +// if remaining_seconds > Duration::from_secs(0) { +// Some(vec![format!( +// "Remaining files to index (rate limit resets in {}s): {}", +// remaining_seconds.as_secs(), +// remaining_files +// )]) +// } else { +// Some(vec![format!("Remaining files to index: {}", remaining_files)]) +// } +// } else { +// Some(vec![format!("Remaining files to index: {}", remaining_files)]) +// } +// } +// } +// SemanticIndexStatus::NotIndexed => None, +// } +// }); - 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()] - } else { - vec![] - } - } else { - match current_mode { - SearchMode::Semantic => { - let mut minor_text: Vec = Vec::new(); - minor_text.push("".into()); - if let Some(semantic_status) = semantic_status { - minor_text.extend(semantic_status); - } - if show_minor_text { - minor_text - .push("Simply explain the code you are looking to find.".into()); - minor_text.push( - "ex. 'prompt user for permissions to index their project'".into(), - ); - } - minor_text - } - _ => 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 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()] +// } else { +// vec![] +// } +// } else { +// match current_mode { +// SearchMode::Semantic => { +// let mut minor_text: Vec = Vec::new(); +// minor_text.push("".into()); +// if let Some(semantic_status) = semantic_status { +// minor_text.extend(semantic_status); +// } +// if show_minor_text { +// minor_text +// .push("Simply explain the code you are looking to find.".into()); +// minor_text.push( +// "ex. 'prompt user for permissions to index their project'".into(), +// ); +// } +// minor_text +// } +// _ => 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 = - cx.binding_for_action(&PreviousHistoryQuery {}) - .map(|binding| { - binding - .keystrokes() - .iter() - .map(|k| k.to_string()) - .collect::>() - }); - let next_query_keystrokes = - cx.binding_for_action(&NextHistoryQuery {}).map(|binding| { - binding - .keystrokes() - .iter() - .map(|k| k.to_string()) - .collect::>() - }); - let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) { - (Some(previous_query_keystrokes), Some(next_query_keystrokes)) => { - format!( - "Search ({}/{} for previous/next query)", - previous_query_keystrokes.join(" "), - next_query_keystrokes.join(" ") - ) - } - (None, Some(next_query_keystrokes)) => { - format!( - "Search ({} for next query)", - next_query_keystrokes.join(" ") - ) - } - (Some(previous_query_keystrokes), None) => { - format!( - "Search ({} for previous query)", - previous_query_keystrokes.join(" ") - ) - } - (None, None) => String::new(), - }; - self.query_editor.update(cx, |editor, cx| { - editor.set_placeholder_text(new_placeholder_text, cx); - }); +// let previous_query_keystrokes = +// cx.binding_for_action(&PreviousHistoryQuery {}) +// .map(|binding| { +// binding +// .keystrokes() +// .iter() +// .map(|k| k.to_string()) +// .collect::>() +// }); +// let next_query_keystrokes = +// cx.binding_for_action(&NextHistoryQuery {}).map(|binding| { +// binding +// .keystrokes() +// .iter() +// .map(|k| k.to_string()) +// .collect::>() +// }); +// let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) { +// (Some(previous_query_keystrokes), Some(next_query_keystrokes)) => { +// format!( +// "Search ({}/{} for previous/next query)", +// previous_query_keystrokes.join(" "), +// next_query_keystrokes.join(" ") +// ) +// } +// (None, Some(next_query_keystrokes)) => { +// format!( +// "Search ({} for next query)", +// next_query_keystrokes.join(" ") +// ) +// } +// (Some(previous_query_keystrokes), None) => { +// format!( +// "Search ({} for previous query)", +// previous_query_keystrokes.join(" ") +// ) +// } +// (None, None) => String::new(), +// }; +// self.query_editor.update(cx, |editor, cx| { +// editor.set_placeholder_text(new_placeholder_text, cx); +// }); - MouseEventHandler::new::(0, cx, |_, _| { - Flex::column() - .with_child(Flex::column().contained().flex(1., true)) - .with_child( - 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() - .flex(7., true), - ) - .contained() - .with_background_color(theme.editor.background) - }) - .on_down(MouseButton::Left, |_, _, cx| { - cx.focus_parent(); - }) - .into_any_named("project search view") - } else { - ChildView::new(&self.results_editor, cx) - .flex(1., true) - .into_any_named("project search view") - } - } +// MouseEventHandler::new::(0, cx, |_, _| { +// Flex::column() +// .with_child(Flex::column().contained().flex(1., true)) +// .with_child( +// 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() +// .flex(7., true), +// ) +// .contained() +// .with_background_color(theme.editor.background) +// }) +// .on_down(MouseButton::Left, |_, _, cx| { +// cx.focus_parent(); +// }) +// .into_any_named("project search view") +// } else { +// ChildView::new(&self.results_editor, cx) +// .flex(1., true) +// .into_any_named("project search view") +// } +// } - fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - let handle = cx.weak_handle(); - cx.update_global(|state: &mut ActiveSearches, cx| { - state - .0 - .insert(self.model.read(cx).project.downgrade(), handle) - }); +// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext) { +// let handle = cx.weak_handle(); +// cx.update_global(|state: &mut ActiveSearches, cx| { +// state +// .0 +// .insert(self.model.read(cx).project.downgrade(), handle) +// }); - cx.update_global(|state: &mut ActiveSettings, cx| { - state.0.insert( - self.model.read(cx).project.downgrade(), - self.current_settings(), - ); - }); +// cx.update_global(|state: &mut ActiveSettings, cx| { +// state.0.insert( +// self.model.read(cx).project.downgrade(), +// self.current_settings(), +// ); +// }); - if cx.is_self_focused() { - if self.query_editor_was_focused { - cx.focus(&self.query_editor); - } else { - cx.focus(&self.results_editor); - } - } +// if cx.is_self_focused() { +// if self.query_editor_was_focused { +// cx.focus(&self.query_editor); +// } else { +// cx.focus(&self.results_editor); +// } +// } +// } +// } + +impl FocusableView for ProjectSearchView { + fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + self.results_editor.focus_handle(cx) } } impl Item for ProjectSearchView { - fn tab_tooltip_text(&self, cx: &AppContext) -> Option> { + type Event = ViewEvent; + fn tab_tooltip_text(&self, cx: &AppContext) -> Option { let query_text = self.query_editor.read(cx).text(cx); query_text @@ -528,20 +547,17 @@ 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, - self_handle: &'a ViewHandle, + self_handle: &'a View, _: &'a AppContext, - ) -> Option<&'a AnyViewHandle> { + ) -> Option { if type_id == TypeId::of::() { - Some(self_handle) + Some(self_handle.clone().into()) } else if type_id == TypeId::of::() { - Some(&self.results_editor) + Some(self.results_editor.clone().into()) } else { None } @@ -552,45 +568,44 @@ impl Item for ProjectSearchView { .update(cx, |editor, cx| editor.deactivated(cx)); } - fn tab_content( - &self, - _detail: Option, - tab_theme: &theme::Tab, - cx: &AppContext, - ) -> AnyElement { - Flex::row() - .with_child( - Svg::new("icons/magnifying_glass.svg") - .with_color(tab_theme.label.text.color) - .constrained() - .with_width(tab_theme.type_icon_width) - .aligned() - .contained() - .with_margin_right(tab_theme.spacing), - ) - .with_child({ - let tab_name: Option> = self - .model - .read(cx) - .search_history - .current() - .as_ref() - .map(|query| { - let query_text = util::truncate_and_trailoff(query, MAX_TAB_TITLE_LEN); - query_text.into() - }); - Label::new( - tab_name - .filter(|name| !name.is_empty()) - .unwrap_or("Project search".into()), - tab_theme.label.clone(), - ) - .aligned() - }) - .into_any() + fn tab_content(&self, _: Option, cx: &WindowContext<'_>) -> AnyElement { + // Flex::row() + // .with_child( + // Svg::new("icons/magnifying_glass.svg") + // .with_color(tab_theme.label.text.color) + // .constrained() + // .with_width(tab_theme.type_icon_width) + // .aligned() + // .contained() + // .with_margin_right(tab_theme.spacing), + // ) + // .with_child({ + // let tab_name: Option> = self + // .model + // .read(cx) + // .search_history + // .current() + // .as_ref() + // .map(|query| { + // let query_text = util::truncate_and_trailoff(query, MAX_TAB_TITLE_LEN); + // query_text.into() + // }); + // Label::new( + // tab_name + // .filter(|name| !name.is_empty()) + // .unwrap_or("Project search".into()), + // tab_theme.label.clone(), + // ) + // .aligned() + // }) + div().into_any() } - fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) { + fn for_each_project_item( + &self, + cx: &AppContext, + f: &mut dyn FnMut(EntityId, &dyn project::Item), + ) { self.results_editor.for_each_project_item(cx, f) } @@ -612,7 +627,7 @@ impl Item for ProjectSearchView { fn save( &mut self, - project: ModelHandle, + project: Model, cx: &mut ViewContext, ) -> Task> { self.results_editor @@ -621,7 +636,7 @@ impl Item for ProjectSearchView { fn save_as( &mut self, - _: ModelHandle, + _: Model, _: PathBuf, _: &mut ViewContext, ) -> Task> { @@ -630,19 +645,23 @@ impl Item for ProjectSearchView { fn reload( &mut self, - project: ModelHandle, + project: Model, cx: &mut ViewContext, ) -> Task> { self.results_editor .update(cx, |editor, cx| editor.reload(project, cx)) } - fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext) -> Option + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) -> Option> where Self: Sized, { let model = self.model.update(cx, |model, cx| model.clone(cx)); - Some(Self::new(model, cx, None)) + Some(cx.build_view(|cx| Self::new(model, cx, None))) } fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext) { @@ -661,14 +680,17 @@ impl Item for ProjectSearchView { .update(cx, |editor, cx| editor.navigate(data, cx)) } - fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> { + fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) { match event { ViewEvent::UpdateTab => { - smallvec::smallvec![ItemEvent::UpdateBreadcrumbs, ItemEvent::UpdateTab] + f(ItemEvent::UpdateBreadcrumbs); + f(ItemEvent::UpdateTab); } - ViewEvent::EditorEvent(editor_event) => Editor::to_item_events(editor_event), - ViewEvent::Dismiss => smallvec::smallvec![ItemEvent::CloseItem], - _ => SmallVec::new(), + ViewEvent::EditorEvent(editor_event) => { + Editor::to_item_events(editor_event, f); + } + ViewEvent::Dismiss => f(ItemEvent::CloseItem), + _ => {} } } @@ -689,12 +711,12 @@ impl Item for ProjectSearchView { } fn deserialize( - _project: ModelHandle, - _workspace: WeakViewHandle, + _project: Model, + _workspace: WeakView, _workspace_id: workspace::WorkspaceId, _item_id: workspace::ItemId, _cx: &mut ViewContext, - ) -> Task>> { + ) -> Task>> { unimplemented!() } } @@ -751,7 +773,7 @@ impl ProjectSearchView { fn semantic_index_changed( &mut self, - semantic_index: ModelHandle, + semantic_index: Model, cx: &mut ViewContext, ) { let project = self.model.read(cx).project.clone(); @@ -767,7 +789,7 @@ impl ProjectSearchView { semantic_state.maintain_rate_limit = Some(cx.spawn(|this, mut cx| async move { loop { - cx.background().timer(Duration::from_secs(1)).await; + cx.background_executor().timer(Duration::from_secs(1)).await; this.update(&mut cx, |_, cx| cx.notify()).log_err(); } })); @@ -829,7 +851,7 @@ impl ProjectSearchView { ) })?; - if answer.next().await == Some(0) { + if answer.await? == 0 { this.update(&mut cx, |this, _| { this.semantic_permissioned = Some(true); })?; @@ -907,7 +929,7 @@ impl ProjectSearchView { } fn new( - model: ModelHandle, + model: Model, cx: &mut ViewContext, settings: Option, ) -> Self { @@ -940,32 +962,26 @@ impl ProjectSearchView { cx.observe(&model, |this, _, cx| this.model_changed(cx)) .detach(); - let query_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(Arc::new(|theme| theme.search.editor.input.clone())), - cx, - ); + let query_editor = cx.build_view(|cx| { + let mut editor = Editor::single_line(cx); editor.set_placeholder_text("Text search all files", cx); editor.set_text(query_text, cx); editor }); // Subscribe to query_editor in order to reraise editor events for workspace item activation purposes - cx.subscribe(&query_editor, |_, _, event, cx| { + cx.subscribe(&query_editor, |_, _, event: &EditorEvent, cx| { cx.emit(ViewEvent::EditorEvent(event.clone())) }) .detach(); - let replacement_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(Arc::new(|theme| theme.search.editor.input.clone())), - cx, - ); + let replacement_editor = cx.build_view(|cx| { + let mut editor = Editor::single_line(cx); editor.set_placeholder_text("Replace in project..", cx); if let Some(text) = replacement_text { editor.set_text(text, cx); } editor }); - let results_editor = cx.add_view(|cx| { + let results_editor = cx.build_view(|cx| { let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), cx); editor.set_searchable(false); editor @@ -973,8 +989,8 @@ impl ProjectSearchView { cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)) .detach(); - cx.subscribe(&results_editor, |this, _, event, cx| { - if matches!(event, editor::Event::SelectionsChanged { .. }) { + cx.subscribe(&results_editor, |this, _, event: &EditorEvent, cx| { + if matches!(event, editor::EditorEvent::SelectionsChanged { .. }) { this.update_match_index(cx); } // Reraise editor events for workspace item activation purposes @@ -982,36 +998,26 @@ impl ProjectSearchView { }) .detach(); - let included_files_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(Arc::new(|theme| { - theme.search.include_exclude_editor.input.clone() - })), - cx, - ); + let included_files_editor = cx.build_view(|cx| { + let mut editor = Editor::single_line(cx); editor.set_placeholder_text("Include: crates/**/*.toml", cx); editor }); // Subscribe to include_files_editor in order to reraise editor events for workspace item activation purposes - cx.subscribe(&included_files_editor, |_, _, event, cx| { + cx.subscribe(&included_files_editor, |_, _, event: &EditorEvent, cx| { cx.emit(ViewEvent::EditorEvent(event.clone())) }) .detach(); - let excluded_files_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(Arc::new(|theme| { - theme.search.include_exclude_editor.input.clone() - })), - cx, - ); + let excluded_files_editor = cx.build_view(|cx| { + let mut editor = Editor::single_line(cx); editor.set_placeholder_text("Exclude: vendor/*, *.lock", cx); editor }); // Subscribe to excluded_files_editor in order to reraise editor events for workspace item activation purposes - cx.subscribe(&excluded_files_editor, |_, _, event, cx| { + cx.subscribe(&excluded_files_editor, |_, _, event: &EditorEvent, cx| { cx.emit(ViewEvent::EditorEvent(event.clone())) }) .detach(); @@ -1063,8 +1069,8 @@ impl ProjectSearchView { return; }; - let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx)); - let search = cx.add_view(|cx| ProjectSearchView::new(model, cx, None)); + let model = cx.build_model(|cx| ProjectSearch::new(workspace.project().clone(), cx)); + let search = cx.build_view(|cx| ProjectSearchView::new(model, cx, None)); workspace.add_item(Box::new(search.clone()), cx); search.update(cx, |search, cx| { search @@ -1084,19 +1090,20 @@ impl ProjectSearchView { ) { // Clean up entries for dropped projects cx.update_global(|state: &mut ActiveSearches, cx| { - state.0.retain(|project, _| project.is_upgradable(cx)) + state.0.retain(|project, _| project.is_upgradable()) }); let active_search = cx .global::() .0 - .get(&workspace.project().downgrade()); + .get(&workspace.project().downgrade()) + .and_then(WeakView::upgrade); let existing = active_search .and_then(|active_search| { workspace .items_of_type::(cx) - .find(|search| search == active_search) + .find(|search| search == &active_search) }) .or_else(|| workspace.item_of_type::(cx)); @@ -1125,8 +1132,8 @@ impl ProjectSearchView { None }; - let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx)); - let view = cx.add_view(|cx| ProjectSearchView::new(model, cx, settings)); + let model = cx.build_model(|cx| ProjectSearch::new(workspace.project().clone(), cx)); + let view = cx.build_view(|cx| ProjectSearchView::new(model, cx, settings)); workspace.add_item(Box::new(view.clone()), cx); view @@ -1263,7 +1270,8 @@ impl ProjectSearchView { query_editor.select_all(&SelectAll, cx); }); self.query_editor_was_focused = true; - cx.focus(&self.query_editor); + let editor_handle = self.query_editor.focus_handle(cx); + cx.focus(&editor_handle); } fn set_query(&mut self, query: &str, cx: &mut ViewContext) { @@ -1277,7 +1285,8 @@ impl ProjectSearchView { query_editor.change_selections(None, cx, |s| s.select_ranges([cursor.clone()..cursor])); }); self.query_editor_was_focused = false; - cx.focus(&self.results_editor); + let results_handle = self.results_editor.focus_handle(cx); + cx.focus(&results_handle); } fn model_changed(&mut self, cx: &mut ViewContext) { @@ -1301,11 +1310,11 @@ impl ProjectSearchView { } editor.highlight_background::( match_ranges, - |theme| theme.search.match_background, + |theme| theme.search_match_background, cx, ); }); - if is_new_search && self.query_editor.is_focused(cx) { + if is_new_search && self.query_editor.focus_handle(cx).is_focused(cx) { self.focus_results_editor(cx); } } @@ -1337,15 +1346,14 @@ impl ProjectSearchView { .and_then(|item| item.downcast::()) { search_view.update(cx, |search_view, cx| { - if !search_view.results_editor.is_focused(cx) + if !search_view.results_editor.focus_handle(cx).is_focused(cx) && !search_view.model.read(cx).match_ranges.is_empty() { + cx.stop_propagation(); return search_view.focus_results_editor(cx); } }); } - - cx.propagate_action(); } } @@ -1371,23 +1379,24 @@ impl ProjectSearchBar { let new_mode = crate::mode::next_mode(&this.current_mode, SemanticIndex::enabled(cx)); this.activate_search_mode(new_mode, cx); - cx.focus(&this.query_editor); + let editor_handle = this.query_editor.focus_handle(cx); + cx.focus(&editor_handle); }) } } fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - let mut should_propagate = true; if let Some(search_view) = self.active_project_search.as_ref() { search_view.update(cx, |search_view, cx| { - if !search_view.replacement_editor.is_focused(cx) { - should_propagate = false; + if !search_view + .replacement_editor + .focus_handle(cx) + .is_focused(cx) + { + cx.stop_propagation(); search_view.search(cx); } }); } - if should_propagate { - cx.propagate_action(); - } } fn search_in_new(workspace: &mut Workspace, _: &SearchInNew, cx: &mut ViewContext) { @@ -1408,13 +1417,13 @@ impl ProjectSearchBar { new_query }); if let Some(new_query) = new_query { - let model = cx.add_model(|cx| { + let model = cx.build_model(|cx| { let mut model = ProjectSearch::new(workspace.project().clone(), cx); model.search(new_query, cx); model }); workspace.add_item( - Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx, None))), + Box::new(cx.build_view(|cx| ProjectSearchView::new(model, cx, None))), cx, ); } @@ -1427,8 +1436,7 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |view, cx| view.select_match(Direction::Next, cx)); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } @@ -1438,8 +1446,7 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |view, cx| view.replace_next(&ReplaceNext, cx)); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } fn replace_all(pane: &mut Pane, _: &ReplaceAll, cx: &mut ViewContext) { @@ -1448,8 +1455,7 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |view, cx| view.replace_all(&ReplaceAll, cx)); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } fn select_prev_match(pane: &mut Pane, _: &SelectPrevMatch, cx: &mut ViewContext) { @@ -1458,8 +1464,7 @@ impl ProjectSearchBar { .and_then(|item| item.downcast::()) { search_view.update(cx, |view, cx| view.select_match(Direction::Prev, cx)); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } @@ -1476,7 +1481,6 @@ impl ProjectSearchBar { Some(active_project_search) => active_project_search, None => { - cx.propagate_action(); return; } }; @@ -1495,12 +1499,11 @@ impl ProjectSearchBar { let current_index = match views .iter() .enumerate() - .find(|(_, view)| view.is_focused(cx)) + .find(|(_, view)| view.focus_handle(cx).is_focused(cx)) { Some((index, _)) => index, None => { - cx.propagate_action(); return; } }; @@ -1510,7 +1513,9 @@ impl ProjectSearchBar { Direction::Prev if current_index == 0 => views.len() - 1, Direction::Prev => (current_index - 1) % views.len(), }; - cx.focus(views[new_index]); + let next_focus_handle = views[new_index].focus_handle(cx); + cx.focus(&next_focus_handle); + cx.stop_propagation(); }); } @@ -1532,30 +1537,28 @@ impl ProjectSearchBar { search.update(cx, |this, cx| { this.replace_enabled = !this.replace_enabled; if !this.replace_enabled { - cx.focus(&this.query_editor); + let editor_handle = this.query_editor.focus_handle(cx); + cx.focus(&editor_handle); } cx.notify(); }); } } fn toggle_replace_on_a_pane(pane: &mut Pane, _: &ToggleReplace, cx: &mut ViewContext) { - let mut should_propagate = true; if let Some(search_view) = pane .active_item() .and_then(|item| item.downcast::()) { search_view.update(cx, |this, cx| { - should_propagate = false; + cx.stop_propagation(); this.replace_enabled = !this.replace_enabled; if !this.replace_enabled { - cx.focus(&this.query_editor); + let editor_handle = this.query_editor.focus_handle(cx); + cx.focus(&editor_handle); } cx.notify(); }); } - if should_propagate { - cx.propagate_action(); - } } fn activate_text_mode(pane: &mut Pane, _: &ActivateTextMode, cx: &mut ViewContext) { if let Some(search_view) = pane @@ -1565,8 +1568,7 @@ impl ProjectSearchBar { search_view.update(cx, |view, cx| { view.activate_search_mode(SearchMode::Text, cx) }); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } @@ -1578,8 +1580,7 @@ impl ProjectSearchBar { search_view.update(cx, |view, cx| { view.activate_search_mode(SearchMode::Regex, cx) }); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } @@ -1596,8 +1597,7 @@ impl ProjectSearchBar { search_view.update(cx, |view, cx| { view.activate_search_mode(SearchMode::Semantic, cx) }); - } else { - cx.propagate_action(); + cx.stop_propagation(); } } } @@ -1612,7 +1612,7 @@ impl ProjectSearchBar { search_view .excluded_files_editor .update(cx, |_, cx| cx.notify()); - cx.refresh_windows(); + cx.refresh(); cx.notify(); }); cx.notify(); @@ -1682,314 +1682,323 @@ impl ProjectSearchBar { } } -impl Entity for ProjectSearchBar { - type Event = (); -} +impl Render for ProjectSearchBar { + type Element = Div; -impl View for ProjectSearchBar { - fn ui_name() -> &'static str { - "ProjectSearchBar" - } - - fn update_keymap_context( - &self, - keymap: &mut gpui::keymap_matcher::KeymapContext, - cx: &AppContext, - ) { - Self::reset_to_default_keymap_context(keymap); - let in_replace = self - .active_project_search - .as_ref() - .map(|search| { - search - .read(cx) - .replacement_editor - .read_with(cx, |_, cx| cx.is_self_focused()) - }) - .flatten() - .unwrap_or(false); - if in_replace { - keymap.add_identifier("in_replace"); - } - } - - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - 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 - } else { - theme.search.editor.input.container - }; - - let search = _search.read(cx); - let filter_button = render_option_button_icon( - search.filters_enabled, - "icons/filter.svg", - 0, - "Toggle filters", - Box::new(ToggleFilters), - move |_, this, cx| { - this.toggle_filters(cx); - }, - cx, - ); - - let search = _search.read(cx); - let is_semantic_available = SemanticIndex::enabled(cx); - let is_semantic_disabled = search.semantic_state.is_none(); - let icon_style = theme.search.editor_icon.clone(); - let is_active = search.active_match_index.is_some(); - - 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.bits as usize, - format!("Toggle {}", option.label()), - option.to_toggle_action(), - move |_, this, cx| { - this.toggle_search_option(option, cx); - }, - cx, - ) - }; - let case_sensitive = is_semantic_disabled.then(|| { - render_option_button_icon( - "icons/case_insensitive.svg", - SearchOptions::CASE_SENSITIVE, - cx, - ) - }); - - let whole_word = is_semantic_disabled.then(|| { - render_option_button_icon("icons/word_search.svg", SearchOptions::WHOLE_WORD, cx) - }); - - let include_ignored = is_semantic_disabled.then(|| { - render_option_button_icon( - "icons/file_icons/git.svg", - SearchOptions::INCLUDE_IGNORED, - cx, - ) - }); - - let search_button_for_mode = |mode, side, 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, - side, - is_active, - move |_, this, cx| { - this.activate_search_mode(mode, cx); - }, - cx, - ) - }; - - 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 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() - }); - let should_show_replace_input = search.replace_enabled; - let replacement = should_show_replace_input.then(|| { - Flex::row() - .with_child( - Svg::for_style(theme.search.replace_icon.clone().icon) - .contained() - .with_style(theme.search.replace_icon.clone().container), - ) - .with_child(ChildView::new(&search.replacement_editor, cx).flex(1., true)) - .align_children_center() - .flex(1., true) - .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) - }); - let replace_all = should_show_replace_input.then(|| { - super::replace_action( - ReplaceAll, - "Replace all", - "icons/replace_all.svg", - theme.tooltip.clone(), - theme.search.action_button.clone(), - ) - }); - let replace_next = should_show_replace_input.then(|| { - super::replace_action( - ReplaceNext, - "Replace next", - "icons/replace_next.svg", - theme.tooltip.clone(), - theme.search.action_button.clone(), - ) - }); - let query_column = Flex::column() - .with_spacing(theme.search.search_row_spacing) - .with_child( - Flex::row() - .with_child( - Svg::for_style(icon_style.icon) - .contained() - .with_style(icon_style.container), - ) - .with_child(ChildView::new(&search.query_editor, cx).flex(1., true)) - .with_child( - Flex::row() - .with_child(filter_button) - .with_children(case_sensitive) - .with_children(whole_word) - .flex(1., false) - .constrained() - .contained(), - ) - .align_children_center() - .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(search.filters_enabled.then(|| { - Flex::row() - .with_child( - Flex::row() - .with_child( - ChildView::new(&search.included_files_editor, cx) - .contained() - .constrained() - .with_height(theme.search.search_bar_row_height) - .flex(1., true), - ) - .with_children(include_ignored) - .contained() - .with_style(include_container_style) - .constrained() - .with_height(theme.search.search_bar_row_height) - .flex(1., true), - ) - .with_child( - ChildView::new(&search.excluded_files_editor, cx) - .contained() - .with_style(exclude_container_style) - .constrained() - .with_height(theme.search.search_bar_row_height) - .flex(1., true), - ) - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .flex(1., false) - })) - .flex(1., false); - let switches_column = Flex::row() - .align_children_center() - .with_child(super::toggle_replace_button( - search.replace_enabled, - theme.tooltip.clone(), - theme.search.option_button_component.clone(), - )) - .constrained() - .with_height(theme.search.search_bar_row_height) - .contained() - .with_style(theme.search.option_button_group); - let mode_column = - Flex::row() - .with_child(search_button_for_mode( - SearchMode::Text, - Some(Side::Left), - cx, - )) - .with_child(search_button_for_mode( - SearchMode::Regex, - if is_semantic_available { - None - } else { - Some(Side::Right) - }, - cx, - )) - .with_children(is_semantic_available.then(|| { - search_button_for_mode(SearchMode::Semantic, Some(Side::Right), cx) - })) - .contained() - .with_style(theme.search.modes_container); - - 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)); - } - }, - cx, - ) - }; - - let nav_column = Flex::row() - .with_children(replace_next) - .with_children(replace_all) - .with_child(Flex::row().with_children(matches)) - .with_child(nav_button_for_direction("<", Direction::Prev, cx)) - .with_child(nav_button_for_direction(">", Direction::Next, cx)) - .constrained() - .with_height(theme.search.search_bar_row_height) - .flex_float(); - - Flex::row() - .with_child(query_column) - .with_child(mode_column) - .with_child(switches_column) - .with_children(replacement) - .with_child(nav_column) - .contained() - .with_style(theme.search.container) - .into_any_named("project search") - } else { - Empty::new().into_any() - } + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div() } } +// impl Entity for ProjectSearchBar { +// type Event = (); +// } + +// impl View for ProjectSearchBar { +// fn ui_name() -> &'static str { +// "ProjectSearchBar" +// } + +// fn update_keymap_context( +// &self, +// keymap: &mut gpui::keymap_matcher::KeymapContext, +// cx: &AppContext, +// ) { +// Self::reset_to_default_keymap_context(keymap); +// let in_replace = self +// .active_project_search +// .as_ref() +// .map(|search| { +// search +// .read(cx) +// .replacement_editor +// .read_with(cx, |_, cx| cx.is_self_focused()) +// }) +// .flatten() +// .unwrap_or(false); +// if in_replace { +// keymap.add_identifier("in_replace"); +// } +// } + +// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { +// 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 +// } else { +// theme.search.editor.input.container +// }; + +// let search = _search.read(cx); +// let filter_button = render_option_button_icon( +// search.filters_enabled, +// "icons/filter.svg", +// 0, +// "Toggle filters", +// Box::new(ToggleFilters), +// move |_, this, cx| { +// this.toggle_filters(cx); +// }, +// cx, +// ); + +// let search = _search.read(cx); +// let is_semantic_available = SemanticIndex::enabled(cx); +// let is_semantic_disabled = search.semantic_state.is_none(); +// let icon_style = theme.search.editor_icon.clone(); +// let is_active = search.active_match_index.is_some(); + +// 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.bits as usize, +// format!("Toggle {}", option.label()), +// option.to_toggle_action(), +// move |_, this, cx| { +// this.toggle_search_option(option, cx); +// }, +// cx, +// ) +// }; +// let case_sensitive = is_semantic_disabled.then(|| { +// render_option_button_icon( +// "icons/case_insensitive.svg", +// SearchOptions::CASE_SENSITIVE, +// cx, +// ) +// }); + +// let whole_word = is_semantic_disabled.then(|| { +// render_option_button_icon("icons/word_search.svg", SearchOptions::WHOLE_WORD, cx) +// }); + +// let include_ignored = is_semantic_disabled.then(|| { +// render_option_button_icon( +// "icons/file_icons/git.svg", +// SearchOptions::INCLUDE_IGNORED, +// cx, +// ) +// }); + +// let search_button_for_mode = |mode, side, 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, +// side, +// is_active, +// move |_, this, cx| { +// this.activate_search_mode(mode, cx); +// }, +// cx, +// ) +// }; + +// 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 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() +// }); +// let should_show_replace_input = search.replace_enabled; +// let replacement = should_show_replace_input.then(|| { +// Flex::row() +// .with_child( +// Svg::for_style(theme.search.replace_icon.clone().icon) +// .contained() +// .with_style(theme.search.replace_icon.clone().container), +// ) +// .with_child(ChildView::new(&search.replacement_editor, cx).flex(1., true)) +// .align_children_center() +// .flex(1., true) +// .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) +// }); +// let replace_all = should_show_replace_input.then(|| { +// super::replace_action( +// ReplaceAll, +// "Replace all", +// "icons/replace_all.svg", +// theme.tooltip.clone(), +// theme.search.action_button.clone(), +// ) +// }); +// let replace_next = should_show_replace_input.then(|| { +// super::replace_action( +// ReplaceNext, +// "Replace next", +// "icons/replace_next.svg", +// theme.tooltip.clone(), +// theme.search.action_button.clone(), +// ) +// }); +// let query_column = Flex::column() +// .with_spacing(theme.search.search_row_spacing) +// .with_child( +// Flex::row() +// .with_child( +// Svg::for_style(icon_style.icon) +// .contained() +// .with_style(icon_style.container), +// ) +// .with_child(ChildView::new(&search.query_editor, cx).flex(1., true)) +// .with_child( +// Flex::row() +// .with_child(filter_button) +// .with_children(case_sensitive) +// .with_children(whole_word) +// .flex(1., false) +// .constrained() +// .contained(), +// ) +// .align_children_center() +// .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(search.filters_enabled.then(|| { +// Flex::row() +// .with_child( +// Flex::row() +// .with_child( +// ChildView::new(&search.included_files_editor, cx) +// .contained() +// .constrained() +// .with_height(theme.search.search_bar_row_height) +// .flex(1., true), +// ) +// .with_children(include_ignored) +// .contained() +// .with_style(include_container_style) +// .constrained() +// .with_height(theme.search.search_bar_row_height) +// .flex(1., true), +// ) +// .with_child( +// ChildView::new(&search.excluded_files_editor, cx) +// .contained() +// .with_style(exclude_container_style) +// .constrained() +// .with_height(theme.search.search_bar_row_height) +// .flex(1., true), +// ) +// .constrained() +// .with_min_width(theme.search.editor.min_width) +// .with_max_width(theme.search.editor.max_width) +// .flex(1., false) +// })) +// .flex(1., false); +// let switches_column = Flex::row() +// .align_children_center() +// .with_child(super::toggle_replace_button( +// search.replace_enabled, +// theme.tooltip.clone(), +// theme.search.option_button_component.clone(), +// )) +// .constrained() +// .with_height(theme.search.search_bar_row_height) +// .contained() +// .with_style(theme.search.option_button_group); +// let mode_column = +// Flex::row() +// .with_child(search_button_for_mode( +// SearchMode::Text, +// Some(Side::Left), +// cx, +// )) +// .with_child(search_button_for_mode( +// SearchMode::Regex, +// if is_semantic_available { +// None +// } else { +// Some(Side::Right) +// }, +// cx, +// )) +// .with_children(is_semantic_available.then(|| { +// search_button_for_mode(SearchMode::Semantic, Some(Side::Right), cx) +// })) +// .contained() +// .with_style(theme.search.modes_container); + +// 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)); +// } +// }, +// cx, +// ) +// }; + +// let nav_column = Flex::row() +// .with_children(replace_next) +// .with_children(replace_all) +// .with_child(Flex::row().with_children(matches)) +// .with_child(nav_button_for_direction("<", Direction::Prev, cx)) +// .with_child(nav_button_for_direction(">", Direction::Next, cx)) +// .constrained() +// .with_height(theme.search.search_bar_row_height) +// .flex_float(); + +// Flex::row() +// .with_child(query_column) +// .with_child(mode_column) +// .with_child(switches_column) +// .with_children(replacement) +// .with_child(nav_column) +// .contained() +// .with_style(theme.search.container) +// .into_any_named("project search") +// } else { +// Empty::new().into_any() +// } +// } +// } + +impl EventEmitter for ProjectSearchBar {} impl ToolbarItemView for ProjectSearchBar { fn set_active_pane_item( @@ -2009,15 +2018,13 @@ impl ToolbarItemView for ProjectSearchBar { self.subscription = Some(cx.observe(&search, |_, _, cx| cx.notify())); self.active_project_search = Some(search); - ToolbarItemLocation::PrimaryLeft { - flex: Some((1., true)), - } + ToolbarItemLocation::PrimaryLeft {} } else { ToolbarItemLocation::Hidden } } - fn row_count(&self, cx: &ViewContext) -> usize { + fn row_count(&self, cx: &WindowContext<'_>) -> usize { if let Some(search) = self.active_project_search.as_ref() { if search.read(cx).filters_enabled { return 2; @@ -2043,7 +2050,7 @@ pub mod tests { async fn test_project_search(deterministic: Arc, cx: &mut TestAppContext) { init_test(cx); - let fs = FakeFs::new(cx.background()); + let fs = FakeFs::new(cx.background_executor()); fs.insert_tree( "/dir", json!({ @@ -2163,7 +2170,7 @@ pub mod tests { async fn test_project_search_focus(deterministic: Arc, cx: &mut TestAppContext) { init_test(cx); - let fs = FakeFs::new(cx.background()); + let fs = FakeFs::new(cx.background_executor()); fs.insert_tree( "/dir", json!({ @@ -2341,7 +2348,7 @@ pub mod tests { ) { init_test(cx); - let fs = FakeFs::new(cx.background()); + let fs = FakeFs::new(cx.background_executor()); fs.insert_tree( "/dir", json!({ @@ -2468,7 +2475,7 @@ pub mod tests { async fn test_search_query_history(cx: &mut TestAppContext) { init_test(cx); - let fs = FakeFs::new(cx.background()); + let fs = FakeFs::new(cx.background_executor()); fs.insert_tree( "/dir", json!({ diff --git a/crates/search2/src/search.rs b/crates/search2/src/search.rs index 13def6b4a7..eb540b5013 100644 --- a/crates/search2/src/search.rs +++ b/crates/search2/src/search.rs @@ -13,12 +13,12 @@ use ui::{ButtonStyle, Icon, IconButton}; pub mod buffer_search; mod history; mod mode; -//pub mod project_search; +pub mod project_search; pub(crate) mod search_bar; pub fn init(cx: &mut AppContext) { buffer_search::init(cx); - //project_search::init(cx); + project_search::init(cx); } actions!( @@ -44,6 +44,7 @@ bitflags! { const NONE = 0b000; const WHOLE_WORD = 0b001; const CASE_SENSITIVE = 0b010; + const INCLUDE_IGNORED = 0b100; } } From b04838c23a7964eea393797d977fecfc2089ad53 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:26:40 +0100 Subject: [PATCH 02/18] WIP, search bar looks kinda okay --- crates/search2/src/buffer_search.rs | 2 +- crates/search2/src/project_search.rs | 183 ++++++++++++++++++++++----- crates/ui2/src/components/icon.rs | 4 + crates/zed2/src/zed2.rs | 5 +- 4 files changed, 156 insertions(+), 38 deletions(-) diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index cbbeeb0f12..7982778936 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -165,7 +165,7 @@ impl Render for BufferSearchBar { let replace_all = should_show_replace_input .then(|| super::render_replace_button(ReplaceAll, ui::Icon::ReplaceAll)); let replace_next = should_show_replace_input - .then(|| super::render_replace_button(ReplaceNext, ui::Icon::Replace)); + .then(|| super::render_replace_button(ReplaceNext, ui::Icon::ReplaceNext)); let in_replace = self.replacement_editor.focus_handle(cx).is_focused(cx); h_stack() diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index e75b4f26b1..c0147bad1e 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -13,9 +13,10 @@ use editor::{ MultiBuffer, SelectAll, MAX_TAB_TITLE_LEN, }; use gpui::{ - actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Div, Element, Entity, - EntityId, EventEmitter, FocusableView, Model, ModelContext, PromptLevel, Render, SharedString, - Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, + actions, div, white, Action, AnyElement, AnyView, AppContext, Context as _, Div, Element, + Entity, EntityId, EventEmitter, FocusableView, InteractiveElement, IntoElement, Model, + ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, Task, + View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, }; use menu::Confirm; use project::{ @@ -35,6 +36,11 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; +use theme::ActiveTheme; +use ui::{ + h_stack, v_stack, Button, Clickable, Color, Disableable, Icon, IconButton, IconElement, Label, + Selectable, +}; use util::{paths::PathMatcher, ResultExt as _}; use workspace::{ item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, @@ -318,7 +324,7 @@ impl EventEmitter for ProjectSearchView {} impl Render for ProjectSearchView { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div() + div().child(Label::new("xd")) } } // impl Entity for ProjectSearchView { @@ -569,36 +575,23 @@ impl Item for ProjectSearchView { } fn tab_content(&self, _: Option, cx: &WindowContext<'_>) -> AnyElement { - // Flex::row() - // .with_child( - // Svg::new("icons/magnifying_glass.svg") - // .with_color(tab_theme.label.text.color) - // .constrained() - // .with_width(tab_theme.type_icon_width) - // .aligned() - // .contained() - // .with_margin_right(tab_theme.spacing), - // ) - // .with_child({ - // let tab_name: Option> = self - // .model - // .read(cx) - // .search_history - // .current() - // .as_ref() - // .map(|query| { - // let query_text = util::truncate_and_trailoff(query, MAX_TAB_TITLE_LEN); - // query_text.into() - // }); - // Label::new( - // tab_name - // .filter(|name| !name.is_empty()) - // .unwrap_or("Project search".into()), - // tab_theme.label.clone(), - // ) - // .aligned() - // }) - div().into_any() + let last_query: Option = self + .model + .read(cx) + .search_history + .current() + .as_ref() + .map(|query| { + let query_text = util::truncate_and_trailoff(query, MAX_TAB_TITLE_LEN); + query_text.into() + }); + let tab_name = last_query + .filter(|query| !query.is_empty()) + .unwrap_or_else(|| "Project search".into()); + h_stack() + .child(IconElement::new(Icon::MagnifyingGlass)) + .child(Label::new(tab_name)) + .into_any() } fn for_each_project_item( @@ -1686,7 +1679,127 @@ impl Render for ProjectSearchBar { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div() + let Some(search) = self.active_project_search.clone() else { + return div(); + }; + let search = search.read(cx); + let query_column = v_stack() + .flex_1() + .child( + h_stack() + .min_w_80() + .on_action(cx.listener(|this, _: &ToggleFilters, cx| { + this.toggle_filters(cx); + })) + .on_action(cx.listener(|this, _: &ToggleWholeWord, cx| { + this.toggle_search_option(SearchOptions::WHOLE_WORD, cx); + })) + .on_action(cx.listener(|this, _: &ToggleCaseSensitive, cx| { + this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); + })) + .on_action(cx.listener(|this, action: &ToggleReplace, cx| { + this.toggle_replace(action, cx); + })) + .on_action(cx.listener(|this, action: &ActivateTextMode, cx| { + this.activate_search_mode(SearchMode::Text, cx) + })) + .on_action(cx.listener(|this, action: &ActivateRegexMode, cx| { + this.activate_search_mode(SearchMode::Regex, cx) + })) + .child(IconElement::new(Icon::MagnifyingGlass)) + .child(search.query_editor.clone()) + .child( + h_stack() + .child( + IconButton::new("project-search-filter-button", Icon::Filter) + .on_click(|_, cx| { + cx.dispatch_action(ToggleFilters.boxed_clone()) + }), + ) + .child(IconButton::new( + "project-search-case-sensitive", + Icon::CaseSensitive, + )) + .child(IconButton::new( + "project-search-whole-word", + Icon::WholeWord, + )), + ) + .border_2() + .bg(white()) + .rounded_lg(), + ) + .when(search.filters_enabled, |this| { + this.child( + h_stack() + .child(search.included_files_editor.clone()) + .child(search.excluded_files_editor.clone()), + ) + }); + let mode_column = h_stack() + .child( + h_stack() + .child( + Button::new("project-search-text-button", "Text") + .selected(search.current_mode == SearchMode::Text) + .on_click(|_, cx| cx.dispatch_action(ActivateTextMode.boxed_clone())), + ) + .child( + Button::new("project-search-regex-button", "Regex") + .selected(search.current_mode == SearchMode::Regex) + .on_click(|_, cx| cx.dispatch_action(ActivateRegexMode.boxed_clone())), + ), + ) + .child( + IconButton::new("project-search-toggle-replace", Icon::Replace).on_click( + |_, cx| { + cx.dispatch_action(ToggleReplace.boxed_clone()); + }, + ), + ); + let replace_column = if search.replace_enabled { + h_stack() + .bg(white()) + .flex_1() + .border_2() + .rounded_lg() + .child(IconElement::new(Icon::Replace).size(ui::IconSize::Small)) + .child(search.replacement_editor.clone()) + } else { + // Fill out the space if we don't have a replacement editor. + h_stack().size_full() + }; + let actions_column = h_stack() + .when(search.replace_enabled, |this| { + this.children([ + IconButton::new("project-search-replace-next", Icon::ReplaceNext), + IconButton::new("project-search-replace-all", Icon::ReplaceAll), + ]) + }) + .when_some(search.active_match_index, |this, index| { + let match_quantity = search.model.read(cx).match_ranges.len(); + debug_assert!(match_quantity > index); + this.child(IconButton::new( + "project-search-select-all", + Icon::SelectAll, + )) + .child(Label::new(format!("{index}/{match_quantity}"))) + }) + .children([ + IconButton::new("project-search-prev-match", Icon::ChevronLeft) + .disabled(search.active_match_index.is_none()), + IconButton::new("project-search-next-match", Icon::ChevronRight) + .disabled(search.active_match_index.is_none()), + ]); + h_stack() + .size_full() + .p_1() + .m_2() + .justify_between() + .child(query_column) + .child(mode_column) + .child(replace_column) + .child(actions_column) } } // impl Entity for ProjectSearchBar { diff --git a/crates/ui2/src/components/icon.rs b/crates/ui2/src/components/icon.rs index a5b09782f5..78df956969 100644 --- a/crates/ui2/src/components/icon.rs +++ b/crates/ui2/src/components/icon.rs @@ -61,6 +61,7 @@ pub enum Icon { FileRust, FileToml, FileTree, + Filter, Folder, FolderOpen, FolderX, @@ -80,6 +81,7 @@ pub enum Icon { Quote, Replace, ReplaceAll, + ReplaceNext, Screen, SelectAll, Split, @@ -138,6 +140,7 @@ impl Icon { Icon::FileRust => "icons/file_icons/rust.svg", Icon::FileToml => "icons/file_icons/toml.svg", Icon::FileTree => "icons/project.svg", + Icon::Filter => "icons/filter.svg", Icon::Folder => "icons/file_icons/folder.svg", Icon::FolderOpen => "icons/file_icons/folder_open.svg", Icon::FolderX => "icons/stop_sharing.svg", @@ -157,6 +160,7 @@ impl Icon { Icon::Quote => "icons/quote.svg", Icon::Replace => "icons/replace.svg", Icon::ReplaceAll => "icons/replace_all.svg", + Icon::ReplaceNext => "icons/replace_next.svg", Icon::Screen => "icons/desktop.svg", Icon::SelectAll => "icons/select-all.svg", Icon::Split => "icons/split.svg", diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index d36d16a654..3fab03aa93 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -24,6 +24,7 @@ use anyhow::{anyhow, Context as _}; use futures::{channel::mpsc, StreamExt}; use project_panel::ProjectPanel; use quick_action_bar::QuickActionBar; +use search::project_search::ProjectSearchBar; use settings::{initial_local_settings_content, load_default_keymap, KeymapFile, Settings}; use std::{borrow::Cow, ops::Deref, sync::Arc}; use terminal_view::terminal_panel::TerminalPanel; @@ -426,8 +427,8 @@ fn initialize_pane(workspace: &mut Workspace, pane: &View, cx: &mut ViewCo toolbar.add_item(quick_action_bar, cx); let diagnostic_editor_controls = cx.build_view(|_| diagnostics::ToolbarControls::new()); // toolbar.add_item(diagnostic_editor_controls, cx); - // let project_search_bar = cx.add_view(|_| ProjectSearchBar::new()); - // toolbar.add_item(project_search_bar, cx); + let project_search_bar = cx.build_view(|_| ProjectSearchBar::new()); + toolbar.add_item(project_search_bar, cx); // let lsp_log_item = // cx.add_view(|_| language_tools::LspLogToolbarItemView::new()); // toolbar.add_item(lsp_log_item, cx); From ed5c05b272aec8e240ebeb178717d2a00e5cd008 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:43:34 +0100 Subject: [PATCH 03/18] Searches work, but the results are not displayed --- crates/search2/src/project_search.rs | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index c0147bad1e..56fb66d7e1 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -324,7 +324,7 @@ impl EventEmitter for ProjectSearchView {} impl Render for ProjectSearchView { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div().child(Label::new("xd")) + div().child(self.results_editor.clone()) } } // impl Entity for ProjectSearchView { @@ -1688,6 +1688,7 @@ impl Render for ProjectSearchBar { .child( h_stack() .min_w_80() + .on_action(cx.listener(|this, action, cx| this.confirm(action, cx))) .on_action(cx.listener(|this, _: &ToggleFilters, cx| { this.toggle_filters(cx); })) @@ -1697,7 +1698,7 @@ impl Render for ProjectSearchBar { .on_action(cx.listener(|this, _: &ToggleCaseSensitive, cx| { this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); })) - .on_action(cx.listener(|this, action: &ToggleReplace, cx| { + .on_action(cx.listener(|this, action, cx| { this.toggle_replace(action, cx); })) .on_action(cx.listener(|this, action: &ActivateTextMode, cx| { @@ -1760,6 +1761,7 @@ impl Render for ProjectSearchBar { let replace_column = if search.replace_enabled { h_stack() .bg(white()) + .p_1() .flex_1() .border_2() .rounded_lg() @@ -1772,24 +1774,34 @@ impl Render for ProjectSearchBar { let actions_column = h_stack() .when(search.replace_enabled, |this| { this.children([ - IconButton::new("project-search-replace-next", Icon::ReplaceNext), - IconButton::new("project-search-replace-all", Icon::ReplaceAll), + IconButton::new("project-search-replace-next", Icon::ReplaceNext).on_click( + |_, cx| { + cx.dispatch_action(ReplaceNext.boxed_clone()); + }, + ), + IconButton::new("project-search-replace-all", Icon::ReplaceAll).on_click( + |_, cx| { + cx.dispatch_action(ReplaceAll.boxed_clone()); + }, + ), ]) }) .when_some(search.active_match_index, |this, index| { let match_quantity = search.model.read(cx).match_ranges.len(); debug_assert!(match_quantity > index); - this.child(IconButton::new( - "project-search-select-all", - Icon::SelectAll, - )) + this.child( + IconButton::new("project-search-select-all", Icon::SelectAll) + .on_click(|_, cx| cx.dispatch_action(SelectAll.boxed_clone())), + ) .child(Label::new(format!("{index}/{match_quantity}"))) }) .children([ IconButton::new("project-search-prev-match", Icon::ChevronLeft) - .disabled(search.active_match_index.is_none()), + .disabled(search.active_match_index.is_none()) + .on_click(|_, cx| cx.dispatch_action(SelectPrevMatch.boxed_clone())), IconButton::new("project-search-next-match", Icon::ChevronRight) - .disabled(search.active_match_index.is_none()), + .disabled(search.active_match_index.is_none()) + .on_click(|_, cx| cx.dispatch_action(SelectNextMatch.boxed_clone())), ]); h_stack() .size_full() From e9c40963ab03a4cdeec356273417fd726fa9f111 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:48:53 +0100 Subject: [PATCH 04/18] Render multibuffer --- crates/search2/src/project_search.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 56fb66d7e1..b2144587be 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -324,7 +324,10 @@ impl EventEmitter for ProjectSearchView {} impl Render for ProjectSearchView { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div().child(self.results_editor.clone()) + div() + .flex_1() + .size_full() + .child(self.results_editor.clone()) } } // impl Entity for ProjectSearchView { From 1bf94f025169d75902d4624423fc770482de2620 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:55:03 +0100 Subject: [PATCH 05/18] Do not render multibuffer without matches --- crates/search2/src/project_search.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index b2144587be..18c101088b 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -324,10 +324,14 @@ impl EventEmitter for ProjectSearchView {} impl Render for ProjectSearchView { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div() - .flex_1() - .size_full() - .child(self.results_editor.clone()) + if self.has_matches() { + div() + .flex_1() + .size_full() + .child(self.results_editor.clone()) + } else { + div() + } } } // impl Entity for ProjectSearchView { From b09d2219d03be2bb8eec06ef5a07acf949c9881d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 9 Dec 2023 12:00:44 +0100 Subject: [PATCH 06/18] Add basic landing page --- crates/search2/src/project_search.rs | 199 +++++++++++++++++---------- 1 file changed, 129 insertions(+), 70 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 18c101088b..b1229b1b9f 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -14,9 +14,9 @@ use editor::{ }; use gpui::{ actions, div, white, Action, AnyElement, AnyView, AppContext, Context as _, Div, Element, - Entity, EntityId, EventEmitter, FocusableView, InteractiveElement, IntoElement, Model, - ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, Task, - View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, + Entity, EntityId, EventEmitter, FocusableView, InteractiveElement, IntoElement, KeyContext, + Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, + Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, }; use menu::Confirm; use project::{ @@ -39,7 +39,7 @@ use std::{ use theme::ActiveTheme; use ui::{ h_stack, v_stack, Button, Clickable, Color, Disableable, Icon, IconButton, IconElement, Label, - Selectable, + LabelCommon, LabelSize, Selectable, }; use util::{paths::PathMatcher, ResultExt as _}; use workspace::{ @@ -330,10 +330,30 @@ impl Render for ProjectSearchView { .size_full() .child(self.results_editor.clone()) } else { - div() + let has_no_results = self.model.read(cx).no_results.unwrap_or(false); + let middle_text = if has_no_results { + div() + .items_center() + .max_w_96() + .child(Label::new("No results for a given query")) + } else { + div() + .items_center() + .max_w_96() + .child(Label::new(self.landing_text_minor()).size(LabelSize::Small)) + }; + v_stack().flex_1().size_full().justify_center().child( + h_stack() + .size_full() + .justify_center() + .child(h_stack().flex_1()) + .child(middle_text) + .child(h_stack().flex_1()), + ) } } } + // impl Entity for ProjectSearchView { // type Event = ViewEvent; // } @@ -441,49 +461,6 @@ impl Render for ProjectSearchView { // } // }; -// let previous_query_keystrokes = -// cx.binding_for_action(&PreviousHistoryQuery {}) -// .map(|binding| { -// binding -// .keystrokes() -// .iter() -// .map(|k| k.to_string()) -// .collect::>() -// }); -// let next_query_keystrokes = -// cx.binding_for_action(&NextHistoryQuery {}).map(|binding| { -// binding -// .keystrokes() -// .iter() -// .map(|k| k.to_string()) -// .collect::>() -// }); -// let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) { -// (Some(previous_query_keystrokes), Some(next_query_keystrokes)) => { -// format!( -// "Search ({}/{} for previous/next query)", -// previous_query_keystrokes.join(" "), -// next_query_keystrokes.join(" ") -// ) -// } -// (None, Some(next_query_keystrokes)) => { -// format!( -// "Search ({} for next query)", -// next_query_keystrokes.join(" ") -// ) -// } -// (Some(previous_query_keystrokes), None) => { -// format!( -// "Search ({} for previous query)", -// previous_query_keystrokes.join(" ") -// ) -// } -// (None, None) => String::new(), -// }; -// self.query_editor.update(cx, |editor, cx| { -// editor.set_placeholder_text(new_placeholder_text, cx); -// }); - // MouseEventHandler::new::(0, cx, |_, _| { // Flex::column() // .with_child(Flex::column().contained().flex(1., true)) @@ -1355,6 +1332,12 @@ impl ProjectSearchView { }); } } + fn landing_text_minor(&self) -> SharedString { + match self.current_mode { + SearchMode::Text | SearchMode::Regex => "Include/exclude specific paths with the filter option. Matching exact word and/or casing is available too.".into(), + SearchMode::Semantic => ".Simply explain the code you are looking to find. ex. 'prompt user for permissions to index their project'".into() + } + } } impl Default for ProjectSearchBar { @@ -1680,6 +1663,47 @@ impl ProjectSearchBar { }); } } + fn new_placeholder_text(&self, cx: &mut ViewContext) -> Option { + let previous_query_keystrokes = cx + .bindings_for_action(&PreviousHistoryQuery {}) + .into_iter() + .next() + .map(|binding| { + binding + .keystrokes() + .iter() + .map(|k| k.to_string()) + .collect::>() + }); + let next_query_keystrokes = cx + .bindings_for_action(&NextHistoryQuery {}) + .into_iter() + .next() + .map(|binding| { + binding + .keystrokes() + .iter() + .map(|k| k.to_string()) + .collect::>() + }); + let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) { + (Some(previous_query_keystrokes), Some(next_query_keystrokes)) => Some(format!( + "Search ({}/{} for previous/next query)", + previous_query_keystrokes.join(" "), + next_query_keystrokes.join(" ") + )), + (None, Some(next_query_keystrokes)) => Some(format!( + "Search ({} for next query)", + next_query_keystrokes.join(" ") + )), + (Some(previous_query_keystrokes), None) => Some(format!( + "Search ({} for previous query)", + previous_query_keystrokes.join(" ") + )), + (None, None) => None, + }; + new_placeholder_text + } } impl Render for ProjectSearchBar { @@ -1689,7 +1713,17 @@ impl Render for ProjectSearchBar { let Some(search) = self.active_project_search.clone() else { return div(); }; + let mut key_context = KeyContext::default(); + key_context.add("ProjectSearchBar"); + if let Some(placeholder_text) = self.new_placeholder_text(cx) { + search.update(cx, |search, cx| { + search.query_editor.update(cx, |this, cx| { + this.set_placeholder_text(placeholder_text, cx) + }) + }); + } let search = search.read(cx); + let query_column = v_stack() .flex_1() .child( @@ -1714,6 +1748,10 @@ impl Render for ProjectSearchBar { .on_action(cx.listener(|this, action: &ActivateRegexMode, cx| { this.activate_search_mode(SearchMode::Regex, cx) })) + .on_action( + cx.listener(|this, action, cx| this.previous_history_query(action, cx)), + ) + .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx))) .child(IconElement::new(Icon::MagnifyingGlass)) .child(search.query_editor.clone()) .child( @@ -1740,34 +1778,54 @@ impl Render for ProjectSearchBar { .when(search.filters_enabled, |this| { this.child( h_stack() - .child(search.included_files_editor.clone()) - .child(search.excluded_files_editor.clone()), + .mt_2() + .flex_1() + .justify_between() + .child( + h_stack() + .flex_1() + .border_1() + .mr_2() + .child(search.included_files_editor.clone()), + ) + .child( + h_stack() + .flex_1() + .border_1() + .ml_2() + .child(search.excluded_files_editor.clone()), + ), ) }); - let mode_column = h_stack() - .child( - h_stack() - .child( - Button::new("project-search-text-button", "Text") - .selected(search.current_mode == SearchMode::Text) - .on_click(|_, cx| cx.dispatch_action(ActivateTextMode.boxed_clone())), - ) - .child( - Button::new("project-search-regex-button", "Regex") - .selected(search.current_mode == SearchMode::Regex) - .on_click(|_, cx| cx.dispatch_action(ActivateRegexMode.boxed_clone())), + let mode_column = v_stack().items_start().justify_start().child( + h_stack() + .child( + h_stack() + .child( + Button::new("project-search-text-button", "Text") + .selected(search.current_mode == SearchMode::Text) + .on_click(|_, cx| { + cx.dispatch_action(ActivateTextMode.boxed_clone()) + }), + ) + .child( + Button::new("project-search-regex-button", "Regex") + .selected(search.current_mode == SearchMode::Regex) + .on_click(|_, cx| { + cx.dispatch_action(ActivateRegexMode.boxed_clone()) + }), + ), + ) + .child( + IconButton::new("project-search-toggle-replace", Icon::Replace).on_click( + |_, cx| { + cx.dispatch_action(ToggleReplace.boxed_clone()); + }, ), - ) - .child( - IconButton::new("project-search-toggle-replace", Icon::Replace).on_click( - |_, cx| { - cx.dispatch_action(ToggleReplace.boxed_clone()); - }, ), - ); + ); let replace_column = if search.replace_enabled { h_stack() - .bg(white()) .p_1() .flex_1() .border_2() @@ -1811,6 +1869,7 @@ impl Render for ProjectSearchBar { .on_click(|_, cx| cx.dispatch_action(SelectNextMatch.boxed_clone())), ]); h_stack() + .key_context(key_context) .size_full() .p_1() .m_2() From 0be58eb61a802022cf2d5f230405817bfd99f05b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 9 Dec 2023 12:24:55 +0100 Subject: [PATCH 07/18] Add a bunch of tooltips --- crates/search2/src/project_search.rs | 127 ++++++++++++++++++++------- 1 file changed, 95 insertions(+), 32 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index b1229b1b9f..82243a69c7 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -38,8 +38,8 @@ use std::{ }; use theme::ActiveTheme; use ui::{ - h_stack, v_stack, Button, Clickable, Color, Disableable, Icon, IconButton, IconElement, Label, - LabelCommon, LabelSize, Selectable, + h_stack, v_stack, Button, ButtonCommon, Clickable, Color, Disableable, Icon, IconButton, + IconElement, Label, LabelCommon, LabelSize, Selectable, Tooltip, }; use util::{paths::PathMatcher, ResultExt as _}; use workspace::{ @@ -1519,10 +1519,12 @@ impl ProjectSearchBar { if let Some(search) = &self.active_project_search { search.update(cx, |this, cx| { this.replace_enabled = !this.replace_enabled; - if !this.replace_enabled { - let editor_handle = this.query_editor.focus_handle(cx); - cx.focus(&editor_handle); - } + let editor_to_focus = if !this.replace_enabled { + this.query_editor.focus_handle(cx) + } else { + this.replacement_editor.focus_handle(cx) + }; + cx.focus(&editor_to_focus); cx.notify(); }); } @@ -1742,34 +1744,80 @@ impl Render for ProjectSearchBar { .on_action(cx.listener(|this, action, cx| { this.toggle_replace(action, cx); })) - .on_action(cx.listener(|this, action: &ActivateTextMode, cx| { + .on_action(cx.listener(|this, _: &ActivateTextMode, cx| { this.activate_search_mode(SearchMode::Text, cx) })) - .on_action(cx.listener(|this, action: &ActivateRegexMode, cx| { + .on_action(cx.listener(|this, _: &ActivateRegexMode, cx| { this.activate_search_mode(SearchMode::Regex, cx) })) .on_action( cx.listener(|this, action, cx| this.previous_history_query(action, cx)), ) .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx))) + .on_action(cx.listener(|this, action, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_next(action, cx); + }) + } + })) + .on_action(cx.listener(|this, action, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_all(action, cx); + }) + } + })) .child(IconElement::new(Icon::MagnifyingGlass)) .child(search.query_editor.clone()) .child( h_stack() .child( IconButton::new("project-search-filter-button", Icon::Filter) + .tooltip(|cx| { + Tooltip::for_action("Toggle filters", &ToggleFilters, cx) + }) .on_click(|_, cx| { cx.dispatch_action(ToggleFilters.boxed_clone()) - }), + }) + .selected( + self.active_project_search + .as_ref() + .map(|search| search.read(cx).filters_enabled) + .unwrap_or_default(), + ), ) - .child(IconButton::new( - "project-search-case-sensitive", - Icon::CaseSensitive, - )) - .child(IconButton::new( - "project-search-whole-word", - Icon::WholeWord, - )), + .child( + IconButton::new( + "project-search-case-sensitive", + Icon::CaseSensitive, + ) + .tooltip(|cx| { + Tooltip::for_action( + "Toggle case sensitive", + &ToggleCaseSensitive, + cx, + ) + }) + .selected(self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx)) + .on_click(|_, cx| { + cx.dispatch_action(ToggleCaseSensitive.boxed_clone()) + }), + ) + .child( + IconButton::new("project-search-whole-word", Icon::WholeWord) + .tooltip(|cx| { + Tooltip::for_action( + "Toggle whole word", + &ToggleWholeWord, + cx, + ) + }) + .selected(self.is_option_enabled(SearchOptions::WHOLE_WORD, cx)) + .on_click(|_, cx| { + cx.dispatch_action(ToggleWholeWord.boxed_clone()) + }), + ), ) .border_2() .bg(white()) @@ -1806,6 +1854,9 @@ impl Render for ProjectSearchBar { .selected(search.current_mode == SearchMode::Text) .on_click(|_, cx| { cx.dispatch_action(ActivateTextMode.boxed_clone()) + }) + .tooltip(|cx| { + Tooltip::for_action("Toggle text search", &ActivateTextMode, cx) }), ) .child( @@ -1813,15 +1864,22 @@ impl Render for ProjectSearchBar { .selected(search.current_mode == SearchMode::Regex) .on_click(|_, cx| { cx.dispatch_action(ActivateRegexMode.boxed_clone()) + }) + .tooltip(|cx| { + Tooltip::for_action( + "Toggle regular expression search", + &ActivateRegexMode, + cx, + ) }), ), ) .child( - IconButton::new("project-search-toggle-replace", Icon::Replace).on_click( - |_, cx| { + IconButton::new("project-search-toggle-replace", Icon::Replace) + .on_click(|_, cx| { cx.dispatch_action(ToggleReplace.boxed_clone()); - }, - ), + }) + .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)), ), ); let replace_column = if search.replace_enabled { @@ -1839,16 +1897,16 @@ impl Render for ProjectSearchBar { let actions_column = h_stack() .when(search.replace_enabled, |this| { this.children([ - IconButton::new("project-search-replace-next", Icon::ReplaceNext).on_click( - |_, cx| { + IconButton::new("project-search-replace-next", Icon::ReplaceNext) + .on_click(|_, cx| { cx.dispatch_action(ReplaceNext.boxed_clone()); - }, - ), - IconButton::new("project-search-replace-all", Icon::ReplaceAll).on_click( - |_, cx| { + }) + .tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)), + IconButton::new("project-search-replace-all", Icon::ReplaceAll) + .on_click(|_, cx| { cx.dispatch_action(ReplaceAll.boxed_clone()); - }, - ), + }) + .tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)), ]) }) .when_some(search.active_match_index, |this, index| { @@ -1856,17 +1914,22 @@ impl Render for ProjectSearchBar { debug_assert!(match_quantity > index); this.child( IconButton::new("project-search-select-all", Icon::SelectAll) - .on_click(|_, cx| cx.dispatch_action(SelectAll.boxed_clone())), + .on_click(|_, cx| cx.dispatch_action(SelectAll.boxed_clone())) + .tooltip(|cx| Tooltip::for_action("Select all matches", &SelectAll, cx)), ) .child(Label::new(format!("{index}/{match_quantity}"))) }) .children([ IconButton::new("project-search-prev-match", Icon::ChevronLeft) .disabled(search.active_match_index.is_none()) - .on_click(|_, cx| cx.dispatch_action(SelectPrevMatch.boxed_clone())), + .on_click(|_, cx| cx.dispatch_action(SelectPrevMatch.boxed_clone())) + .tooltip(|cx| { + Tooltip::for_action("Go to previous match", &SelectPrevMatch, cx) + }), IconButton::new("project-search-next-match", Icon::ChevronRight) .disabled(search.active_match_index.is_none()) - .on_click(|_, cx| cx.dispatch_action(SelectNextMatch.boxed_clone())), + .on_click(|_, cx| cx.dispatch_action(SelectNextMatch.boxed_clone())) + .tooltip(|cx| Tooltip::for_action("Go to next match", &SelectNextMatch, cx)), ]); h_stack() .key_context(key_context) From fe315c6111abca7659c3bf3dd3449331377b8416 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:35:25 +0100 Subject: [PATCH 08/18] Bind actions on the whole search bar, not just the query editor --- crates/search2/src/project_search.rs | 64 ++++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 82243a69c7..2a143e0596 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1732,42 +1732,10 @@ impl Render for ProjectSearchBar { h_stack() .min_w_80() .on_action(cx.listener(|this, action, cx| this.confirm(action, cx))) - .on_action(cx.listener(|this, _: &ToggleFilters, cx| { - this.toggle_filters(cx); - })) - .on_action(cx.listener(|this, _: &ToggleWholeWord, cx| { - this.toggle_search_option(SearchOptions::WHOLE_WORD, cx); - })) - .on_action(cx.listener(|this, _: &ToggleCaseSensitive, cx| { - this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); - })) - .on_action(cx.listener(|this, action, cx| { - this.toggle_replace(action, cx); - })) - .on_action(cx.listener(|this, _: &ActivateTextMode, cx| { - this.activate_search_mode(SearchMode::Text, cx) - })) - .on_action(cx.listener(|this, _: &ActivateRegexMode, cx| { - this.activate_search_mode(SearchMode::Regex, cx) - })) .on_action( cx.listener(|this, action, cx| this.previous_history_query(action, cx)), ) .on_action(cx.listener(|this, action, cx| this.next_history_query(action, cx))) - .on_action(cx.listener(|this, action, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |this, cx| { - this.replace_next(action, cx); - }) - } - })) - .on_action(cx.listener(|this, action, cx| { - if let Some(search) = this.active_project_search.as_ref() { - search.update(cx, |this, cx| { - this.replace_all(action, cx); - }) - } - })) .child(IconElement::new(Icon::MagnifyingGlass)) .child(search.query_editor.clone()) .child( @@ -1937,6 +1905,38 @@ impl Render for ProjectSearchBar { .p_1() .m_2() .justify_between() + .on_action(cx.listener(|this, _: &ToggleFilters, cx| { + this.toggle_filters(cx); + })) + .on_action(cx.listener(|this, _: &ToggleWholeWord, cx| { + this.toggle_search_option(SearchOptions::WHOLE_WORD, cx); + })) + .on_action(cx.listener(|this, _: &ToggleCaseSensitive, cx| { + this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); + })) + .on_action(cx.listener(|this, action, cx| { + this.toggle_replace(action, cx); + })) + .on_action(cx.listener(|this, _: &ActivateTextMode, cx| { + this.activate_search_mode(SearchMode::Text, cx) + })) + .on_action(cx.listener(|this, _: &ActivateRegexMode, cx| { + this.activate_search_mode(SearchMode::Regex, cx) + })) + .on_action(cx.listener(|this, action, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_next(action, cx); + }) + } + })) + .on_action(cx.listener(|this, action, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_all(action, cx); + }) + } + })) .child(query_column) .child(mode_column) .child(replace_column) From 1599ee54671e2de25c6f41b1bf2f7cd49c648895 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:56:02 +0100 Subject: [PATCH 09/18] Add major text --- crates/search2/src/project_search.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 2a143e0596..a6dc4933c6 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -330,24 +330,27 @@ impl Render for ProjectSearchView { .size_full() .child(self.results_editor.clone()) } else { - let has_no_results = self.model.read(cx).no_results.unwrap_or(false); - let middle_text = if has_no_results { - div() - .items_center() - .max_w_96() - .child(Label::new("No results for a given query")) + let model = self.model.read(cx); + let has_no_results = model.no_results.unwrap_or(false); + let is_search_underway = model.active_query.is_some(); + let major_text = if is_search_underway { + Label::new("Searching...") + } else if has_no_results { + Label::new("No results for a given query") } else { - div() - .items_center() - .max_w_96() - .child(Label::new(self.landing_text_minor()).size(LabelSize::Small)) + Label::new(format!("{} search all files", self.current_mode.label())) }; + let major_text = div().justify_center().max_w_96().child(major_text); + let middle_text = div() + .items_center() + .max_w_96() + .child(Label::new(self.landing_text_minor()).size(LabelSize::Small)); v_stack().flex_1().size_full().justify_center().child( h_stack() .size_full() .justify_center() .child(h_stack().flex_1()) - .child(middle_text) + .child(v_stack().child(major_text).child(middle_text)) .child(h_stack().flex_1()), ) } From 38d844ab66d8ed4bd13bce372bd5453d78c612de Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:03:43 +0100 Subject: [PATCH 10/18] fixup! Add major text --- crates/search2/src/project_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index a6dc4933c6..df5978b419 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -332,7 +332,7 @@ impl Render for ProjectSearchView { } else { let model = self.model.read(cx); let has_no_results = model.no_results.unwrap_or(false); - let is_search_underway = model.active_query.is_some(); + let is_search_underway = model.pending_search.is_some(); let major_text = if is_search_underway { Label::new("Searching...") } else if has_no_results { From 66e0650f75ad77cc1240524a3023e3428ab6dc7d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:32:28 +0100 Subject: [PATCH 11/18] Forego using dispatch_action for click handlers --- crates/search2/src/project_search.rs | 84 +++++++++++++++++----------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index df5978b419..1b2a502ab8 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1748,9 +1748,9 @@ impl Render for ProjectSearchBar { .tooltip(|cx| { Tooltip::for_action("Toggle filters", &ToggleFilters, cx) }) - .on_click(|_, cx| { - cx.dispatch_action(ToggleFilters.boxed_clone()) - }) + .on_click(cx.listener(|this, _, cx| { + this.toggle_filters(cx); + })) .selected( self.active_project_search .as_ref() @@ -1771,9 +1771,14 @@ impl Render for ProjectSearchBar { ) }) .selected(self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx)) - .on_click(|_, cx| { - cx.dispatch_action(ToggleCaseSensitive.boxed_clone()) - }), + .on_click(cx.listener( + |this, _, cx| { + this.toggle_search_option( + SearchOptions::CASE_SENSITIVE, + cx, + ); + }, + )), ) .child( IconButton::new("project-search-whole-word", Icon::WholeWord) @@ -1785,9 +1790,9 @@ impl Render for ProjectSearchBar { ) }) .selected(self.is_option_enabled(SearchOptions::WHOLE_WORD, cx)) - .on_click(|_, cx| { - cx.dispatch_action(ToggleWholeWord.boxed_clone()) - }), + .on_click(cx.listener(|this, _, cx| { + this.toggle_search_option(SearchOptions::WHOLE_WORD, cx); + })), ), ) .border_2() @@ -1823,9 +1828,9 @@ impl Render for ProjectSearchBar { .child( Button::new("project-search-text-button", "Text") .selected(search.current_mode == SearchMode::Text) - .on_click(|_, cx| { - cx.dispatch_action(ActivateTextMode.boxed_clone()) - }) + .on_click(cx.listener(|this, _, cx| { + this.activate_search_mode(SearchMode::Text, cx) + })) .tooltip(|cx| { Tooltip::for_action("Toggle text search", &ActivateTextMode, cx) }), @@ -1833,9 +1838,9 @@ impl Render for ProjectSearchBar { .child( Button::new("project-search-regex-button", "Regex") .selected(search.current_mode == SearchMode::Regex) - .on_click(|_, cx| { - cx.dispatch_action(ActivateRegexMode.boxed_clone()) - }) + .on_click(cx.listener(|this, _, cx| { + this.activate_search_mode(SearchMode::Regex, cx) + })) .tooltip(|cx| { Tooltip::for_action( "Toggle regular expression search", @@ -1847,9 +1852,9 @@ impl Render for ProjectSearchBar { ) .child( IconButton::new("project-search-toggle-replace", Icon::Replace) - .on_click(|_, cx| { - cx.dispatch_action(ToggleReplace.boxed_clone()); - }) + .on_click(cx.listener(|this, _, cx| { + this.toggle_replace(&ToggleReplace, cx); + })) .tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)), ), ); @@ -1869,37 +1874,52 @@ impl Render for ProjectSearchBar { .when(search.replace_enabled, |this| { this.children([ IconButton::new("project-search-replace-next", Icon::ReplaceNext) - .on_click(|_, cx| { - cx.dispatch_action(ReplaceNext.boxed_clone()); - }) + .on_click(cx.listener(|this, _, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_next(&ReplaceNext, cx); + }) + } + })) .tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)), IconButton::new("project-search-replace-all", Icon::ReplaceAll) - .on_click(|_, cx| { - cx.dispatch_action(ReplaceAll.boxed_clone()); - }) + .on_click(cx.listener(|this, _, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.replace_all(&ReplaceAll, cx); + }) + } + })) .tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)), ]) }) .when_some(search.active_match_index, |this, index| { let match_quantity = search.model.read(cx).match_ranges.len(); debug_assert!(match_quantity > index); - this.child( - IconButton::new("project-search-select-all", Icon::SelectAll) - .on_click(|_, cx| cx.dispatch_action(SelectAll.boxed_clone())) - .tooltip(|cx| Tooltip::for_action("Select all matches", &SelectAll, cx)), - ) - .child(Label::new(format!("{index}/{match_quantity}"))) + this.child(Label::new(format!("{index}/{match_quantity}"))) }) .children([ IconButton::new("project-search-prev-match", Icon::ChevronLeft) .disabled(search.active_match_index.is_none()) - .on_click(|_, cx| cx.dispatch_action(SelectPrevMatch.boxed_clone())) + .on_click(cx.listener(|this, _, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.select_match(Direction::Prev, cx); + }) + } + })) .tooltip(|cx| { Tooltip::for_action("Go to previous match", &SelectPrevMatch, cx) }), IconButton::new("project-search-next-match", Icon::ChevronRight) .disabled(search.active_match_index.is_none()) - .on_click(|_, cx| cx.dispatch_action(SelectNextMatch.boxed_clone())) + .on_click(cx.listener(|this, _, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |this, cx| { + this.select_match(Direction::Next, cx); + }) + } + })) .tooltip(|cx| Tooltip::for_action("Go to next match", &SelectNextMatch, cx)), ]); h_stack() From 3f87c3541f0685fba9c975f28b58439320a87b3b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:40:38 +0100 Subject: [PATCH 12/18] Make the query editor expand in size less --- crates/search2/src/project_search.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 1b2a502ab8..253730a692 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1730,7 +1730,7 @@ impl Render for ProjectSearchBar { let search = search.read(cx); let query_column = v_stack() - .flex_1() + //.flex_1() .child( h_stack() .min_w_80() @@ -1868,7 +1868,7 @@ impl Render for ProjectSearchBar { .child(search.replacement_editor.clone()) } else { // Fill out the space if we don't have a replacement editor. - h_stack().size_full() + h_stack().flex_1() }; let actions_column = h_stack() .when(search.replace_enabled, |this| { From 7330495709480fc4cb4fa3677879a670002cea1f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:47:02 +0100 Subject: [PATCH 13/18] Make match index 1-based --- crates/search2/src/project_search.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 253730a692..df9088063a 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1893,10 +1893,14 @@ impl Render for ProjectSearchBar { .tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)), ]) }) - .when_some(search.active_match_index, |this, index| { + .when_some(search.active_match_index, |mut this, index| { + let index = index + 1; let match_quantity = search.model.read(cx).match_ranges.len(); - debug_assert!(match_quantity > index); - this.child(Label::new(format!("{index}/{match_quantity}"))) + if match_quantity > 0 { + debug_assert!(match_quantity >= index); + this = this.child(Label::new(format!("{index}/{match_quantity}"))) + } + this }) .children([ IconButton::new("project-search-prev-match", Icon::ChevronLeft) From 3c49011d084bbfb13bb37edd7e3a3d298c972224 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:48:42 +0100 Subject: [PATCH 14/18] Fix up warnings (automatic) --- crates/search2/src/project_search.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index df9088063a..be39bb030b 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1,7 +1,6 @@ use crate::{ history::SearchHistory, mode::SearchMode, - search_bar::{render_nav_button, render_search_mode_button}, ActivateRegexMode, ActivateSemanticMode, ActivateTextMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleReplace, ToggleWholeWord, @@ -24,21 +23,19 @@ use project::{ Entry, Project, }; use semantic_index::{SemanticIndex, SemanticIndexStatus}; -use smallvec::SmallVec; + use smol::stream::StreamExt; use std::{ any::{Any, TypeId}, - borrow::Cow, collections::HashSet, mem, ops::{Not, Range}, path::PathBuf, - sync::Arc, - time::{Duration, Instant}, + time::{Duration}, }; -use theme::ActiveTheme; + use ui::{ - h_stack, v_stack, Button, ButtonCommon, Clickable, Color, Disableable, Icon, IconButton, + h_stack, v_stack, Button, ButtonCommon, Clickable, Disableable, Icon, IconButton, IconElement, Label, LabelCommon, LabelSize, Selectable, Tooltip, }; use util::{paths::PathMatcher, ResultExt as _}; @@ -61,7 +58,7 @@ pub fn init(cx: &mut AppContext) { // todo!() po cx.set_global(ActiveSearches::default()); cx.set_global(ActiveSettings::default()); - cx.observe_new_views(|workspace: &mut Workspace, cx| { + cx.observe_new_views(|workspace: &mut Workspace, _cx| { workspace.register_action(ProjectSearchView::deploy); }) .detach(); @@ -97,7 +94,7 @@ pub fn init(cx: &mut AppContext) { // add_toggle_filters_action::(cx); } -fn add_toggle_filters_action(cx: &mut AppContext) { +fn add_toggle_filters_action(_cx: &mut AppContext) { // todo!() po // cx.register_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { // if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { @@ -109,7 +106,7 @@ fn add_toggle_filters_action(cx: &mut AppContext) { // }); } -fn add_toggle_option_action(option: SearchOptions, cx: &mut AppContext) { +fn add_toggle_option_action(_option: SearchOptions, _cx: &mut AppContext) { // todo!() po // cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { // if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { @@ -811,7 +808,7 @@ impl ProjectSearchView { let has_permission = has_permission.await?; if !has_permission { - let mut answer = this.update(&mut cx, |this, cx| { + let answer = this.update(&mut cx, |this, cx| { let project = this.model.read(cx).project.clone(); let project_name = project .read(cx) @@ -1069,7 +1066,7 @@ impl ProjectSearchView { cx: &mut ViewContext, ) { // Clean up entries for dropped projects - cx.update_global(|state: &mut ActiveSearches, cx| { + cx.update_global(|state: &mut ActiveSearches, _cx| { state.0.retain(|project, _| project.is_upgradable()) }); From d4a246cae8629c39014581cb295656b1efd9fb11 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:53:19 +0100 Subject: [PATCH 15/18] Further warnings churn --- crates/search2/src/project_search.rs | 152 +++++---------------------- 1 file changed, 24 insertions(+), 128 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index be39bb030b..88d81a7e13 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1,9 +1,8 @@ use crate::{ - history::SearchHistory, - mode::SearchMode, - ActivateRegexMode, ActivateSemanticMode, ActivateTextMode, CycleMode, NextHistoryQuery, - PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions, SelectNextMatch, SelectPrevMatch, - ToggleCaseSensitive, ToggleReplace, ToggleWholeWord, + history::SearchHistory, mode::SearchMode, ActivateRegexMode, ActivateSemanticMode, + ActivateTextMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, + SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleReplace, + ToggleWholeWord, }; use anyhow::{Context as _, Result}; use collections::HashMap; @@ -12,10 +11,10 @@ use editor::{ MultiBuffer, SelectAll, MAX_TAB_TITLE_LEN, }; use gpui::{ - actions, div, white, Action, AnyElement, AnyView, AppContext, Context as _, Div, Element, - Entity, EntityId, EventEmitter, FocusableView, InteractiveElement, IntoElement, KeyContext, - Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, - Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, + actions, div, white, AnyElement, AnyView, AppContext, Context as _, Div, Element, EntityId, + EventEmitter, FocusableView, InteractiveElement, IntoElement, KeyContext, Model, ModelContext, + ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, Task, View, + ViewContext, VisualContext, WeakModel, WeakView, WindowContext, }; use menu::Confirm; use project::{ @@ -31,12 +30,12 @@ use std::{ mem, ops::{Not, Range}, path::PathBuf, - time::{Duration}, + time::Duration, }; use ui::{ - h_stack, v_stack, Button, ButtonCommon, Clickable, Disableable, Icon, IconButton, - IconElement, Label, LabelCommon, LabelSize, Selectable, Tooltip, + h_stack, v_stack, Button, ButtonCommon, Clickable, Disableable, Icon, IconButton, IconElement, + Label, LabelCommon, LabelSize, Selectable, Tooltip, }; use util::{paths::PathMatcher, ResultExt as _}; use workspace::{ @@ -62,62 +61,6 @@ pub fn init(cx: &mut AppContext) { workspace.register_action(ProjectSearchView::deploy); }) .detach(); - - // cx.add_action(ProjectSearchView::deploy); - // cx.add_action(ProjectSearchView::move_focus_to_results); - // cx.add_action(ProjectSearchBar::confirm); - // 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::replace_next); - // cx.add_action(ProjectSearchBar::replace_all); - // 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::toggle_replace); - // cx.add_action(ProjectSearchBar::toggle_replace_on_a_pane); - // cx.add_action(ProjectSearchBar::activate_text_mode); - - // // This action should only be registered if the semantic index is enabled - // // We are registering it all the time, as I dont want to introduce a dependency - // // for Semantic Index Settings globally whenever search is tested. - // cx.add_action(ProjectSearchBar::activate_semantic_mode); - - // cx.capture_action(ProjectSearchBar::tab); - // cx.capture_action(ProjectSearchBar::tab_previous); - // cx.capture_action(ProjectSearchView::replace_all); - // cx.capture_action(ProjectSearchView::replace_next); - // add_toggle_option_action::(SearchOptions::CASE_SENSITIVE, cx); - // add_toggle_option_action::(SearchOptions::WHOLE_WORD, cx); - // add_toggle_option_action::(SearchOptions::INCLUDE_IGNORED, cx); - // add_toggle_filters_action::(cx); -} - -fn add_toggle_filters_action(_cx: &mut AppContext) { - // todo!() po - // cx.register_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)) { - // cx.stop_propagation(); - // return; - // } - // } - // }); -} - -fn add_toggle_option_action(_option: SearchOptions, _cx: &mut AppContext) { - // todo!() po - // cx.add_action(move |pane: &mut Pane, _: &A, cx: &mut ViewContext| { - // if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { - // if search_bar.update(cx, |search_bar, cx| { - // search_bar.toggle_search_option(option, cx) - // }) { - // cx.stop_propagation(); - // return; - // } - // } - // }); } struct ProjectSearch { @@ -220,7 +163,8 @@ impl ProjectSearch { this.match_ranges.clear(); this.excerpts.update(cx, |this, cx| this.clear(cx)); this.no_results = Some(true); - }); + }) + .ok()?; while let Some((buffer, anchors)) = matches.next().await { let mut ranges = this @@ -233,15 +177,17 @@ impl ProjectSearch { .ok()?; while let Some(range) = ranges.next().await { - this.update(&mut cx, |this, _| this.match_ranges.push(range)); + this.update(&mut cx, |this, _| this.match_ranges.push(range)) + .ok()?; } - this.update(&mut cx, |_, cx| cx.notify()); + this.update(&mut cx, |_, cx| cx.notify()).ok()?; } this.update(&mut cx, |this, cx| { this.pending_search.take(); cx.notify(); - }); + }) + .ok()?; None })); @@ -276,7 +222,8 @@ impl ProjectSearch { this.excerpts.update(cx, |excerpts, cx| { excerpts.clear(cx); }); - }); + }) + .ok()?; for (buffer, ranges) in matches { let mut match_ranges = this .update(&mut cx, |this, cx| { @@ -293,14 +240,16 @@ impl ProjectSearch { this.match_ranges.push(match_range); } cx.notify(); - }); + }) + .ok()?; } } this.update(&mut cx, |this, cx| { this.pending_search.take(); cx.notify(); - }); + }) + .ok()?; None })); @@ -1317,21 +1266,6 @@ impl ProjectSearchView { self.active_match_index.is_some() } - fn move_focus_to_results(pane: &mut Pane, _: &ToggleFocus, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |search_view, cx| { - if !search_view.results_editor.focus_handle(cx).is_focused(cx) - && !search_view.model.read(cx).match_ranges.is_empty() - { - cx.stop_propagation(); - return search_view.focus_results_editor(cx); - } - }); - } - } fn landing_text_minor(&self) -> SharedString { match self.current_mode { SearchMode::Text | SearchMode::Regex => "Include/exclude specific paths with the filter option. Matching exact word and/or casing is available too.".into(), @@ -1413,44 +1347,6 @@ impl ProjectSearchBar { } } - fn select_next_match(pane: &mut Pane, _: &SelectNextMatch, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| view.select_match(Direction::Next, cx)); - cx.stop_propagation(); - } - } - - fn replace_next(pane: &mut Pane, _: &ReplaceNext, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| view.replace_next(&ReplaceNext, cx)); - cx.stop_propagation(); - } - } - fn replace_all(pane: &mut Pane, _: &ReplaceAll, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| view.replace_all(&ReplaceAll, cx)); - cx.stop_propagation(); - } - } - fn select_prev_match(pane: &mut Pane, _: &SelectPrevMatch, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| view.select_match(Direction::Prev, cx)); - cx.stop_propagation(); - } - } - fn tab(&mut self, _: &editor::Tab, cx: &mut ViewContext) { self.cycle_field(Direction::Next, cx); } From 9f1e7a1e21db62c06219dd5b0f0f120d4c194266 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:54:36 +0100 Subject: [PATCH 16/18] fixup! Further warnings churn --- crates/search2/src/project_search.rs | 57 ---------------------------- 1 file changed, 57 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 88d81a7e13..d006e26a9a 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1425,63 +1425,6 @@ impl ProjectSearchBar { }); } } - fn toggle_replace_on_a_pane(pane: &mut Pane, _: &ToggleReplace, cx: &mut ViewContext) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |this, cx| { - cx.stop_propagation(); - this.replace_enabled = !this.replace_enabled; - if !this.replace_enabled { - let editor_handle = this.query_editor.focus_handle(cx); - cx.focus(&editor_handle); - } - cx.notify(); - }); - } - } - fn activate_text_mode(pane: &mut Pane, _: &ActivateTextMode, 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::Text, cx) - }); - cx.stop_propagation(); - } - } - - 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) - }); - cx.stop_propagation(); - } - } - - fn activate_semantic_mode( - pane: &mut Pane, - _: &ActivateSemanticMode, - cx: &mut ViewContext, - ) { - if SemanticIndex::enabled(cx) { - if let Some(search_view) = pane - .active_item() - .and_then(|item| item.downcast::()) - { - search_view.update(cx, |view, cx| { - view.activate_search_mode(SearchMode::Semantic, cx) - }); - cx.stop_propagation(); - } - } - } fn toggle_filters(&mut self, cx: &mut ViewContext) -> bool { if let Some(search_view) = self.active_project_search.as_ref() { From 40c1ef88e22f12e88641a23ced3f550be7395b62 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:39:17 +0100 Subject: [PATCH 17/18] Add Editor::Tab and CycleMode actions --- crates/search2/src/project_search.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index d006e26a9a..3768f00b42 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -1287,18 +1287,15 @@ 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 new_mode = - crate::mode::next_mode(&this.current_mode, SemanticIndex::enabled(cx)); + fn cycle_mode(&self, _: &CycleMode, cx: &mut ViewContext) { + if let Some(view) = self.active_project_search.as_ref() { + view.update(cx, |this, cx| { + // todo: po: 2nd argument of `next_mode` should be `SemanticIndex::enabled(cx))`, but we need to flesh out port of semantic_index first. + let new_mode = crate::mode::next_mode(&this.current_mode, false); this.activate_search_mode(new_mode, cx); let editor_handle = this.query_editor.focus_handle(cx); cx.focus(&editor_handle); - }) + }); } } fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { @@ -1800,6 +1797,15 @@ impl Render for ProjectSearchBar { }) } })) + .on_action(cx.listener(|this, action, cx| { + this.tab(action, cx); + })) + .on_action(cx.listener(|this, action, cx| { + this.tab_previous(action, cx); + })) + .on_action(cx.listener(|this, action, cx| { + this.cycle_mode(action, cx); + })) .child(query_column) .child(mode_column) .child(replace_column) From 01ecbec2b0dc8eb38005cd45bfaac04a668578ca Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:52:12 +0100 Subject: [PATCH 18/18] Comment out the tests --- crates/search2/src/project_search.rs | 1322 +++++++++++++------------- 1 file changed, 661 insertions(+), 661 deletions(-) diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index 8e1850e678..dd87113159 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -2140,711 +2140,711 @@ impl ToolbarItemView for ProjectSearchBar { } } -#[cfg(test)] -pub mod tests { - use super::*; - use editor::DisplayPoint; - use gpui::{color::Color, executor::Deterministic, TestAppContext}; - use project::FakeFs; - use semantic_index::semantic_index_settings::SemanticIndexSettings; - use serde_json::json; - use settings::SettingsStore; - use std::sync::Arc; - use theme::ThemeSettings; +// #[cfg(test)] +// pub mod tests { +// use super::*; +// use editor::DisplayPoint; +// use gpui::{color::Color, executor::Deterministic, TestAppContext}; +// use project::FakeFs; +// use semantic_index::semantic_index_settings::SemanticIndexSettings; +// use serde_json::json; +// use settings::SettingsStore; +// use std::sync::Arc; +// use theme::ThemeSettings; - #[gpui::test] - async fn test_project_search(deterministic: Arc, cx: &mut TestAppContext) { - init_test(cx); +// #[gpui::test] +// async fn test_project_search(deterministic: Arc, cx: &mut TestAppContext) { +// init_test(cx); - let fs = FakeFs::new(cx.background_executor()); - fs.insert_tree( - "/dir", - json!({ - "one.rs": "const ONE: usize = 1;", - "two.rs": "const TWO: usize = one::ONE + one::ONE;", - "three.rs": "const THREE: usize = one::ONE + two::TWO;", - "four.rs": "const FOUR: usize = one::ONE + three::THREE;", - }), - ) - .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); - let search_view = cx - .add_window(|cx| ProjectSearchView::new(search.clone(), cx, None)) - .root(cx); +// let fs = FakeFs::new(cx.background_executor()); +// fs.insert_tree( +// "/dir", +// json!({ +// "one.rs": "const ONE: usize = 1;", +// "two.rs": "const TWO: usize = one::ONE + one::ONE;", +// "three.rs": "const THREE: usize = one::ONE + two::TWO;", +// "four.rs": "const FOUR: usize = one::ONE + three::THREE;", +// }), +// ) +// .await; +// let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; +// let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); +// let search_view = cx +// .add_window(|cx| ProjectSearchView::new(search.clone(), cx, None)) +// .root(cx); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); - search_view.search(cx); - }); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;" - ); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.all_text_background_highlights(cx)), - &[ - ( - DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35), - Color::red() - ), - ( - DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40), - Color::red() - ), - ( - DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9), - Color::red() - ) - ] - ); - assert_eq!(search_view.active_match_index, Some(0)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] - ); +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); +// search_view.search(cx); +// }); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;" +// ); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.all_text_background_highlights(cx)), +// &[ +// ( +// DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35), +// Color::red() +// ), +// ( +// DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40), +// Color::red() +// ), +// ( +// DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9), +// Color::red() +// ) +// ] +// ); +// assert_eq!(search_view.active_match_index, Some(0)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] +// ); - search_view.select_match(Direction::Next, cx); - }); +// search_view.select_match(Direction::Next, cx); +// }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.active_match_index, Some(1)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] - ); - search_view.select_match(Direction::Next, cx); - }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.active_match_index, Some(1)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] +// ); +// search_view.select_match(Direction::Next, cx); +// }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.active_match_index, Some(2)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] - ); - search_view.select_match(Direction::Next, cx); - }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.active_match_index, Some(2)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] +// ); +// search_view.select_match(Direction::Next, cx); +// }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.active_match_index, Some(0)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] - ); - search_view.select_match(Direction::Prev, cx); - }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.active_match_index, Some(0)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)] +// ); +// search_view.select_match(Direction::Prev, cx); +// }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.active_match_index, Some(2)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] - ); - search_view.select_match(Direction::Prev, cx); - }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.active_match_index, Some(2)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)] +// ); +// search_view.select_match(Direction::Prev, cx); +// }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.active_match_index, Some(1)); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] - ); - }); - } +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.active_match_index, Some(1)); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.selections.display_ranges(cx)), +// [DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)] +// ); +// }); +// } - #[gpui::test] - async fn test_project_search_focus(deterministic: Arc, cx: &mut TestAppContext) { - init_test(cx); +// #[gpui::test] +// async fn test_project_search_focus(deterministic: Arc, cx: &mut TestAppContext) { +// init_test(cx); - let fs = FakeFs::new(cx.background_executor()); - fs.insert_tree( - "/dir", - json!({ - "one.rs": "const ONE: usize = 1;", - "two.rs": "const TWO: usize = one::ONE + one::ONE;", - "three.rs": "const THREE: usize = one::ONE + two::TWO;", - "four.rs": "const FOUR: usize = one::ONE + three::THREE;", - }), - ) - .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let window = cx.add_window(|cx| Workspace::test_new(project, cx)); - let workspace = window.root(cx); +// let fs = FakeFs::new(cx.background_executor()); +// fs.insert_tree( +// "/dir", +// json!({ +// "one.rs": "const ONE: usize = 1;", +// "two.rs": "const TWO: usize = one::ONE + one::ONE;", +// "three.rs": "const THREE: usize = one::ONE + two::TWO;", +// "four.rs": "const FOUR: usize = one::ONE + three::THREE;", +// }), +// ) +// .await; +// let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); - let active_item = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }); - assert!( - active_item.is_none(), - "Expected no search panel to be active, but got: {active_item:?}" - ); +// let active_item = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }); +// assert!( +// active_item.is_none(), +// "Expected no search panel to be active, but got: {active_item:?}" +// ); - workspace.update(cx, |workspace, cx| { - ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) - }); +// workspace.update(cx, |workspace, cx| { +// ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) +// }); - let Some(search_view) = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }) else { - panic!("Search view expected to appear after new search event trigger") - }; - let search_view_id = search_view.id(); +// let Some(search_view) = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }) else { +// panic!("Search view expected to appear after new search event trigger") +// }; +// let search_view_id = search_view.id(); - cx.spawn(|mut cx| async move { - window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); - }) - .detach(); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert!( - search_view.query_editor.is_focused(cx), - "Empty search view should be focused after the toggle focus event: no results panel to focus on", - ); - }); +// cx.spawn(|mut cx| async move { +// window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); +// }) +// .detach(); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert!( +// search_view.query_editor.is_focused(cx), +// "Empty search view should be focused after the toggle focus event: no results panel to focus on", +// ); +// }); - search_view.update(cx, |search_view, cx| { - let query_editor = &search_view.query_editor; - assert!( - query_editor.is_focused(cx), - "Search view should be focused after the new search view is activated", - ); - let query_text = query_editor.read(cx).text(cx); - assert!( - query_text.is_empty(), - "New search query should be empty but got '{query_text}'", - ); - let results_text = search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)); - assert!( - results_text.is_empty(), - "Empty search view should have no results but got '{results_text}'" - ); - }); +// search_view.update(cx, |search_view, cx| { +// let query_editor = &search_view.query_editor; +// assert!( +// query_editor.is_focused(cx), +// "Search view should be focused after the new search view is activated", +// ); +// let query_text = query_editor.read(cx).text(cx); +// assert!( +// query_text.is_empty(), +// "New search query should be empty but got '{query_text}'", +// ); +// let results_text = search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)); +// assert!( +// results_text.is_empty(), +// "Empty search view should have no results but got '{results_text}'" +// ); +// }); - search_view.update(cx, |search_view, cx| { - search_view.query_editor.update(cx, |query_editor, cx| { - query_editor.set_text("sOMETHINGtHATsURELYdOESnOTeXIST", cx) - }); - search_view.search(cx); - }); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - let results_text = search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)); - assert!( - results_text.is_empty(), - "Search view for mismatching query should have no results but got '{results_text}'" - ); - assert!( - search_view.query_editor.is_focused(cx), - "Search view should be focused after mismatching query had been used in search", - ); - }); - cx.spawn( - |mut cx| async move { window.dispatch_action(search_view_id, &ToggleFocus, &mut cx) }, - ) - .detach(); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert!( - search_view.query_editor.is_focused(cx), - "Search view with mismatching query should be focused after the toggle focus event: still no results panel to focus on", - ); - }); +// search_view.update(cx, |search_view, cx| { +// search_view.query_editor.update(cx, |query_editor, cx| { +// query_editor.set_text("sOMETHINGtHATsURELYdOESnOTeXIST", cx) +// }); +// search_view.search(cx); +// }); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// let results_text = search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)); +// assert!( +// results_text.is_empty(), +// "Search view for mismatching query should have no results but got '{results_text}'" +// ); +// assert!( +// search_view.query_editor.is_focused(cx), +// "Search view should be focused after mismatching query had been used in search", +// ); +// }); +// cx.spawn( +// |mut cx| async move { window.dispatch_action(search_view_id, &ToggleFocus, &mut cx) }, +// ) +// .detach(); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert!( +// search_view.query_editor.is_focused(cx), +// "Search view with mismatching query should be focused after the toggle focus event: still no results panel to focus on", +// ); +// }); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); - search_view.search(cx); - }); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", - "Search view results should match the query" - ); - assert!( - search_view.results_editor.is_focused(cx), - "Search view with mismatching query should be focused after search results are available", - ); - }); - cx.spawn(|mut cx| async move { - window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); - }) - .detach(); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert!( - search_view.results_editor.is_focused(cx), - "Search view with matching query should still have its results editor focused after the toggle focus event", - ); - }); +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); +// search_view.search(cx); +// }); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", +// "Search view results should match the query" +// ); +// assert!( +// search_view.results_editor.is_focused(cx), +// "Search view with mismatching query should be focused after search results are available", +// ); +// }); +// cx.spawn(|mut cx| async move { +// window.dispatch_action(search_view_id, &ToggleFocus, &mut cx); +// }) +// .detach(); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert!( +// search_view.results_editor.is_focused(cx), +// "Search view with matching query should still have its results editor focused after the toggle focus event", +// ); +// }); - workspace.update(cx, |workspace, cx| { - ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) - }); - deterministic.run_until_parked(); - let Some(search_view_2) = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }) else { - panic!("Search view expected to appear after new search event trigger") - }; - let search_view_id_2 = search_view_2.id(); - assert_ne!( - search_view_2, search_view, - "New search view should be open after `workspace::NewSearch` event" - ); +// workspace.update(cx, |workspace, cx| { +// ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) +// }); +// deterministic.run_until_parked(); +// let Some(search_view_2) = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }) else { +// panic!("Search view expected to appear after new search event trigger") +// }; +// let search_view_id_2 = search_view_2.id(); +// assert_ne!( +// search_view_2, search_view, +// "New search view should be open after `workspace::NewSearch` event" +// ); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO", "First search view should not have an updated query"); - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", - "Results of the first search view should not update too" - ); - assert!( - !search_view.query_editor.is_focused(cx), - "Focus should be moved away from the first search view" - ); - }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO", "First search view should not have an updated query"); +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", +// "Results of the first search view should not update too" +// ); +// assert!( +// !search_view.query_editor.is_focused(cx), +// "Focus should be moved away from the first search view" +// ); +// }); - search_view_2.update(cx, |search_view_2, cx| { - assert_eq!( - search_view_2.query_editor.read(cx).text(cx), - "two", - "New search view should get the query from the text cursor was at during the event spawn (first search view's first result)" - ); - assert_eq!( - search_view_2 - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "", - "No search results should be in the 2nd view yet, as we did not spawn a search for it" - ); - assert!( - search_view_2.query_editor.is_focused(cx), - "Focus should be moved into query editor fo the new window" - ); - }); +// search_view_2.update(cx, |search_view_2, cx| { +// assert_eq!( +// search_view_2.query_editor.read(cx).text(cx), +// "two", +// "New search view should get the query from the text cursor was at during the event spawn (first search view's first result)" +// ); +// assert_eq!( +// search_view_2 +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "", +// "No search results should be in the 2nd view yet, as we did not spawn a search for it" +// ); +// assert!( +// search_view_2.query_editor.is_focused(cx), +// "Focus should be moved into query editor fo the new window" +// ); +// }); - search_view_2.update(cx, |search_view_2, cx| { - search_view_2 - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("FOUR", cx)); - search_view_2.search(cx); - }); - deterministic.run_until_parked(); - search_view_2.update(cx, |search_view_2, cx| { - assert_eq!( - search_view_2 - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "\n\nconst FOUR: usize = one::ONE + three::THREE;", - "New search view with the updated query should have new search results" - ); - assert!( - search_view_2.results_editor.is_focused(cx), - "Search view with mismatching query should be focused after search results are available", - ); - }); +// search_view_2.update(cx, |search_view_2, cx| { +// search_view_2 +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("FOUR", cx)); +// search_view_2.search(cx); +// }); +// deterministic.run_until_parked(); +// search_view_2.update(cx, |search_view_2, cx| { +// assert_eq!( +// search_view_2 +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "\n\nconst FOUR: usize = one::ONE + three::THREE;", +// "New search view with the updated query should have new search results" +// ); +// assert!( +// search_view_2.results_editor.is_focused(cx), +// "Search view with mismatching query should be focused after search results are available", +// ); +// }); - cx.spawn(|mut cx| async move { - window.dispatch_action(search_view_id_2, &ToggleFocus, &mut cx); - }) - .detach(); - deterministic.run_until_parked(); - search_view_id_2.update(cx, |search_view_2, cx| { - assert!( - search_view_2.results_editor.is_focused(cx), - "Search view with matching query should switch focus to the results editor after the toggle focus event", - ); - }); - } +// cx.spawn(|mut cx| async move { +// window.dispatch_action(search_view_id_2, &ToggleFocus, &mut cx); +// }) +// .detach(); +// deterministic.run_until_parked(); +// search_view_id_2.update(cx, |search_view_2, cx| { +// assert!( +// search_view_2.results_editor.is_focused(cx), +// "Search view with matching query should switch focus to the results editor after the toggle focus event", +// ); +// }); +// } - #[gpui::test] - async fn test_new_project_search_in_directory( - deterministic: Arc, - cx: &mut TestAppContext, - ) { - init_test(cx); +// #[gpui::test] +// async fn test_new_project_search_in_directory( +// deterministic: Arc, +// cx: &mut TestAppContext, +// ) { +// init_test(cx); - let fs = FakeFs::new(cx.background_executor()); - fs.insert_tree( - "/dir", - json!({ - "a": { - "one.rs": "const ONE: usize = 1;", - "two.rs": "const TWO: usize = one::ONE + one::ONE;", - }, - "b": { - "three.rs": "const THREE: usize = one::ONE + two::TWO;", - "four.rs": "const FOUR: usize = one::ONE + three::THREE;", - }, - }), - ) - .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let worktree_id = project.read_with(cx, |project, cx| { - project.worktrees(cx).next().unwrap().read(cx).id() - }); - let workspace = cx - .add_window(|cx| Workspace::test_new(project, cx)) - .root(cx); +// let fs = FakeFs::new(cx.background_executor()); +// fs.insert_tree( +// "/dir", +// json!({ +// "a": { +// "one.rs": "const ONE: usize = 1;", +// "two.rs": "const TWO: usize = one::ONE + one::ONE;", +// }, +// "b": { +// "three.rs": "const THREE: usize = one::ONE + two::TWO;", +// "four.rs": "const FOUR: usize = one::ONE + three::THREE;", +// }, +// }), +// ) +// .await; +// let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; +// let worktree_id = project.read_with(cx, |project, cx| { +// project.worktrees(cx).next().unwrap().read(cx).id() +// }); +// let workspace = cx +// .add_window(|cx| Workspace::test_new(project, cx)) +// .root(cx); - let active_item = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }); - assert!( - active_item.is_none(), - "Expected no search panel to be active, but got: {active_item:?}" - ); +// let active_item = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }); +// assert!( +// active_item.is_none(), +// "Expected no search panel to be active, but got: {active_item:?}" +// ); - let one_file_entry = cx.update(|cx| { - workspace - .read(cx) - .project() - .read(cx) - .entry_for_path(&(worktree_id, "a/one.rs").into(), cx) - .expect("no entry for /a/one.rs file") - }); - assert!(one_file_entry.is_file()); - workspace.update(cx, |workspace, cx| { - ProjectSearchView::new_search_in_directory(workspace, &one_file_entry, cx) - }); - let active_search_entry = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }); - assert!( - active_search_entry.is_none(), - "Expected no search panel to be active for file entry" - ); +// let one_file_entry = cx.update(|cx| { +// workspace +// .read(cx) +// .project() +// .read(cx) +// .entry_for_path(&(worktree_id, "a/one.rs").into(), cx) +// .expect("no entry for /a/one.rs file") +// }); +// assert!(one_file_entry.is_file()); +// workspace.update(cx, |workspace, cx| { +// ProjectSearchView::new_search_in_directory(workspace, &one_file_entry, cx) +// }); +// let active_search_entry = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }); +// assert!( +// active_search_entry.is_none(), +// "Expected no search panel to be active for file entry" +// ); - let a_dir_entry = cx.update(|cx| { - workspace - .read(cx) - .project() - .read(cx) - .entry_for_path(&(worktree_id, "a").into(), cx) - .expect("no entry for /a/ directory") - }); - assert!(a_dir_entry.is_dir()); - workspace.update(cx, |workspace, cx| { - ProjectSearchView::new_search_in_directory(workspace, &a_dir_entry, cx) - }); +// let a_dir_entry = cx.update(|cx| { +// workspace +// .read(cx) +// .project() +// .read(cx) +// .entry_for_path(&(worktree_id, "a").into(), cx) +// .expect("no entry for /a/ directory") +// }); +// assert!(a_dir_entry.is_dir()); +// workspace.update(cx, |workspace, cx| { +// ProjectSearchView::new_search_in_directory(workspace, &a_dir_entry, cx) +// }); - let Some(search_view) = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - }) else { - panic!("Search view expected to appear after new search in directory event trigger") - }; - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert!( - search_view.query_editor.is_focused(cx), - "On new search in directory, focus should be moved into query editor" - ); - search_view.excluded_files_editor.update(cx, |editor, cx| { - assert!( - editor.display_text(cx).is_empty(), - "New search in directory should not have any excluded files" - ); - }); - search_view.included_files_editor.update(cx, |editor, cx| { - assert_eq!( - editor.display_text(cx), - a_dir_entry.path.to_str().unwrap(), - "New search in directory should have included dir entry path" - ); - }); - }); +// let Some(search_view) = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// }) else { +// panic!("Search view expected to appear after new search in directory event trigger") +// }; +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert!( +// search_view.query_editor.is_focused(cx), +// "On new search in directory, focus should be moved into query editor" +// ); +// search_view.excluded_files_editor.update(cx, |editor, cx| { +// assert!( +// editor.display_text(cx).is_empty(), +// "New search in directory should not have any excluded files" +// ); +// }); +// search_view.included_files_editor.update(cx, |editor, cx| { +// assert_eq!( +// editor.display_text(cx), +// a_dir_entry.path.to_str().unwrap(), +// "New search in directory should have included dir entry path" +// ); +// }); +// }); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("const", cx)); - search_view.search(cx); - }); - deterministic.run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert_eq!( - search_view - .results_editor - .update(cx, |editor, cx| editor.display_text(cx)), - "\n\nconst ONE: usize = 1;\n\n\nconst TWO: usize = one::ONE + one::ONE;", - "New search in directory should have a filter that matches a certain directory" - ); - }); - } +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("const", cx)); +// search_view.search(cx); +// }); +// deterministic.run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert_eq!( +// search_view +// .results_editor +// .update(cx, |editor, cx| editor.display_text(cx)), +// "\n\nconst ONE: usize = 1;\n\n\nconst TWO: usize = one::ONE + one::ONE;", +// "New search in directory should have a filter that matches a certain directory" +// ); +// }); +// } - #[gpui::test] - async fn test_search_query_history(cx: &mut TestAppContext) { - init_test(cx); +// #[gpui::test] +// async fn test_search_query_history(cx: &mut TestAppContext) { +// init_test(cx); - let fs = FakeFs::new(cx.background_executor()); - fs.insert_tree( - "/dir", - json!({ - "one.rs": "const ONE: usize = 1;", - "two.rs": "const TWO: usize = one::ONE + one::ONE;", - "three.rs": "const THREE: usize = one::ONE + two::TWO;", - "four.rs": "const FOUR: usize = one::ONE + three::THREE;", - }), - ) - .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; - let window = cx.add_window(|cx| Workspace::test_new(project, cx)); - let workspace = window.root(cx); - workspace.update(cx, |workspace, cx| { - ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) - }); +// let fs = FakeFs::new(cx.background_executor()); +// fs.insert_tree( +// "/dir", +// json!({ +// "one.rs": "const ONE: usize = 1;", +// "two.rs": "const TWO: usize = one::ONE + one::ONE;", +// "three.rs": "const THREE: usize = one::ONE + two::TWO;", +// "four.rs": "const FOUR: usize = one::ONE + three::THREE;", +// }), +// ) +// .await; +// let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; +// let window = cx.add_window(|cx| Workspace::test_new(project, cx)); +// let workspace = window.root(cx); +// workspace.update(cx, |workspace, cx| { +// ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) +// }); - let search_view = cx.read(|cx| { - workspace - .read(cx) - .active_pane() - .read(cx) - .active_item() - .and_then(|item| item.downcast::()) - .expect("Search view expected to appear after new search event trigger") - }); +// let search_view = cx.read(|cx| { +// workspace +// .read(cx) +// .active_pane() +// .read(cx) +// .active_item() +// .and_then(|item| item.downcast::()) +// .expect("Search view expected to appear after new search event trigger") +// }); - let search_bar = window.add_view(cx, |cx| { - let mut search_bar = ProjectSearchBar::new(); - search_bar.set_active_pane_item(Some(&search_view), cx); - // search_bar.show(cx); - search_bar - }); +// let search_bar = window.add_view(cx, |cx| { +// let mut search_bar = ProjectSearchBar::new(); +// search_bar.set_active_pane_item(Some(&search_view), cx); +// // search_bar.show(cx); +// search_bar +// }); - // Add 3 search items into the history + another unsubmitted one. - search_view.update(cx, |search_view, cx| { - search_view.search_options = SearchOptions::CASE_SENSITIVE; - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("ONE", cx)); - search_view.search(cx); - }); - cx.foreground().run_until_parked(); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); - search_view.search(cx); - }); - cx.foreground().run_until_parked(); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("THREE", cx)); - search_view.search(cx); - }); - cx.foreground().run_until_parked(); - search_view.update(cx, |search_view, cx| { - search_view.query_editor.update(cx, |query_editor, cx| { - query_editor.set_text("JUST_TEXT_INPUT", cx) - }); - }); - cx.foreground().run_until_parked(); +// // Add 3 search items into the history + another unsubmitted one. +// search_view.update(cx, |search_view, cx| { +// search_view.search_options = SearchOptions::CASE_SENSITIVE; +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("ONE", cx)); +// search_view.search(cx); +// }); +// cx.foreground().run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("TWO", cx)); +// search_view.search(cx); +// }); +// cx.foreground().run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("THREE", cx)); +// search_view.search(cx); +// }); +// cx.foreground().run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// search_view.query_editor.update(cx, |query_editor, cx| { +// query_editor.set_text("JUST_TEXT_INPUT", cx) +// }); +// }); +// cx.foreground().run_until_parked(); - // Ensure that the latest input with search settings is active. - search_view.update(cx, |search_view, cx| { - assert_eq!( - search_view.query_editor.read(cx).text(cx), - "JUST_TEXT_INPUT" - ); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // Ensure that the latest input with search settings is active. +// search_view.update(cx, |search_view, cx| { +// assert_eq!( +// search_view.query_editor.read(cx).text(cx), +// "JUST_TEXT_INPUT" +// ); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // Next history query after the latest should set the query to the empty string. - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), ""); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), ""); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // Next history query after the latest should set the query to the empty string. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), ""); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), ""); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // First previous query for empty current query should set the query to the latest submitted one. - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // First previous query for empty current query should set the query to the latest submitted one. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // Further previous items should go over the history in reverse order. - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // Further previous items should go over the history in reverse order. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // Previous items should never go behind the first history item. - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // Previous items should never go behind the first history item. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // Next items should go over the history in the original order. - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// // Next items should go over the history in the original order. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - search_view.update(cx, |search_view, cx| { - search_view - .query_editor - .update(cx, |query_editor, cx| query_editor.set_text("TWO_NEW", cx)); - search_view.search(cx); - }); - cx.foreground().run_until_parked(); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); +// search_view.update(cx, |search_view, cx| { +// search_view +// .query_editor +// .update(cx, |query_editor, cx| query_editor.set_text("TWO_NEW", cx)); +// search_view.search(cx); +// }); +// cx.foreground().run_until_parked(); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); - // New search input should add another entry to history and move the selection to the end of the history. - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.previous_history_query(&PreviousHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW"); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - search_bar.update(cx, |search_bar, cx| { - search_bar.next_history_query(&NextHistoryQuery, cx); - }); - search_view.update(cx, |search_view, cx| { - assert_eq!(search_view.query_editor.read(cx).text(cx), ""); - assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); - }); - } +// // New search input should add another entry to history and move the selection to the end of the history. +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.previous_history_query(&PreviousHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW"); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// search_bar.update(cx, |search_bar, cx| { +// search_bar.next_history_query(&NextHistoryQuery, cx); +// }); +// search_view.update(cx, |search_view, cx| { +// assert_eq!(search_view.query_editor.read(cx).text(cx), ""); +// assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE); +// }); +// } - pub fn init_test(cx: &mut TestAppContext) { - cx.foreground().forbid_parking(); - let fonts = cx.font_cache(); - let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default); - theme.search.match_background = Color::red(); +// pub fn init_test(cx: &mut TestAppContext) { +// cx.foreground().forbid_parking(); +// let fonts = cx.font_cache(); +// let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default); +// theme.search.match_background = Color::red(); - cx.update(|cx| { - cx.set_global(SettingsStore::test(cx)); - cx.set_global(ActiveSearches::default()); - settings::register::(cx); +// cx.update(|cx| { +// cx.set_global(SettingsStore::test(cx)); +// cx.set_global(ActiveSearches::default()); +// settings::register::(cx); - theme::init((), cx); - cx.update_global::(|store, _| { - let mut settings = store.get::(None).clone(); - settings.theme = Arc::new(theme); - store.override_global(settings) - }); +// theme::init((), cx); +// cx.update_global::(|store, _| { +// let mut settings = store.get::(None).clone(); +// settings.theme = Arc::new(theme); +// store.override_global(settings) +// }); - language::init(cx); - client::init_settings(cx); - editor::init(cx); - workspace::init_settings(cx); - Project::init_settings(cx); - super::init(cx); - }); - } -} +// language::init(cx); +// client::init_settings(cx); +// editor::init(cx); +// workspace::init_settings(cx); +// Project::init_settings(cx); +// super::init(cx); +// }); +// } +// }