diff --git a/crates/docs_preprocessor/src/main.rs b/crates/docs_preprocessor/src/main.rs index 3eaae6a1d4..6655d86d25 100644 --- a/crates/docs_preprocessor/src/main.rs +++ b/crates/docs_preprocessor/src/main.rs @@ -336,17 +336,28 @@ fn is_missing_action(name: &str) -> bool { actions_available() && find_action_by_name(name).is_none() } -// Find the binding in reverse order, as the last binding takes precedence. +// Find the last binding (in keymap order) for the given action. +// Exact action matches are preferred over parameterized variants. fn find_binding_in_keymap(keymap: &KeymapFile, action: &str) -> Option { - keymap.sections().rev().find_map(|section| { - section.bindings().rev().find_map(|(keystroke, a)| { - if name_for_action(a.to_string()) == action { - Some(keystroke.to_string()) - } else { - None - } + let find = |predicate: &dyn Fn(&str) -> bool| { + keymap.sections().rev().find_map(|section| { + section.bindings().rev().find_map(|(keystroke, a)| { + if predicate(&a.to_string()) { + Some(keystroke.to_string()) + } else { + None + } + }) }) - }) + }; + + // Look for exact match + if let Some(binding) = find(&|a| a == action) { + return Some(binding); + } + + // Look for parameterized match + find(&|a| name_for_action(a.to_string()) == action) } fn find_binding(os: Os, action: &str) -> Option { @@ -872,3 +883,68 @@ fn keymap_schema_for_actions( &deprecation_messages, ) } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_find_binding_prefers_exact_match_over_parameterized() { + let keymap: KeymapFile = serde_json::from_value(json!([ + { + "bindings": { + "ctrl-tab": "agents_sidebar::ToggleThreadSwitcher", + "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }] + } + } + ])) + .unwrap(); + + let binding = find_binding_in_keymap(&keymap, "agents_sidebar::ToggleThreadSwitcher"); + assert_eq!(binding.as_deref(), Some("ctrl-tab")); + } + + #[test] + fn test_find_binding_falls_back_to_parameterized_match() { + let keymap: KeymapFile = serde_json::from_value(json!([ + { + "bindings": { + "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }] + } + } + ])) + .unwrap(); + + let binding = find_binding_in_keymap(&keymap, "agents_sidebar::ToggleThreadSwitcher"); + assert_eq!(binding.as_deref(), Some("ctrl-shift-tab")); + } + + #[test] + fn test_find_binding_prefers_exact_match_regardless_of_order() { + let keymap: KeymapFile = serde_json::from_value(json!([ + { + "bindings": { + "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }], + "ctrl-tab": "agents_sidebar::ToggleThreadSwitcher" + } + } + ])) + .unwrap(); + + let binding = find_binding_in_keymap(&keymap, "agents_sidebar::ToggleThreadSwitcher"); + assert_eq!(binding.as_deref(), Some("ctrl-tab")); + } + + #[test] + fn test_find_binding_later_section_overrides_earlier() { + let keymap: KeymapFile = serde_json::from_value(json!([ + { "bindings": { "ctrl-a": "some::Action" } }, + { "bindings": { "ctrl-b": "some::Action" } } + ])) + .unwrap(); + + let binding = find_binding_in_keymap(&keymap, "some::Action"); + assert_eq!(binding.as_deref(), Some("ctrl-b")); + } +}