Change keymap precedence to favor user (#37557)
Closes #35623 Previously if a base keymap had a `null` set to an action, leading to a `NoAction` being assigned to the keymap, if a user wanted to take advantage of that keymap (in this particular case, `cmd-2`), the keymap binding check would favor the `NoAction` over the user, since technically the context depth matched better. Instead, we should always prefer the user's settings over whatever base or default. Release Notes: - Fixed keymap precedence by favoring user settings over base keymap / configs.
This commit is contained in:
@@ -168,9 +168,21 @@ impl Keymap {
|
||||
|
||||
let mut bindings: SmallVec<[_; 1]> = SmallVec::new();
|
||||
let mut first_binding_index = None;
|
||||
|
||||
for (_, ix, binding) in matched_bindings {
|
||||
if is_no_action(&*binding.action) {
|
||||
break;
|
||||
// Only break if this is a user-defined NoAction binding
|
||||
// This allows user keymaps to override base keymap NoAction bindings
|
||||
if let Some(meta) = binding.meta {
|
||||
if meta.0 == 0 {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If no meta is set, assume it's a user binding for safety
|
||||
break;
|
||||
}
|
||||
// For non-user NoAction bindings, continue searching for user overrides
|
||||
continue;
|
||||
}
|
||||
bindings.push(binding.clone());
|
||||
first_binding_index.get_or_insert(ix);
|
||||
@@ -617,6 +629,33 @@ mod tests {
|
||||
assert!(!matched.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_context_precedence_with_same_source() {
|
||||
// Test case: User has both Workspace and Editor bindings for the same key
|
||||
// Editor binding should take precedence over Workspace binding
|
||||
let bindings = [
|
||||
KeyBinding::new("cmd-r", ActionAlpha {}, Some("Workspace")),
|
||||
KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor")),
|
||||
];
|
||||
|
||||
let mut keymap = Keymap::default();
|
||||
keymap.add_bindings(bindings);
|
||||
|
||||
// Test with context stack: [Workspace, Editor] (Editor is deeper)
|
||||
let (result, _) = keymap.bindings_for_input(
|
||||
&[Keystroke::parse("cmd-r").unwrap()],
|
||||
&[
|
||||
KeyContext::parse("Workspace").unwrap(),
|
||||
KeyContext::parse("Editor").unwrap(),
|
||||
],
|
||||
);
|
||||
|
||||
// Both bindings should be returned, but Editor binding should be first (highest precedence)
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result[0].action.partial_eq(&ActionBeta {})); // Editor binding first
|
||||
assert!(result[1].action.partial_eq(&ActionAlpha {})); // Workspace binding second
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bindings_for_action() {
|
||||
let bindings = [
|
||||
@@ -643,4 +682,32 @@ mod tests {
|
||||
assert_eq!(actual, expected, "{:?}", action);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_source_precedence_sorting() {
|
||||
// KeybindSource precedence: User (0) > Vim (1) > Base (2) > Default (3)
|
||||
// Test that user keymaps take precedence over default keymaps at the same context depth
|
||||
let mut keymap = Keymap::default();
|
||||
|
||||
// Add a default keymap binding first
|
||||
let mut default_binding = KeyBinding::new("cmd-r", ActionAlpha {}, Some("Editor"));
|
||||
default_binding.set_meta(KeyBindingMetaIndex(3)); // Default source
|
||||
keymap.add_bindings([default_binding]);
|
||||
|
||||
// Add a user keymap binding
|
||||
let mut user_binding = KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor"));
|
||||
user_binding.set_meta(KeyBindingMetaIndex(0)); // User source
|
||||
keymap.add_bindings([user_binding]);
|
||||
|
||||
// Test with Editor context stack
|
||||
let (result, _) = keymap.bindings_for_input(
|
||||
&[Keystroke::parse("cmd-r").unwrap()],
|
||||
&[KeyContext::parse("Editor").unwrap()],
|
||||
);
|
||||
|
||||
// User binding should take precedence over default binding
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result[0].action.partial_eq(&ActionBeta {}));
|
||||
assert!(result[1].action.partial_eq(&ActionAlpha {}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,11 +70,14 @@ impl VimTestContext {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings::<VimModeSetting>(cx, |s| s.vim_mode = Some(enabled));
|
||||
});
|
||||
let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
|
||||
let mut default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
|
||||
"keymaps/default-macos.json",
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
for key_binding in &mut default_key_bindings {
|
||||
key_binding.set_meta(settings::KeybindSource::Default.meta());
|
||||
}
|
||||
cx.bind_keys(default_key_bindings);
|
||||
if enabled {
|
||||
let vim_key_bindings = settings::KeymapFile::load_asset(
|
||||
|
||||
Reference in New Issue
Block a user