Merge branch 'bug_tags_separated_by_whitespace' into render

This commit is contained in:
Tom Alexander 2020-05-03 14:46:12 -04:00
commit 7f65e73229
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 144 additions and 70 deletions

View File

@ -2,9 +2,7 @@ use nom::branch::alt;
use nom::bytes::complete::escaped_transform;
use nom::bytes::complete::is_a;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_until;
use nom::bytes::complete::take_until_parser_matches;
use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
use nom::character::complete::line_ending;
use nom::character::complete::multispace0;
use nom::character::complete::one_of;
@ -53,6 +51,11 @@ pub enum Special {
RightCurlyBrace,
}
#[derive(Clone, Debug, PartialEq)]
pub enum IgnoredWhitespace<'a> {
StartOfLine(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Comment<'a> {
value: &'a str,
@ -85,7 +88,7 @@ pub enum Filter {
#[derive(Clone, Debug, PartialEq)]
pub struct Span<'a> {
pub contents: Vec<&'a str>,
pub contents: &'a str,
}
#[derive(Clone, Debug, PartialEq)]
@ -132,7 +135,7 @@ pub struct Body<'a> {
pub elements: Vec<TemplateElement<'a>>,
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Template<'a> {
pub contents: Body<'a>,
}
@ -141,6 +144,7 @@ pub struct Template<'a> {
pub enum TemplateElement<'a> {
TESpan(Span<'a>),
TETag(DustTag<'a>),
TEIgnoredWhitespace(IgnoredWhitespace<'a>),
}
/// Any element significant to dust that isn't plain text
@ -475,36 +479,35 @@ fn filter(i: &str) -> IResult<&str, Filter> {
)(i)
}
/// Whitespace at the beginning of lines is ignored so inside a span
/// we are matching a newline character followed by as much contiguous
/// whitespace as possible, all of which will be thrown away by other
/// parsers.
fn span_end_of_line(i: &str) -> IResult<&str, (&str, &str)> {
tuple((line_ending, multispace0))(i)
/// Whitespace at the beginning of lines is ignored so we are matching
/// a newline character followed by as much contiguous whitespace as
/// possible, all of which will be thrown away by other parsers.
fn ignore_new_line_leading_whitespace(i: &str) -> IResult<&str, IgnoredWhitespace> {
map(
recognize(tuple((line_ending, multispace0))),
IgnoredWhitespace::StartOfLine,
)(i)
}
fn span_line(i: &str) -> IResult<&str, &str> {
verify(
/// Any text that is not a Dust element or ignored whitespace
fn span(i: &str) -> IResult<&str, Span> {
let (remaining, line) = verify(
take_until_parser_matches(alt((
tag("{"),
line_ending,
recognize(all_consuming(eof_whitespace)),
))),
|s: &str| s.len() > 0,
)(i)
}
/// Any text that is not a Dust element
fn span(i: &str) -> IResult<&str, Span> {
let (remaining, lines) = preceded(
opt(span_end_of_line),
many1(terminated(span_line, opt(span_end_of_line))),
)(i)?;
Ok((remaining, Span { contents: lines }))
Ok((remaining, Span { contents: line }))
}
fn body(i: &str) -> IResult<&str, Body> {
let (remaining, template_elements) = many1(alt((
map(
ignore_new_line_leading_whitespace,
TemplateElement::TEIgnoredWhitespace,
),
map(span, TemplateElement::TESpan),
map(dust_tag, TemplateElement::TETag),
)))(i)?;
@ -613,24 +616,8 @@ mod tests {
#[test]
fn test_span_end_of_line() {
assert_eq!(
super::span_end_of_line("\n \t \n\nfoo"),
Ok(("foo", ("\n", " \t \n\n")))
);
}
#[test]
fn test_span_line() {
assert_eq!(
super::span_line("this is just some text"),
Ok(("", "this is just some text"))
);
assert_eq!(
super::span_line("this is just some text {~lb}"),
Ok(("{~lb}", "this is just some text "))
);
assert_eq!(
super::span_line("{~lb}"),
Err(Error(("{~lb}", ErrorKind::Verify)))
super::ignore_new_line_leading_whitespace("\n \t \n\nfoo"),
Ok(("foo", IgnoredWhitespace::StartOfLine("\n \t \n\n")))
);
}
@ -641,7 +628,7 @@ mod tests {
Ok((
"",
Span {
contents: vec!["this is just some text"]
contents: "this is just some text"
}
))
);
@ -650,7 +637,7 @@ mod tests {
Ok((
"{~lb}",
Span {
contents: vec!["this is just some text "]
contents: "this is just some text "
}
))
);
@ -659,20 +646,44 @@ mod tests {
Err(Error(("{~lb}", ErrorKind::Verify)))
);
assert_eq!(
super::span("this is \t \n\n \t \n \t multiline text\n {foo}"),
super::body("this is \t \n\n \t \n \t multiline text\n {foo}"),
Ok((
"{foo}",
Span {
contents: vec!["this is \t ", "multiline text"]
"",
Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: "this is \t "
}),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n\n \t \n \t "
)),
TemplateElement::TESpan(Span {
contents: "multiline text"
}),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n "
)),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["foo"] },
filters: vec![]
}))
]
}
))
);
assert_eq!(
super::span("\n leading whitespace"),
super::body("\n leading whitespace"),
Ok((
"",
Span {
contents: vec!["leading whitespace"]
Body {
elements: vec![
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n "
)),
TemplateElement::TESpan(Span {
contents: "leading whitespace"
}),
]
}
))
);
@ -732,9 +743,7 @@ mod tests {
},
contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["hello "]
}),
TemplateElement::TESpan(Span { contents: "hello " }),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
filters: Vec::new()
@ -759,9 +768,7 @@ mod tests {
},
contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["hello "]
}),
TemplateElement::TESpan(Span { contents: "hello " }),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
filters: Vec::new()
@ -771,7 +778,7 @@ mod tests {
else_contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["goodbye "]
contents: "goodbye "
}),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
@ -808,9 +815,7 @@ mod tests {
name: "foo",
contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["hello "]
}),
TemplateElement::TESpan(Span { contents: "hello " }),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
filters: Vec::new()
@ -846,9 +851,7 @@ mod tests {
name: "foo",
contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["hello "]
}),
TemplateElement::TESpan(Span { contents: "hello " }),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
filters: Vec::new()
@ -935,15 +938,13 @@ mod tests {
contents: Some(Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: vec!["Pet the "]
contents: "Pet the "
}),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] },
filters: Vec::new()
})),
TemplateElement::TESpan(Span {
contents: vec!["!"]
})
TemplateElement::TESpan(Span { contents: "!" })
]
}),
else_contents: None
@ -976,4 +977,79 @@ mod tests {
))
);
}
#[test]
fn test_full_document_new_line_equality() {
assert_eq!(
super::template(
"- simple -{~n}
{#names}{.}{/names}
{~n}- new lines -{~n}
{#names}
{.}
{/names}"
),
Ok::<_, nom::Err<(&str, ErrorKind)>>((
"",
Template {
contents: Body {
elements: vec![
TemplateElement::TESpan(Span {
contents: "- simple -"
}),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSection(Container {
path: Path {
keys: vec!["names"]
},
contents: Some(Body {
elements: vec![TemplateElement::TETag(DustTag::DTReference(
Reference {
path: Path { keys: vec![] },
filters: vec![]
}
))]
}),
else_contents: None,
})),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TESpan(Span {
contents: "- new lines -"
}),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSection(Container {
path: Path {
keys: vec!["names"]
},
contents: Some(Body {
elements: vec![
TemplateElement::TEIgnoredWhitespace(
IgnoredWhitespace::StartOfLine("\n")
),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec![] },
filters: vec![]
})),
TemplateElement::TEIgnoredWhitespace(
IgnoredWhitespace::StartOfLine("\n")
)
]
}),
else_contents: None,
})),
]
}
}
))
);
}
}

View File

@ -85,10 +85,8 @@ impl<'a> DustRenderer<'a> {
let mut output = String::new();
for elem in &body.elements {
match elem {
TemplateElement::TESpan(span) => span
.contents
.iter()
.for_each(|line: &&str| output.push_str(line)),
TemplateElement::TEIgnoredWhitespace(_) => {}
TemplateElement::TESpan(span) => output.push_str(span.contents),
TemplateElement::TETag(dt) => {
output.push_str(&self.render_tag(dt, context)?);
}