Skip to repository content1006 lines · 33.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:17:56.225Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
terminal_path_like_target.rs
1use super::{HoverTarget, HoveredWord, TerminalView};
2use anyhow::Result;
3use editor::items::open_resolved_target;
4use gpui::{Context, Task, TaskExt, WeakEntity, Window};
5use std::path::PathBuf;
6use terminal::PathLikeTarget;
7#[cfg(not(test))]
8use workspace::path_link::possible_open_target;
9#[cfg(test)]
10use workspace::path_link::{
11 BackgroundPathChecks, OpenTargetFoundBy, possible_open_target_with_fs_checks,
12};
13use workspace::{Workspace, path_link::OpenTarget};
14
15pub(super) fn hover_path_like_target(
16 workspace: &WeakEntity<Workspace>,
17 hovered_word: HoveredWord,
18 path_like_target: &PathLikeTarget,
19 cx: &mut Context<TerminalView>,
20) -> Task<()> {
21 #[cfg(not(test))]
22 {
23 possible_hover_target(workspace, hovered_word, path_like_target, cx)
24 }
25 #[cfg(test)]
26 {
27 possible_hover_target(
28 workspace,
29 hovered_word,
30 path_like_target,
31 cx,
32 BackgroundPathChecks::LocalFileSystem,
33 )
34 }
35}
36
37fn possible_hover_target(
38 workspace: &WeakEntity<Workspace>,
39 hovered_word: HoveredWord,
40 path_like_target: &PathLikeTarget,
41 cx: &mut Context<TerminalView>,
42 #[cfg(test)] background_path_checks: BackgroundPathChecks,
43) -> Task<()> {
44 #[cfg(not(test))]
45 let file_to_open_task = possible_open_target(
46 workspace,
47 &path_like_target.maybe_path,
48 path_like_target.terminal_dir.as_deref(),
49 cx,
50 );
51 #[cfg(test)]
52 let file_to_open_task = possible_open_target_with_fs_checks(
53 workspace,
54 &path_like_target.maybe_path,
55 path_like_target.terminal_dir.as_deref(),
56 cx,
57 background_path_checks,
58 );
59 cx.spawn(async move |terminal_view, cx| {
60 let file_to_open = file_to_open_task.await;
61 terminal_view
62 .update(cx, |terminal_view, _| match file_to_open {
63 Some(OpenTarget::Path(path, ..) | OpenTarget::Worktree(path, ..)) => {
64 terminal_view.hover = Some(HoverTarget {
65 tooltip: path
66 .to_string(&|path: &PathBuf| path.to_string_lossy().into_owned()),
67 hovered_word,
68 });
69 }
70 None => {
71 terminal_view.hover = None;
72 }
73 })
74 .ok();
75 })
76}
77
78pub(super) fn open_path_like_target(
79 workspace: &WeakEntity<Workspace>,
80 terminal_view: &mut TerminalView,
81 path_like_target: &PathLikeTarget,
82 window: &mut Window,
83 cx: &mut Context<TerminalView>,
84) {
85 #[cfg(not(test))]
86 {
87 possibly_open_target(workspace, terminal_view, path_like_target, window, cx)
88 .detach_and_log_err(cx)
89 }
90 #[cfg(test)]
91 {
92 possibly_open_target(
93 workspace,
94 terminal_view,
95 path_like_target,
96 window,
97 cx,
98 BackgroundPathChecks::LocalFileSystem,
99 )
100 .detach_and_log_err(cx)
101 }
102}
103
104fn possibly_open_target(
105 workspace: &WeakEntity<Workspace>,
106 terminal_view: &mut TerminalView,
107 path_like_target: &PathLikeTarget,
108 window: &mut Window,
109 cx: &mut Context<TerminalView>,
110 #[cfg(test)] background_path_checks: BackgroundPathChecks,
111) -> Task<Result<Option<OpenTarget>>> {
112 if terminal_view.hover.is_none() {
113 return Task::ready(Ok(None));
114 }
115 let workspace = workspace.clone();
116 let path_like_target = path_like_target.clone();
117 cx.spawn_in(window, async move |terminal_view, cx| {
118 let Some(open_target) = terminal_view
119 .update(cx, |_, cx| {
120 #[cfg(not(test))]
121 {
122 possible_open_target(
123 &workspace,
124 &path_like_target.maybe_path,
125 path_like_target.terminal_dir.as_deref(),
126 cx,
127 )
128 }
129 #[cfg(test)]
130 {
131 possible_open_target_with_fs_checks(
132 &workspace,
133 &path_like_target.maybe_path,
134 path_like_target.terminal_dir.as_deref(),
135 cx,
136 background_path_checks,
137 )
138 }
139 })?
140 .await
141 else {
142 return Ok(None);
143 };
144
145 let opened = open_resolved_target(&workspace, &open_target, cx).await?;
146 Ok(opened.then_some(open_target))
147 })
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use gpui::{AppContext as _, TestAppContext};
154 use project::Project;
155 use serde_json::json;
156 use std::path::{Path, PathBuf};
157 use terminal::{
158 HoveredWord, Point, Range, TerminalBuilder,
159 terminal_settings::{AlternateScroll, CursorShape},
160 };
161 use util::path;
162 use util::paths::PathStyle;
163 use workspace::{AppState, MultiWorkspace};
164
165 async fn init_test(
166 app_cx: &mut TestAppContext,
167 trees: impl IntoIterator<Item = (&str, serde_json::Value)>,
168 worktree_roots: impl IntoIterator<Item = &str>,
169 ) -> impl AsyncFnMut(
170 HoveredWord,
171 PathLikeTarget,
172 BackgroundPathChecks,
173 ) -> (Option<HoverTarget>, Option<OpenTarget>) {
174 let fs = app_cx.update(AppState::test).fs.as_fake().clone();
175
176 app_cx.update(|cx| {
177 theme_settings::init(theme::LoadThemes::JustBase, cx);
178 editor::init(cx);
179 });
180
181 for (path, tree) in trees {
182 fs.insert_tree(path, tree).await;
183 }
184
185 let project: gpui::Entity<Project> = Project::test(
186 fs.clone(),
187 worktree_roots.into_iter().map(Path::new),
188 app_cx,
189 )
190 .await;
191
192 let (multi_workspace, cx) = app_cx
193 .add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
194 let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
195
196 let terminal = app_cx.new(|cx| {
197 TerminalBuilder::new_display_only(
198 CursorShape::default(),
199 AlternateScroll::On,
200 None,
201 0,
202 cx.background_executor(),
203 PathStyle::local(),
204 )
205 .subscribe(cx)
206 });
207
208 let workspace_a = workspace.clone();
209 let (terminal_view, cx) = app_cx.add_window_view(|window, cx| {
210 TerminalView::new(
211 terminal,
212 workspace_a.downgrade(),
213 None,
214 project.downgrade(),
215 window,
216 cx,
217 )
218 });
219
220 async move |hovered_word: HoveredWord,
221 path_like_target: PathLikeTarget,
222 background_path_checks: BackgroundPathChecks|
223 -> (Option<HoverTarget>, Option<OpenTarget>) {
224 let workspace_a = workspace.clone();
225 terminal_view
226 .update(cx, |_, cx| {
227 possible_hover_target(
228 &workspace_a.downgrade(),
229 hovered_word,
230 &path_like_target,
231 cx,
232 background_path_checks,
233 )
234 })
235 .await;
236
237 let hover_target =
238 terminal_view.read_with(cx, |terminal_view, _| terminal_view.hover.clone());
239
240 let open_target = terminal_view
241 .update_in(cx, |terminal_view, window, cx| {
242 possibly_open_target(
243 &workspace.downgrade(),
244 terminal_view,
245 &path_like_target,
246 window,
247 cx,
248 background_path_checks,
249 )
250 })
251 .await
252 .expect("Failed to possibly open target");
253
254 (hover_target, open_target)
255 }
256 }
257
258 async fn test_path_like_simple(
259 test_path_like: &mut impl AsyncFnMut(
260 HoveredWord,
261 PathLikeTarget,
262 BackgroundPathChecks,
263 ) -> (Option<HoverTarget>, Option<OpenTarget>),
264 maybe_path: &str,
265 tooltip: &str,
266 terminal_dir: Option<PathBuf>,
267 background_path_checks: BackgroundPathChecks,
268 open_target_found_by: OpenTargetFoundBy,
269 file: &str,
270 line: u32,
271 ) {
272 let (hover_target, open_target) = test_path_like(
273 HoveredWord {
274 word: maybe_path.to_string(),
275 word_match: Range::new(Point::new(0, 0), Point::new(0, 0)),
276 id: 0,
277 },
278 PathLikeTarget {
279 maybe_path: maybe_path.to_string(),
280 terminal_dir,
281 },
282 background_path_checks,
283 )
284 .await;
285
286 let Some(hover_target) = hover_target else {
287 assert!(
288 hover_target.is_some(),
289 "Hover target should not be `None` at {file}:{line}:"
290 );
291 return;
292 };
293
294 assert_eq!(
295 hover_target.tooltip, tooltip,
296 "Tooltip mismatch at {file}:{line}:"
297 );
298 assert_eq!(
299 hover_target.hovered_word.word, maybe_path,
300 "Hovered word mismatch at {file}:{line}:"
301 );
302
303 let Some(open_target) = open_target else {
304 assert!(
305 open_target.is_some(),
306 "Open target should not be `None` at {file}:{line}:"
307 );
308 return;
309 };
310
311 assert_eq!(
312 open_target.path().path,
313 Path::new(tooltip),
314 "Open target path mismatch at {file}:{line}:"
315 );
316
317 assert_eq!(
318 open_target.found_by(),
319 open_target_found_by,
320 "Open target found by mismatch at {file}:{line}:"
321 );
322 }
323
324 macro_rules! none_or_some_pathbuf {
325 (None) => {
326 None
327 };
328 ($cwd:literal) => {
329 Some($crate::PathBuf::from(path!($cwd)))
330 };
331 }
332
333 macro_rules! test_path_like {
334 (
335 $test_path_like:expr,
336 $maybe_path:literal,
337 $tooltip:literal,
338 $cwd:tt,
339 $found_by:expr
340 ) => {{
341 test_path_like!(
342 $test_path_like,
343 $maybe_path,
344 $tooltip,
345 $cwd,
346 BackgroundPathChecks::LocalFileSystem,
347 $found_by
348 );
349 test_path_like!(
350 $test_path_like,
351 $maybe_path,
352 $tooltip,
353 $cwd,
354 BackgroundPathChecks::ProjectPathResolution,
355 $found_by
356 );
357 }};
358
359 (
360 $test_path_like:expr,
361 $maybe_path:literal,
362 $tooltip:literal,
363 $cwd:tt,
364 $background_fs_checks:path,
365 $found_by:expr
366 ) => {
367 test_path_like_simple(
368 &mut $test_path_like,
369 path!($maybe_path),
370 path!($tooltip),
371 none_or_some_pathbuf!($cwd),
372 $background_fs_checks,
373 $found_by,
374 std::file!(),
375 std::line!(),
376 )
377 .await
378 };
379 }
380
381 // Note the arms of `test`, `test_local`, and `test_remote` should be collapsed once macro
382 // metavariable expressions (#![feature(macro_metavar_expr)]) are stabilized.
383 // See https://github.com/rust-lang/rust/issues/83527
384 #[doc = "test_path_likes!(<cx>, <trees>, <worktrees>, { $(<tests>;)+ })"]
385 macro_rules! test_path_likes {
386 ($cx:expr, $trees:expr, $worktrees:expr, { $($tests:expr;)+ }) => { {
387 let mut test_path_like = init_test($cx, $trees, $worktrees).await;
388 #[doc ="test!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
389 #[doc ="\\[, found by \\])"]
390 #[allow(unused_macros)]
391 macro_rules! test {
392 ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
393 test_path_like!(
394 test_path_like,
395 $maybe_path,
396 $tooltip,
397 $cwd,
398 OpenTargetFoundBy::WorktreeExact
399 )
400 };
401 ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
402 test_path_like!(
403 test_path_like,
404 $maybe_path,
405 $tooltip,
406 $cwd,
407 OpenTargetFoundBy::$found_by
408 )
409 }
410 }
411 #[doc ="test_local!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
412 #[doc ="\\[, found by \\])"]
413 #[allow(unused_macros)]
414 macro_rules! test_local {
415 ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
416 test_path_like!(
417 test_path_like,
418 $maybe_path,
419 $tooltip,
420 $cwd,
421 BackgroundPathChecks::LocalFileSystem,
422 OpenTargetFoundBy::WorktreeExact
423 )
424 };
425 ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
426 test_path_like!(
427 test_path_like,
428 $maybe_path,
429 $tooltip,
430 $cwd,
431 BackgroundPathChecks::LocalFileSystem,
432 OpenTargetFoundBy::$found_by
433 )
434 }
435 }
436 #[doc ="test_remote!(<hovered maybe_path>, <expected tooltip>, <terminal cwd> "]
437 #[doc ="\\[, found by \\])"]
438 #[allow(unused_macros)]
439 macro_rules! test_remote {
440 ($maybe_path:literal, $tooltip:literal, $cwd:tt) => {
441 test_path_like!(
442 test_path_like,
443 $maybe_path,
444 $tooltip,
445 $cwd,
446 BackgroundPathChecks::ProjectPathResolution,
447 OpenTargetFoundBy::WorktreeExact
448 )
449 };
450 ($maybe_path:literal, $tooltip:literal, $cwd:tt, $found_by:ident) => {
451 test_path_like!(
452 test_path_like,
453 $maybe_path,
454 $tooltip,
455 $cwd,
456 BackgroundPathChecks::ProjectPathResolution,
457 OpenTargetFoundBy::$found_by
458 )
459 }
460 }
461 $($tests);+
462 } }
463 }
464
465 #[gpui::test]
466 async fn one_folder_worktree(cx: &mut TestAppContext) {
467 test_path_likes!(
468 cx,
469 vec![(
470 path!("/test"),
471 json!({
472 "lib.rs": "",
473 "test.rs": "",
474 }),
475 )],
476 vec![path!("/test")],
477 {
478 test!("lib.rs", "/test/lib.rs", None);
479 test!("/test/lib.rs", "/test/lib.rs", None);
480 test!("test.rs", "/test/test.rs", None);
481 test!("/test/test.rs", "/test/test.rs", None);
482 }
483 )
484 }
485
486 #[gpui::test]
487 async fn mixed_worktrees(cx: &mut TestAppContext) {
488 test_path_likes!(
489 cx,
490 vec![
491 (
492 path!("/"),
493 json!({
494 "file.txt": "",
495 }),
496 ),
497 (
498 path!("/test"),
499 json!({
500 "lib.rs": "",
501 "test.rs": "",
502 "file.txt": "",
503 }),
504 ),
505 ],
506 vec![path!("/file.txt"), path!("/test")],
507 {
508 test!("file.txt", "/file.txt", "/");
509 test!("/file.txt", "/file.txt", "/");
510
511 test!("lib.rs", "/test/lib.rs", "/test");
512 test!("test.rs", "/test/test.rs", "/test");
513 test!("file.txt", "/test/file.txt", "/test");
514
515 test!("/test/lib.rs", "/test/lib.rs", "/test");
516 test!("/test/test.rs", "/test/test.rs", "/test");
517 test!("/test/file.txt", "/test/file.txt", "/test");
518 }
519 )
520 }
521
522 #[gpui::test]
523 async fn worktree_file_preferred(cx: &mut TestAppContext) {
524 test_path_likes!(
525 cx,
526 vec![
527 (
528 path!("/"),
529 json!({
530 "file.txt": "",
531 }),
532 ),
533 (
534 path!("/test"),
535 json!({
536 "file.txt": "",
537 }),
538 ),
539 ],
540 vec![path!("/test")],
541 {
542 test!("file.txt", "/test/file.txt", "/test");
543 }
544 )
545 }
546
547 mod issues {
548 use super::*;
549
550 // https://github.com/zed-industries/zed/issues/28407
551 #[gpui::test]
552 async fn issue_28407_siblings(cx: &mut TestAppContext) {
553 test_path_likes!(
554 cx,
555 vec![(
556 path!("/dir1"),
557 json!({
558 "dir 2": {
559 "C.py": ""
560 },
561 "dir 3": {
562 "C.py": ""
563 },
564 }),
565 )],
566 vec![path!("/dir1")],
567 {
568 test!("C.py", "/dir1/dir 2/C.py", "/dir1", WorktreeScan);
569 test!("C.py", "/dir1/dir 2/C.py", "/dir1/dir 2");
570 test!("C.py", "/dir1/dir 3/C.py", "/dir1/dir 3");
571 }
572 )
573 }
574
575 // https://github.com/zed-industries/zed/issues/28407
576 // See https://github.com/zed-industries/zed/issues/34027
577 // See https://github.com/zed-industries/zed/issues/33498
578 #[gpui::test]
579 async fn issue_28407_nesting(cx: &mut TestAppContext) {
580 test_path_likes!(
581 cx,
582 vec![(
583 path!("/project"),
584 json!({
585 "lib": {
586 "src": {
587 "main.rs": "",
588 "only_in_lib.rs": ""
589 },
590 },
591 "src": {
592 "main.rs": ""
593 },
594 }),
595 )],
596 vec![path!("/project")],
597 {
598 test!("main.rs", "/project/src/main.rs", "/project/src");
599 test!("main.rs", "/project/lib/src/main.rs", "/project/lib/src");
600
601 test!("src/main.rs", "/project/src/main.rs", "/project");
602 test!("src/main.rs", "/project/src/main.rs", "/project/src");
603 test!("src/main.rs", "/project/lib/src/main.rs", "/project/lib");
604
605 test!("lib/src/main.rs", "/project/lib/src/main.rs", "/project");
606 test!(
607 "lib/src/main.rs",
608 "/project/lib/src/main.rs",
609 "/project/src"
610 );
611 test!(
612 "lib/src/main.rs",
613 "/project/lib/src/main.rs",
614 "/project/lib"
615 );
616 test!(
617 "lib/src/main.rs",
618 "/project/lib/src/main.rs",
619 "/project/lib/src"
620 );
621 test!(
622 "src/only_in_lib.rs",
623 "/project/lib/src/only_in_lib.rs",
624 "/project/lib/src",
625 WorktreeScan
626 );
627 }
628 )
629 }
630
631 // https://github.com/zed-industries/zed/issues/28339
632 #[gpui::test]
633 async fn issue_28339(cx: &mut TestAppContext) {
634 test_path_likes!(
635 cx,
636 vec![(
637 path!("/tmp"),
638 json!({
639 "issue28339": {
640 "foo": {
641 "bar.txt": ""
642 },
643 },
644 }),
645 )],
646 vec![path!("/tmp")],
647 {
648 test_local!(
649 "foo/./bar.txt",
650 "/tmp/issue28339/foo/bar.txt",
651 "/tmp/issue28339",
652 WorktreeExact
653 );
654 test_local!(
655 "foo/../foo/bar.txt",
656 "/tmp/issue28339/foo/bar.txt",
657 "/tmp/issue28339",
658 WorktreeExact
659 );
660 test_local!(
661 "foo/..///foo/bar.txt",
662 "/tmp/issue28339/foo/bar.txt",
663 "/tmp/issue28339",
664 WorktreeExact
665 );
666 test_local!(
667 "issue28339/../issue28339/foo/../foo/bar.txt",
668 "/tmp/issue28339/foo/bar.txt",
669 "/tmp/issue28339",
670 WorktreeExact
671 );
672 test_local!(
673 "./bar.txt",
674 "/tmp/issue28339/foo/bar.txt",
675 "/tmp/issue28339/foo",
676 WorktreeExact
677 );
678 test_local!(
679 "../foo/bar.txt",
680 "/tmp/issue28339/foo/bar.txt",
681 "/tmp/issue28339/foo",
682 WorktreeExact
683 );
684 }
685 )
686 }
687
688 // https://github.com/zed-industries/zed/issues/28339
689 #[gpui::test]
690 async fn issue_28339_remote(cx: &mut TestAppContext) {
691 test_path_likes!(
692 cx,
693 vec![(
694 path!("/tmp"),
695 json!({
696 "issue28339": {
697 "foo": {
698 "bar.txt": ""
699 },
700 },
701 }),
702 )],
703 vec![path!("/tmp")],
704 {
705 test_remote!(
706 "foo/./bar.txt",
707 "/tmp/issue28339/foo/bar.txt",
708 "/tmp/issue28339"
709 );
710 test_remote!(
711 "foo/../foo/bar.txt",
712 "/tmp/issue28339/foo/bar.txt",
713 "/tmp/issue28339"
714 );
715 test_remote!(
716 "foo/..///foo/bar.txt",
717 "/tmp/issue28339/foo/bar.txt",
718 "/tmp/issue28339"
719 );
720 test_remote!(
721 "issue28339/../issue28339/foo/../foo/bar.txt",
722 "/tmp/issue28339/foo/bar.txt",
723 "/tmp/issue28339"
724 );
725 test_remote!(
726 "./bar.txt",
727 "/tmp/issue28339/foo/bar.txt",
728 "/tmp/issue28339/foo"
729 );
730 test_remote!(
731 "../foo/bar.txt",
732 "/tmp/issue28339/foo/bar.txt",
733 "/tmp/issue28339/foo"
734 );
735 }
736 )
737 }
738
739 // https://github.com/zed-industries/zed/issues/34027
740 #[gpui::test]
741 async fn issue_34027(cx: &mut TestAppContext) {
742 test_path_likes!(
743 cx,
744 vec![(
745 path!("/tmp/issue34027"),
746 json!({
747 "test.txt": "",
748 "foo": {
749 "test.txt": "",
750 }
751 }),
752 ),],
753 vec![path!("/tmp/issue34027")],
754 {
755 test!("test.txt", "/tmp/issue34027/test.txt", "/tmp/issue34027");
756 test!(
757 "test.txt",
758 "/tmp/issue34027/foo/test.txt",
759 "/tmp/issue34027/foo"
760 );
761 }
762 )
763 }
764
765 // https://github.com/zed-industries/zed/issues/34027
766 #[gpui::test]
767 async fn issue_34027_siblings(cx: &mut TestAppContext) {
768 test_path_likes!(
769 cx,
770 vec![(
771 path!("/test"),
772 json!({
773 "sub1": {
774 "file.txt": "",
775 },
776 "sub2": {
777 "file.txt": "",
778 }
779 }),
780 ),],
781 vec![path!("/test")],
782 {
783 test!("file.txt", "/test/sub1/file.txt", "/test/sub1");
784 test!("file.txt", "/test/sub2/file.txt", "/test/sub2");
785 test!("sub1/file.txt", "/test/sub1/file.txt", "/test/sub1");
786 test!("sub2/file.txt", "/test/sub2/file.txt", "/test/sub2");
787 test!("sub1/file.txt", "/test/sub1/file.txt", "/test/sub2");
788 test!("sub2/file.txt", "/test/sub2/file.txt", "/test/sub1");
789 }
790 )
791 }
792
793 // https://github.com/zed-industries/zed/issues/34027
794 #[gpui::test]
795 async fn issue_34027_nesting(cx: &mut TestAppContext) {
796 test_path_likes!(
797 cx,
798 vec![(
799 path!("/test"),
800 json!({
801 "sub1": {
802 "file.txt": "",
803 "subsub1": {
804 "file.txt": "",
805 }
806 },
807 "sub2": {
808 "file.txt": "",
809 "subsub1": {
810 "file.txt": "",
811 }
812 }
813 }),
814 ),],
815 vec![path!("/test")],
816 {
817 test!(
818 "file.txt",
819 "/test/sub1/subsub1/file.txt",
820 "/test/sub1/subsub1"
821 );
822 test!(
823 "file.txt",
824 "/test/sub2/subsub1/file.txt",
825 "/test/sub2/subsub1"
826 );
827 test!(
828 "subsub1/file.txt",
829 "/test/sub1/subsub1/file.txt",
830 "/test",
831 WorktreeScan
832 );
833 test!(
834 "subsub1/file.txt",
835 "/test/sub1/subsub1/file.txt",
836 "/test",
837 WorktreeScan
838 );
839 test!(
840 "subsub1/file.txt",
841 "/test/sub1/subsub1/file.txt",
842 "/test/sub1"
843 );
844 test!(
845 "subsub1/file.txt",
846 "/test/sub2/subsub1/file.txt",
847 "/test/sub2"
848 );
849 test!(
850 "subsub1/file.txt",
851 "/test/sub1/subsub1/file.txt",
852 "/test/sub1/subsub1",
853 WorktreeScan
854 );
855 }
856 )
857 }
858
859 // https://github.com/zed-industries/zed/issues/34027
860 #[gpui::test]
861 async fn issue_34027_non_worktree_local_file(cx: &mut TestAppContext) {
862 test_path_likes!(
863 cx,
864 vec![
865 (
866 path!("/"),
867 json!({
868 "file.txt": "",
869 }),
870 ),
871 (
872 path!("/test"),
873 json!({
874 "file.txt": "",
875 }),
876 ),
877 ],
878 vec![path!("/test")],
879 {
880 // Note: Opening a non-worktree file adds that file as a single file worktree.
881 test_local!("file.txt", "/file.txt", "/", BackgroundPathResolution);
882 }
883 )
884 }
885
886 // https://github.com/zed-industries/zed/issues/34027
887 #[gpui::test]
888 async fn issue_34027_non_worktree_remote_file(cx: &mut TestAppContext) {
889 test_path_likes!(
890 cx,
891 vec![
892 (
893 path!("/"),
894 json!({
895 "file.txt": "",
896 }),
897 ),
898 (
899 path!("/test"),
900 json!({
901 "file.txt": "",
902 }),
903 ),
904 ],
905 vec![path!("/test")],
906 {
907 // Note: Opening a non-worktree file adds that file as a single file worktree.
908 test_remote!("file.txt", "/file.txt", "/", BackgroundPathResolution);
909 test_remote!("/test/file.txt", "/test/file.txt", "/");
910 }
911 )
912 }
913
914 // https://github.com/zed-industries/zed/issues/39159
915 #[gpui::test]
916 async fn issue_39159_remote_absolute_path_outside_worktree(cx: &mut TestAppContext) {
917 test_path_likes!(
918 cx,
919 vec![
920 (
921 path!("/tmp"),
922 json!({
923 "a.txt": "",
924 }),
925 ),
926 (
927 path!("/code/project"),
928 json!({
929 "src": {
930 "lib.rs": "",
931 },
932 }),
933 ),
934 ],
935 vec![path!("/code/project")],
936 {
937 test_remote!(
938 "/tmp/a.txt",
939 "/tmp/a.txt",
940 "/code/project",
941 BackgroundPathResolution
942 );
943 }
944 )
945 }
946
947 // See https://github.com/zed-industries/zed/issues/34027
948 #[gpui::test]
949 #[should_panic(expected = "Tooltip mismatch")]
950 async fn issue_34027_gaps(cx: &mut TestAppContext) {
951 test_path_likes!(
952 cx,
953 vec![(
954 path!("/project"),
955 json!({
956 "lib": {
957 "src": {
958 "main.rs": ""
959 },
960 },
961 "src": {
962 "main.rs": ""
963 },
964 }),
965 )],
966 vec![path!("/project")],
967 {
968 test!("main.rs", "/project/src/main.rs", "/project");
969 test!("main.rs", "/project/lib/src/main.rs", "/project/lib");
970 }
971 )
972 }
973
974 // See https://github.com/zed-industries/zed/issues/34027
975 #[gpui::test]
976 #[should_panic(expected = "Tooltip mismatch")]
977 async fn issue_34027_overlap(cx: &mut TestAppContext) {
978 test_path_likes!(
979 cx,
980 vec![(
981 path!("/project"),
982 json!({
983 "lib": {
984 "src": {
985 "main.rs": ""
986 },
987 },
988 "src": {
989 "main.rs": ""
990 },
991 }),
992 )],
993 vec![path!("/project")],
994 {
995 // Finds "/project/src/main.rs"
996 test!(
997 "src/main.rs",
998 "/project/lib/src/main.rs",
999 "/project/lib/src"
1000 );
1001 }
1002 )
1003 }
1004 }
1005}
1006