diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index 02e664a109..671bcd68a7 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -181,7 +181,11 @@ impl Render for BufferSearchBar { if in_replace { key_context.add("in_replace"); } - + let editor_border = if self.query_contains_error { + Color::Error.color(cx) + } else { + cx.theme().colors().border + }; h_stack() .w_full() .gap_2() @@ -217,7 +221,7 @@ impl Render for BufferSearchBar { .py_1() .gap_2() .border_1() - .border_color(cx.theme().colors().border) + .border_color(editor_border) .rounded_lg() .child(IconElement::new(Icon::MagnifyingGlass)) .child(self.render_text_input(&self.query_editor, cx)) @@ -852,6 +856,7 @@ impl BufferSearchBar { Ok(query) => query.with_replacement(self.replacement(cx)), Err(_) => { self.query_contains_error = true; + self.active_match_index = None; cx.notify(); return done_rx; } @@ -868,6 +873,7 @@ impl BufferSearchBar { Ok(query) => query.with_replacement(self.replacement(cx)), Err(_) => { self.query_contains_error = true; + self.active_match_index = None; cx.notify(); return done_rx; } diff --git a/crates/search2/src/project_search.rs b/crates/search2/src/project_search.rs index efb60c414c..ab94466cf7 100644 --- a/crates/search2/src/project_search.rs +++ b/crates/search2/src/project_search.rs @@ -13,10 +13,10 @@ use editor::{ use editor::{EditorElement, EditorStyle}; use gpui::{ actions, div, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, EventEmitter, - FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement, IntoElement, KeyContext, - Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription, - Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, - WindowContext, + FocusHandle, FocusableView, FontStyle, FontWeight, Hsla, InteractiveElement, IntoElement, + KeyContext, Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, + Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView, + WhiteSpace, WindowContext, }; use menu::Confirm; use project::{ @@ -1007,33 +1007,46 @@ impl ProjectSearchView { } fn build_search_query(&mut self, cx: &mut ViewContext) -> Option { + // Do not bail early in this function, as we want to fill out `self.panels_with_errors`. let text = self.query_editor.read(cx).text(cx); let included_files = match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) { Ok(included_files) => { - self.panels_with_errors.remove(&InputPanel::Include); + let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Include); + if should_unmark_error { + cx.notify(); + } included_files } Err(_e) => { - self.panels_with_errors.insert(InputPanel::Include); - cx.notify(); - return None; + let should_mark_error = self.panels_with_errors.insert(InputPanel::Include); + if should_mark_error { + cx.notify(); + } + vec![] } }; let excluded_files = match Self::parse_path_matches(&self.excluded_files_editor.read(cx).text(cx)) { Ok(excluded_files) => { - self.panels_with_errors.remove(&InputPanel::Exclude); + let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Exclude); + if should_unmark_error { + cx.notify(); + } + excluded_files } Err(_e) => { - self.panels_with_errors.insert(InputPanel::Exclude); - cx.notify(); - return None; + let should_mark_error = self.panels_with_errors.insert(InputPanel::Exclude); + if should_mark_error { + cx.notify(); + } + vec![] } }; + let current_mode = self.current_mode; - match current_mode { + let query = match current_mode { SearchMode::Regex => { match SearchQuery::regex( text, @@ -1044,12 +1057,20 @@ impl ProjectSearchView { excluded_files, ) { Ok(query) => { - self.panels_with_errors.remove(&InputPanel::Query); + let should_unmark_error = + self.panels_with_errors.remove(&InputPanel::Query); + if should_unmark_error { + cx.notify(); + } + Some(query) } Err(_e) => { - self.panels_with_errors.insert(InputPanel::Query); - cx.notify(); + let should_mark_error = self.panels_with_errors.insert(InputPanel::Query); + if should_mark_error { + cx.notify(); + } + None } } @@ -1063,16 +1084,27 @@ impl ProjectSearchView { excluded_files, ) { Ok(query) => { - self.panels_with_errors.remove(&InputPanel::Query); + let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Query); + if should_unmark_error { + cx.notify(); + } + Some(query) } Err(_e) => { - self.panels_with_errors.insert(InputPanel::Query); - cx.notify(); + let should_mark_error = self.panels_with_errors.insert(InputPanel::Query); + if should_mark_error { + cx.notify(); + } + None } }, + }; + if !self.panels_with_errors.is_empty() { + return None; } + query } fn parse_path_matches(text: &str) -> anyhow::Result> { @@ -1185,6 +1217,13 @@ impl ProjectSearchView { SearchMode::Semantic => "\nSimply explain the code you are looking to find. ex. 'prompt user for permissions to index their project'".into() } } + fn border_color_for(&self, panel: InputPanel, cx: &WindowContext) -> Hsla { + if self.panels_with_errors.contains(&panel) { + Color::Error.color(cx) + } else { + cx.theme().colors().border + } + } } impl Default for ProjectSearchBar { @@ -1507,6 +1546,7 @@ impl Render for ProjectSearchBar { } let search = search.read(cx); let semantic_is_available = SemanticIndex::enabled(cx); + let query_column = v_stack().child( h_stack() .min_w(rems(512. / 16.)) @@ -1515,7 +1555,7 @@ impl Render for ProjectSearchBar { .gap_2() .bg(cx.theme().colors().editor_background) .border_1() - .border_color(cx.theme().colors().border) + .border_color(search.border_color_for(InputPanel::Query, cx)) .rounded_lg() .on_action(cx.listener(|this, action, cx| this.confirm(action, cx))) .on_action(cx.listener(|this, action, cx| this.previous_history_query(action, cx))) @@ -1789,7 +1829,7 @@ impl Render for ProjectSearchBar { .px_2() .py_1() .border_1() - .border_color(cx.theme().colors().border) + .border_color(search.border_color_for(InputPanel::Include, cx)) .rounded_lg() .child(self.render_text_input(&search.included_files_editor, cx)) .when(search.current_mode != SearchMode::Semantic, |this| { @@ -1815,7 +1855,7 @@ impl Render for ProjectSearchBar { .px_2() .py_1() .border_1() - .border_color(cx.theme().colors().border) + .border_color(search.border_color_for(InputPanel::Exclude, cx)) .rounded_lg() .child(self.render_text_input(&search.excluded_files_editor, cx)), ),