keymap_file.rs

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