diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index bd2ade4246b17cd50fbc8c86dad3a4ad8105d2ee..3c627d7803e1d5f5b13247defe587b85c027c0ce 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 dec5cbd9f398b9c26c7ac6b2cac86020531cb116..ed6ece0556e0381b42ac03b1e485a2262a4c9dc5 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 f863e8488a0e2853d7a088b25ac817467b30f95f..54905b22678cd133af47f370c4b411fa1d9e0c3f 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 2157f97634daaf370e12a57efbfa1a75d589603a..564b8934897734155a45a7b951fe320105b19f12 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 cb54d9d47a087315a31c4748e59682360f20ffa6..680c813078b86db571d71487f6f235475147f31a 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 0aa17e534ab760694c999b9b38cf1c56bbd27371..a0bd6b1d33d176feaabef386b8380f724c8f30aa 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 d03d8f0571f9b1873af948a560470b50b437875e..5873d8fe396d719e54b17b81852dac4c0cc64bce 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 58ca694d89ee025806a056977d08456c117feee8..3258ae9af59ab28b33bfad997e76803a6fa58194 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 2dcaf72ef251629a286bfa859e8bc2decb7dc9dc..f8b3924e6282b1339a5bf43276efa48e25d26fe5 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 56d524cdc7130c37c533bdb9433f11797593da20..c77a2170dd01cdbe79629f1e06225a01c28f29de 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(