Test the search inclusions/exclusions

This commit is contained in:
Kirill Bulatov 2023-05-09 23:33:55 +03:00 committed by Kirill Bulatov
parent 80fc1bc276
commit dfdf7e4866
4 changed files with 353 additions and 30 deletions

View file

@ -4208,11 +4208,9 @@ impl Project {
if matching_paths_tx.is_closed() { if matching_paths_tx.is_closed() {
break; break;
} }
let matches = if !query let matches = if query
.file_matches(Some(&entry.path)) .file_matches(Some(&entry.path))
{ {
false
} else {
abs_path.clear(); abs_path.clear();
abs_path.push(&snapshot.abs_path()); abs_path.push(&snapshot.abs_path());
abs_path.push(&entry.path); abs_path.push(&entry.path);
@ -4223,6 +4221,8 @@ impl Project {
} else { } else {
false false
} }
} else {
false
}; };
if matches { if matches {

View file

@ -3335,28 +3335,348 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
("four.rs".to_string(), vec![25..28, 36..39]) ("four.rs".to_string(), vec![25..28, 36..39])
]) ])
); );
}
async fn search(
project: &ModelHandle<Project>, #[gpui::test]
query: SearchQuery, async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
cx: &mut gpui::TestAppContext, let search_query = "file";
) -> Result<HashMap<String, Vec<Range<usize>>>> {
let results = project let fs = FakeFs::new(cx.background());
.update(cx, |project, cx| project.search(query, cx)) fs.insert_tree(
.await?; "/dir",
json!({
Ok(results "one.rs": r#"// Rust file one"#,
.into_iter() "one.ts": r#"// TypeScript file one"#,
.map(|(buffer, ranges)| { "two.rs": r#"// Rust file two"#,
buffer.read_with(cx, |buffer, _| { "two.ts": r#"// TypeScript file two"#,
let path = buffer.file().unwrap().path().to_string_lossy().to_string(); }),
let ranges = ranges )
.into_iter() .await;
.map(|range| range.to_offset(buffer)) let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
.collect::<Vec<_>>();
(path, ranges) assert!(
}) search(
}) &project,
.collect()) SearchQuery::text(
} search_query,
false,
true,
vec![glob::Pattern::new("*.odd").unwrap()],
Vec::new()
),
cx
)
.await
.unwrap()
.is_empty(),
"If no inclusions match, no files should be returned"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![glob::Pattern::new("*.rs").unwrap()],
Vec::new()
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.rs".to_string(), vec![8..12]),
("two.rs".to_string(), vec![8..12]),
]),
"Rust only search should give only Rust files"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap(),
],
Vec::new()
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.ts".to_string(), vec![14..18]),
("two.ts".to_string(), vec![14..18]),
]),
"TypeScript only search should give only TypeScript files, even if other inclusions don't match anything"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![
glob::Pattern::new("*.rs").unwrap(),
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap(),
],
Vec::new()
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.rs".to_string(), vec![8..12]),
("one.ts".to_string(), vec![14..18]),
("two.rs".to_string(), vec![8..12]),
("two.ts".to_string(), vec![14..18]),
]),
"Rust and typescript search should give both Rust and TypeScript files, even if other inclusions don't match anything"
);
}
#[gpui::test]
async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
let search_query = "file";
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/dir",
json!({
"one.rs": r#"// Rust file one"#,
"one.ts": r#"// TypeScript file one"#,
"two.rs": r#"// Rust file two"#,
"two.ts": r#"// TypeScript file two"#,
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
Vec::new(),
vec![glob::Pattern::new("*.odd").unwrap()],
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.rs".to_string(), vec![8..12]),
("one.ts".to_string(), vec![14..18]),
("two.rs".to_string(), vec![8..12]),
("two.ts".to_string(), vec![14..18]),
]),
"If no exclusions match, all files should be returned"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
Vec::new(),
vec![glob::Pattern::new("*.rs").unwrap()],
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.ts".to_string(), vec![14..18]),
("two.ts".to_string(), vec![14..18]),
]),
"Rust exclusion search should give only TypeScript files"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
Vec::new(),
vec![
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap(),
],
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.rs".to_string(), vec![8..12]),
("two.rs".to_string(), vec![8..12]),
]),
"TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything"
);
assert!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
Vec::new(),
vec![
glob::Pattern::new("*.rs").unwrap(),
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap(),
],
),
cx
)
.await
.unwrap().is_empty(),
"Rust and typescript exclusion should give no files, even if other exclusions don't match anything"
);
}
#[gpui::test]
async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) {
let search_query = "file";
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/dir",
json!({
"one.rs": r#"// Rust file one"#,
"one.ts": r#"// TypeScript file one"#,
"two.rs": r#"// Rust file two"#,
"two.ts": r#"// TypeScript file two"#,
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
assert!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![glob::Pattern::new("*.odd").unwrap()],
vec![glob::Pattern::new("*.odd").unwrap()],
),
cx
)
.await
.unwrap()
.is_empty(),
"If both no exclusions and inclusions match, exclusions should win and return nothing"
);
assert!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![glob::Pattern::new("*.ts").unwrap()],
vec![glob::Pattern::new("*.ts").unwrap()],
),
cx
)
.await
.unwrap()
.is_empty(),
"If both TypeScript exclusions and inclusions match, exclusions should win and return nothing files."
);
assert!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap()
],
vec![
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap()
],
),
cx
)
.await
.unwrap()
.is_empty(),
"Non-matching inclusions and exclusions should not change that."
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
vec![
glob::Pattern::new("*.ts").unwrap(),
glob::Pattern::new("*.odd").unwrap()
],
vec![
glob::Pattern::new("*.rs").unwrap(),
glob::Pattern::new("*.odd").unwrap()
],
),
cx
)
.await
.unwrap(),
HashMap::from_iter([
("one.ts".to_string(), vec![14..18]),
("two.ts".to_string(), vec![14..18]),
]),
"Non-intersecting TypeScript inclusions and Rust exclusions should return TypeScript files"
);
}
async fn search(
project: &ModelHandle<Project>,
query: SearchQuery,
cx: &mut gpui::TestAppContext,
) -> Result<HashMap<String, Vec<Range<usize>>>> {
let results = project
.update(cx, |project, cx| project.search(query, cx))
.await?;
Ok(results
.into_iter()
.map(|(buffer, ranges)| {
buffer.read_with(cx, |buffer, _| {
let path = buffer.file().unwrap().path().to_string_lossy().to_string();
let ranges = ranges
.into_iter()
.map(|range| range.to_offset(buffer))
.collect::<Vec<_>>();
(path, ranges)
})
})
.collect())
} }

