wayver's git archive


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

src/utils/markdown.rs@bd1a8f79badd439ab7a73e193be91ad175aafa67

raw
Date Commit Message Author Files + -
2026-02-17 21:07 initial mvp wayverd 74 10800 0
...

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