keymap_file.rs

 1use anyhow::{Context, Result};
 2use assets::Assets;
 3use collections::BTreeMap;
 4use gpui::{keymap::Binding, MutableAppContext};
 5use serde::Deserialize;
 6use serde_json::value::RawValue;
 7
 8#[derive(Deserialize, Default, Clone)]
 9#[serde(transparent)]
10pub struct KeymapFile(BTreeMap<String, ActionsByKeystroke>);
11
12type ActionsByKeystroke = BTreeMap<String, Box<RawValue>>;
13
14#[derive(Deserialize)]
15struct ActionWithData<'a>(#[serde(borrow)] &'a str, #[serde(borrow)] &'a RawValue);
16
17impl KeymapFile {
18    pub fn load_defaults(cx: &mut MutableAppContext) {
19        for path in ["keymaps/default.json", "keymaps/vim.json"] {
20            Self::load(path, cx).unwrap();
21        }
22    }
23
24    pub fn load(asset_path: &str, cx: &mut MutableAppContext) -> Result<()> {
25        let content = Assets::get(asset_path).unwrap().data;
26        let content_str = std::str::from_utf8(content.as_ref()).unwrap();
27        Ok(serde_json::from_str::<Self>(content_str)?.add(cx)?)
28    }
29
30    pub fn add(self, cx: &mut MutableAppContext) -> Result<()> {
31        for (context, actions) in self.0 {
32            let context = if context == "*" { None } else { Some(context) };
33            cx.add_bindings(
34                actions
35                    .into_iter()
36                    .map(|(keystroke, action)| {
37                        let action = action.get();
38
39                        // This is a workaround for a limitation in serde: serde-rs/json#497
40                        // We want to deserialize the action data as a `RawValue` so that we can
41                        // deserialize the action itself dynamically directly from the JSON
42                        // string. But `RawValue` currently does not work inside of an untagged enum.
43                        let action = if action.starts_with('[') {
44                            let ActionWithData(name, data) = serde_json::from_str(action)?;
45                            cx.deserialize_action(name, Some(data.get()))
46                        } else {
47                            let name = serde_json::from_str(action)?;
48                            cx.deserialize_action(name, None)
49                        }
50                        .with_context(|| {
51                            format!(
52                            "invalid binding value for keystroke {keystroke}, context {context:?}"
53                        )
54                        })?;
55                        Binding::load(&keystroke, action, context.as_deref())
56                    })
57                    .collect::<Result<Vec<_>>>()?,
58            )
59        }
60        Ok(())
61    }
62}