Show regex query error under the search bar (#33638)

Closes #17223

Release Notes:

- Show regex parsing errors under the search bar for buffer and project
search.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Mikal Sande 2025-06-30 16:05:33 +02:00 committed by GitHub
parent 22ab4c53d1
commit 59e88ce82b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 9 deletions

View file

@ -101,7 +101,7 @@ pub struct BufferSearchBar {
search_options: SearchOptions, search_options: SearchOptions,
default_options: SearchOptions, default_options: SearchOptions,
configured_options: SearchOptions, configured_options: SearchOptions,
query_contains_error: bool, query_error: Option<String>,
dismissed: bool, dismissed: bool,
search_history: SearchHistory, search_history: SearchHistory,
search_history_cursor: SearchHistoryCursor, search_history_cursor: SearchHistoryCursor,
@ -217,7 +217,7 @@ impl Render for BufferSearchBar {
if in_replace { if in_replace {
key_context.add("in_replace"); key_context.add("in_replace");
} }
let editor_border = if self.query_contains_error { let editor_border = if self.query_error.is_some() {
Color::Error.color(cx) Color::Error.color(cx)
} else { } else {
cx.theme().colors().border cx.theme().colors().border
@ -469,6 +469,14 @@ impl Render for BufferSearchBar {
) )
}); });
let query_error_line = self.query_error.as_ref().map(|error| {
Label::new(error)
.size(LabelSize::Small)
.color(Color::Error)
.mt_neg_1()
.ml_2()
});
v_flex() v_flex()
.id("buffer_search") .id("buffer_search")
.gap_2() .gap_2()
@ -524,6 +532,7 @@ impl Render for BufferSearchBar {
.w_full() .w_full()
}, },
)) ))
.children(query_error_line)
.children(replace_line) .children(replace_line)
} }
} }
@ -728,7 +737,7 @@ impl BufferSearchBar {
configured_options: search_options, configured_options: search_options,
search_options, search_options,
pending_search: None, pending_search: None,
query_contains_error: false, query_error: None,
dismissed: true, dismissed: true,
search_history: SearchHistory::new( search_history: SearchHistory::new(
Some(MAX_BUFFER_SEARCH_HISTORY_SIZE), Some(MAX_BUFFER_SEARCH_HISTORY_SIZE),
@ -1230,7 +1239,7 @@ impl BufferSearchBar {
self.pending_search.take(); self.pending_search.take();
if let Some(active_searchable_item) = self.active_searchable_item.as_ref() { if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
self.query_contains_error = false; self.query_error = None;
if query.is_empty() { if query.is_empty() {
self.clear_active_searchable_item_matches(window, cx); self.clear_active_searchable_item_matches(window, cx);
let _ = done_tx.send(()); let _ = done_tx.send(());
@ -1255,8 +1264,8 @@ impl BufferSearchBar {
None, None,
) { ) {
Ok(query) => query.with_replacement(self.replacement(cx)), Ok(query) => query.with_replacement(self.replacement(cx)),
Err(_) => { Err(e) => {
self.query_contains_error = true; self.query_error = Some(e.to_string());
self.clear_active_searchable_item_matches(window, cx); self.clear_active_searchable_item_matches(window, cx);
cx.notify(); cx.notify();
return done_rx; return done_rx;
@ -1274,8 +1283,8 @@ impl BufferSearchBar {
None, None,
) { ) {
Ok(query) => query.with_replacement(self.replacement(cx)), Ok(query) => query.with_replacement(self.replacement(cx)),
Err(_) => { Err(e) => {
self.query_contains_error = true; self.query_error = Some(e.to_string());
self.clear_active_searchable_item_matches(window, cx); self.clear_active_searchable_item_matches(window, cx);
cx.notify(); cx.notify();
return done_rx; return done_rx;

View file

@ -208,6 +208,7 @@ pub struct ProjectSearchView {
included_opened_only: bool, included_opened_only: bool,
regex_language: Option<Arc<Language>>, regex_language: Option<Arc<Language>>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
query_error: Option<String>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -876,6 +877,7 @@ impl ProjectSearchView {
included_opened_only: false, included_opened_only: false,
regex_language: None, regex_language: None,
_subscriptions: subscriptions, _subscriptions: subscriptions,
query_error: None,
}; };
this.entity_changed(window, cx); this.entity_changed(window, cx);
this this
@ -1209,14 +1211,16 @@ impl ProjectSearchView {
if should_unmark_error { if should_unmark_error {
cx.notify(); cx.notify();
} }
self.query_error = None;
Some(query) Some(query)
} }
Err(_e) => { Err(e) => {
let should_mark_error = self.panels_with_errors.insert(InputPanel::Query); let should_mark_error = self.panels_with_errors.insert(InputPanel::Query);
if should_mark_error { if should_mark_error {
cx.notify(); cx.notify();
} }
self.query_error = Some(e.to_string());
None None
} }
@ -2291,6 +2295,14 @@ impl Render for ProjectSearchBar {
key_context.add("in_replace"); key_context.add("in_replace");
} }
let query_error_line = search.query_error.as_ref().map(|error| {
Label::new(error)
.size(LabelSize::Small)
.color(Color::Error)
.mt_neg_1()
.ml_2()
});
v_flex() v_flex()
.py(px(1.0)) .py(px(1.0))
.key_context(key_context) .key_context(key_context)
@ -2342,6 +2354,7 @@ impl Render for ProjectSearchBar {
.gap_2() .gap_2()
.w_full() .w_full()
.child(search_line) .child(search_line)
.children(query_error_line)
.children(replace_line) .children(replace_line)
.children(filter_line) .children(filter_line)
} }