use std::rc::Rc;
use serde::Deserialize;
use crate::{Action, App, DummyKeyboardMapper, KeyBinding, KeyBindingContextPredicate, NoAction};
#[derive(Debug, Deserialize)]
struct KeymapEntry {
key: String,
action: Option<String>,
#[serde(default)]
context: Option<String>,
}
pub fn load_keymap_json(cx: &mut App, json: &str) -> anyhow::Result<Vec<KeyBinding>> {
let entries: Vec<KeymapEntry> = serde_json_lenient::from_str(json)?;
entries.iter().map(|entry| load_entry(cx, entry)).collect()
}
fn load_entry(cx: &mut App, entry: &KeymapEntry) -> anyhow::Result<KeyBinding> {
let action: Box<dyn Action> = match &entry.action {
None => Box::new(NoAction {}),
Some(name) => cx.build_action(name, None)?,
};
let predicate: Option<Rc<KeyBindingContextPredicate>> = entry
.context
.as_deref()
.map(KeyBindingContextPredicate::parse)
.transpose()?
.map(Rc::new);
Ok(KeyBinding::load(
&entry.key,
action,
predicate,
false,
None,
&DummyKeyboardMapper,
)?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::is_no_action;
#[rgpui::test]
fn loads_bindings_and_null_disables(cx: &mut crate::TestAppContext) {
let json = r#"[
// 注释与尾逗号可写(lenient 解析)。
{ "key": "ctrl-s", "action": "input::Undo", "context": "Input" },
{ "key": "ctrl-k", "action": null },
]"#;
let bindings = cx.update(|cx| load_keymap_json(cx, json).expect("合法 keymap 必过"));
assert_eq!(bindings.len(), 2);
assert_eq!(bindings[0].action().name(), "input::Undo");
assert!(bindings[0].predicate().is_some());
assert!(is_no_action(bindings[1].action()));
}
#[rgpui::test]
fn unknown_action_errors(cx: &mut crate::TestAppContext) {
let json = r#"[{ "key": "ctrl-s", "action": "nope::Nope" }]"#;
cx.update(|cx| {
assert!(load_keymap_json(cx, json).is_err());
});
}
#[rgpui::test]
fn malformed_inputs_error(cx: &mut crate::TestAppContext) {
cx.update(|cx| {
assert!(load_keymap_json(cx, "not json").is_err());
assert!(load_keymap_json(cx, r#"{"key": "a"}"#).is_err());
let bad_predicate =
r#"[{ "key": "ctrl-s", "action": "input::Undo", "context": "&&&" }]"#;
assert!(load_keymap_json(cx, bad_predicate).is_err());
let empty = load_keymap_json(cx, "[]").expect("空数组合法");
assert!(empty.is_empty());
});
}
}