Load all key bindings that parse and use markdown in error notifications (#23113)
* Collects and reports all parse errors * Shares parsed `KeyBindingContextPredicate` among the actions. * Updates gpui keybinding and action parsing to return structured errors. * Renames "block" to "section" to match the docs, as types like `KeymapSection` are shown in `json-language-server` hovers. * Removes wrapping of `context` and `use_key_equivalents` fields so that `json-language-server` auto-inserts `""` and `false` instead of `null`. * Updates `add_to_cx` to take `&self`, so that the user keymap doesn't get unnecessarily cloned. In retrospect I wish I'd just switched to using TreeSitter to do the parsing and provide proper diagnostics. This is tracked in #23333 Release Notes: - Improved handling of errors within the user keymap file. Parse errors within context, keystrokes, or actions no longer prevent loading the key bindings that do parse.
This commit is contained in:
parent
c929533e00
commit
711dc21eb2
18 changed files with 552 additions and 196 deletions
|
@ -1,6 +1,8 @@
|
|||
use anyhow::anyhow;
|
||||
use serde::Deserialize;
|
||||
use std::fmt::Write;
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{Display, Write},
|
||||
};
|
||||
|
||||
/// A keystroke and associated metadata generated by the platform
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
|
||||
|
@ -18,6 +20,31 @@ pub struct Keystroke {
|
|||
pub key_char: Option<String>,
|
||||
}
|
||||
|
||||
/// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use
|
||||
/// markdown to display it.
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidKeystrokeError {
|
||||
/// The invalid keystroke.
|
||||
pub keystroke: String,
|
||||
}
|
||||
|
||||
impl Error for InvalidKeystrokeError {}
|
||||
|
||||
impl Display for InvalidKeystrokeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Invalid keystroke \"{}\". {}",
|
||||
self.keystroke, KEYSTROKE_PARSE_EXPECTED_MESSAGE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sentence explaining what keystroke parser expects, starting with "Expected ..."
|
||||
pub const KEYSTROKE_PARSE_EXPECTED_MESSAGE: &str = "Expected a sequence of modifiers \
|
||||
(`ctrl`, `alt`, `shift`, `fn`, `cmd`, `super`, or `win`) \
|
||||
followed by a key, separated by `-`.";
|
||||
|
||||
impl Keystroke {
|
||||
/// When matching a key we cannot know whether the user intended to type
|
||||
/// the key_char or the key itself. On some non-US keyboards keys we use in our
|
||||
|
@ -51,7 +78,7 @@ impl Keystroke {
|
|||
/// [ctrl-][alt-][shift-][cmd-][fn-]key[->key_char]
|
||||
/// key_char syntax is only used for generating test events,
|
||||
/// when matching a key with an key_char set will be matched without it.
|
||||
pub fn parse(source: &str) -> anyhow::Result<Self> {
|
||||
pub fn parse(source: &str) -> std::result::Result<Self, InvalidKeystrokeError> {
|
||||
let mut control = false;
|
||||
let mut alt = false;
|
||||
let mut shift = false;
|
||||
|
@ -78,7 +105,9 @@ impl Keystroke {
|
|||
key_char = Some(String::from(&next[1..]));
|
||||
components.next();
|
||||
} else {
|
||||
return Err(anyhow!("Invalid keystroke `{}`", source));
|
||||
return Err(InvalidKeystrokeError {
|
||||
keystroke: source.to_owned(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
key = Some(String::from(component));
|
||||
|
@ -87,8 +116,8 @@ impl Keystroke {
|
|||
}
|
||||
}
|
||||
|
||||
//Allow for the user to specify a keystroke modifier as the key itself
|
||||
//This sets the `key` to the modifier, and disables the modifier
|
||||
// Allow for the user to specify a keystroke modifier as the key itself
|
||||
// This sets the `key` to the modifier, and disables the modifier
|
||||
if key.is_none() {
|
||||
if shift {
|
||||
key = Some("shift".to_string());
|
||||
|
@ -108,7 +137,9 @@ impl Keystroke {
|
|||
}
|
||||
}
|
||||
|
||||
let key = key.ok_or_else(|| anyhow!("Invalid keystroke `{}`", source))?;
|
||||
let key = key.ok_or_else(|| InvalidKeystrokeError {
|
||||
keystroke: source.to_owned(),
|
||||
})?;
|
||||
|
||||
Ok(Keystroke {
|
||||
modifiers: Modifiers {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue