mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 07:20:55 +00:00
Filter paragraphbreaks
only needs core
This commit is contained in:
parent
781d88042c
commit
b9490c4b5d
@ -149,65 +149,6 @@ fn flush_linebreaksbr(dest: &mut (impl fmt::Write + ?Sized), s: &str) -> fmt::Re
|
|||||||
dest.write_str(&s.replace('\n', "<br/>"))
|
dest.write_str(&s.replace('\n', "<br/>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces only paragraph breaks in plain text with appropriate HTML
|
|
||||||
///
|
|
||||||
/// A new line followed by a blank line becomes a paragraph break `<p>`.
|
|
||||||
/// Paragraph tags only wrap content; empty paragraphs are removed.
|
|
||||||
/// No `<br/>` tags are added.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #[cfg(feature = "code-in-doc")] {
|
|
||||||
/// # use askama::Template;
|
|
||||||
/// /// ```jinja
|
|
||||||
/// /// {{ lines|paragraphbreaks }}
|
|
||||||
/// /// ```
|
|
||||||
/// #[derive(Template)]
|
|
||||||
/// #[template(ext = "html", in_doc = true)]
|
|
||||||
/// struct Example<'a> {
|
|
||||||
/// lines: &'a str,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// Example { lines: "Foo\nBar\n\nBaz" }.to_string(),
|
|
||||||
/// "<p>Foo\nBar</p><p>Baz</p>"
|
|
||||||
/// );
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
pub fn paragraphbreaks<S: fmt::Display>(
|
|
||||||
source: S,
|
|
||||||
) -> Result<HtmlSafeOutput<Paragraphbreaks<S>>, Infallible> {
|
|
||||||
Ok(HtmlSafeOutput(Paragraphbreaks(source)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Paragraphbreaks<S>(S);
|
|
||||||
|
|
||||||
impl<S: fmt::Display> fmt::Display for Paragraphbreaks<S> {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut buffer;
|
|
||||||
flush_paragraphbreaks(dest, try_to_str!(self.0 => buffer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: FastWritable> FastWritable for Paragraphbreaks<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_paragraphbreaks(dest, &buffer)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_paragraphbreaks(dest: &mut (impl fmt::Write + ?Sized), s: &str) -> fmt::Result {
|
|
||||||
let linebroken = s.replace("\n\n", "</p><p>").replace("<p></p>", "");
|
|
||||||
write!(dest, "<p>{linebroken}</p>")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts to lowercase
|
/// Converts to lowercase
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -609,24 +550,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_paragraphbreaks() {
|
|
||||||
assert_eq!(
|
|
||||||
paragraphbreaks("Foo\nBar Baz").unwrap().to_string(),
|
|
||||||
"<p>Foo\nBar Baz</p>"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
paragraphbreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
|
||||||
"<p>Foo\nBar</p><p>Baz</p>"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
paragraphbreaks("Foo\n\n\n\n\nBar\n\nBaz")
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
"<p>Foo</p><p>\nBar</p><p>Baz</p>"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lower() {
|
fn test_lower() {
|
||||||
assert_eq!(lower("Foo").unwrap().to_string(), "foo");
|
assert_eq!(lower("Foo").unwrap().to_string(), "foo");
|
||||||
|
@ -641,41 +641,94 @@ impl<'a> fmt::Write for WordCountWriter<'a> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn linebreaks<S: fmt::Display>(source: S) -> Result<HtmlSafeOutput<Linebreaks<S>>, Infallible> {
|
pub fn linebreaks<S: fmt::Display>(
|
||||||
Ok(HtmlSafeOutput(Linebreaks(source)))
|
source: S,
|
||||||
|
) -> Result<HtmlSafeOutput<NewlineCounting<S>>, Infallible> {
|
||||||
|
Ok(HtmlSafeOutput(NewlineCounting {
|
||||||
|
source,
|
||||||
|
one: "<br/>",
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Linebreaks<S>(S);
|
/// Replaces only paragraph breaks in plain text with appropriate HTML
|
||||||
|
///
|
||||||
|
/// A new line followed by a blank line becomes a paragraph break `<p>`.
|
||||||
|
/// Paragraph tags only wrap content; empty paragraphs are removed.
|
||||||
|
/// No `<br/>` tags are added.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "code-in-doc")] {
|
||||||
|
/// # use askama::Template;
|
||||||
|
/// /// ```jinja
|
||||||
|
/// /// {{ lines|paragraphbreaks }}
|
||||||
|
/// /// ```
|
||||||
|
/// #[derive(Template)]
|
||||||
|
/// #[template(ext = "html", in_doc = true)]
|
||||||
|
/// struct Example<'a> {
|
||||||
|
/// lines: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Example { lines: "Foo\nBar\n\nBaz" }.to_string(),
|
||||||
|
/// "<p>Foo\nBar</p><p>Baz</p>"
|
||||||
|
/// );
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn paragraphbreaks<S: fmt::Display>(
|
||||||
|
source: S,
|
||||||
|
) -> Result<HtmlSafeOutput<NewlineCounting<S>>, Infallible> {
|
||||||
|
Ok(HtmlSafeOutput(NewlineCounting { source, one: "\n" }))
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: fmt::Display> fmt::Display for Linebreaks<S> {
|
pub struct NewlineCounting<S> {
|
||||||
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
|
source: S,
|
||||||
dest.write_str("<p>")?;
|
one: &'static str,
|
||||||
let mut formatter = LinebreakFormatter { dest, counter: -1 };
|
}
|
||||||
write!(formatter, "{}", self.0)?;
|
|
||||||
formatter.dest.write_str("</p>")
|
impl<S> NewlineCounting<S> {
|
||||||
|
#[inline]
|
||||||
|
fn run<'a, F, W, E>(&self, dest: &'a mut W, inner: F) -> Result<(), E>
|
||||||
|
where
|
||||||
|
W: fmt::Write + ?Sized,
|
||||||
|
F: FnOnce(&mut NewlineCountingFormatter<'a, W>) -> Result<(), E>,
|
||||||
|
E: From<fmt::Error>,
|
||||||
|
{
|
||||||
|
let mut formatter = NewlineCountingFormatter {
|
||||||
|
dest,
|
||||||
|
counter: -1,
|
||||||
|
one: self.one,
|
||||||
|
};
|
||||||
|
formatter.dest.write_str("<p>")?;
|
||||||
|
inner(&mut formatter)?;
|
||||||
|
formatter.dest.write_str("</p>")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: FastWritable> FastWritable for Linebreaks<S> {
|
impl<S: fmt::Display> fmt::Display for NewlineCounting<S> {
|
||||||
|
fn fmt(&self, dest: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.run(dest, |f| write!(f, "{}", self.source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: FastWritable> FastWritable for NewlineCounting<S> {
|
||||||
fn write_into<W: fmt::Write + ?Sized>(
|
fn write_into<W: fmt::Write + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
dest: &mut W,
|
dest: &mut W,
|
||||||
values: &dyn crate::Values,
|
values: &dyn crate::Values,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
dest.write_str("<p>")?;
|
self.run(dest, |f| self.source.write_into(f, values))
|
||||||
let mut formatter = LinebreakFormatter { dest, counter: -1 };
|
|
||||||
self.0.write_into(&mut formatter, values)?;
|
|
||||||
dest.write_str("</p>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LinebreakFormatter<'a, W: ?Sized> {
|
struct NewlineCountingFormatter<'a, W: ?Sized> {
|
||||||
dest: &'a mut W,
|
dest: &'a mut W,
|
||||||
counter: isize,
|
counter: isize,
|
||||||
|
one: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: fmt::Write + ?Sized> fmt::Write for LinebreakFormatter<'_, W> {
|
impl<W: fmt::Write + ?Sized> fmt::Write for NewlineCountingFormatter<'_, W> {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -693,7 +746,7 @@ impl<W: fmt::Write + ?Sized> fmt::Write for LinebreakFormatter<'_, W> {
|
|||||||
if !line.is_empty() {
|
if !line.is_empty() {
|
||||||
match replace(&mut self.counter, if has_eol { 1 } else { 0 }) {
|
match replace(&mut self.counter, if has_eol { 1 } else { 0 }) {
|
||||||
..=0 => {}
|
..=0 => {}
|
||||||
1 => self.dest.write_str("<br/>")?,
|
1 => self.dest.write_str(self.one)?,
|
||||||
2.. => self.dest.write_str("</p><p>")?,
|
2.. => self.dest.write_str("</p><p>")?,
|
||||||
}
|
}
|
||||||
self.dest.write_str(line)?;
|
self.dest.write_str(line)?;
|
||||||
@ -827,4 +880,22 @@ mod tests {
|
|||||||
"<p>Foo<br/>Bar</p><p>Baz</p>"
|
"<p>Foo<br/>Bar</p><p>Baz</p>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_paragraphbreaks() {
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\nBar Baz").unwrap().to_string(),
|
||||||
|
"<p>Foo\nBar Baz</p>"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\nBar\n\nBaz").unwrap().to_string(),
|
||||||
|
"<p>Foo\nBar</p><p>Baz</p>"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
paragraphbreaks("Foo\n\n\n\n\nBar\n\nBaz")
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
"<p>Foo</p><p>Bar</p><p>Baz</p>"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,12 @@ mod urlencode;
|
|||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use self::alloc::{
|
pub use self::alloc::{
|
||||||
capitalize, fmt, format, linebreaksbr, lower, lowercase, paragraphbreaks, title, titlecase,
|
capitalize, fmt, format, linebreaksbr, lower, lowercase, title, titlecase, trim, upper,
|
||||||
trim, upper, uppercase,
|
uppercase,
|
||||||
};
|
};
|
||||||
pub use self::core::{
|
pub use self::core::{
|
||||||
PluralizeCount, center, join, linebreaks, pluralize, reject, reject_with, truncate, wordcount,
|
PluralizeCount, center, join, linebreaks, paragraphbreaks, pluralize, reject, reject_with,
|
||||||
|
truncate, wordcount,
|
||||||
};
|
};
|
||||||
pub use self::escape::{
|
pub use self::escape::{
|
||||||
AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text,
|
AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text,
|
||||||
|
@ -323,9 +323,8 @@ impl<'a> Generator<'a, '_> {
|
|||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
args: &[WithSpan<'a, Expr<'a>>],
|
args: &[WithSpan<'a, Expr<'a>>],
|
||||||
node: Span<'_>,
|
_node: Span<'_>,
|
||||||
) -> Result<DisplayWrap, CompileError> {
|
) -> Result<DisplayWrap, CompileError> {
|
||||||
ensure_filter_has_feature_alloc(ctx, "paragraphbreaks", node)?;
|
|
||||||
self.visit_linebreaks_filters(ctx, buf, "paragraphbreaks", args)
|
self.visit_linebreaks_filters(ctx, buf, "paragraphbreaks", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,11 +346,6 @@ hello<br />world<br /><br />from<br />askama
|
|||||||
### paragraphbreaks
|
### paragraphbreaks
|
||||||
[#paragraphbreaks]: #paragraphbreaks
|
[#paragraphbreaks]: #paragraphbreaks
|
||||||
|
|
||||||
<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
|
```jinja
|
||||||
{{ text_to_break | paragraphbreaks }}
|
{{ text_to_break | paragraphbreaks }}
|
||||||
```
|
```
|
||||||
|
@ -302,11 +302,9 @@ fn filter_nested_filter_blocks() {
|
|||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template.render().unwrap(),
|
template.render().unwrap(),
|
||||||
r"[
|
"[
|
||||||
<P>HELLO &#38;</P><P>GOODBYE!
|
<P>HELLO &#38;</P><P>GOODBYE!</P>
|
||||||
</P>
|
<P>HELLO &#38;</P><P>GOODBYE!</P>]
|
||||||
<P>HELLO &#38;</P><P>GOODBYE!
|
|
||||||
</P>]
|
|
||||||
2"
|
2"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user