refactor(file_finder): refactor found_path to make it compatible with opened files outside of current project
Signed-off-by: Benjamin <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
parent
dd6fce6d4e
commit
2250950659
2 changed files with 149 additions and 85 deletions
|
@ -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;
|
||||||
|
@ -1486,7 +1556,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)
|
||||||
|
@ -1497,24 +1567,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()
|
||||||
|
@ -1524,15 +1603,6 @@ impl PickerDelegate for FileFinderDelegate {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => split_or_open(
|
|
||||||
workspace,
|
|
||||||
ProjectPath {
|
|
||||||
worktree_id,
|
|
||||||
path: Arc::clone(&path.project.path),
|
|
||||||
},
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2760,13 +2760,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
|
||||||
|
@ -2810,7 +2804,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(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue