Draft search include/exclude logic

This commit is contained in:
Kirill Bulatov 2023-05-07 22:50:54 +03:00 committed by Kirill Bulatov
parent 915154b047
commit b5abac6af6
5 changed files with 100 additions and 38 deletions

View File

@ -4208,16 +4208,21 @@ impl Project {
if matching_paths_tx.is_closed() { if matching_paths_tx.is_closed() {
break; break;
} }
let matches = if !query
abs_path.clear(); .file_matches(Some(&entry.path))
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()
{ {
query.detect(file).unwrap_or(false)
} else {
false 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 { if matches {
@ -4299,15 +4304,21 @@ impl Project {
let mut buffers_rx = buffers_rx.clone(); let mut buffers_rx = buffers_rx.clone();
scope.spawn(async move { scope.spawn(async move {
while let Some((buffer, snapshot)) = buffers_rx.next().await { while let Some((buffer, snapshot)) = buffers_rx.next().await {
let buffer_matches = query let buffer_matches = if query.file_matches(
.search(snapshot.as_rope()) snapshot.file().map(|file| file.path().as_ref()),
.await ) {
.iter() query
.map(|range| { .search(snapshot.as_rope())
snapshot.anchor_before(range.start) .await
..snapshot.anchor_after(range.end) .iter()
}) .map(|range| {
.collect::<Vec<_>>(); snapshot.anchor_before(range.start)
..snapshot.anchor_after(range.end)
})
.collect()
} else {
Vec::new()
};
if !buffer_matches.is_empty() { if !buffer_matches.is_empty() {
worker_matched_buffers worker_matched_buffers
.insert(buffer.clone(), buffer_matches); .insert(buffer.clone(), buffer_matches);

View File

@ -8,10 +8,11 @@ use smol::future::yield_now;
use std::{ use std::{
io::{BufRead, BufReader, Read}, io::{BufRead, BufReader, Read},
ops::Range, ops::Range,
path::Path,
sync::Arc, sync::Arc,
}; };
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum SearchQuery { pub enum SearchQuery {
Text { Text {
search: Arc<AhoCorasick<usize>>, search: Arc<AhoCorasick<usize>>,
@ -97,11 +98,13 @@ impl SearchQuery {
message message
.files_to_include .files_to_include
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
message message
.files_to_exclude .files_to_exclude
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
) )
@ -113,11 +116,13 @@ impl SearchQuery {
message message
.files_to_include .files_to_include
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
message message
.files_to_exclude .files_to_exclude
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
)) ))
@ -290,6 +295,7 @@ impl SearchQuery {
} => files_to_include, } => files_to_include,
} }
} }
pub fn files_to_exclude(&self) -> &[glob::Pattern] { pub fn files_to_exclude(&self) -> &[glob::Pattern] {
match self { match self {
Self::Text { Self::Text {
@ -300,4 +306,21 @@ impl SearchQuery {
} => files_to_exclude, } => 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(),
}
}
} }

View File

@ -555,23 +555,30 @@ 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> {
let text = self.query_editor.read(cx).text(cx); let text = self.query_editor.read(cx).text(cx);
let included_files = self let Ok(included_files) = self
.included_files_editor .included_files_editor
.read(cx) .read(cx)
.text(cx) .text(cx)
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>() .collect::<Result<_, _>>() else {
// TODO kb validation self.query_contains_error = true;
.unwrap_or_default(); cx.notify();
let excluded_files = self return None
};
let Ok(excluded_files) = self
.excluded_files_editor .excluded_files_editor
.read(cx) .read(cx)
.text(cx) .text(cx)
.split(',') .split(',')
.filter(|glob_str| !glob_str.trim().is_empty())
.map(|glob_str| glob::Pattern::new(glob_str)) .map(|glob_str| glob::Pattern::new(glob_str))
.collect::<Result<_, _>>() .collect::<Result<_, _>>() else {
.unwrap_or_default(); self.query_contains_error = true;
cx.notify();
return None
};
if self.regex { if self.regex {
match SearchQuery::regex( match SearchQuery::regex(
text, text,
@ -928,11 +935,11 @@ impl View for ProjectSearchBar {
let included_files_view = ChildView::new(&search.included_files_editor, cx) let included_files_view = ChildView::new(&search.included_files_editor, cx)
.aligned() .aligned()
.left() .left()
.flex(1., true); .flex(0.5, true);
let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx) let excluded_files_view = ChildView::new(&search.excluded_files_editor, cx)
.aligned() .aligned()
.left() .right()
.flex(1., true); .flex(0.5, true);
Flex::row() Flex::row()
.with_child( .with_child(
@ -983,25 +990,41 @@ impl View for ProjectSearchBar {
.with_style(theme.search.option_button_group) .with_style(theme.search.option_button_group)
.aligned(), .aligned(),
) )
// TODO kb better layout
.with_child( .with_child(
// TODO kb better layout
Flex::row() Flex::row()
.with_child( .with_child(
Label::new("Include files:", theme.search.match_index.text.clone()) Label::new(
.contained() "Include:",
.with_style(theme.search.match_index.container) theme.search.include_exclude_inputs.text.clone(),
.aligned(), )
.contained()
.with_style(theme.search.include_exclude_inputs.container)
.aligned(),
) )
.with_child(included_files_view) .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( .with_child(
Label::new("Exclude files:", theme.search.match_index.text.clone()) Label::new(
.contained() "Exclude:",
.with_style(theme.search.match_index.container) theme.search.include_exclude_inputs.text.clone(),
.aligned(), )
.contained()
.with_style(theme.search.include_exclude_inputs.container)
.aligned(),
) )
.with_child(excluded_files_view) .with_child(excluded_files_view)
.contained() .contained()
.with_style(editor_container) .with_style(theme.search.editor.input.container)
.aligned() .aligned()
.constrained() .constrained()
.with_min_width(theme.search.editor.min_width) .with_min_width(theme.search.editor.min_width)

View File

@ -319,6 +319,7 @@ pub struct Search {
pub editor: FindEditor, pub editor: FindEditor,
pub invalid_editor: ContainerStyle, pub invalid_editor: ContainerStyle,
pub option_button_group: ContainerStyle, pub option_button_group: ContainerStyle,
pub include_exclude_inputs: ContainedText,
pub option_button: Interactive<ContainedText>, pub option_button: Interactive<ContainedText>,
pub match_background: Color, pub match_background: Color,
pub match_index: ContainedText, pub match_index: ContainedText,

View File

@ -74,6 +74,10 @@ export default function search(colorScheme: ColorScheme) {
right: 12, right: 12,
}, },
}, },
includeExcludeInputs: {
...text(layer, "mono", "variant"),
padding: 6,
},
resultsStatus: { resultsStatus: {
...text(layer, "mono", "on"), ...text(layer, "mono", "on"),
size: 18, size: 18,