Make workspace::SendKeystrokes not use layout key equivalents (#47061)

Closes #46509

Release Notes:

- Fixed: `workspace::SendKeystrokes` would not allow remapping keys in
different keyboard layouts

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
everdroneandConrad Irwin authored and GitHub committed 2026-01-27 03:28:00 +00:00
1 parent cb84a955ed
commit a74abd34f1
2 files changed
+120 -1

No files matched your search

+119
View File
@@ -2540,6 +2540,125 @@ async fn test_deactivate(cx: &mut gpui::TestAppContext) {
});
}
// workspace::SendKeystrokes should pass literal keystrokes without triggering vim motions.
// When sending `" _ x`, the `_` should select the blackhole register, not trigger
// vim::StartOfLineDownward.
#[gpui::test]
async fn test_send_keystrokes_underscore_is_literal_46509(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
// Bind a key to send `" _ x` which should:
// `"` - start register selection
// `_` - select blackhole register (NOT vim::StartOfLineDownward)
// `x` - delete character into blackhole register
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g x",
workspace::SendKeystrokes("\" _ x".to_string()),
Some("VimControl"),
)])
});
cx.set_state("helˇlo", Mode::Normal);
cx.simulate_keystrokes("g x");
cx.run_until_parked();
cx.assert_state("helˇo", Mode::Normal);
}
#[gpui::test]
async fn test_send_keystrokes_no_key_equivalent_mapping_46509(cx: &mut gpui::TestAppContext) {
use collections::HashMap;
use gpui::{KeybindingKeystroke, Keystroke, PlatformKeyboardMapper};
// create a mock Danish keyboard mapper
// on Danish keyboards, the macOS key equivalents mapping includes: '{' -> 'Æ' and '}' -> 'Ø'
// this means the `{` character is produced by the key labeled `Æ` (with shift modifier)
struct DanishKeyboardMapper;
impl PlatformKeyboardMapper for DanishKeyboardMapper {
fn map_key_equivalent(
&self,
mut keystroke: Keystroke,
use_key_equivalents: bool,
) -> KeybindingKeystroke {
if use_key_equivalents {
if keystroke.key == "{" {
keystroke.key = "Æ".to_string();
}
if keystroke.key == "}" {
keystroke.key = "Ø".to_string();
}
}
KeybindingKeystroke::from_keystroke(keystroke)
}
fn get_key_equivalents(&self) -> Option<&HashMap<char, char>> {
None
}
}
let mapper = DanishKeyboardMapper;
let keystroke_brace = Keystroke::parse("{").unwrap();
let mapped_with_bug = mapper.map_key_equivalent(keystroke_brace.clone(), true);
assert_eq!(
mapped_with_bug.key(),
"Æ",
"BUG: With use_key_equivalents=true, {{ is mapped to Æ on Danish keyboard"
);
// Fixed behavior, where the literal `{` character is preserved
let mapped_fixed = mapper.map_key_equivalent(keystroke_brace.clone(), false);
assert_eq!(
mapped_fixed.key(),
"{",
"FIX: With use_key_equivalents=false, {{ stays as {{"
);
// Same applies to }
let keystroke_close = Keystroke::parse("}").unwrap();
let mapped_close_bug = mapper.map_key_equivalent(keystroke_close.clone(), true);
assert_eq!(mapped_close_bug.key(), "Ø");
let mapped_close_fixed = mapper.map_key_equivalent(keystroke_close.clone(), false);
assert_eq!(mapped_close_fixed.key(), "}");
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g p",
workspace::SendKeystrokes("{".to_string()),
Some("vim_mode == normal"),
)])
});
cx.set_state(
indoc! {"
first paragraph
second paragraphˇ
third paragraph
"},
Mode::Normal,
);
cx.simulate_keystrokes("g p");
cx.run_until_parked();
cx.assert_state(
indoc! {"
first paragraph
ˇ
second paragraph
third paragraph
"},
Mode::Normal,
);
}
#[gpui::test]
async fn test_project_search_opens_in_normal_mode(cx: &mut gpui::TestAppContext) {
VimTestContext::init(cx);
+1 -1
View File
@@ -2722,7 +2722,7 @@ impl Workspace {
.flat_map(|k| Keystroke::parse(k).log_err())
.map(|k| {
cx.keyboard_mapper()
.map_key_equivalent(k, true)
.map_key_equivalent(k, false)
.inner()
.clone()
})