1use anyhow::Result;
2use mdbook::book::{Book, BookItem};
3use mdbook::errors::Error;
4use mdbook::preprocess::{Preprocessor, PreprocessorContext as MdBookContext};
5use settings::KeymapFile;
6use std::sync::Arc;
7use util::asset_str;
8
9mod templates;
10
11use templates::{ActionTemplate, KeybindingTemplate, Template};
12
13pub struct PreprocessorContext {
14 macos_keymap: Arc<KeymapFile>,
15 linux_keymap: Arc<KeymapFile>,
16}
17
18impl PreprocessorContext {
19 pub fn new() -> Result<Self> {
20 let macos_keymap = Arc::new(load_keymap("keymaps/default-macos.json")?);
21 let linux_keymap = Arc::new(load_keymap("keymaps/default-linux.json")?);
22 Ok(Self {
23 macos_keymap,
24 linux_keymap,
25 })
26 }
27
28 pub fn find_binding(&self, os: &str, action: &str) -> Option<String> {
29 let keymap = match os {
30 "macos" => &self.macos_keymap,
31 "linux" => &self.linux_keymap,
32 _ => return None,
33 };
34
35 // Find the binding in reverse order, as the last binding takes precedence.
36 keymap.sections().rev().find_map(|section| {
37 section.bindings().rev().find_map(|(keystroke, a)| {
38 if a.to_string() == action {
39 Some(keystroke.to_string())
40 } else {
41 None
42 }
43 })
44 })
45 }
46}
47
48fn load_keymap(asset_path: &str) -> Result<KeymapFile> {
49 let content = asset_str::<settings::SettingsAssets>(asset_path);
50 KeymapFile::parse(content.as_ref())
51}
52
53pub struct ZedDocsPreprocessor {
54 context: PreprocessorContext,
55 templates: Vec<Box<dyn Template>>,
56}
57
58impl ZedDocsPreprocessor {
59 pub fn new() -> Result<Self> {
60 let context = PreprocessorContext::new()?;
61 let templates: Vec<Box<dyn Template>> = vec![
62 Box::new(KeybindingTemplate::new()),
63 Box::new(ActionTemplate::new()),
64 ];
65 Ok(Self { context, templates })
66 }
67
68 fn process_content(&self, content: &str) -> String {
69 let mut processed = content.to_string();
70 for template in &self.templates {
71 processed = template.process(&self.context, &processed);
72 }
73 processed
74 }
75}
76
77impl Preprocessor for ZedDocsPreprocessor {
78 fn name(&self) -> &str {
79 "zed-docs-preprocessor"
80 }
81
82 fn run(&self, _ctx: &MdBookContext, mut book: Book) -> Result<Book, Error> {
83 book.for_each_mut(|item| {
84 if let BookItem::Chapter(chapter) = item {
85 chapter.content = self.process_content(&chapter.content);
86 }
87 });
88 Ok(book)
89 }
90
91 fn supports_renderer(&self, renderer: &str) -> bool {
92 renderer != "not-supported"
93 }
94}