From 7b14c7be5c2306febcecad7b3370521be2b870db Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Wed, 18 Mar 2026 12:36:23 -0700 Subject: [PATCH] pane: Add "Reveal in Finder" to tab context menu (#51615) The tab context menu has "Copy Path", "Open in Terminal", and "Reveal In Project Panel" but no way to reveal the file in the system file manager. This action already exists in three other context menus (editor right-click, project panel, outline panel) but was missing from tab right-click. ## Changes Adds a platform-specific entry to the tab context menu: - **macOS:** "Reveal in Finder" - **Windows:** "Reveal in File Explorer" - **Linux:** "Reveal in File Manager" Placed after "Copy Relative Path" and before "Pin Tab". Gated behind `is_local` (including WSL with host interop) to match the project panel's behavior. Uses the existing `project.reveal_path()` infrastructure, which handles platform-specific file manager invocation and WSL path conversion. ## Prior art Every major editor has this in the tab context menu: - VS Code: "Reveal in Finder" (macOS) / "Reveal in File Explorer" (Windows) - JetBrains IDEs: Right-click tab -> "Open in" -> "Finder" - Sublime Text: Right-click tab -> "Reveal in Finder" Zed already has this in the editor body right-click menu (`Cmd-K R`), project panel (`Alt-Cmd-R`), and outline panel. The tab context menu was the only place it was missing. This contribution was developed with AI assistance (Claude Code). Release Notes: - Added "Reveal in Finder" to the tab context menu ## Screenshot ![Reveal in Finder in tab context menu](https://github.com/mvanhorn/zed/releases/download/untagged-02ded227b30e3bcce8db/IMG_8537.png) --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- crates/editor/src/actions.rs | 2 -- crates/editor/src/editor.rs | 1 + crates/editor/src/mouse_context_menu.rs | 8 +----- crates/outline_panel/src/outline_panel.rs | 8 +----- crates/project_panel/src/project_panel.rs | 8 +----- crates/ui/src/utils.rs | 11 +++++++++ crates/workspace/src/pane.rs | 30 ++++++++++++++++++++++- crates/zed_actions/src/lib.rs | 2 ++ 8 files changed, 46 insertions(+), 24 deletions(-) diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index d1d2390fd86755c2fdf5fad28552f674b01932ec..a51ee787f8b531aa650d13afee2cf9550c2a26fa 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -699,8 +699,6 @@ actions!( Rename, /// Restarts the language server for the current file. RestartLanguageServer, - /// Reveals the current file in the system file manager. - RevealInFileManager, /// Reverses the order of selected lines. ReverseLines, /// Reloads the file from disk. diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 99814b3cbce9914e1c2634bc1456267912c4d744..b4f88ea50136cc169761c0899444e7544804bf4d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -221,6 +221,7 @@ use workspace::{ notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt}, searchable::SearchEvent, }; +pub use zed_actions::editor::RevealInFileManager; use zed_actions::editor::{MoveDown, MoveUp}; use crate::{ diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index af7b256d78ecea90112ca9d23175c9d33f134d94..2ddbb48b5fc434f65521c6dd230537aedb71dabb 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -286,13 +286,7 @@ pub fn deploy_context_menu( .separator() .action_disabled_when( !has_reveal_target, - if cfg!(target_os = "macos") { - "Reveal in Finder" - } else if cfg!(target_os = "windows") { - "Reveal in File Explorer" - } else { - "Reveal in File Manager" - }, + ui::utils::reveal_in_file_manager_label(false), Box::new(RevealInFileManager), ) .when(is_markdown, |builder| { diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index e35301ee16b3631290e232ac0838dc781e88aebf..c4d491db923b4151855d2c45461e370d061537ab 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -1490,13 +1490,7 @@ impl OutlinePanel { let context_menu = ContextMenu::build(window, cx, |menu, _, _| { menu.context(self.focus_handle.clone()) .action( - if cfg!(target_os = "macos") { - "Reveal in Finder" - } else if cfg!(target_os = "windows") { - "Reveal in File Explorer" - } else { - "Reveal in File Manager" - }, + ui::utils::reveal_in_file_manager_label(false), Box::new(RevealInFileManager), ) .action("Open in Terminal", Box::new(OpenInTerminal)) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 652740582da8b064b8fb3036180cd386d4e2ea8f..eafaf208d98b1c37727b6f3df2224be37470a37d 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1201,13 +1201,7 @@ impl ProjectPanel { .separator() .when(is_local, |menu| { menu.action( - if cfg!(target_os = "macos") && !is_remote { - "Reveal in Finder" - } else if cfg!(target_os = "windows") && !is_remote { - "Reveal in File Explorer" - } else { - "Reveal in File Manager" - }, + ui::utils::reveal_in_file_manager_label(is_remote), Box::new(RevealInFileManager), ) }) diff --git a/crates/ui/src/utils.rs b/crates/ui/src/utils.rs index b73915162f9e6be937af7323e95fb9d6a82d6c52..2f2a148e1985d026371c96297eb92cc4ec079a3b 100644 --- a/crates/ui/src/utils.rs +++ b/crates/ui/src/utils.rs @@ -23,3 +23,14 @@ pub use with_rem_size::*; pub fn is_light(cx: &mut App) -> bool { cx.theme().appearance.is_light() } + +/// Returns the platform-appropriate label for the "reveal in file manager" action. +pub fn reveal_in_file_manager_label(is_remote: bool) -> &'static str { + if cfg!(target_os = "macos") && !is_remote { + "Reveal in Finder" + } else if cfg!(target_os = "windows") && !is_remote { + "Reveal in File Explorer" + } else { + "Reveal in File Manager" + } +} diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 5f1177e58d5dcb0e8617ac1eb6068b7a9858685c..54f83bbbe64309a8a00f74d68508a403bc7003f9 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -3192,6 +3192,7 @@ impl Pane { }); let entry_abs_path = pane.read(cx).entry_abs_path(entry, cx); + let reveal_path = entry_abs_path.clone(); let parent_abs_path = entry_abs_path .as_deref() .and_then(|abs_path| Some(abs_path.parent()?.to_path_buf())); @@ -3201,6 +3202,15 @@ impl Pane { let visible_in_project_panel = relative_path.is_some() && worktree.is_some_and(|worktree| worktree.read(cx).is_visible()); + let is_local = pane.read(cx).project.upgrade().is_some_and(|project| { + let project = project.read(cx); + project.is_local() || project.is_via_wsl_with_host_interop(cx) + }); + let is_remote = pane + .read(cx) + .project + .upgrade() + .is_some_and(|project| project.read(cx).is_remote()); let entry_id = entry.to_proto(); @@ -3233,8 +3243,26 @@ impl Pane { }), ) }) + .when(is_local, |menu| { + menu.when_some(reveal_path, |menu, reveal_path| { + menu.separator().entry( + ui::utils::reveal_in_file_manager_label(is_remote), + Some(Box::new( + zed_actions::editor::RevealInFileManager, + )), + window.handler_for(&pane, move |pane, _, cx| { + if let Some(project) = pane.project.upgrade() { + project.update(cx, |project, cx| { + project.reveal_path(&reveal_path, cx); + }); + } else { + cx.reveal_path(&reveal_path); + } + }), + ) + }) + }) .map(pin_tab_entries) - .separator() .when(visible_in_project_panel, |menu| { menu.entry( "Reveal In Project Panel", diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index fe1575bd155031c82c0610e2d96f2dc7e1a6ec3d..0306854c0ad546998c122bc79aa9caf18bbace81 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -197,6 +197,8 @@ pub mod editor { MoveUp, /// Moves cursor down. MoveDown, + /// Reveals the current file in the system file manager. + RevealInFileManager, ] ); }