search: Add "Error" borders for query editors with malformed content. (#3824)
This commit also changes the way search queries are built (we do not bail early anymore if include/exclude editor queries are malformed) to propagate error status of the panel. Release Notes: - N/A
This commit is contained in:
parent
789db30c42
commit
1c20a7fc37
2 changed files with 70 additions and 24 deletions
|
@ -181,7 +181,11 @@ 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 {
|
||||||
|
Color::Error.color(cx)
|
||||||
|
} else {
|
||||||
|
cx.theme().colors().border
|
||||||
|
};
|
||||||
h_stack()
|
h_stack()
|
||||||
.w_full()
|
.w_full()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
|
@ -217,7 +221,7 @@ impl Render for BufferSearchBar {
|
||||||
.py_1()
|
.py_1()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(editor_border)
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.child(IconElement::new(Icon::MagnifyingGlass))
|
.child(IconElement::new(Icon::MagnifyingGlass))
|
||||||
.child(self.render_text_input(&self.query_editor, cx))
|
.child(self.render_text_input(&self.query_editor, cx))
|
||||||
|
@ -852,6 +856,7 @@ impl BufferSearchBar {
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.query_contains_error = true;
|
self.query_contains_error = true;
|
||||||
|
self.active_match_index = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return done_rx;
|
return done_rx;
|
||||||
}
|
}
|
||||||
|
@ -868,6 +873,7 @@ impl BufferSearchBar {
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.query_contains_error = true;
|
self.query_contains_error = true;
|
||||||
|
self.active_match_index = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return done_rx;
|
return done_rx;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@ use editor::{
|
||||||
use editor::{EditorElement, EditorStyle};
|
use editor::{EditorElement, EditorStyle};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, EventEmitter,
|
actions, div, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, EventEmitter,
|
||||||
FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement, IntoElement, KeyContext,
|
FocusHandle, FocusableView, FontStyle, FontWeight, Hsla, InteractiveElement, IntoElement,
|
||||||
Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled, Subscription,
|
KeyContext, Model, ModelContext, ParentElement, PromptLevel, Render, SharedString, Styled,
|
||||||
Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace,
|
Subscription, Task, TextStyle, View, ViewContext, VisualContext, WeakModel, WeakView,
|
||||||
WindowContext,
|
WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use menu::Confirm;
|
use menu::Confirm;
|
||||||
use project::{
|
use project::{
|
||||||
|
@ -1007,33 +1007,46 @@ impl ProjectSearchView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
|
fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
|
||||||
|
// 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 text = self.query_editor.read(cx).text(cx);
|
||||||
let included_files =
|
let included_files =
|
||||||
match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) {
|
match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) {
|
||||||
Ok(included_files) => {
|
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
|
included_files
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
self.panels_with_errors.insert(InputPanel::Include);
|
let should_mark_error = self.panels_with_errors.insert(InputPanel::Include);
|
||||||
|
if should_mark_error {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return None;
|
}
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let excluded_files =
|
let excluded_files =
|
||||||
match Self::parse_path_matches(&self.excluded_files_editor.read(cx).text(cx)) {
|
match Self::parse_path_matches(&self.excluded_files_editor.read(cx).text(cx)) {
|
||||||
Ok(excluded_files) => {
|
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
|
excluded_files
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
self.panels_with_errors.insert(InputPanel::Exclude);
|
let should_mark_error = self.panels_with_errors.insert(InputPanel::Exclude);
|
||||||
|
if should_mark_error {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
return None;
|
}
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_mode = self.current_mode;
|
let current_mode = self.current_mode;
|
||||||
match current_mode {
|
let query = match current_mode {
|
||||||
SearchMode::Regex => {
|
SearchMode::Regex => {
|
||||||
match SearchQuery::regex(
|
match SearchQuery::regex(
|
||||||
text,
|
text,
|
||||||
|
@ -1044,12 +1057,20 @@ impl ProjectSearchView {
|
||||||
excluded_files,
|
excluded_files,
|
||||||
) {
|
) {
|
||||||
Ok(query) => {
|
Ok(query) => {
|
||||||
|
let should_unmark_error =
|
||||||
self.panels_with_errors.remove(&InputPanel::Query);
|
self.panels_with_errors.remove(&InputPanel::Query);
|
||||||
|
if should_unmark_error {
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
Some(query)
|
Some(query)
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
self.panels_with_errors.insert(InputPanel::Query);
|
let should_mark_error = self.panels_with_errors.insert(InputPanel::Query);
|
||||||
|
if should_mark_error {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1063,16 +1084,27 @@ impl ProjectSearchView {
|
||||||
excluded_files,
|
excluded_files,
|
||||||
) {
|
) {
|
||||||
Ok(query) => {
|
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)
|
Some(query)
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
self.panels_with_errors.insert(InputPanel::Query);
|
let should_mark_error = self.panels_with_errors.insert(InputPanel::Query);
|
||||||
|
if should_mark_error {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
if !self.panels_with_errors.is_empty() {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
query
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_path_matches(text: &str) -> anyhow::Result<Vec<PathMatcher>> {
|
fn parse_path_matches(text: &str) -> anyhow::Result<Vec<PathMatcher>> {
|
||||||
|
@ -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()
|
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 {
|
impl Default for ProjectSearchBar {
|
||||||
|
@ -1507,6 +1546,7 @@ impl Render for ProjectSearchBar {
|
||||||
}
|
}
|
||||||
let search = search.read(cx);
|
let search = search.read(cx);
|
||||||
let semantic_is_available = SemanticIndex::enabled(cx);
|
let semantic_is_available = SemanticIndex::enabled(cx);
|
||||||
|
|
||||||
let query_column = v_stack().child(
|
let query_column = v_stack().child(
|
||||||
h_stack()
|
h_stack()
|
||||||
.min_w(rems(512. / 16.))
|
.min_w(rems(512. / 16.))
|
||||||
|
@ -1515,7 +1555,7 @@ impl Render for ProjectSearchBar {
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(search.border_color_for(InputPanel::Query, cx))
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.on_action(cx.listener(|this, action, cx| this.confirm(action, cx)))
|
.on_action(cx.listener(|this, action, cx| this.confirm(action, cx)))
|
||||||
.on_action(cx.listener(|this, action, cx| this.previous_history_query(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()
|
.px_2()
|
||||||
.py_1()
|
.py_1()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(search.border_color_for(InputPanel::Include, cx))
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.child(self.render_text_input(&search.included_files_editor, cx))
|
.child(self.render_text_input(&search.included_files_editor, cx))
|
||||||
.when(search.current_mode != SearchMode::Semantic, |this| {
|
.when(search.current_mode != SearchMode::Semantic, |this| {
|
||||||
|
@ -1815,7 +1855,7 @@ impl Render for ProjectSearchBar {
|
||||||
.px_2()
|
.px_2()
|
||||||
.py_1()
|
.py_1()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(search.border_color_for(InputPanel::Exclude, cx))
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.child(self.render_text_input(&search.excluded_files_editor, cx)),
|
.child(self.render_text_input(&search.excluded_files_editor, cx)),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue