Filter linebreaksbr only needs core

This commit is contained in:
René Kijewski 2025-06-14 15:12:02 +02:00
parent b9490c4b5d
commit 94e7757078
5 changed files with 87 additions and 83 deletions

View File

@ -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
/// /// <div>{{ lines|linebreaksbr }}</div>
/// /// ```
/// #[derive(Template)]
/// #[template(ext = "html", in_doc = true)]
/// struct Example<'a> {
/// lines: &'a str,
/// }
///
/// assert_eq!(
/// Example { lines: "a\nb\nc" }.to_string(),
/// "<div>a<br/>b<br/>c</div>"
/// );
/// # }
/// ```
#[inline]
pub fn linebreaksbr<S: fmt::Display>(
source: S,
) -> Result<HtmlSafeOutput<Linebreaksbr<S>>, Infallible> {
Ok(HtmlSafeOutput(Linebreaksbr(source)))
}
pub struct Linebreaksbr<S>(S);
impl<S: fmt::Display> fmt::Display for Linebreaksbr<S> {
#[inline]
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buffer;
flush_linebreaksbr(dest, try_to_str!(self.0 => buffer))
}
}
impl<S: FastWritable> FastWritable for Linebreaksbr<S> {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&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', "<br/>"))
}
/// Converts to lowercase
///
/// ```
@ -541,15 +486,6 @@ mod tests {
use super::*;
#[test]
fn test_linebreaksbr() {
assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo<br/>Bar");
assert_eq!(
linebreaksbr("Foo\nBar\n\nBaz").unwrap().to_string(),
"Foo<br/>Bar<br/><br/>Baz"
);
}
#[test]
fn test_lower() {
assert_eq!(lower("Foo").unwrap().to_string(), "foo");

View File

@ -735,14 +735,7 @@ impl<W: fmt::Write + ?Sized> 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<W: fmt::Write + ?Sized> 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
/// /// <div>{{ lines|linebreaksbr }}</div>
/// /// ```
/// #[derive(Template)]
/// #[template(ext = "html", in_doc = true)]
/// struct Example<'a> {
/// lines: &'a str,
/// }
///
/// assert_eq!(
/// Example { lines: "a\nb\nc" }.to_string(),
/// "<div>a<br/>b<br/>c</div>"
/// );
/// # }
/// ```
#[inline]
pub fn linebreaksbr<S: fmt::Display>(
source: S,
) -> Result<HtmlSafeOutput<Linebreaksbr<S>>, Infallible> {
Ok(HtmlSafeOutput(Linebreaksbr(source)))
}
pub struct Linebreaksbr<S>(S);
impl<S: fmt::Display> fmt::Display for Linebreaksbr<S> {
#[inline]
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(LinebreaksbrFormatter(dest), "{}", self.0)
}
}
struct LinebreaksbrFormatter<'a, W: ?Sized>(&'a mut W);
impl<S: FastWritable> FastWritable for Linebreaksbr<S> {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn crate::Values,
) -> crate::Result<()> {
self.0.write_into(&mut LinebreaksbrFormatter(dest), values)
}
}
impl<W: fmt::Write + ?Sized> 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("<br/>")?;
}
}
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 {
"<p>Foo</p><p>Bar</p><p>Baz</p>"
);
}
#[test]
fn test_linebreaksbr() {
assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo<br/>Bar");
assert_eq!(
linebreaksbr("Foo\nBar\n\nBaz").unwrap().to_string(),
"Foo<br/>Bar<br/><br/>Baz"
);
}
}

View File

@ -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,

View File

@ -333,9 +333,8 @@ impl<'a> Generator<'a, '_> {
ctx: &Context<'_>,
buf: &mut Buffer,
args: &[WithSpan<'a, Expr<'a>>],
node: Span<'_>,
_node: Span<'_>,
) -> Result<DisplayWrap, CompileError> {
ensure_filter_has_feature_alloc(ctx, "linebreaksbr", node)?;
self.visit_linebreaks_filters(ctx, buf, "linebreaksbr", args)
}

View File

@ -320,11 +320,6 @@ Output:
### linebreaksbr
[#linebreaksbr]: #linebreaksbr
<blockquote class="right" style="padding:0.5ex 1ex; margin:0 0 1ex 1ex; font-size:80%">
enabled by <code>"alloc"</code><br/>
enabled by <code>"default"</code>
</blockquote>
```jinja
{{ text_to_break | linebreaksbr }}
```