@@ -4,38 +4,63 @@ use editor::Editor;
use gpui::{App, AppContext, Context, Task, WeakEntity, Window};
use itertools::Itertools;
use project::{Entry, Metadata};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use terminal::PathLikeTarget;
use util::{ResultExt, debug_panic, paths::PathWithPosition};
use workspace::{OpenOptions, OpenVisible, Workspace};
+/// The way we found the open target. This is important to have for test assertions.
+/// For example, remote projects never look in the file system.
+#[cfg(test)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+enum OpenTargetFoundBy {
+ WorktreeExact,
+ WorktreeScan,
+ FileSystemBackground,
+}
+
+#[cfg(test)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+enum BackgroundFsChecks {
+ Enabled,
+ Disabled,
+}
+
#[derive(Debug, Clone)]
enum OpenTarget {
- Worktree(PathWithPosition, Entry),
+ Worktree(PathWithPosition, Entry, #[cfg(test)] OpenTargetFoundBy),
File(PathWithPosition, Metadata),
}
impl OpenTarget {
fn is_file(&self) -> bool {
match self {
- OpenTarget::Worktree(_, entry) => entry.is_file(),
+ OpenTarget::Worktree(_, entry, ..) => entry.is_file(),
OpenTarget::File(_, metadata) => !metadata.is_dir,
}
}
fn is_dir(&self) -> bool {
match self {
- OpenTarget::Worktree(_, entry) => entry.is_dir(),
+ OpenTarget::Worktree(_, entry, ..) => entry.is_dir(),
OpenTarget::File(_, metadata) => metadata.is_dir,
}
}
fn path(&self) -> &PathWithPosition {
match self {
- OpenTarget::Worktree(path, _) => path,
+ OpenTarget::Worktree(path, ..) => path,
OpenTarget::File(path, _) => path,
}
}
+
+ #[cfg(test)]
+ fn found_by(&self) -> OpenTargetFoundBy {
+ match self {
+ OpenTarget::Worktree(.., found_by) => *found_by,
+ OpenTarget::File(..) => OpenTargetFoundBy::FileSystemBackground,
+ }
+ }
}
pub(super) fn hover_path_like_target(
@@ -44,12 +69,41 @@ pub(super) fn hover_path_like_target(
path_like_target: &PathLikeTarget,
cx: &mut Context<TerminalView>,
) -> Task<()> {
- let file_to_open_task = possible_open_target(workspace, path_like_target, cx);
+ #[cfg(not(test))]
+ {
+ possible_hover_target(workspace, hovered_word, path_like_target, cx)
+ }
+ #[cfg(test)]
+ {
+ possible_hover_target(
+ workspace,
+ hovered_word,
+ path_like_target,
+ cx,
+ BackgroundFsChecks::Enabled,
+ )
+ }
+}
+
+fn possible_hover_target(
+ workspace: &WeakEntity<Workspace>,
+ hovered_word: HoveredWord,
+ path_like_target: &PathLikeTarget,
+ cx: &mut Context<TerminalView>,
+ #[cfg(test)] background_fs_checks: BackgroundFsChecks,
+) -> Task<()> {
+ let file_to_open_task = possible_open_target(
+ workspace,
+ path_like_target,
+ cx,
+ #[cfg(test)]
+ background_fs_checks,
+ );
cx.spawn(async move |terminal_view, cx| {
let file_to_open = file_to_open_task.await;
terminal_view
.update(cx, |terminal_view, _| match file_to_open {
- Some(OpenTarget::File(path, _) | OpenTarget::Worktree(path, _)) => {
+ Some(OpenTarget::File(path, _) | OpenTarget::Worktree(path, ..)) => {
terminal_view.hover = Some(HoverTarget {
tooltip: path.to_string(|path| path.to_string_lossy().to_string()),
hovered_word,
@@ -67,6 +121,7 @@ fn possible_open_target(
workspace: &WeakEntity<Workspace>,
path_like_target: &PathLikeTarget,
cx: &App,
+ #[cfg(test)] background_fs_checks: BackgroundFsChecks,
) -> Task<Option<OpenTarget>> {
let Some(workspace) = workspace.upgrade() else {
return Task::ready(None);
@@ -117,9 +172,19 @@ fn possible_open_target(
// If we won't find paths "easily", we can traverse the entire worktree to look what ends with the potential path suffix.
// That will be slow, though, so do the fast checks first.
let mut worktree_paths_to_check = Vec::new();
- for worktree in &worktree_candidates {
+ let mut is_cwd_in_worktree = false;
+ let mut open_target = None;
+ 'worktree_loop: for worktree in &worktree_candidates {
let worktree_root = worktree.read(cx).abs_path();
let mut paths_to_check = Vec::with_capacity(potential_paths.len());
+ let relative_cwd = cwd
+ .and_then(|cwd| cwd.strip_prefix(&worktree_root).ok())
+ .and_then(|cwd_stripped| {
+ (cwd_stripped != Path::new("")).then(|| {
+ is_cwd_in_worktree = true;
+ cwd_stripped
+ })
+ });
for path_with_position in &potential_paths {
let path_to_check = if worktree_root.ends_with(&path_with_position.path) {
@@ -130,10 +195,13 @@ fn possible_open_target(
};
match worktree.read(cx).root_entry() {
Some(root_entry) => {
- return Task::ready(Some(OpenTarget::Worktree(
+ open_target = Some(OpenTarget::Worktree(
root_path_with_position,
root_entry.clone(),
- )));
+ #[cfg(test)]
+ OpenTargetFoundBy::WorktreeExact,
+ ));
+ break 'worktree_loop;
}
None => root_path_with_position,
}
@@ -150,16 +218,26 @@ fn possible_open_target(
};
if path_to_check.path.is_relative()
- && let Some(entry) = worktree.read(cx).entry_for_path(&path_to_check.path)
+ && !worktree.read(cx).is_single_file()
+ && let Some(entry) = relative_cwd
+ .and_then(|relative_cwd| {
+ worktree
+ .read(cx)
+ .entry_for_path(&relative_cwd.join(&path_to_check.path))
+ })
+ .or_else(|| worktree.read(cx).entry_for_path(&path_to_check.path))
{
- return Task::ready(Some(OpenTarget::Worktree(
+ open_target = Some(OpenTarget::Worktree(
PathWithPosition {
path: worktree_root.join(&entry.path),
row: path_to_check.row,
column: path_to_check.column,
},
entry.clone(),
- )));
+ #[cfg(test)]
+ OpenTargetFoundBy::WorktreeExact,
+ ));
+ break 'worktree_loop;
}
paths_to_check.push(path_to_check);
@@ -170,101 +248,143 @@ fn possible_open_target(
}
}
+ #[cfg(not(test))]
+ let enable_background_fs_checks = workspace.read(cx).project().read(cx).is_local();
+ #[cfg(test)]
+ let enable_background_fs_checks = background_fs_checks == BackgroundFsChecks::Enabled;
+
+ if open_target.is_some() {
+ // We we want to prefer open targets found via background fs checks over worktree matches,
+ // however we can return early if either:
+ // - This is a remote project, or
+ // - If the terminal working directory is inside of at least one worktree
+ if !enable_background_fs_checks || is_cwd_in_worktree {
+ return Task::ready(open_target);
+ }
+ }
+
// Before entire worktree traversal(s), make an attempt to do FS checks if available.
- let fs_paths_to_check = if workspace.read(cx).project().read(cx).is_local() {
- potential_paths
- .into_iter()
- .flat_map(|path_to_check| {
- let mut paths_to_check = Vec::new();
- let maybe_path = &path_to_check.path;
- if maybe_path.starts_with("~") {
- if let Some(home_path) =
- maybe_path
- .strip_prefix("~")
- .ok()
- .and_then(|stripped_maybe_path| {
- Some(dirs::home_dir()?.join(stripped_maybe_path))
- })
- {
- paths_to_check.push(PathWithPosition {
- path: home_path,
- row: path_to_check.row,
- column: path_to_check.column,
- });
- }
- } else {
- paths_to_check.push(PathWithPosition {
- path: maybe_path.clone(),
- row: path_to_check.row,
- column: path_to_check.column,
- });
- if maybe_path.is_relative() {
- if let Some(cwd) = &cwd {
+ let fs_paths_to_check =
+ if enable_background_fs_checks {
+ let fs_cwd_paths_to_check = cwd
+ .iter()
+ .flat_map(|cwd| {
+ let mut paths_to_check = Vec::new();
+ for path_to_check in &potential_paths {
+ let maybe_path = &path_to_check.path;
+ if path_to_check.path.is_relative() {
paths_to_check.push(PathWithPosition {
- path: cwd.join(maybe_path),
+ path: cwd.join(&maybe_path),
row: path_to_check.row,
column: path_to_check.column,
});
}
- for worktree in &worktree_candidates {
- paths_to_check.push(PathWithPosition {
- path: worktree.read(cx).abs_path().join(maybe_path),
- row: path_to_check.row,
- column: path_to_check.column,
- });
- }
- }
- }
- paths_to_check
- })
- .collect()
- } else {
- Vec::new()
- };
-
- let worktree_check_task = cx.spawn(async move |cx| {
- for (worktree, worktree_paths_to_check) in worktree_paths_to_check {
- let found_entry = worktree
- .update(cx, |worktree, _| {
- let worktree_root = worktree.abs_path();
- let traversal = worktree.traverse_from_path(true, true, false, "".as_ref());
- for entry in traversal {
- if let Some(path_in_worktree) = worktree_paths_to_check
- .iter()
- .find(|path_to_check| entry.path.ends_with(&path_to_check.path))
- {
- return Some(OpenTarget::Worktree(
- PathWithPosition {
- path: worktree_root.join(&entry.path),
- row: path_in_worktree.row,
- column: path_in_worktree.column,
- },
- entry.clone(),
- ));
- }
}
- None
+ paths_to_check
})
- .ok()?;
- if let Some(found_entry) = found_entry {
- return Some(found_entry);
- }
- }
- None
- });
+ .collect::<Vec<_>>();
+ fs_cwd_paths_to_check
+ .into_iter()
+ .chain(
+ potential_paths
+ .into_iter()
+ .flat_map(|path_to_check| {
+ let mut paths_to_check = Vec::new();
+ let maybe_path = &path_to_check.path;
+ if maybe_path.starts_with("~") {
+ if let Some(home_path) = maybe_path.strip_prefix("~").ok().and_then(
+ |stripped_maybe_path| {
+ Some(dirs::home_dir()?.join(stripped_maybe_path))
+ },
+ ) {
+ paths_to_check.push(PathWithPosition {
+ path: home_path,
+ row: path_to_check.row,
+ column: path_to_check.column,
+ });
+ }
+ } else {
+ paths_to_check.push(PathWithPosition {
+ path: maybe_path.clone(),
+ row: path_to_check.row,
+ column: path_to_check.column,
+ });
+ if maybe_path.is_relative() {
+ for worktree in &worktree_candidates {
+ if !worktree.read(cx).is_single_file() {
+ paths_to_check.push(PathWithPosition {
+ path: worktree.read(cx).abs_path().join(maybe_path),
+ row: path_to_check.row,
+ column: path_to_check.column,
+ });
+ }
+ }
+ }
+ }
+ paths_to_check
+ })
+ .collect::<Vec<_>>(),
+ )
+ .collect()
+ } else {
+ Vec::new()
+ };
let fs = workspace.read(cx).project().read(cx).fs().clone();
- cx.background_spawn(async move {
+ let background_fs_checks_task = cx.background_spawn(async move {
for mut path_to_check in fs_paths_to_check {
if let Some(fs_path_to_check) = fs.canonicalize(&path_to_check.path).await.ok()
&& let Some(metadata) = fs.metadata(&fs_path_to_check).await.ok().flatten()
{
- path_to_check.path = fs_path_to_check;
- return Some(OpenTarget::File(path_to_check, metadata));
+ if open_target
+ .as_ref()
+ .map(|open_target| open_target.path().path != fs_path_to_check)
+ .unwrap_or(true)
+ {
+ path_to_check.path = fs_path_to_check;
+ return Some(OpenTarget::File(path_to_check, metadata));
+ }
+
+ break;
}
}
- worktree_check_task.await
+ open_target
+ });
+
+ cx.spawn(async move |cx| {
+ background_fs_checks_task.await.or_else(|| {
+ for (worktree, worktree_paths_to_check) in worktree_paths_to_check {
+ let found_entry = worktree
+ .update(cx, |worktree, _| -> Option<OpenTarget> {
+ let worktree_root = worktree.abs_path();
+ let traversal = worktree.traverse_from_path(true, true, false, "".as_ref());
+ for entry in traversal {
+ if let Some(path_in_worktree) = worktree_paths_to_check
+ .iter()
+ .find(|path_to_check| entry.path.ends_with(&path_to_check.path))
+ {
+ return Some(OpenTarget::Worktree(
+ PathWithPosition {
+ path: worktree_root.join(&entry.path),
+ row: path_in_worktree.row,
+ column: path_in_worktree.column,
+ },
+ entry.clone(),
+ #[cfg(test)]
+ OpenTargetFoundBy::WorktreeScan,
+ ));
+ }
+ }
+ None
+ })
+ .ok()?;
+ if let Some(found_entry) = found_entry {
+ return Some(found_entry);
+ }
+ }
+ None
+ })
})
}
@@ -275,8 +395,23 @@ pub(super) fn open_path_like_target(
window: &mut Window,
cx: &mut Context<TerminalView>,
) {
- possibly_open_target(workspace, terminal_view, path_like_target, window, cx)
+ #[cfg(not(test))]
+ {
+ possibly_open_target(workspace, terminal_view, path_like_target, window, cx)
+ .detach_and_log_err(cx)
+ }
+ #[cfg(test)]
+ {
+ possibly_open_target(
+ workspace,
+ terminal_view,
+ path_like_target,
+ window,
+ cx,
+ BackgroundFsChecks::Enabled,
+ )
.detach_and_log_err(cx)
+ }
}
fn possibly_open_target(
@@ -285,6 +420,7 @@ fn possibly_open_target(
path_like_target: &PathLikeTarget,
window: &mut Window,
cx: &mut Context<TerminalView>,
+ #[cfg(test)] background_fs_checks: BackgroundFsChecks,
) -> Task<Result<Option<OpenTarget>>> {
if terminal_view.hover.is_none() {
return Task::ready(Ok(None));
@@ -294,7 +430,13 @@ fn possibly_open_target(
cx.spawn_in(window, async move |terminal_view, cx| {
let Some(open_target) = terminal_view
.update(cx, |_, cx| {
- possible_open_target(&workspace, &path_like_target, cx)
+ possible_open_target(
+ &workspace,
+ &path_like_target,
+ cx,
+ #[cfg(test)]
+ background_fs_checks,
+ )
})?
.await
else {
@@ -375,8 +517,11 @@ mod tests {
app_cx: &mut TestAppContext,
trees: impl IntoIterator<Item = (&str, serde_json::Value)>,
worktree_roots: impl IntoIterator<Item = &str>,
- ) -> impl AsyncFnMut(HoveredWord, PathLikeTarget) -> (Option<HoverTarget>, Option<OpenTarget>)
- {
+ ) -> impl AsyncFnMut(
+ HoveredWord,
+ PathLikeTarget,
+ BackgroundFsChecks,
+ ) -> (Option<HoverTarget>, Option<OpenTarget>) {
let fs = app_cx.update(AppState::test).fs.as_fake().clone();
app_cx.update(|cx| {
@@ -393,10 +538,7 @@ mod tests {
let project = Project::test(
fs.clone(),
- worktree_roots
- .into_iter()
- .map(Path::new)
- .collect::<Vec<_>>(),
+ worktree_roots.into_iter().map(Path::new),
app_cx,
)
.await;
@@ -424,16 +566,18 @@ mod tests {
});
async move |hovered_word: HoveredWord,
- path_like_target: PathLikeTarget|
+ path_like_target: PathLikeTarget,
+ background_fs_checks: BackgroundFsChecks|
-> (Option<HoverTarget>, Option<OpenTarget>) {
let workspace_a = workspace.clone();
terminal_view
.update(cx, |_, cx| {
- hover_path_like_target(
+ possible_hover_target(
&workspace_a.downgrade(),
hovered_word,
&path_like_target,
cx,
+ background_fs_checks,
)
})
.await;
@@ -449,6 +593,7 @@ mod tests {
&path_like_target,
window,
cx,
+ background_fs_checks,
)
})
.await
@@ -462,10 +607,13 @@ mod tests {
test_path_like: &mut impl AsyncFnMut(
HoveredWord,
PathLikeTarget,
+ BackgroundFsChecks,
) -> (Option<HoverTarget>, Option<OpenTarget>),
maybe_path: &str,
tooltip: &str,
terminal_dir: Option<PathBuf>,
+ background_fs_checks: BackgroundFsChecks,
+ mut open_target_found_by: OpenTargetFoundBy,
file: &str,
line: u32,
) {
@@ -479,6 +627,7 @@ mod tests {
maybe_path: maybe_path.to_string(),
terminal_dir,
},
+ background_fs_checks,
)
.await;
@@ -512,24 +661,70 @@ mod tests {
Path::new(tooltip),
"Open target path mismatch at {file}:{line}:"
);
+
+ if background_fs_checks == BackgroundFsChecks::Disabled
+ && open_target_found_by == OpenTargetFoundBy::FileSystemBackground
+ {
+ open_target_found_by = OpenTargetFoundBy::WorktreeScan;
+ }
+
+ assert_eq!(
+ open_target.found_by(),
+ open_target_found_by,
+ "Open target found by mismatch at {file}:{line}:"
+ );
}
- macro_rules! none_or_some {
- () => {
+ macro_rules! none_or_some_pathbuf {
+ (None) => {
None
};
- ($some:expr) => {
- Some($some)
+ ($cwd:literal) => {
+ Some($crate::PathBuf::from(path!($cwd)))
};
}
macro_rules! test_path_like {
- ($test_path_like:expr, $maybe_path:literal, $tooltip:literal $(, $cwd:literal)?) => {
+ (
+ $test_path_like:expr,
+ $maybe_path:literal,
+ $tooltip:literal,
+ $cwd:tt,
+ $found_by:expr
+ ) => {{
+ test_path_like!(
+ $test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Enabled,
+ $found_by
+ );
+ test_path_like!(
+ $test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Disabled,
+ $found_by
+ );
+ }};
+
+ (
+ $test_path_like:expr,
+ $maybe_path:literal,
+ $tooltip:literal,
+ $cwd:tt,
+ $background_fs_checks:path,
+ $found_by:expr
+ ) => {
test_path_like_simple(
&mut $test_path_like,
path!($maybe_path),
path!($tooltip),
- none_or_some!($($crate::PathBuf::from(path!($cwd)))?),
+ none_or_some_pathbuf!($cwd),
+ $background_fs_checks,
+ $found_by,
std::file!(),
std::line!(),
)
@@ -537,17 +732,84 @@ mod tests {
};
}
+ // Note the arms of `test`, `test_local`, and `test_remote` should be collapsed once macro
+ // metavariable expressions (#![feature(macro_metavar_expr)]) are stabilized.
+ // See https://github.com/rust-lang/rust/issues/83527
#[doc = "test_path_likes!(<cx>, <trees>, <worktrees>, { $(<tests>;)+ })"]
macro_rules! test_path_likes {
($cx:expr, $trees:expr, $worktrees:expr, { $($tests:expr;)+ }) => { {
let mut test_path_like = init_test($cx, $trees, $worktrees).await;
- #[doc ="test!(<hovered maybe_path>, <expected tooltip>, <terminal cwd>)"]
+ #[doc ="test!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
+ #[doc ="\\[, found by \\])"]
+ #[allow(unused_macros)]
macro_rules! test {
- ($maybe_path:literal, $tooltip:literal) => {
- test_path_like!(test_path_like, $maybe_path, $tooltip)
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ OpenTargetFoundBy::WorktreeExact
+ )
+ };
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ OpenTargetFoundBy::$found_by
+ )
+ }
+ }
+ #[doc ="test_local!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
+ #[doc ="\\[, found by \\])"]
+ #[allow(unused_macros)]
+ macro_rules! test_local {
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Enabled,
+ OpenTargetFoundBy::WorktreeExact
+ )
+ };
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Enabled,
+ OpenTargetFoundBy::$found_by
+ )
+ }
+ }
+ #[doc ="test_remote!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
+ #[doc ="\\[, found by \\])"]
+ #[allow(unused_macros)]
+ macro_rules! test_remote {
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Disabled,
+ OpenTargetFoundBy::WorktreeExact
+ )
};
- ($maybe_path:literal, $tooltip:literal, $cwd:literal) => {
- test_path_like!(test_path_like, $maybe_path, $tooltip, $cwd)
+ ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
+ test_path_like!(
+ test_path_like,
+ $maybe_path,
+ $tooltip,
+ $cwd,
+ BackgroundFsChecks::Disabled,
+ OpenTargetFoundBy::$found_by
+ )
}
}
$($tests);+
@@ -567,8 +829,10 @@ mod tests {
)],
vec![path!("/test")],
{
- test!("lib.rs", "/test/lib.rs");
- test!("test.rs", "/test/test.rs");
+ test!("lib.rs", "/test/lib.rs", None);
+ test!("/test/lib.rs", "/test/lib.rs", None);
+ test!("test.rs", "/test/test.rs", None);
+ test!("/test/test.rs", "/test/test.rs", None);
}
)
}
@@ -596,9 +860,15 @@ mod tests {
vec![path!("/file.txt"), path!("/test")],
{
test!("file.txt", "/file.txt", "/");
+ test!("/file.txt", "/file.txt", "/");
+
test!("lib.rs", "/test/lib.rs", "/test");
test!("test.rs", "/test/test.rs", "/test");
test!("file.txt", "/test/file.txt", "/test");
+
+ test!("/test/lib.rs", "/test/lib.rs", "/test");
+ test!("/test/test.rs", "/test/test.rs", "/test");
+ test!("/test/file.txt", "/test/file.txt", "/test");
}
)
}
@@ -649,7 +919,7 @@ mod tests {
)],
vec![path!("/dir1")],
{
- test!("C.py", "/dir1/dir 2/C.py", "/dir1");
+ test!("C.py", "/dir1/dir 2/C.py", "/dir1", WorktreeScan);
test!("C.py", "/dir1/dir 2/C.py", "/dir1/dir 2");
test!("C.py", "/dir1/dir 3/C.py", "/dir1/dir 3");
}
@@ -660,7 +930,6 @@ mod tests {
// See https://github.com/zed-industries/zed/issues/34027
// See https://github.com/zed-industries/zed/issues/33498
#[gpui::test]
- #[should_panic(expected = "Tooltip mismatch")]
async fn issue_28407_nesting(cx: &mut TestAppContext) {
test_path_likes!(
cx,
@@ -669,7 +938,8 @@ mod tests {
json!({
"lib": {
"src": {
- "main.rs": ""
+ "main.rs": "",
+ "only_in_lib.rs": ""
},
},
"src": {
@@ -679,22 +949,12 @@ mod tests {
)],
vec![path!("/project")],
{
- // Failing currently
- test!("main.rs", "/project/src/main.rs", "/project");
test!("main.rs", "/project/src/main.rs", "/project/src");
- test!("main.rs", "/project/lib/src/main.rs", "/project/lib");
test!("main.rs", "/project/lib/src/main.rs", "/project/lib/src");
test!("src/main.rs", "/project/src/main.rs", "/project");
test!("src/main.rs", "/project/src/main.rs", "/project/src");
- // Failing currently
test!("src/main.rs", "/project/lib/src/main.rs", "/project/lib");
- // Failing currently
- test!(
- "src/main.rs",
- "/project/lib/src/main.rs",
- "/project/lib/src"
- );
test!("lib/src/main.rs", "/project/lib/src/main.rs", "/project");
test!(
@@ -712,11 +972,19 @@ mod tests {
"/project/lib/src/main.rs",
"/project/lib/src"
);
+ test!(
+ "src/only_in_lib.rs",
+ "/project/lib/src/only_in_lib.rs",
+ "/project/lib/src",
+ WorktreeScan
+ );
}
)
}
// https://github.com/zed-industries/zed/issues/28339
+ // Note: These could all be found by WorktreeExact if we used
+ // `fs::normalize_path(&maybe_path)`
#[gpui::test]
async fn issue_28339(cx: &mut TestAppContext) {
test_path_likes!(
@@ -733,32 +1001,90 @@ mod tests {
)],
vec![path!("/tmp")],
{
- test!(
+ test_local!(
"foo/./bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339"
);
- test!(
+ test_local!(
+ "foo/../foo/bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339",
+ FileSystemBackground
+ );
+ test_local!(
+ "foo/..///foo/bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339",
+ FileSystemBackground
+ );
+ test_local!(
+ "issue28339/../issue28339/foo/../foo/bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339",
+ FileSystemBackground
+ );
+ test_local!(
+ "./bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339/foo"
+ );
+ test_local!(
+ "../foo/bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339/foo",
+ FileSystemBackground
+ );
+ }
+ )
+ }
+
+ // https://github.com/zed-industries/zed/issues/28339
+ // Note: These could all be found by WorktreeExact if we used
+ // `fs::normalize_path(&maybe_path)`
+ #[gpui::test]
+ #[should_panic(expected = "Hover target should not be `None`")]
+ async fn issue_28339_remote(cx: &mut TestAppContext) {
+ test_path_likes!(
+ cx,
+ vec![(
+ path!("/tmp"),
+ json!({
+ "issue28339": {
+ "foo": {
+ "bar.txt": ""
+ },
+ },
+ }),
+ )],
+ vec![path!("/tmp")],
+ {
+ test_remote!(
+ "foo/./bar.txt",
+ "/tmp/issue28339/foo/bar.txt",
+ "/tmp/issue28339"
+ );
+ test_remote!(
"foo/../foo/bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339"
);
- test!(
+ test_remote!(
"foo/..///foo/bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339"
);
- test!(
+ test_remote!(
"issue28339/../issue28339/foo/../foo/bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339"
);
- test!(
+ test_remote!(
"./bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339/foo"
);
- test!(
+ test_remote!(
"../foo/bar.txt",
"/tmp/issue28339/foo/bar.txt",
"/tmp/issue28339/foo"
@@ -769,7 +1095,6 @@ mod tests {
// https://github.com/zed-industries/zed/issues/34027
#[gpui::test]
- #[should_panic(expected = "Tooltip mismatch")]
async fn issue_34027(cx: &mut TestAppContext) {
test_path_likes!(
cx,
@@ -796,8 +1121,128 @@ mod tests {
// https://github.com/zed-industries/zed/issues/34027
#[gpui::test]
- #[should_panic(expected = "Tooltip mismatch")]
- async fn issue_34027_non_worktree_file(cx: &mut TestAppContext) {
+ async fn issue_34027_siblings(cx: &mut TestAppContext) {
+ test_path_likes!(
+ cx,
+ vec![(
+ path!("/test"),
+ json!({
+ "sub1": {
+ "file.txt": "",
+ },
+ "sub2": {
+ "file.txt": "",
+ }
+ }),
+ ),],
+ vec![path!("/test")],
+ {
+ test!("file.txt", "/test/sub1/file.txt", "/test/sub1");
+ test!("file.txt", "/test/sub2/file.txt", "/test/sub2");
+ test!("sub1/file.txt", "/test/sub1/file.txt", "/test/sub1");
+ test!("sub2/file.txt", "/test/sub2/file.txt", "/test/sub2");
+ test!("sub1/file.txt", "/test/sub1/file.txt", "/test/sub2");
+ test!("sub2/file.txt", "/test/sub2/file.txt", "/test/sub1");
+ }
+ )
+ }
+
+ // https://github.com/zed-industries/zed/issues/34027
+ #[gpui::test]
+ async fn issue_34027_nesting(cx: &mut TestAppContext) {
+ test_path_likes!(
+ cx,
+ vec![(
+ path!("/test"),
+ json!({
+ "sub1": {
+ "file.txt": "",
+ "subsub1": {
+ "file.txt": "",
+ }
+ },
+ "sub2": {
+ "file.txt": "",
+ "subsub1": {
+ "file.txt": "",
+ }
+ }
+ }),
+ ),],
+ vec![path!("/test")],
+ {
+ test!(
+ "file.txt",
+ "/test/sub1/subsub1/file.txt",
+ "/test/sub1/subsub1"
+ );
+ test!(
+ "file.txt",
+ "/test/sub2/subsub1/file.txt",
+ "/test/sub2/subsub1"
+ );
+ test!(
+ "subsub1/file.txt",
+ "/test/sub1/subsub1/file.txt",
+ "/test",
+ WorktreeScan
+ );
+ test!(
+ "subsub1/file.txt",
+ "/test/sub1/subsub1/file.txt",
+ "/test",
+ WorktreeScan
+ );
+ test!(
+ "subsub1/file.txt",
+ "/test/sub1/subsub1/file.txt",
+ "/test/sub1"
+ );
+ test!(
+ "subsub1/file.txt",
+ "/test/sub2/subsub1/file.txt",
+ "/test/sub2"
+ );
+ test!(
+ "subsub1/file.txt",
+ "/test/sub1/subsub1/file.txt",
+ "/test/sub1/subsub1",
+ WorktreeScan
+ );
+ }
+ )
+ }
+
+ // https://github.com/zed-industries/zed/issues/34027
+ #[gpui::test]
+ async fn issue_34027_non_worktree_local_file(cx: &mut TestAppContext) {
+ test_path_likes!(
+ cx,
+ vec![
+ (
+ path!("/"),
+ json!({
+ "file.txt": "",
+ }),
+ ),
+ (
+ path!("/test"),
+ json!({
+ "file.txt": "",
+ }),
+ ),
+ ],
+ vec![path!("/test")],
+ {
+ // Note: Opening a non-worktree file adds that file as a single file worktree.
+ test_local!("file.txt", "/file.txt", "/", FileSystemBackground);
+ }
+ )
+ }
+
+ // https://github.com/zed-industries/zed/issues/34027
+ #[gpui::test]
+ async fn issue_34027_non_worktree_remote_file(cx: &mut TestAppContext) {
test_path_likes!(
cx,
vec![