This commit is contained in:
Coenen Benjamin 2025-08-27 00:39:17 +08:00 committed by GitHub
commit aa72b744b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 149 additions and 85 deletions

View file

@ -327,10 +327,10 @@ impl FileFinder {
{ {
let path = match &m { let path = match &m {
Match::History { path, .. } => { Match::History { path, .. } => {
let worktree_id = path.project.worktree_id; let worktree_id = path.worktree_id();
ProjectPath { ProjectPath {
worktree_id, worktree_id,
path: Arc::clone(&path.project.path), path: Arc::clone(&path.path()),
} }
} }
Match::Search(m) => ProjectPath { Match::Search(m) => ProjectPath {
@ -467,7 +467,7 @@ enum Match {
impl Match { impl Match {
fn relative_path(&self) -> Option<&Arc<Path>> { fn relative_path(&self) -> Option<&Arc<Path>> {
match self { match self {
Match::History { path, .. } => Some(&path.project.path), Match::History { path, .. } => Some(path.path()),
Match::Search(panel_match) => Some(&panel_match.0.path), Match::Search(panel_match) => Some(&panel_match.0.path),
Match::CreateNew(_) => None, Match::CreateNew(_) => None,
} }
@ -475,14 +475,20 @@ impl Match {
fn abs_path(&self, project: &Entity<Project>, cx: &App) -> Option<PathBuf> { fn abs_path(&self, project: &Entity<Project>, cx: &App) -> Option<PathBuf> {
match self { match self {
Match::History { path, .. } => path.absolute.clone().or_else(|| { Match::History { path, .. } => match path {
project FoundPath::Project {
.read(cx) abs,
.worktree_for_id(path.project.worktree_id, cx)? path: project_path,
.read(cx) } => abs.clone().or_else(|| {
.absolutize(&path.project.path) project
.ok() .read(cx)
}), .worktree_for_id(path.worktree_id(), cx)?
.read(cx)
.absolutize(&project_path.path)
.ok()
}),
FoundPath::Abs { abs, .. } => abs.to_path_buf().into(),
},
Match::Search(ProjectPanelOrdMatch(path_match)) => project Match::Search(ProjectPanelOrdMatch(path_match)) => project
.read(cx) .read(cx)
.worktree_for_id(WorktreeId::from_usize(path_match.worktree_id), cx)? .worktree_for_id(WorktreeId::from_usize(path_match.worktree_id), cx)?
@ -515,6 +521,8 @@ impl Matches {
&self, &self,
entry: &Match, entry: &Match,
currently_opened: Option<&FoundPath>, currently_opened: Option<&FoundPath>,
project: &Entity<Project>,
cx: &App,
) -> Result<usize, usize> { ) -> Result<usize, usize> {
if let Match::History { if let Match::History {
path, path,
@ -525,13 +533,24 @@ impl Matches {
// since we call `position` only if matches set changed, but the query has not changed. // since we call `position` only if matches set changed, but the query has not changed.
// And History entries do not have panel_match if query is empty, so there's no // And History entries do not have panel_match if query is empty, so there's no
// reason for the matches set to change. // reason for the matches set to change.
self.matches match path {
.iter() FoundPath::Project { path, .. } => self
.position(|m| match m.relative_path() { .matches
Some(p) => path.project.path == *p, .iter()
None => false, .position(|m| match m.relative_path() {
}) Some(p) => path.path == *p,
.ok_or(0) None => false,
})
.ok_or(0),
FoundPath::Abs { abs, .. } => self
.matches
.iter()
.position(|m| match m.abs_path(project, cx) {
Some(p) => abs.as_ref() == &p,
None => false,
})
.ok_or(0),
}
} else { } else {
self.matches.binary_search_by(|m| { self.matches.binary_search_by(|m| {
// `reverse()` since if cmp_matches(a, b) == Ordering::Greater, then a is better than b. // `reverse()` since if cmp_matches(a, b) == Ordering::Greater, then a is better than b.
@ -548,6 +567,8 @@ impl Matches {
query: Option<&FileSearchQuery>, query: Option<&FileSearchQuery>,
new_search_matches: impl Iterator<Item = ProjectPanelOrdMatch>, new_search_matches: impl Iterator<Item = ProjectPanelOrdMatch>,
extend_old_matches: bool, extend_old_matches: bool,
project: &Entity<Project>,
cx: &App,
) { ) {
let Some(query) = query else { let Some(query) = query else {
// assuming that if there's no query, then there's no search matches. // assuming that if there's no query, then there's no search matches.
@ -588,7 +609,7 @@ impl Matches {
.into_values() .into_values()
.chain(new_search_matches.into_iter()) .chain(new_search_matches.into_iter())
{ {
match self.position(&new_match, currently_opened) { match self.position(&new_match, currently_opened, project, cx) {
Ok(_duplicate) => continue, Ok(_duplicate) => continue,
Err(i) => { Err(i) => {
self.matches.insert(i, new_match); self.matches.insert(i, new_match);
@ -703,24 +724,31 @@ fn matching_history_items<'a>(
.into_iter() .into_iter()
.chain(currently_opened) .chain(currently_opened)
.filter_map(|found_path| { .filter_map(|found_path| {
let path = match found_path {
FoundPath::Project { path, .. } => &path.path,
FoundPath::Abs { abs, .. } => abs.as_ref(),
};
let candidate = PathMatchCandidate { let candidate = PathMatchCandidate {
is_dir: false, // You can't open directories as project items is_dir: false, // You can't open directories as project items
path: &found_path.project.path, path,
// Only match history items names, otherwise their paths may match too many queries, producing false positives. // Only match history items names, otherwise their paths may match too many queries, producing false positives.
// E.g. `foo` would match both `something/foo/bar.rs` and `something/foo/foo.rs` and if the former is a history item, // E.g. `foo` would match both `something/foo/bar.rs` and `something/foo/foo.rs` and if the former is a history item,
// it would be shown first always, despite the latter being a better match. // it would be shown first always, despite the latter being a better match.
char_bag: CharBag::from_iter( char_bag: CharBag::from_iter(
found_path path.file_name()?.to_string_lossy().to_lowercase().chars(),
.project
.path
.file_name()?
.to_string_lossy()
.to_lowercase()
.chars(),
), ),
}; };
candidates_paths.insert(&found_path.project, found_path);
Some((found_path.project.worktree_id, candidate)) match found_path {
FoundPath::Project { path, .. } => {
candidates_paths.insert(path, found_path);
Some((path.worktree_id, candidate))
}
FoundPath::Abs { .. } => {
// TODO: include absolute found path to fix https://github.com/zed-industries/zed/issues/24670
None
}
}
}) })
.fold( .fold(
HashMap::default(), HashMap::default(),
@ -766,14 +794,49 @@ fn matching_history_items<'a>(
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct FoundPath { enum FoundPath {
project: ProjectPath, Project {
absolute: Option<PathBuf>, path: ProjectPath,
abs: Option<PathBuf>,
},
Abs {
abs: Arc<Path>,
worktree_id: WorktreeId,
},
} }
impl FoundPath { impl FoundPath {
fn new(project: ProjectPath, absolute: Option<PathBuf>) -> Self { fn new(project_path: ProjectPath, absolute: Option<PathBuf>) -> Self {
Self { project, absolute } if project_path
.path
.to_str()
.map(|p| p.is_empty())
.unwrap_or(false)
{
Self::Abs {
abs: absolute.unwrap_or_default().into(),
worktree_id: project_path.worktree_id,
}
} else {
Self::Project {
path: project_path,
abs: absolute,
}
}
}
fn worktree_id(&self) -> WorktreeId {
match self {
FoundPath::Project { path, .. } => path.worktree_id,
FoundPath::Abs { worktree_id, .. } => *worktree_id,
}
}
fn path(&self) -> &Arc<Path> {
match self {
FoundPath::Project { path, .. } => &path.path,
FoundPath::Abs { abs, .. } => &abs,
}
} }
} }
@ -863,7 +926,10 @@ impl FileFinderDelegate {
let relative_to = self let relative_to = self
.currently_opened_path .currently_opened_path
.as_ref() .as_ref()
.map(|found_path| Arc::clone(&found_path.project.path)); .map(|found_path| match found_path {
FoundPath::Project { path, .. } => path.path.clone(),
FoundPath::Abs { abs, .. } => abs.clone(),
});
let worktrees = self let worktrees = self
.project .project
.read(cx) .read(cx)
@ -942,6 +1008,8 @@ impl FileFinderDelegate {
Some(&query), Some(&query),
matches.into_iter(), matches.into_iter(),
extend_old_matches, extend_old_matches,
&self.project,
cx,
); );
let filename = &query.raw_query; let filename = &query.raw_query;
@ -970,9 +1038,16 @@ impl FileFinderDelegate {
} }
} }
if let Some(FoundPath { ref project, .. }) = self.currently_opened_path { if let Some(found_path) = &self.currently_opened_path {
let worktree_id = project.worktree_id; match found_path {
expect_worktree = self.project.read(cx).worktree_for_id(worktree_id, cx); FoundPath::Project { path, .. } => {
let worktree_id = path.worktree_id;
expect_worktree = self.project.read(cx).worktree_for_id(worktree_id, cx);
}
FoundPath::Abs { worktree_id, .. } => {
expect_worktree = self.project.read(cx).worktree_for_id(*worktree_id, cx);
}
}
} }
if let Some(worktree) = expect_worktree { if let Some(worktree) = expect_worktree {
@ -988,14 +1063,13 @@ impl FileFinderDelegate {
} }
} }
self.selected_index = selected_match.map_or_else( self.selected_index = match selected_match {
|| self.calculate_selected_index(cx), Some(m) => self
|m| { .matches
self.matches .position(&m, self.currently_opened_path.as_ref(), &self.project, cx)
.position(&m, self.currently_opened_path.as_ref()) .unwrap_or(0),
.unwrap_or(0) None => self.calculate_selected_index(cx),
}, };
);
self.latest_search_query = Some(query); self.latest_search_query = Some(query);
self.latest_search_did_cancel = did_cancel; self.latest_search_did_cancel = did_cancel;
@ -1017,36 +1091,30 @@ impl FileFinderDelegate {
path: entry_path, path: entry_path,
panel_match, panel_match,
} => { } => {
let worktree_id = entry_path.project.worktree_id; let worktree_id = entry_path.worktree_id();
let project_relative_path = &entry_path.project.path; let project_path = entry_path.path().clone();
let has_worktree = self let has_worktree = self
.project .project
.read(cx) .read(cx)
.worktree_for_id(worktree_id, cx) .worktree_for_id(worktree_id, cx)
.is_some(); .is_some();
if let Some(absolute_path) = if let FoundPath::Abs { abs, .. } = entry_path
entry_path.absolute.as_ref().filter(|_| !has_worktree) && !has_worktree
{ {
( (
absolute_path abs.file_name()
.file_name()
.map_or_else( .map_or_else(
|| project_relative_path.to_string_lossy(), || project_path.to_string_lossy(),
|file_name| file_name.to_string_lossy(), |file_name| file_name.to_string_lossy(),
) )
.to_string(), .to_string(),
Vec::new(), Vec::new(),
absolute_path.to_string_lossy().to_string(), abs.to_string_lossy().to_string(),
Vec::new(), Vec::new(),
) )
} else { } else {
let mut path = Arc::clone(project_relative_path); let path = Arc::clone(&project_path);
if project_relative_path.as_ref() == Path::new("")
&& let Some(absolute_path) = &entry_path.absolute
{
path = Arc::from(absolute_path.as_path());
}
let mut path_match = PathMatch { let mut path_match = PathMatch {
score: ix as f64, score: ix as f64,
@ -1379,15 +1447,17 @@ impl PickerDelegate for FileFinderDelegate {
self.matches.push_new_matches( self.matches.push_new_matches(
self.history_items.iter().filter(|history_item| { self.history_items.iter().filter(|history_item| {
project project
.worktree_for_id(history_item.project.worktree_id, cx) .worktree_for_id(history_item.worktree_id(), cx)
.is_some() .is_some()
|| ((project.is_local() || project.is_via_ssh()) || ((project.is_local() || project.is_via_ssh())
&& history_item.absolute.is_some()) && matches!(history_item, FoundPath::Abs { .. }))
}), }),
self.currently_opened_path.as_ref(), self.currently_opened_path.as_ref(),
None, None,
None.into_iter(), None.into_iter(),
false, false,
&self.project,
cx,
); );
self.first_update = false; self.first_update = false;
@ -1489,7 +1559,7 @@ impl PickerDelegate for FileFinderDelegate {
} }
Match::History { path, .. } => { Match::History { path, .. } => {
let worktree_id = path.project.worktree_id; let worktree_id = path.worktree_id();
if workspace if workspace
.project() .project()
.read(cx) .read(cx)
@ -1500,24 +1570,33 @@ impl PickerDelegate for FileFinderDelegate {
workspace, workspace,
ProjectPath { ProjectPath {
worktree_id, worktree_id,
path: Arc::clone(&path.project.path), path: path.path().clone(),
}, },
window, window,
cx, cx,
) )
} else { } else {
match path.absolute.as_ref() { match path {
Some(abs_path) => { FoundPath::Project { path, .. } => split_or_open(
workspace,
ProjectPath {
worktree_id,
path: Arc::clone(&path.path),
},
window,
cx,
),
FoundPath::Abs { abs, .. } => {
if secondary { if secondary {
workspace.split_abs_path( workspace.split_abs_path(
abs_path.to_path_buf(), abs.to_path_buf(),
false, false,
window, window,
cx, cx,
) )
} else { } else {
workspace.open_abs_path( workspace.open_abs_path(
abs_path.to_path_buf(), abs.to_path_buf(),
OpenOptions { OpenOptions {
visible: Some(OpenVisible::None), visible: Some(OpenVisible::None),
..Default::default() ..Default::default()
@ -1527,15 +1606,6 @@ impl PickerDelegate for FileFinderDelegate {
) )
} }
} }
None => split_or_open(
workspace,
ProjectPath {
worktree_id,
path: Arc::clone(&path.project.path),
},
window,
cx,
),
} }
} }
} }

View file

@ -2808,13 +2808,7 @@ fn collect_search_matches(picker: &Picker<FileFinderDelegate>) -> SearchEntries
.map(|path_match| { .map(|path_match| {
Path::new(path_match.0.path_prefix.as_ref()).join(&path_match.0.path) Path::new(path_match.0.path_prefix.as_ref()).join(&path_match.0.path)
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| history_path.path().to_path_buf()),
history_path
.absolute
.as_deref()
.unwrap_or_else(|| &history_path.project.path)
.to_path_buf()
}),
); );
search_entries search_entries
.history_found_paths .history_found_paths
@ -2858,7 +2852,7 @@ fn assert_match_at_position(
.get(match_index) .get(match_index)
.unwrap_or_else(|| panic!("Finder has no match for index {match_index}")); .unwrap_or_else(|| panic!("Finder has no match for index {match_index}"));
let match_file_name = match &match_item { let match_file_name = match &match_item {
Match::History { path, .. } => path.absolute.as_deref().unwrap().file_name(), Match::History { path, .. } => path.path().file_name(),
Match::Search(path_match) => path_match.0.path.file_name(), Match::Search(path_match) => path_match.0.path.file_name(),
Match::CreateNew(project_path) => project_path.path.file_name(), Match::CreateNew(project_path) => project_path.path.file_name(),
} }