mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 13:00:57 +00:00
Filter linebreaks
only needs core
This commit is contained in:
parent
477bfe4a3c
commit
781d88042c
@ -95,62 +95,6 @@ pub fn fmt() {}
|
||||
/// Compare with [fmt](./fn.fmt.html).
|
||||
pub fn format() {}
|
||||
|
||||
/// Replaces line breaks in plain text with appropriate HTML
|
||||
///
|
||||
/// A single newline becomes an HTML line break `<br>` and a new line
|
||||
/// followed by a blank line becomes a paragraph break `<p>`.
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "code-in-doc")] {
|
||||
/// # use askama::Template;
|
||||
/// /// ```jinja
|
||||
/// /// <div>{{ example|linebreaks }}</div>
|
||||
/// /// ```
|
||||
/// #[derive(Template)]
|
||||
/// #[template(ext = "html", in_doc = true)]
|
||||
/// struct Example<'a> {
|
||||
/// example: &'a str,
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// Example { example: "Foo\nBar\n\nBaz" }.to_string(),
|
||||
/// "<div><p>Foo<br/>Bar</p><p>Baz</p></div>"
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn linebreaks<S: fmt::Display>(source: S) -> Result<HtmlSafeOutput<Linebreaks<S>>, Infallible> {
|
||||
Ok(HtmlSafeOutput(Linebreaks(source)))
|
||||
}
|
||||
|
||||
pub struct Linebreaks<S>(S);
|
||||
|
||||
impl<S: fmt::Display> fmt::Display for Linebreaks<S> {
|
||||
#[inline]
|
||||
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut buffer;
|
||||
flush_linebreaks(dest, try_to_str!(self.0 => buffer))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: FastWritable> FastWritable for Linebreaks<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_linebreaks(dest, &buffer)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_linebreaks(dest: &mut (impl fmt::Write + ?Sized), s: &str) -> fmt::Result {
|
||||
let linebroken = s.replace("\n\n", "</p><p>").replace('\n', "<br/>");
|
||||
write!(dest, "<p>{linebroken}</p>")
|
||||
}
|
||||
|
||||
/// Converts all newlines in a piece of plain text to HTML line breaks
|
||||
///
|
||||
/// ```
|
||||
@ -656,18 +600,6 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_linebreaks() {
|
||||
assert_eq!(
|
||||
linebreaks("Foo\nBar Baz").unwrap().to_string(),
|
||||
"<p>Foo<br/>Bar Baz</p>"
|
||||
);
|
||||
assert_eq!(
|
||||
linebreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||
"<p>Foo<br/>Bar</p><p>Baz</p>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linebreaksbr() {
|
||||
assert_eq!(linebreaksbr("Foo\nBar").unwrap().to_string(), "Foo<br/>Bar");
|
||||
|
@ -1,10 +1,12 @@
|
||||
use core::cell::Cell;
|
||||
use core::convert::Infallible;
|
||||
use core::fmt::{self, Write};
|
||||
use core::mem::replace;
|
||||
use core::ops::Deref;
|
||||
use core::pin::Pin;
|
||||
|
||||
use super::MAX_LEN;
|
||||
use crate::filters::HtmlSafeOutput;
|
||||
use crate::{Error, FastWritable, Result, Values};
|
||||
|
||||
/// Limit string length, appends '...' if truncated
|
||||
@ -615,6 +617,94 @@ impl<'a> fmt::Write for WordCountWriter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces line breaks in plain text with appropriate HTML
|
||||
///
|
||||
/// A single newline becomes an HTML line break `<br>` and a new line
|
||||
/// followed by a blank line becomes a paragraph break `<p>`.
|
||||
///
|
||||
/// ```
|
||||
/// # #[cfg(feature = "code-in-doc")] {
|
||||
/// # use askama::Template;
|
||||
/// /// ```jinja
|
||||
/// /// <div>{{ example|linebreaks }}</div>
|
||||
/// /// ```
|
||||
/// #[derive(Template)]
|
||||
/// #[template(ext = "html", in_doc = true)]
|
||||
/// struct Example<'a> {
|
||||
/// example: &'a str,
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// Example { example: "Foo\nBar\n\nBaz" }.to_string(),
|
||||
/// "<div><p>Foo<br/>Bar</p><p>Baz</p></div>"
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn linebreaks<S: fmt::Display>(source: S) -> Result<HtmlSafeOutput<Linebreaks<S>>, Infallible> {
|
||||
Ok(HtmlSafeOutput(Linebreaks(source)))
|
||||
}
|
||||
|
||||
pub struct Linebreaks<S>(S);
|
||||
|
||||
impl<S: fmt::Display> fmt::Display for Linebreaks<S> {
|
||||
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
dest.write_str("<p>")?;
|
||||
let mut formatter = LinebreakFormatter { dest, counter: -1 };
|
||||
write!(formatter, "{}", self.0)?;
|
||||
formatter.dest.write_str("</p>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: FastWritable> FastWritable for Linebreaks<S> {
|
||||
fn write_into<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
dest: &mut W,
|
||||
values: &dyn crate::Values,
|
||||
) -> crate::Result<()> {
|
||||
dest.write_str("<p>")?;
|
||||
let mut formatter = LinebreakFormatter { dest, counter: -1 };
|
||||
self.0.write_into(&mut formatter, values)?;
|
||||
dest.write_str("</p>")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct LinebreakFormatter<'a, W: ?Sized> {
|
||||
dest: &'a mut W,
|
||||
counter: isize,
|
||||
}
|
||||
|
||||
impl<W: fmt::Write + ?Sized> fmt::Write for LinebreakFormatter<'_, W> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
if s.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
if !line.is_empty() {
|
||||
match replace(&mut self.counter, if has_eol { 1 } else { 0 }) {
|
||||
..=0 => {}
|
||||
1 => self.dest.write_str("<br/>")?,
|
||||
2.. => self.dest.write_str("</p><p>")?,
|
||||
}
|
||||
self.dest.write_str(line)?;
|
||||
} else if has_eol && self.counter >= 0 {
|
||||
self.counter += 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "alloc"))]
|
||||
mod tests {
|
||||
use alloc::string::{String, ToString};
|
||||
@ -725,4 +815,16 @@ mod tests {
|
||||
assert_eq!(wrap("hello\n\n bar"), 2);
|
||||
assert_eq!(wrap(" hello\n\n bar "), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linebreaks() {
|
||||
assert_eq!(
|
||||
linebreaks("Foo\nBar Baz").unwrap().to_string(),
|
||||
"<p>Foo<br/>Bar Baz</p>"
|
||||
);
|
||||
assert_eq!(
|
||||
linebreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||
"<p>Foo<br/>Bar</p><p>Baz</p>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ mod urlencode;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::alloc::{
|
||||
capitalize, fmt, format, linebreaks, linebreaksbr, lower, lowercase, paragraphbreaks, title,
|
||||
titlecase, trim, upper, uppercase,
|
||||
capitalize, fmt, format, linebreaksbr, lower, lowercase, paragraphbreaks, title, titlecase,
|
||||
trim, upper, uppercase,
|
||||
};
|
||||
pub use self::core::{
|
||||
PluralizeCount, center, join, pluralize, reject, reject_with, truncate, wordcount,
|
||||
PluralizeCount, center, join, linebreaks, pluralize, reject, reject_with, truncate, wordcount,
|
||||
};
|
||||
pub use self::escape::{
|
||||
AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text,
|
||||
|
@ -325,7 +325,8 @@ impl<'a> Generator<'a, '_> {
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
self.visit_linebreaks_filters(ctx, buf, "paragraphbreaks", args, node)
|
||||
ensure_filter_has_feature_alloc(ctx, "paragraphbreaks", node)?;
|
||||
self.visit_linebreaks_filters(ctx, buf, "paragraphbreaks", args)
|
||||
}
|
||||
|
||||
fn visit_linebreaksbr_filter(
|
||||
@ -335,7 +336,8 @@ impl<'a> Generator<'a, '_> {
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
self.visit_linebreaks_filters(ctx, buf, "linebreaksbr", args, node)
|
||||
ensure_filter_has_feature_alloc(ctx, "linebreaksbr", node)?;
|
||||
self.visit_linebreaks_filters(ctx, buf, "linebreaksbr", args)
|
||||
}
|
||||
|
||||
fn visit_linebreaks_filter(
|
||||
@ -343,9 +345,9 @@ impl<'a> Generator<'a, '_> {
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
_node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
self.visit_linebreaks_filters(ctx, buf, "linebreaks", args, node)
|
||||
self.visit_linebreaks_filters(ctx, buf, "linebreaks", args)
|
||||
}
|
||||
|
||||
fn visit_linebreaks_filters(
|
||||
@ -354,9 +356,7 @@ impl<'a> Generator<'a, '_> {
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'a, Expr<'a>>],
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
ensure_filter_has_feature_alloc(ctx, name, node)?;
|
||||
let arg = no_arguments(ctx, name, args)?;
|
||||
buf.write(format_args!(
|
||||
"askama::filters::{name}(&(&&askama::filters::AutoEscaper::new(&(",
|
||||
|
@ -297,11 +297,6 @@ foo, bar, bazz
|
||||
### linebreaks
|
||||
[#linebreaks]: #linebreaks
|
||||
|
||||
<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 | linebreaks }}
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user