View file

@ -428,7 +428,7 @@ impl ProjectSearchView {
editor.set_text(query_text, cx); editor.set_text(query_text, cx);
editor editor
}); });
// Subcribe to query_editor in order to reraise editor events for workspace item activation purposes // Subscribe to query_editor in order to reraise editor events for workspace item activation purposes
cx.subscribe(&query_editor, |_, _, event, cx| { cx.subscribe(&query_editor, |_, _, event, cx| {
cx.emit(ViewEvent::EditorEvent(event.clone())) cx.emit(ViewEvent::EditorEvent(event.clone()))
}) })
@ -462,7 +462,7 @@ impl ProjectSearchView {
editor editor
}); });
// Subcribe to include_files_editor in order to reraise editor events for workspace item activation purposes // Subscribe to include_files_editor in order to reraise editor events for workspace item activation purposes
cx.subscribe(&included_files_editor, |_, _, event, cx| { cx.subscribe(&included_files_editor, |_, _, event, cx| {
cx.emit(ViewEvent::EditorEvent(event.clone())) cx.emit(ViewEvent::EditorEvent(event.clone()))
}) })
@ -479,7 +479,7 @@ impl ProjectSearchView {
editor editor
}); });
// Subcribe to excluded_files_editor in order to reraise editor events for workspace item activation purposes // Subscribe to excluded_files_editor in order to reraise editor events for workspace item activation purposes
cx.subscribe(&excluded_files_editor, |_, _, event, cx| { cx.subscribe(&excluded_files_editor, |_, _, event, cx| {
cx.emit(ViewEvent::EditorEvent(event.clone())) cx.emit(ViewEvent::EditorEvent(event.clone()))
}) })

View file

@ -23,6 +23,9 @@ pub trait ToolbarItemView: View {
fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {} fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
/// Number of times toolbar's height will be repeated to get the effective height.
/// Useful when multiple rows one under each other are needed.
/// The rows have the same width and act as a whole when reacting to resizes and similar events.
fn row_count(&self) -> usize { fn row_count(&self) -> usize {
1 1
} }