Show path matches inside history items matching the query

This commit is contained in:
Kirill Bulatov 2023-09-28 09:49:25 -07:00
parent 1b5ff68c43
commit d30385f07c

View file

@ -1,4 +1,4 @@
use collections::{HashMap, HashSet}; use collections::HashMap;
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor}; use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{ use gpui::{
@ -35,13 +35,13 @@ pub struct FileFinderDelegate {
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Matches { struct Matches {
history: Vec<FoundPath>, history: Vec<(FoundPath, Option<PathMatch>)>,
search: Vec<PathMatch>, search: Vec<PathMatch>,
} }
#[derive(Debug)] #[derive(Debug)]
enum Match<'a> { enum Match<'a> {
History(&'a FoundPath), History(&'a FoundPath, Option<&'a PathMatch>),
Search(&'a PathMatch), Search(&'a PathMatch),
} }
@ -52,7 +52,9 @@ impl Matches {
fn get(&self, index: usize) -> Option<Match<'_>> { fn get(&self, index: usize) -> Option<Match<'_>> {
if index < self.history.len() { if index < self.history.len() {
self.history.get(index).map(Match::History) self.history
.get(index)
.map(|(path, path_match)| Match::History(path, path_match.as_ref()))
} else { } else {
self.search self.search
.get(index - self.history.len()) .get(index - self.history.len())
@ -68,16 +70,25 @@ impl Matches {
extend_old_matches: bool, extend_old_matches: bool,
) { ) {
let matching_history_paths = matching_history_item_paths(history_items, query); let matching_history_paths = matching_history_item_paths(history_items, query);
new_search_matches.retain(|path_match| !matching_history_paths.contains(&path_match.path)); new_search_matches
.retain(|path_match| !matching_history_paths.contains_key(&path_match.path));
let history_items_to_show = history_items let history_items_to_show = history_items
.iter() .iter()
.filter(|history_item| matching_history_paths.contains(&history_item.project.path)) .filter_map(|history_item| {
.cloned() Some((
history_item.clone(),
Some(
matching_history_paths
.get(&history_item.project.path)?
.clone(),
),
))
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.history = history_items_to_show; self.history = history_items_to_show;
if extend_old_matches { if extend_old_matches {
self.search self.search
.retain(|path_match| !matching_history_paths.contains(&path_match.path)); .retain(|path_match| !matching_history_paths.contains_key(&path_match.path));
util::extend_sorted( util::extend_sorted(
&mut self.search, &mut self.search,
new_search_matches.into_iter(), new_search_matches.into_iter(),
@ -93,7 +104,7 @@ impl Matches {
fn matching_history_item_paths( fn matching_history_item_paths(
history_items: &Vec<FoundPath>, history_items: &Vec<FoundPath>,
query: &PathLikeWithPosition<FileSearchQuery>, query: &PathLikeWithPosition<FileSearchQuery>,
) -> HashSet<Arc<Path>> { ) -> HashMap<Arc<Path>, PathMatch> {
let history_items_by_worktrees = history_items let history_items_by_worktrees = history_items
.iter() .iter()
.map(|found_path| { .map(|found_path| {
@ -114,7 +125,7 @@ fn matching_history_item_paths(
candidates candidates
}, },
); );
let mut matching_history_paths = HashSet::default(); let mut matching_history_paths = HashMap::default();
for (worktree, candidates) in history_items_by_worktrees { for (worktree, candidates) in history_items_by_worktrees {
let max_results = candidates.len() + 1; let max_results = candidates.len() + 1;
matching_history_paths.extend( matching_history_paths.extend(
@ -126,7 +137,7 @@ fn matching_history_item_paths(
max_results, max_results,
) )
.into_iter() .into_iter()
.map(|path_match| path_match.path), .map(|path_match| (Arc::clone(&path_match.path), path_match)),
); );
} }
matching_history_paths matching_history_paths
@ -358,7 +369,7 @@ impl FileFinderDelegate {
ix: usize, ix: usize,
) -> (String, Vec<usize>, String, Vec<usize>) { ) -> (String, Vec<usize>, String, Vec<usize>) {
let (file_name, file_name_positions, full_path, full_path_positions) = match path_match { let (file_name, file_name_positions, full_path, full_path_positions) = match path_match {
Match::History(found_path) => { Match::History(found_path, found_path_match) => {
let worktree_id = found_path.project.worktree_id; let worktree_id = found_path.project.worktree_id;
let project_relative_path = &found_path.project.path; let project_relative_path = &found_path.project.path;
let has_worktree = self let has_worktree = self
@ -390,14 +401,22 @@ impl FileFinderDelegate {
path = Arc::from(absolute_path.as_path()); path = Arc::from(absolute_path.as_path());
} }
} }
self.labels_for_path_match(&PathMatch {
let mut path_match = PathMatch {
score: ix as f64, score: ix as f64,
positions: Vec::new(), positions: Vec::new(),
worktree_id: worktree_id.to_usize(), worktree_id: worktree_id.to_usize(),
path, path,
path_prefix: "".into(), path_prefix: "".into(),
distance_to_relative_ancestor: usize::MAX, distance_to_relative_ancestor: usize::MAX,
}) };
if let Some(found_path_match) = found_path_match {
path_match
.positions
.extend(found_path_match.positions.iter())
}
self.labels_for_path_match(&path_match)
} }
Match::Search(path_match) => self.labels_for_path_match(path_match), Match::Search(path_match) => self.labels_for_path_match(path_match),
}; };
@ -494,6 +513,7 @@ impl PickerDelegate for FileFinderDelegate {
.is_some()) .is_some())
}) })
.cloned() .cloned()
.map(|p| (p, None))
.collect(), .collect(),
search: Vec::new(), search: Vec::new(),
}; };
@ -528,7 +548,7 @@ impl PickerDelegate for FileFinderDelegate {
} }
}; };
match m { match m {
Match::History(history_match) => { Match::History(history_match, _) => {
let worktree_id = history_match.project.worktree_id; let worktree_id = history_match.project.worktree_id;
if workspace if workspace
.project() .project()
@ -1723,7 +1743,9 @@ mod tests {
finder.read_with(cx, |finder, _| { finder.read_with(cx, |finder, _| {
let delegate = finder.delegate(); let delegate = finder.delegate();
assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query}, it should be present and others should be filtered out"); assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query}, it should be present and others should be filtered out");
assert_eq!(delegate.matches.history.first().unwrap(), &FoundPath::new( let history_match = delegate.matches.history.first().unwrap();
assert!(history_match.1.is_some(), "Should have path matches for history items after querying");
assert_eq!(history_match.0, FoundPath::new(
ProjectPath { ProjectPath {
worktree_id, worktree_id,
path: Arc::from(Path::new("test/first.rs")), path: Arc::from(Path::new("test/first.rs")),
@ -1767,7 +1789,9 @@ mod tests {
finder.read_with(cx, |finder, _| { finder.read_with(cx, |finder, _| {
let delegate = finder.delegate(); let delegate = finder.delegate();
assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query_again}, it should be present and others should be filtered out, even after non-matching query"); assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query_again}, it should be present and others should be filtered out, even after non-matching query");
assert_eq!(delegate.matches.history.first().unwrap(), &FoundPath::new( let history_match = delegate.matches.history.first().unwrap();
assert!(history_match.1.is_some(), "Should have path matches for history items after querying");
assert_eq!(history_match.0, FoundPath::new(
ProjectPath { ProjectPath {
worktree_id, worktree_id,
path: Arc::from(Path::new("test/first.rs")), path: Arc::from(Path::new("test/first.rs")),