From fd8bfa43c0c20e4af195824ff95503e41ddb82e8 Mon Sep 17 00:00:00 2001 From: René Kijewski Date: Tue, 1 Feb 2022 15:01:17 +0100 Subject: Add markdown filter --- askama_shared/src/filters/mod.rs | 56 +++++++++++++++++++++++++++++++++++++--- askama_shared/src/generator.rs | 41 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) (limited to 'askama_shared/src') diff --git a/askama_shared/src/filters/mod.rs b/askama_shared/src/filters/mod.rs index 35bdd5a..9fb10d2 100644 --- a/askama_shared/src/filters/mod.rs +++ b/askama_shared/src/filters/mod.rs @@ -47,7 +47,7 @@ const URLENCODE_SET: &AsciiSet = &URLENCODE_STRICT_SET.remove(b'/'); // Askama or should refer to a local `filters` module. It should contain all the // filters shipped with Askama, even the optional ones (since optional inclusion // in the const vector based on features seems impossible right now). -pub const BUILT_IN_FILTERS: [&str; 27] = [ +pub const BUILT_IN_FILTERS: &[&str] = &[ "abs", "capitalize", "center", @@ -73,8 +73,10 @@ pub const BUILT_IN_FILTERS: [&str; 27] = [ "urlencode", "urlencode_strict", "wordcount", - "json", // Optional feature; reserve the name anyway - "yaml", // Optional feature; reserve the name anyway + // optional features, reserve the names anyway: + "json", + "markdown", + "yaml", ]; /// Marks a string (or other `Display` type) as safe @@ -379,6 +381,54 @@ pub fn wordcount(s: T) -> Result { Ok(s.split_whitespace().count()) } +#[cfg(feature = "markdown")] +pub fn markdown( + e: E, + s: S, + options: Option<&comrak::ComrakOptions>, +) -> Result> +where + E: Escaper, + S: AsRef, +{ + use comrak::{ + markdown_to_html, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions, + ComrakRenderOptions, + }; + + const DEFAULT_OPTIONS: ComrakOptions = ComrakOptions { + extension: ComrakExtensionOptions { + strikethrough: true, + tagfilter: true, + table: true, + autolink: true, + // default: + tasklist: false, + superscript: false, + header_ids: None, + footnotes: false, + description_lists: false, + front_matter_delimiter: None, + }, + parse: ComrakParseOptions { + // default: + smart: false, + default_info_string: None, + }, + render: ComrakRenderOptions { + unsafe_: false, + escape: true, + // default: + hardbreaks: false, + github_pre_lang: false, + width: 0, + }, + }; + + let s = markdown_to_html(s.as_ref(), options.unwrap_or(&DEFAULT_OPTIONS)); + Ok(MarkupDisplay::new_safe(s, e)) +} + #[cfg(test)] mod tests { use super::*; diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 63a0154..c3beb88 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1097,6 +1097,45 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + #[cfg(not(feature = "markdown"))] + fn _visit_markdown_filter( + &mut self, + _buf: &mut Buffer, + _args: &[Expr<'_>], + ) -> Result { + Err("the `markdown` filter requires the `markdown` feature to be enabled".into()) + } + + #[cfg(feature = "markdown")] + fn _visit_markdown_filter( + &mut self, + buf: &mut Buffer, + args: &[Expr<'_>], + ) -> Result { + let (md, options) = match args { + [md] => (md, None), + [md, options] => (md, Some(options)), + _ => return Err("markdown filter expects no more than one option argument".into()), + }; + + buf.write(&format!( + "::askama::filters::markdown({}, ", + self.input.escaper + )); + self.visit_expr(buf, md)?; + match options { + Some(options) => { + buf.write(", ::core::option::Option::Some("); + self.visit_expr(buf, options)?; + buf.write(")"); + } + None => buf.write(", ::core::option::Option::None"), + } + buf.write(")?"); + + Ok(DisplayWrap::Wrapped) + } + fn visit_filter( &mut self, buf: &mut Buffer, @@ -1115,6 +1154,8 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } else if name == "join" { self._visit_join_filter(buf, args)?; return Ok(DisplayWrap::Unwrapped); + } else if name == "markdown" { + return self._visit_markdown_filter(buf, args); } if name == "tojson" { -- cgit