docs_preprocessor.rs

 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}