diff --git a/crates/ide-index/src/lib.rs b/crates/ide-index/src/lib.rs index 4e4794538..d0b08799b 100644 --- a/crates/ide-index/src/lib.rs +++ b/crates/ide-index/src/lib.rs @@ -218,9 +218,11 @@ impl ProjectIndex { self.shared.enqueue(Job::SaveRecent); } - /// Recently opened files, most recent first. + /// Recently opened files, most recent first; files deleted since (also while + /// the IDE was closed) are left out. Virtual documents are always listed. pub fn recent_files(&self) -> Vec { - self.shared.recent.lock().clone() + let recent = self.shared.recent.lock().clone(); + recent.into_iter().filter(|p| p.to_str().is_some_and(ide_core::text::is_virtual_uri) || p.exists()).collect() } /// Blocks until the initial scan and every queued update has been applied diff --git a/crates/ide-index/src/tests.rs b/crates/ide-index/src/tests.rs index 8094cdc81..3e45fc50a 100644 --- a/crates/ide-index/src/tests.rs +++ b/crates/ide-index/src/tests.rs @@ -178,6 +178,19 @@ fn symbol_ranking_is_idea_like() { assert!(idx.find_symbols("", SymbolFilter::All, 5).is_empty()); } +#[test] +fn recent_files_skip_deleted_files_but_keep_virtual_documents() { + let fx = sample(); + let idx = fx.open(); + let app = fx.root.join("web/app.py"); + let virt = PathBuf::from("jdt://contents/rt.jar/java.lang/String.class"); + idx.mark_recent(&app); + idx.mark_recent(&virt); + assert_eq!(idx.recent_files(), vec![virt.clone(), app.clone()]); + std::fs::remove_file(&app).unwrap(); + assert_eq!(idx.recent_files(), vec![virt]); +} + #[test] fn persistence_skips_unchanged_files_on_reopen() { let fx = sample(); diff --git a/src/features/project-tree/contextMenu.test.ts b/src/features/project-tree/contextMenu.test.ts index d269d7905..fcb76d43e 100644 --- a/src/features/project-tree/contextMenu.test.ts +++ b/src/features/project-tree/contextMenu.test.ts @@ -84,6 +84,7 @@ describe("project view context menu", () => { "Copy Path/Reference…", "Paste", "-", + "Jump to Source", "Find Usages", "Find in Files…", "Replace in Files…", diff --git a/src/features/project-tree/contextMenu.ts b/src/features/project-tree/contextMenu.ts index 7ce9108bc..635e417b9 100644 --- a/src/features/project-tree/contextMenu.ts +++ b/src/features/project-tree/contextMenu.ts @@ -20,6 +20,7 @@ import { findFileUsages, folderOf, openCopyDialog, + openFile, openMoveDialog, openRenameDialog, paste, @@ -196,8 +197,15 @@ export function contextMenuItems(nodes: ProjectNode[]): MenuItem[] { { kind: "separator" }, ); - // Find Usages / Find in Files… / Replace in Files… + // Jump to Source / Find Usages / Find in Files… / Replace in Files… push( + files.length > 0 && { + id: "jumpToSource", + label: "Jump to Source", + icon: "edit", + shortcut: keyLabel("project.open"), + run: () => files.forEach((f) => void openFile(f.path)), + }, inProject && singleFile && { id: "findUsages",