use crate::helpers; use crate::hilighting; use axum::http::Uri; use pulldown_cmark::Event; use pulldown_cmark::Tag; use pulldown_cmark::{Options, Parser}; pub fn render_markdown_to_html(base_uri: Option<&Uri>, markdown: &str) -> String { let options = Options::all(); let mut content_html = String::new(); let parser = Parser::new_ext(markdown, options); let mut code_block = false; let mut code_lang = None; let mut code_accumulator = String::new(); let mut events = Vec::new(); for event in parser { match event { Event::Text(text) => { if code_block { code_accumulator.push_str(&text); } else { events.push(Event::Text(text)); } } Event::Start(Tag::Link(t, mut link, title)) => { if let Some(uri) = base_uri { if !link.contains("://") && !link.contains('@') { // convert relative URIs to absolute URIs link = helpers::uri_with_path(uri, &link).to_string().into(); } } events.push(Event::Start(Tag::Link(t, link, title))); } Event::Start(Tag::Image(t, mut link, title)) => { if let Some(uri) = base_uri { if !link.contains("://") && !link.contains('@') { // convert relative URIs to absolute URIs link = helpers::uri_with_path(uri, &link).to_string().into(); } } events.push(Event::Start(Tag::Image(t, link, title))); } Event::Start(Tag::CodeBlock(kind)) => { code_block = true; if let pulldown_cmark::CodeBlockKind::Fenced(lang) = kind { code_lang = Some(lang); } } Event::End(Tag::CodeBlock(_)) => { code_block = false; let lang = code_lang.take().unwrap_or("".into()); let res = hilighting::hilight(&code_accumulator, &lang, Some("base16-ocean.dark")) .unwrap(); events.push(Event::Html(res.into())); code_accumulator.clear(); } _ => events.push(event), } } events.retain(|e| match e { Event::Text(t) | Event::Html(t) => !t.is_empty(), _ => true, }); pulldown_cmark::html::push_html(&mut content_html, events.into_iter()); content_html }