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,9 +1,12 @@
|
|||
use crate::SharedString;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::HashMap;
|
||||
pub use no_action::{is_no_action, NoAction};
|
||||
use serde_json::json;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Display,
|
||||
};
|
||||
|
||||
/// Actions are used to implement keyboard-driven UI.
|
||||
/// When you declare an action, you can bind keys to the action in the keymap and
|
||||
|
@ -97,6 +100,47 @@ impl dyn Action {
|
|||
}
|
||||
}
|
||||
|
||||
/// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use
|
||||
/// markdown to display it.
|
||||
#[derive(Debug)]
|
||||
pub enum ActionBuildError {
|
||||
/// Indicates that an action with this name has not been registered.
|
||||
NotFound {
|
||||
/// Name of the action that was not found.
|
||||
name: String,
|
||||
},
|
||||
/// Indicates that an error occurred while building the action, typically a JSON deserialization
|
||||
/// error.
|
||||
BuildError {
|
||||
/// Name of the action that was attempting to be built.
|
||||
name: String,
|
||||
/// Error that occurred while building the action.
|
||||
error: anyhow::Error,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::error::Error for ActionBuildError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
ActionBuildError::NotFound { .. } => None,
|
||||
ActionBuildError::BuildError { error, .. } => error.source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ActionBuildError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ActionBuildError::NotFound { name } => {
|
||||
write!(f, "Didn't find an action named \"{name}\"")
|
||||
}
|
||||
ActionBuildError::BuildError { name, error } => {
|
||||
write!(f, "Error while building action \"{name}\": {error}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ActionBuilder = fn(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>;
|
||||
|
||||
pub(crate) struct ActionRegistry {
|
||||
|
@ -201,7 +245,7 @@ impl ActionRegistry {
|
|||
.ok_or_else(|| anyhow!("no action type registered for {:?}", type_id))?
|
||||
.clone();
|
||||
|
||||
self.build_action(&name, None)
|
||||
Ok(self.build_action(&name, None)?)
|
||||
}
|
||||
|
||||
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
|
||||
|
@ -209,14 +253,20 @@ impl ActionRegistry {
|
|||
&self,
|
||||
name: &str,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<Box<dyn Action>> {
|
||||
) -> std::result::Result<Box<dyn Action>, ActionBuildError> {
|
||||
let build_action = self
|
||||
.by_name
|
||||
.get(name)
|
||||
.ok_or_else(|| anyhow!("No action type registered for {}", name))?
|
||||
.ok_or_else(|| ActionBuildError::NotFound {
|
||||
name: name.to_owned(),
|
||||
})?
|
||||
.build;
|
||||
(build_action)(params.unwrap_or_else(|| json!({})))
|
||||
.with_context(|| format!("Attempting to build action {}", name))
|
||||
(build_action)(params.unwrap_or_else(|| json!({}))).map_err(|e| {
|
||||
ActionBuildError::BuildError {
|
||||
name: name.to_owned(),
|
||||
error: e,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_action_names(&self) -> &[SharedString] {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue