From 94e775707888e7fb66cc90c4ad7176682f505ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Sat, 14 Jun 2025 15:12:02 +0200 Subject: [PATCH] Filter `linebreaksbr` only needs `core` --- askama/src/filters/alloc.rs | 64 ------------------- askama/src/filters/core.rs | 91 ++++++++++++++++++++++++--- askama/src/filters/mod.rs | 7 +-- askama_derive/src/generator/filter.rs | 3 +- book/src/filters.md | 5 -- 5 files changed, 87 insertions(+), 83 deletions(-) diff --git a/askama/src/filters/alloc.rs b/askama/src/filters/alloc.rs index 2b6bbb15..d06c031c 100644 --- a/askama/src/filters/alloc.rs +++ b/askama/src/filters/alloc.rs @@ -3,7 +3,6 @@ use alloc::string::String; use core::convert::Infallible; use core::fmt::{self, Write}; -use super::escape::HtmlSafeOutput; use crate::{FastWritable, Result}; /// Return an ephemeral `&str` for `$src: impl fmt::Display` @@ -95,60 +94,6 @@ pub fn fmt() {} /// Compare with [fmt](./fn.fmt.html). pub fn format() {} -/// Converts all newlines in a piece of plain text to HTML line breaks -/// -/// ``` -/// # #[cfg(feature = "code-in-doc")] { -/// # use askama::Template; -/// /// ```jinja -/// ///
{{ lines|linebreaksbr }}
-/// /// ``` -/// #[derive(Template)] -/// #[template(ext = "html", in_doc = true)] -/// struct Example<'a> { -/// lines: &'a str, -/// } -/// -/// assert_eq!( -/// Example { lines: "a\nb\nc" }.to_string(), -/// "
a
b
c
" -/// ); -/// # } -/// ``` -#[inline] -pub fn linebreaksbr( - source: S, -) -> Result>, Infallible> { - Ok(HtmlSafeOutput(Linebreaksbr(source))) -} - -pub struct Linebreaksbr(S); - -impl fmt::Display for Linebreaksbr { - #[inline] - fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buffer; - flush_linebreaksbr(dest, try_to_str!(self.0 => buffer)) - } -} - -impl FastWritable for Linebreaksbr { - #[inline] - fn write_into( - &self, - dest: &mut W, - values: &dyn crate::Values, - ) -> crate::Result<()> { - let mut buffer = String::new(); - self.0.write_into(&mut buffer, values)?; - Ok(flush_linebreaksbr(dest, &buffer)?) - } -} - -fn flush_linebreaksbr(dest: &mut (impl fmt::Write + ?Sized), s: &str) -> fmt::Result { - dest.write_str(&s.replace('\n', "
")) -} - /// Converts to lowercase /// /// ``` @@ -541,15 +486,6 @@ mod tests { use super::*; - #[test] - fn test_linebreaksbr() { - assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo
Bar"); - assert_eq!( - linebreaksbr("Foo\nBar\n\nBaz").unwrap().to_string(), - "Foo
Bar

