From 7f81bfb6b7ad3dfecc5df67b8ce6519f3179fdc8 Mon Sep 17 00:00:00 2001 From: ElKowar Date: Mon, 22 Apr 2024 22:24:25 +0200 Subject: [PATCH] Make keymaps reusable across platforms (#10811) This PR includes two relevant changes: - Platform binds (super, windows, cmd) will now parse on all platforms, regardless of which one is being used. While very counter-intuitive (this means that `cmd-d` will actually be triggered by `win-d` on windows) this makes it possible to reuse keymap files across platforms easily - There is now a KeyContext `os == linux`, `os == macos` or `os == windows` available in keymaps. This allows users to specify certain blocks of keybinds only for one OS, allowing you to minimize the amount of keymappings that you have to re-configure for each platform. Release Notes: - Added `os` KeyContext, set to either `linux`, `macos` or `windows` - Fixed keymap parsing errors when `cmd` was used on linux, `super` was used on mac, etc. --- crates/editor/src/editor.rs | 2 +- crates/extensions_ui/src/extensions_ui.rs | 2 +- crates/gpui/src/keymap/context.rs | 14 ++++++++++++++ crates/gpui/src/platform/keystroke.rs | 7 +------ crates/project_panel/src/project_panel.rs | 2 +- crates/search/src/buffer_search.rs | 2 +- crates/terminal_view/src/terminal_view.rs | 2 +- crates/vim/src/state.rs | 2 +- crates/workspace/src/dock.rs | 2 +- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/workspace.rs | 2 +- 11 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3356d507cd1a47032689e7abc5156fdcc4e491e4..6cd9680d0e074a42b63c98c8df700f45c7995e7e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1550,7 +1550,7 @@ impl Editor { } fn key_context(&self, cx: &AppContext) -> KeyContext { - let mut key_context = KeyContext::default(); + let mut key_context = KeyContext::new_with_defaults(); key_context.add("Editor"); let mode = match self.mode { EditorMode::SingleLine => "single_line", diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 41d82a1405f2770dd0b3726501e29d9912662c04..25ef796784d631e70d15aa100fb8d64ebc4d4bbb 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -700,7 +700,7 @@ impl ExtensionsPage { } fn render_search(&self, cx: &mut ViewContext) -> Div { - let mut key_context = KeyContext::default(); + let mut key_context = KeyContext::new_with_defaults(); key_context.add("BufferSearchBar"); let editor_border = if self.query_contains_error { diff --git a/crates/gpui/src/keymap/context.rs b/crates/gpui/src/keymap/context.rs index 6c22fa9fd69bfacf650e549d786ebfc0cb83de21..6ac22d2162df8270bad4f7b35d2cb3856cddf4fd 100644 --- a/crates/gpui/src/keymap/context.rs +++ b/crates/gpui/src/keymap/context.rs @@ -25,6 +25,20 @@ impl<'a> TryFrom<&'a str> for KeyContext { } impl KeyContext { + /// Initialize a new [`KeyContext`] that contains an `os` key set to either `macos`, `linux`, `windows` or `unknown`. + pub fn new_with_defaults() -> Self { + let mut context = Self::default(); + #[cfg(target_os = "macos")] + context.set("os", "macos"); + #[cfg(target_os = "linux")] + context.set("os", "linux"); + #[cfg(target_os = "windows")] + context.set("os", "windows"); + #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] + context.set("os", "unknown"); + context + } + /// Parse a key context from a string. /// The key context format is very simple: /// - either a single identifier, such as `StatusBar` diff --git a/crates/gpui/src/platform/keystroke.rs b/crates/gpui/src/platform/keystroke.rs index 5f3d75f72ef48de2e5f95281aa8bde5e925b137c..55f8658cd3d40e0999bc0bebf5721af153ac5967 100644 --- a/crates/gpui/src/platform/keystroke.rs +++ b/crates/gpui/src/platform/keystroke.rs @@ -74,12 +74,7 @@ impl Keystroke { "alt" => alt = true, "shift" => shift = true, "fn" => function = true, - #[cfg(target_os = "macos")] - "cmd" => platform = true, - #[cfg(target_os = "linux")] - "super" => platform = true, - #[cfg(target_os = "windows")] - "win" => platform = true, + "cmd" | "super" | "win" => platform = true, _ => { if let Some(next) = components.peek() { if next.is_empty() && source.ends_with('-') { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index b08f46b7e7829fe2ff5090ed14b59af40d168f6a..f592103b20088c30d07d240ec5651ebf96d649cd 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1698,7 +1698,7 @@ impl ProjectPanel { } fn dispatch_context(&self, cx: &ViewContext) -> KeyContext { - let mut dispatch_context = KeyContext::default(); + let mut dispatch_context = KeyContext::new_with_defaults(); dispatch_context.add("ProjectPanel"); dispatch_context.add("menu"); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 71ba5a65c35bab851fed86b5412a9dba65748baa..0e0c33c26530d57c91910033445f629c40a30558 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -188,7 +188,7 @@ impl Render for BufferSearchBar { let should_show_replace_input = self.replace_enabled && supported_options.replacement; let in_replace = self.replacement_editor.focus_handle(cx).is_focused(cx); - let mut key_context = KeyContext::default(); + let mut key_context = KeyContext::new_with_defaults(); key_context.add("BufferSearchBar"); if in_replace { key_context.add("in_replace"); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index cc5c54eca90147bf262faaa45b812782e6fc5dd9..6c3908305fa877636591161e0de5144e6f62076f 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -348,7 +348,7 @@ impl TerminalView { } fn dispatch_context(&self, cx: &AppContext) -> KeyContext { - let mut dispatch_context = KeyContext::default(); + let mut dispatch_context = KeyContext::new_with_defaults(); dispatch_context.add("Terminal"); let mode = self.terminal.read(cx).last_content.mode; diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 82d4fc9af3d8881e25307ed215bdeaa4f461c7c8..9ece818b16cafd9276be3ccc46eb63da97487c59 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -194,7 +194,7 @@ impl EditorState { } pub fn keymap_context_layer(&self) -> KeyContext { - let mut context = KeyContext::default(); + let mut context = KeyContext::new_with_defaults(); context.set( "vim_mode", match self.mode { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 4dad415445676f51efae2957facc12f04f7f62d3..e6ece91f96dfaaeb6eb6fec761e42510853d0e8f 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -552,7 +552,7 @@ impl Dock { } fn dispatch_context() -> KeyContext { - let mut dispatch_context = KeyContext::default(); + let mut dispatch_context = KeyContext::new_with_defaults(); dispatch_context.add("Dock"); dispatch_context diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c2d2b79961b60cbb83c806414727bc6573e8ef4e..de561320fae6632162d4717016c683d77030d4ce 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1944,7 +1944,7 @@ impl FocusableView for Pane { impl Render for Pane { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - let mut key_context = KeyContext::default(); + let mut key_context = KeyContext::new_with_defaults(); key_context.add("Pane"); if self.active_item().is_none() { key_context.add("EmptyPane"); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9c6e07b941a86cbdbc3f72f3cf24baa368be481d..303323f7351bc2754f350ee472b7ede758daef71 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3945,7 +3945,7 @@ struct DraggedDock(DockPosition); impl Render for Workspace { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - let mut context = KeyContext::default(); + let mut context = KeyContext::new_with_defaults(); context.add("Workspace"); let centered_layout = self.centered_layout && self.center.panes().len() == 1