From ae3880e71a86ef1ddc6218d4a87d683a95672c68 Mon Sep 17 00:00:00 2001 From: KorigamiK <72932688+KorigamiK@users.noreply.github.com> Date: Wed, 11 Sep 2024 00:06:36 +0530 Subject: [PATCH] Add ability to open files with system default application (#17231) --- assets/keymaps/default-linux.json | 1 + assets/keymaps/default-macos.json | 2 +- assets/keymaps/vim.json | 1 + crates/gpui/src/app.rs | 5 +++++ crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/linux/platform.rs | 13 +++++++++++++ crates/gpui/src/platform/mac/platform.rs | 14 ++++++++++++++ crates/gpui/src/platform/test/platform.rs | 4 ++++ crates/gpui/src/platform/windows/platform.rs | 13 +++++++++++++ crates/project_panel/src/project_panel.rs | 10 ++++++++++ 10 files changed, 63 insertions(+), 1 deletion(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index bd2ade4246..3c627d7803 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -553,6 +553,7 @@ "ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }], "ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }], "alt-ctrl-r": "project_panel::RevealInFileManager", + "ctrl-shift-enter": "project_panel::OpenWithSystem", "alt-shift-f": "project_panel::NewSearchInDirectory", "shift-down": "menu::SelectNext", "shift-up": "menu::SelectPrev", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index dec5cbd9f3..ed6ece0556 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -563,8 +563,8 @@ "cmd-backspace": ["project_panel::Trash", { "skip_prompt": true }], "cmd-delete": ["project_panel::Delete", { "skip_prompt": false }], "alt-cmd-r": "project_panel::RevealInFileManager", + "ctrl-shift-enter": "project_panel::OpenWithSystem", "cmd-alt-backspace": ["project_panel::Delete", { "skip_prompt": false }], - "alt-shift-f": "project_panel::NewSearchInDirectory", "shift-down": "menu::SelectNext", "shift-up": "menu::SelectPrev", diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index f863e8488a..54905b2267 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -493,6 +493,7 @@ "v": "project_panel::OpenPermanent", "p": "project_panel::Open", "x": "project_panel::RevealInFileManager", + "s": "project_panel::OpenWithSystem", "shift-g": "menu::SelectLast", "g g": "menu::SelectFirst", "-": "project_panel::SelectParent", diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2157f97634..564b893489 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -657,6 +657,11 @@ impl AppContext { self.platform.reveal_path(path) } + /// Opens the specified path with the system's default application. + pub fn open_with_system(&self, path: &Path) { + self.platform.open_with_system(path) + } + /// Returns whether the user has configured scrollbars to auto-hide at the platform level. pub fn should_auto_hide_scrollbars(&self) -> bool { self.platform.should_auto_hide_scrollbars() diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index cb54d9d47a..680c813078 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -149,6 +149,7 @@ pub(crate) trait Platform: 'static { ) -> oneshot::Receiver>>>; fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver>>; fn reveal_path(&self, path: &Path); + fn open_with_system(&self, path: &Path); fn on_quit(&self, callback: Box); fn on_reopen(&self, callback: Box); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 0aa17e534a..a0bd6b1d33 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -351,6 +351,19 @@ impl Platform for P { self.reveal_path(path.to_owned()); } + fn open_with_system(&self, path: &Path) { + let executor = self.background_executor().clone(); + let path = path.to_owned(); + executor + .spawn(async move { + let _ = std::process::Command::new("xdg-open") + .arg(path) + .spawn() + .expect("Failed to open file with xdg-open"); + }) + .detach(); + } + fn on_quit(&self, callback: Box) { self.with_common(|common| { common.callbacks.quit = Some(callback); diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index d03d8f0571..5873d8fe39 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -718,6 +718,20 @@ impl Platform for MacPlatform { } } + fn open_with_system(&self, path: &Path) { + let path = path.to_path_buf(); + self.0 + .lock() + .background_executor + .spawn(async move { + std::process::Command::new("open") + .arg(path) + .spawn() + .expect("Failed to open file"); + }) + .detach(); + } + fn on_quit(&self, callback: Box) { self.0.lock().quit = Some(callback); } diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 58ca694d89..3258ae9af5 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -318,6 +318,10 @@ impl Platform for TestPlatform { fn register_url_scheme(&self, _: &str) -> Task> { unimplemented!() } + + fn open_with_system(&self, _path: &Path) { + unimplemented!() + } } #[cfg(target_os = "windows")] diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 2dcaf72ef2..f8b3924e62 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -400,6 +400,19 @@ impl Platform for WindowsPlatform { .detach(); } + fn open_with_system(&self, path: &Path) { + let executor = self.background_executor().clone(); + let path = path.to_owned(); + executor + .spawn(async move { + let _ = std::process::Command::new("cmd") + .args(&["/c", "start", "", path.to_str().expect("path to string")]) + .spawn() + .expect("Failed to open file"); + }) + .detach(); + } + fn on_quit(&self, callback: Box) { self.state.borrow_mut().callbacks.quit = Some(callback); } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 56d524cdc7..c77a2170dd 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -146,6 +146,7 @@ actions!( CopyRelativePath, Duplicate, RevealInFileManager, + OpenWithSystem, Cut, Paste, Rename, @@ -500,6 +501,7 @@ impl ProjectPanel { .when(cfg!(not(target_os = "macos")), |menu| { menu.action("Reveal in File Manager", Box::new(RevealInFileManager)) }) + .action("Open in Default App", Box::new(OpenWithSystem)) .action("Open in Terminal", Box::new(OpenInTerminal)) .when(is_dir, |menu| { menu.separator() @@ -1497,6 +1499,13 @@ impl ProjectPanel { } } + fn open_system(&mut self, _: &OpenWithSystem, cx: &mut ViewContext) { + if let Some((worktree, entry)) = self.selected_entry(cx) { + let abs_path = worktree.abs_path().join(&entry.path); + cx.open_with_system(&abs_path); + } + } + fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext) { if let Some((worktree, entry)) = self.selected_sub_entry(cx) { let abs_path = worktree.abs_path().join(&entry.path); @@ -2711,6 +2720,7 @@ impl Render for ProjectPanel { }) .when(project.is_local_or_ssh(), |el| { el.on_action(cx.listener(Self::reveal_in_finder)) + .on_action(cx.listener(Self::open_system)) .on_action(cx.listener(Self::open_in_terminal)) }) .on_mouse_down(