Accept null as a valid action, to disable a keystroke

co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
Kirill Bulatov 2023-07-04 20:58:33 +03:00
parent 76af424d79
commit 4c51ab8a25
4 changed files with 197 additions and 21 deletions

View file

@ -1,7 +1,7 @@
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{anyhow, Context, Result};
use collections::BTreeMap;
use gpui::{keymap_matcher::Binding, AppContext};
use gpui::{keymap_matcher::Binding, AppContext, NoAction};
use schemars::{
gen::{SchemaGenerator, SchemaSettings},
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
@ -11,18 +11,18 @@ use serde::Deserialize;
use serde_json::Value;
use util::{asset_str, ResultExt};
#[derive(Deserialize, Default, Clone, JsonSchema)]
#[derive(Debug, Deserialize, Default, Clone, JsonSchema)]
#[serde(transparent)]
pub struct KeymapFile(Vec<KeymapBlock>);
#[derive(Deserialize, Default, Clone, JsonSchema)]
#[derive(Debug, Deserialize, Default, Clone, JsonSchema)]
pub struct KeymapBlock {
#[serde(default)]
context: Option<String>,
bindings: BTreeMap<String, KeymapAction>,
}
#[derive(Deserialize, Default, Clone)]
#[derive(Debug, Deserialize, Default, Clone)]
#[serde(transparent)]
pub struct KeymapAction(Value);
@ -61,21 +61,22 @@ impl KeymapFile {
// We want to deserialize the action data as a `RawValue` so that we can
// deserialize the action itself dynamically directly from the JSON
// string. But `RawValue` currently does not work inside of an untagged enum.
if let Value::Array(items) = action {
let Ok([name, data]): Result<[serde_json::Value; 2], _> = items.try_into() else {
return Some(Err(anyhow!("Expected array of length 2")));
};
let serde_json::Value::String(name) = name else {
return Some(Err(anyhow!("Expected first item in array to be a string.")))
};
cx.deserialize_action(
&name,
Some(data),
)
} else if let Value::String(name) = action {
cx.deserialize_action(&name, None)
} else {
return Some(Err(anyhow!("Expected two-element array, got {:?}", action)));
match action {
Value::Array(items) => {
let Ok([name, data]): Result<[serde_json::Value; 2], _> = items.try_into() else {
return Some(Err(anyhow!("Expected array of length 2")));
};
let serde_json::Value::String(name) = name else {
return Some(Err(anyhow!("Expected first item in array to be a string.")))
};
cx.deserialize_action(
&name,
Some(data),
)
},
Value::String(name) => cx.deserialize_action(&name, None),
Value::Null => Ok(no_action()),
_ => return Some(Err(anyhow!("Expected two-element array, got {action:?}"))),
}
.with_context(|| {
format!(
@ -115,6 +116,10 @@ impl KeymapFile {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
..Default::default()
}),
Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Null))),
..Default::default()
}),
]),
..Default::default()
})),
@ -129,6 +134,10 @@ impl KeymapFile {
}
}
fn no_action() -> Box<dyn gpui::Action> {
Box::new(NoAction {})
}
#[cfg(test)]
mod tests {
use crate::KeymapFile;