1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Insert KaTeX autorender script into rustdoc
//!
//! Usage
//! -----
//!
//! ```rust
//! #[cfg_attr(doc, katexit::katexit)]
//! /// We can write $\LaTeX$ expressions
//! ///
//! /// Display style
//! /// -------------
//! ///
//! /// $$
//! /// c = \\pm\\sqrt{a^2 + b^2}
//! /// $$
//! pub fn my_func() {}
//! ```
//!
//! How it works
//! -------------
//!
//! `#[katexit]` proc-macro inserts [KaTeX autorender script][autorender] as `#[doc = "{script}"]`.
//! Since the markdown to HTML translator of rustdoc through the HTML partition embedded in markdown,
//! this will be passed as it is to the browser.
//! When you open the page generated by rustdoc, the autorender script starts rendering the
//! math expression enclosed by `$` written in the document section
//! (i.e. this does not work offline).
//!
//! [autorender]: https://katex.org/docs/autorender.html

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

#[proc_macro_attribute]
pub fn katexit(_attr: TokenStream, item: TokenStream) -> TokenStream {
    katexit2(item.into()).into()
}

// This is a copy of https://katex.org/docs/autorender.html (accessed on 2021/8/28)
const KATEX_HEADER: &str = r#"
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css" integrity="sha384-RZU/ijkSsFbcmivfdRBQDtwuwVqK7GMOw6IMvKyeWL2K5UAlyp6WonmB8m7Jd0Hn" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.js" integrity="sha384-pK1WpvzWVBQiP0/GjnvRxV4mOb0oxFuyRxJlk6vVw146n3egcN5C925NCP7a7BY8" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/contrib/auto-render.min.js" integrity="sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl" crossorigin="anonymous"></script>
<script>
    document.addEventListener("DOMContentLoaded", function() {
        renderMathInElement(document.body, {
          // customised options
          // • auto-render specific keys, e.g.:
          delimiters: [
              {left: '$$', right: '$$', display: true},
              {left: '$', right: '$', display: false},
              {left: '\\(', right: '\\)', display: false},
              {left: '\\[', right: '\\]', display: true}
          ],
          // • rendering keys, e.g.:
          throwOnError : false
        });
    });
</script>
"#;

fn katexit2(item: TokenStream2) -> TokenStream2 {
    quote! {
        #[doc = #KATEX_HEADER]
        #item
    }
}