ZIm/crates/docs_preprocessor/src/docs_preprocessor.rs
Michael Sloan 711dc21eb2
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.
2025-01-18 22:27:08 +00:00

93 lines
2.6 KiB
Rust

use anyhow::Result;
use mdbook::book::{Book, BookItem};
use mdbook::errors::Error;
use mdbook::preprocess::{Preprocessor, PreprocessorContext as MdBookContext};
use settings::KeymapFile;
use std::sync::Arc;
use util::asset_str;
mod templates;
use templates::{ActionTemplate, KeybindingTemplate, Template};
pub struct PreprocessorContext {
macos_keymap: Arc<KeymapFile>,
linux_keymap: Arc<KeymapFile>,
}
impl PreprocessorContext {
pub fn new() -> Result<Self> {
let macos_keymap = Arc::new(load_keymap("keymaps/default-macos.json")?);
let linux_keymap = Arc::new(load_keymap("keymaps/default-linux.json")?);
Ok(Self {
macos_keymap,
linux_keymap,
})
}
pub fn find_binding(&self, os: &str, action: &str) -> Option<String> {
let keymap = match os {
"macos" => &self.macos_keymap,
"linux" => &self.linux_keymap,
_ => return None,
};
keymap.sections().find_map(|section| {
section.bindings().find_map(|(keystroke, a)| {
if a.to_string() == action {
Some(keystroke.to_string())
} else {
None
}
})
})
}
}
fn load_keymap(asset_path: &str) -> Result<KeymapFile> {
let content = asset_str::<settings::SettingsAssets>(asset_path);
KeymapFile::parse(content.as_ref())
}
pub struct ZedDocsPreprocessor {
context: PreprocessorContext,
templates: Vec<Box<dyn Template>>,
}
impl ZedDocsPreprocessor {
pub fn new() -> Result<Self> {
let context = PreprocessorContext::new()?;
let templates: Vec<Box<dyn Template>> = vec![
Box::new(KeybindingTemplate::new()),
Box::new(ActionTemplate::new()),
];
Ok(Self { context, templates })
}
fn process_content(&self, content: &str) -> String {
let mut processed = content.to_string();
for template in &self.templates {
processed = template.process(&self.context, &processed);
}
processed
}
}
impl Preprocessor for ZedDocsPreprocessor {
fn name(&self) -> &str {
"zed-docs-preprocessor"
}
fn run(&self, _ctx: &MdBookContext, mut book: Book) -> Result<Book, Error> {
book.for_each_mut(|item| {
if let BookItem::Chapter(chapter) = item {
chapter.content = self.process_content(&chapter.content);
}
});
Ok(book)
}
fn supports_renderer(&self, renderer: &str) -> bool {
renderer != "not-supported"
}
}