diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b3d432763e..a48111fdd2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4208,16 +4208,21 @@ impl Project { if matching_paths_tx.is_closed() { break; } - - abs_path.clear(); - abs_path.push(&snapshot.abs_path()); - abs_path.push(&entry.path); - let matches = if let Some(file) = - fs.open_sync(&abs_path).await.log_err() + let matches = if !query + .file_matches(Some(&entry.path)) { - query.detect(file).unwrap_or(false) - } else { false + } else { + abs_path.clear(); + abs_path.push(&snapshot.abs_path()); + abs_path.push(&entry.path); + if let Some(file) = + fs.open_sync(&abs_path).await.log_err() + { + query.detect(file).unwrap_or(false) + } else { + false + } }; if matches { @@ -4299,15 +4304,21 @@ impl Project { let mut buffers_rx = buffers_rx.clone(); scope.spawn(async move { while let Some((buffer, snapshot)) = buffers_rx.next().await { - let buffer_matches = query - .search(snapshot.as_rope()) - .await - .iter() - .map(|range| { - snapshot.anchor_before(range.start) - ..snapshot.anchor_after(range.end) - }) - .collect::>(); + let buffer_matches = if query.file_matches( + snapshot.file().map(|file| file.path().as_ref()), + ) { + query + .search(snapshot.as_rope()) + .await + .iter() + .map(|range| { + snapshot.anchor_before(range.start) + ..snapshot.anchor_after(range.end) + }) + .collect() + } else { + Vec::new() + }; if !buffer_matches.is_empty() { worker_matched_buffers .insert(buffer.clone(), buffer_matches); diff --git a/crates/project/src/search.rs b/crates/project/src/search.rs index 5d856a3b93..48146d13d8 100644 --- a/crates/project/src/search.rs +++ b/crates/project/src/search.rs @@ -8,10 +8,11 @@ use smol::future::yield_now; use std::{ io::{BufRead, BufReader, Read}, ops::Range, + path::Path, sync::Arc, }; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum SearchQuery { Text { search: Arc>, @@ -97,11 +98,13 @@ impl SearchQuery { message .files_to_include .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) .collect::>()?, message .files_to_exclude .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) .collect::>()?, ) @@ -113,11 +116,13 @@ impl SearchQuery { message .files_to_include .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) .collect::>()?, message .files_to_exclude .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) .collect::>()?, )) @@ -290,6 +295,7 @@ impl SearchQuery { } => files_to_include, } } + pub fn files_to_exclude(&self) -> &[glob::Pattern] { match self { Self::Text { @@ -300,4 +306,21 @@ impl SearchQuery { } => files_to_exclude, } } + + pub fn file_matches(&self, file_path: Option<&Path>) -> bool { + match file_path { + Some(file_path) => { + !self + .files_to_exclude() + .iter() + .any(|exclude_glob| exclude_glob.matches_path(file_path)) + && (self.files_to_include().is_empty() + || self + .files_to_include() + .iter() + .any(|include_glob| include_glob.matches_path(file_path))) + } + None => self.files_to_include().is_empty(), + } + } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index fa5ce31f05..99e5fbbcb1 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -555,23 +555,30 @@ impl ProjectSearchView { fn build_search_query(&mut self, cx: &mut ViewContext) -> Option { let text = self.query_editor.read(cx).text(cx); - let included_files = self + let Ok(included_files) = self .included_files_editor .read(cx) .text(cx) .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) - .collect::>() - // TODO kb validation - .unwrap_or_default(); - let excluded_files = self + .collect::>() else { + self.query_contains_error = true; + cx.notify(); + return None + }; + let Ok(excluded_files) = self .excluded_files_editor .read(cx) .text(cx) .split(',') + .filter(|glob_str| !glob_str.trim().is_empty()) .map(|glob_str| glob::Pattern::new(glob_str)) - .collect::>() - .unwrap_or_default(); + .collect::>() else { + self.query_contains_error = true; + cx.notify(); + return None + }; if self.regex { match SearchQuery::regex( text, @@ -928,11 +935,11 @@ impl View for ProjectSearchBar { let included_files_view = ChildView::new(&search.included_files_editor, cx) .aligned() .left() - .flex(1., true); + .flex(0.5, true); let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx) .aligned() - .left() - .flex(1., true); + .right() + .flex(0.5, true); Flex::row() .with_child( @@ -983,25 +990,41 @@ impl View for ProjectSearchBar { .with_style(theme.search.option_button_group) .aligned(), ) + // TODO kb better layout .with_child( - // TODO kb better layout Flex::row() .with_child( - Label::new("Include files:", theme.search.match_index.text.clone()) - .contained() - .with_style(theme.search.match_index.container) - .aligned(), + Label::new( + "Include:", + theme.search.include_exclude_inputs.text.clone(), + ) + .contained() + .with_style(theme.search.include_exclude_inputs.container) + .aligned(), ) .with_child(included_files_view) + .contained() + .with_style(theme.search.editor.input.container) + .aligned() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .flex(1., false), + ) + .with_child( + Flex::row() .with_child( - Label::new("Exclude files:", theme.search.match_index.text.clone()) - .contained() - .with_style(theme.search.match_index.container) - .aligned(), + Label::new( + "Exclude:", + theme.search.include_exclude_inputs.text.clone(), + ) + .contained() + .with_style(theme.search.include_exclude_inputs.container) + .aligned(), ) .with_child(excluded_files_view) .contained() - .with_style(editor_container) + .with_style(theme.search.editor.input.container) .aligned() .constrained() .with_min_width(theme.search.editor.min_width) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 0bd23a0b87..e8c56ac71a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -319,6 +319,7 @@ pub struct Search { pub editor: FindEditor, pub invalid_editor: ContainerStyle, pub option_button_group: ContainerStyle, + pub include_exclude_inputs: ContainedText, pub option_button: Interactive, pub match_background: Color, pub match_index: ContainedText, diff --git a/styles/src/styleTree/search.ts b/styles/src/styleTree/search.ts index 7a00b93278..22c136368f 100644 --- a/styles/src/styleTree/search.ts +++ b/styles/src/styleTree/search.ts @@ -74,6 +74,10 @@ export default function search(colorScheme: ColorScheme) { right: 12, }, }, + includeExcludeInputs: { + ...text(layer, "mono", "variant"), + padding: 6, + }, resultsStatus: { ...text(layer, "mono", "on"), size: 18,