use lazy_static::lazy_static; use markdown_it::generics::inline::full_link; use markdown_it::{MarkdownIt, Node}; use maud::{Markup, PreEscaped, Render, html}; use page::RequestContext; use page::page::{Page, PageMetadata}; use crate::components::md::emote::InlineEmote; use crate::components::md::frontmatter::{TomlFrontMatter, YamlFrontMatter}; use crate::components::md::link::SmartLink; use crate::components::md::mdx::InlineMdx; mod emote; mod frontmatter; mod link; mod mdx; lazy_static! { static ref MdParser: MarkdownIt = { let mut md = markdown_it::MarkdownIt::new(); { use markdown_it::plugins::cmark::*; inline::newline::add(&mut md); inline::escape::add(&mut md); inline::backticks::add(&mut md); inline::emphasis::add(&mut md); // Replaced with smart links //inline::link::add(&mut md); full_link::add::(&mut md, |href, title| { Node::new(SmartLink { url: href.unwrap_or_default(), title, }) }); inline::image::add(&mut md); inline::autolink::add(&mut md); inline::entity::add(&mut md); block::code::add(&mut md); block::fence::add(&mut md); block::blockquote::add(&mut md); block::hr::add(&mut md); block::list::add(&mut md); block::reference::add(&mut md); block::heading::add(&mut md); block::lheading::add(&mut md); block::paragraph::add(&mut md); } { markdown_it::plugins::html::add(&mut md); md_footnote::add(&mut md); } md.block.add_rule::().before_all(); md.block.add_rule::().before_all(); md.inline.add_rule::(); md.inline.add_rule::(); md }; } pub struct Markdown<'a>(pub &'a str); impl Render for Markdown<'_> { fn render(&self) -> Markup { let md = Self::parse(self.0); let html = md.render(); return PreEscaped(html); } } impl Markdown<'_> { pub fn parse(md_str: &str) -> Node { MdParser.parse(md_str) } } // // MARK: helpers // /// Try to read page metadata from a markdown file's frontmatter. /// - returns `none` if there is no frontmatter /// - returns an error if we fail to parse frontmatter pub fn meta_from_markdown(root_node: &Node) -> Result, toml::de::Error> { root_node .children .first() .and_then(|x| x.cast::()) .map(|x| toml::from_str::(&x.content)) .map_or(Ok(None), |v| v.map(Some)) } pub fn backlinks(page: &Page, ctx: &RequestContext) -> Option { let mut last = None; let mut backlinks = vec![("/", "home")]; if page.meta.backlinks.unwrap_or(false) { let mut segments = ctx.route.split("/").skip(1).collect::>(); last = segments.pop(); let mut end = 0; for s in segments { end += s.len(); backlinks.push((&ctx.route[0..=end], s)); end += 1; // trailing slash } } last.map(|last| { html! { div { @for (url, text) in backlinks { a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) } "/" } span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (last) } } } }) }