Baz" - ); - } - #[test] fn test_lower() { assert_eq!(lower("Foo").unwrap().to_string(), "foo"); diff --git a/askama/src/filters/core.rs b/askama/src/filters/core.rs index 35d01371..35d10757 100644 --- a/askama/src/filters/core.rs +++ b/askama/src/filters/core.rs @@ -735,14 +735,7 @@ impl fmt::Write for NewlineCountingFormatter<'_, W> { } for line in s.split_inclusive('\n') { - let (has_eol, line) = if let Some(line) = line.strip_suffix("\r\n") { - (true, line) - } else if let Some(line) = line.strip_suffix('\n') { - (true, line) - } else { - (false, line) - }; - + let (has_eol, line) = strip_newline_suffix(line); if !line.is_empty() { match replace(&mut self.counter, if has_eol { 1 } else { 0 }) { ..=0 => {} @@ -758,6 +751,79 @@ impl fmt::Write for NewlineCountingFormatter<'_, W> { } } +/// Converts all newlines in a piece of plain text to HTML line breaks +/// +/// ``` +/// # #[cfg(feature = "code-in-doc")] { +/// # use askama::Template; +/// /// ```jinja +/// ///
{{ lines|linebreaksbr }}
+/// /// ``` +/// #[derive(Template)] +/// #[template(ext = "html", in_doc = true)] +/// struct Example<'a> { +/// lines: &'a str, +/// } +/// +/// assert_eq!( +/// Example { lines: "a\nb\nc" }.to_string(), +/// "
a
b
c
" +/// ); +/// # } +/// ``` +#[inline] +pub fn linebreaksbr( + source: S, +) -> Result>, Infallible> { + Ok(HtmlSafeOutput(Linebreaksbr(source))) +} + +pub struct Linebreaksbr(S); + +impl fmt::Display for Linebreaksbr { + #[inline] + fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(LinebreaksbrFormatter(dest), "{}", self.0) + } +} + +struct LinebreaksbrFormatter<'a, W: ?Sized>(&'a mut W); + +impl FastWritable for Linebreaksbr { + #[inline] + fn write_into( + &self, + dest: &mut W, + values: &dyn crate::Values, + ) -> crate::Result<()> { + self.0.write_into(&mut LinebreaksbrFormatter(dest), values) + } +} + +impl fmt::Write for LinebreaksbrFormatter<'_, W> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for line in s.split_inclusive('\n') { + let (has_eol, line) = strip_newline_suffix(line); + self.0.write_str(line)?; + if has_eol { + self.0.write_str("
")?; + } + } + Ok(()) + } +} + +/// Returns whether a newline suffix was stripped and the (maybe stripped) line. +fn strip_newline_suffix(line: &str) -> (bool, &str) { + if let Some(line) = line.strip_suffix("\r\n") { + (true, line) + } else if let Some(line) = line.strip_suffix('\n') { + (true, line) + } else { + (false, line) + } +} + #[cfg(all(test, feature = "alloc"))] mod tests { use alloc::string::{String, ToString}; @@ -898,4 +964,13 @@ mod tests { "

Foo

Bar

Baz

" ); } + + #[test] + fn test_linebreaksbr() { + assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo
Bar"); + assert_eq!( + linebreaksbr("Foo\nBar\n\nBaz").unwrap().to_string(), + "Foo
Bar

Baz" + ); + } } diff --git a/askama/src/filters/mod.rs b/askama/src/filters/mod.rs index af07b62c..e611e01f 100644 --- a/askama/src/filters/mod.rs +++ b/askama/src/filters/mod.rs @@ -25,12 +25,11 @@ mod urlencode; #[cfg(feature = "alloc")] pub use self::alloc::{ - capitalize, fmt, format, linebreaksbr, lower, lowercase, title, titlecase, trim, upper, - uppercase, + capitalize, fmt, format, lower, lowercase, title, titlecase, trim, upper, uppercase, }; pub use self::core::{ - PluralizeCount, center, join, linebreaks, paragraphbreaks, pluralize, reject, reject_with, - truncate, wordcount, + PluralizeCount, center, join, linebreaks, linebreaksbr, paragraphbreaks, pluralize, reject, + reject_with, truncate, wordcount, }; pub use self::escape::{ AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text, diff --git a/askama_derive/src/generator/filter.rs b/askama_derive/src/generator/filter.rs index 04abc80c..e8476978 100644 --- a/askama_derive/src/generator/filter.rs +++ b/askama_derive/src/generator/filter.rs @@ -333,9 +333,8 @@ impl<'a> Generator<'a, '_> { ctx: &Context<'_>, buf: &mut Buffer, args: &[WithSpan<'a, Expr<'a>>], - node: Span<'_>, + _node: Span<'_>, ) -> Result { - ensure_filter_has_feature_alloc(ctx, "linebreaksbr", node)?; self.visit_linebreaks_filters(ctx, buf, "linebreaksbr", args) } diff --git a/book/src/filters.md b/book/src/filters.md index 90ee58cf..ef2f7c70 100644 --- a/book/src/filters.md +++ b/book/src/filters.md @@ -320,11 +320,6 @@ Output: ### linebreaksbr [#linebreaksbr]: #linebreaksbr -
-enabled by "alloc"
-enabled by "default" -
- ```jinja {{ text_to_break | linebreaksbr }} ```