summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src-tauri/src/markdown.rs
blob: 39a64a4318c68381e48f673823f6858d3840f346 (plain)
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
use comrak::{
    Arena, Options, create_formatter, html::ChildRendering, nodes::NodeValue, parse_document,
};
use std::fmt::Write;

create_formatter!(ExternalLinkFormatter, {
    NodeValue::Link(ref nl) => |context, node, entering| {
        let skip = context.options.parse.relaxed_autolinks
            && node.parent().is_some_and(|p| comrak::node_matches!(p, NodeValue::Link(..)));
        if skip {
            return Ok(ChildRendering::HTML);
        }

        if entering {
            context.write_str("<a")?;
            comrak::html::render_sourcepos(context, node)?;

            context.write_str(" href=\"")?;
            let url = &nl.url;
            if context.options.render.r#unsafe || !comrak::html::dangerous_url(url) {
                if let Some(rewriter) = &context.options.extension.link_url_rewriter {
                    context.escape_href(&rewriter.to_html(url))?;
                } else {
                    context.escape_href(url)?;
                }
            }
            context.write_str("\"")?;

            if !nl.title.is_empty() {
                context.write_str(" title=\"")?;
                context.escape(&nl.title)?;
                context.write_str("\"")?;
            }

            context.write_str(
                " class=\"external-link\" target=\"_blank\" rel=\"noopener noreferrer\">",
            )?;
        } else {
            context.write_str("</a>")?;
        }
    },
});

pub fn parse_markdown(input: &str) -> String {
    let mut options = Options::default();
    options.extension.strikethrough = true;
    options.extension.table = true;
    options.extension.tasklist = true;
    options.extension.autolink = true;
    options.render.r#unsafe = true;

    let arena = Arena::new();
    let doc = parse_document(&arena, input, &options);
    let mut html = String::new();
    ExternalLinkFormatter::format_document(doc, &options, &mut html).unwrap_or_default();
    html
}

#[tauri::command]
#[specta::specta]
pub async fn parse_markdown_command(markdown: String) -> Result<String, String> {
    Ok(parse_markdown(&markdown))
}