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 keymap.sections().find_map(|section| {
36 section.bindings().find_map(|(keystroke, a)| {
37 if a.to_string() == action {
38 Some(keystroke.to_string())
39 } else {
40 None
41 }
42 })
43 })
44 }
45}
46
47fn load_keymap(asset_path: &str) -> Result<KeymapFile> {
48 let content = asset_str::<settings::SettingsAssets>(asset_path);
49 KeymapFile::parse(content.as_ref())
50}
51
52pub struct ZedDocsPreprocessor {
53 context: PreprocessorContext,
54 templates: Vec<Box<dyn Template>>,
55}
56
57impl ZedDocsPreprocessor {
58 pub fn new() -> Result<Self> {
59 let context = PreprocessorContext::new()?;
60 let templates: Vec<Box<dyn Template>> = vec![
61 Box::new(KeybindingTemplate::new()),
62 Box::new(ActionTemplate::new()),
63 ];
64 Ok(Self { context, templates })
65 }
66
67 fn process_content(&self, content: &str) -> String {
68 let mut processed = content.to_string();
69 for template in &self.templates {
70 processed = template.process(&self.context, &processed);
71 }
72 processed
73 }
74}
75
76impl Preprocessor for ZedDocsPreprocessor {
77 fn name(&self) -> &str {
78 "zed-docs-preprocessor"
79 }
80
81 fn run(&self, _ctx: &MdBookContext, mut book: Book) -> Result<Book, Error> {
82 book.for_each_mut(|item| {
83 if let BookItem::Chapter(chapter) = item {
84 chapter.content = self.process_content(&chapter.content);
85 }
86 });
87 Ok(book)
88 }
89
90 fn supports_renderer(&self, renderer: &str) -> bool {
91 renderer != "not-supported"
92 }
93}