wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/utils/markdown.rs@3aec1a1bb245ce255f59532eba2bdfe1d2879510

raw
Date Commit Message Author Files + -
2026-02-19 17:51 large refactoring wayverd 53 2153 1683
...

1use std::{
2    borrow::Cow,
3    collections::HashMap,
4    fmt::{self, Write},
5};
6
7use comrak::{
8    Options, adapters::SyntaxHighlighterAdapter, html::write_opening_tag,
9    markdown_to_html_with_plugins, options::Plugins,
10};
11use syntect::{
12    Error,
13    html::{ClassStyle, ClassedHTMLGenerator},
14    parsing::{SyntaxReference, SyntaxSet},
15    util::LinesWithEndings,
16};
17
18#[tracing::instrument(skip_all)]
19pub(crate) fn render(syntaxes: &SyntaxSet, input: &str) -> String {
20    let adaptor = SyntectAdapter {
21        syntax_set: syntaxes,
22    };
23
24    let options = Options::default();
25
26    let mut plugins = Plugins::default();
27
28    plugins.render.codefence_syntax_highlighter = Some(&adaptor);
29
30    markdown_to_html_with_plugins(input, &options, &plugins)
31}
32
33struct SyntectAdapter<'s> {
34    syntax_set: &'s SyntaxSet,
35}
36
37impl SyntectAdapter<'_> {
38    fn highlight_html(&self, code: &str, syntax: &SyntaxReference) -> Result<String, Error> {
39        let mut html_generator =
40            ClassedHTMLGenerator::new_with_class_style(syntax, self.syntax_set, ClassStyle::Spaced);
41        for line in LinesWithEndings::from(code) {
42            html_generator.parse_html_for_line_which_includes_newline(line)?;
43        }
44        Ok(html_generator.finalize())
45    }
46}
47
48impl SyntaxHighlighterAdapter for SyntectAdapter<'_> {
49    fn write_highlighted(
50        &self,
51        output: &mut dyn Write,
52        lang: Option<&str>,
53        code: &str,
54    ) -> fmt::Result {
55        let fallback_syntax = "Plain Text";
56
57        let lang: &str = match lang {
58            Some(l) if !l.is_empty() => l,
59            _ => fallback_syntax,
60        };
61
62        let syntax = self
63            .syntax_set
64            .find_syntax_by_token(lang)
65            .unwrap_or_else(|| {
66                self.syntax_set
67                    .find_syntax_by_first_line(code)
68                    .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
69            });
70
71        match self.highlight_html(code, syntax) {
72            Ok(highlighted_code) => output.write_str(&highlighted_code),
73            Err(_) => output.write_str(code),
74        }
75    }
76
77    fn write_pre_tag(
78        &self,
79        output: &mut dyn Write,
80        _attributes: HashMap<&'static str, Cow<'_, str>>,
81    ) -> fmt::Result {
82        let mut attributes: HashMap<&str, &str> = HashMap::new();
83        attributes.insert("class", "syntax-highlighting");
84        write_opening_tag(output, "pre", attributes)
85    }
86
87    fn write_code_tag(
88        &self,
89        output: &mut dyn Write,
90        attributes: HashMap<&'static str, Cow<'_, str>>,
91    ) -> fmt::Result {
92        write_opening_tag(output, "code", attributes)
93    }
94}
95