Finished transitioning to the new top-level ignored whitespace template element.

This commit is contained in:
Tom Alexander 2020-05-03 14:44:09 -04:00
parent 908ae078b0
commit bafff8e7a0
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 97 additions and 91 deletions

View File

@ -2,9 +2,7 @@ use nom::branch::alt;
use nom::bytes::complete::escaped_transform; use nom::bytes::complete::escaped_transform;
use nom::bytes::complete::is_a; use nom::bytes::complete::is_a;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
use nom::bytes::complete::take_until;
use nom::bytes::complete::take_until_parser_matches;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::multispace0; use nom::character::complete::multispace0;
use nom::character::complete::one_of; use nom::character::complete::one_of;
@ -90,7 +88,7 @@ pub enum Filter {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Span<'a> { pub struct Span<'a> {
pub contents: Vec<&'a str>, pub contents: &'a str,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -153,26 +151,23 @@ pub enum TemplateElement<'a> {
/// ///
/// These elements are always wrapped in curly braces /// These elements are always wrapped in curly braces
fn dust_tag(i: &str) -> IResult<&str, DustTag> { fn dust_tag(i: &str) -> IResult<&str, DustTag> {
preceded( alt((
opt(span_end_of_line), map(special, DustTag::DTSpecial),
alt(( map(comment, DustTag::DTComment),
map(special, DustTag::DTSpecial), map(reference, DustTag::DTReference),
map(comment, DustTag::DTComment), conditional("{#", DustTag::DTSection),
map(reference, DustTag::DTReference), conditional("{?", DustTag::DTExists),
conditional("{#", DustTag::DTSection), conditional("{^", DustTag::DTNotExists),
conditional("{?", DustTag::DTExists), named_block("{+", DustTag::DTBlock),
conditional("{^", DustTag::DTNotExists), named_block("{<", DustTag::DTInlinePartial),
named_block("{+", DustTag::DTBlock), partial("{>", DustTag::DTPartial),
named_block("{<", DustTag::DTInlinePartial), parameterized_block("{@", "gte", DustTag::DTHelperGreaterThenOrEquals),
partial("{>", DustTag::DTPartial), parameterized_block("{@", "lte", DustTag::DTHelperLessThenOrEquals),
parameterized_block("{@", "gte", DustTag::DTHelperGreaterThenOrEquals), parameterized_block("{@", "eq", DustTag::DTHelperEquals),
parameterized_block("{@", "lte", DustTag::DTHelperLessThenOrEquals), parameterized_block("{@", "ne", DustTag::DTHelperNotEquals),
parameterized_block("{@", "eq", DustTag::DTHelperEquals), parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan),
parameterized_block("{@", "ne", DustTag::DTHelperNotEquals), parameterized_block("{@", "lt", DustTag::DTHelperLessThan),
parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan), ))(i)
parameterized_block("{@", "lt", DustTag::DTHelperLessThan),
)),
)(i)
} }
/// Special characters /// Special characters
@ -487,32 +482,32 @@ fn filter(i: &str) -> IResult<&str, Filter> {
/// Whitespace at the beginning of lines is ignored so we are matching /// Whitespace at the beginning of lines is ignored so we are matching
/// a newline character followed by as much contiguous whitespace as /// a newline character followed by as much contiguous whitespace as
/// possible, all of which will be thrown away by other parsers. /// possible, all of which will be thrown away by other parsers.
fn span_end_of_line(i: &str) -> IResult<&str, (&str, &str)> { fn ignore_new_line_leading_whitespace(i: &str) -> IResult<&str, IgnoredWhitespace> {
tuple((line_ending, multispace0))(i) map(
recognize(tuple((line_ending, multispace0))),
IgnoredWhitespace::StartOfLine,
)(i)
} }
fn span_line(i: &str) -> IResult<&str, &str> { /// Any text that is not a Dust element or ignored whitespace
verify( fn span(i: &str) -> IResult<&str, Span> {
let (remaining, line) = verify(
take_until_parser_matches(alt(( take_until_parser_matches(alt((
tag("{"), tag("{"),
line_ending, line_ending,
recognize(all_consuming(eof_whitespace)), recognize(all_consuming(eof_whitespace)),
))), ))),
|s: &str| s.len() > 0, |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)?; )(i)?;
Ok((remaining, Span { contents: lines })) Ok((remaining, Span { contents: line }))
} }
fn body(i: &str) -> IResult<&str, Body> { fn body(i: &str) -> IResult<&str, Body> {
let (remaining, template_elements) = many1(alt(( let (remaining, template_elements) = many1(alt((
map(
ignore_new_line_leading_whitespace,
TemplateElement::TEIgnoredWhitespace,
),
map(span, TemplateElement::TESpan), map(span, TemplateElement::TESpan),
map(dust_tag, TemplateElement::TETag), map(dust_tag, TemplateElement::TETag),
)))(i)?; )))(i)?;
@ -621,24 +616,8 @@ mod tests {
#[test] #[test]
fn test_span_end_of_line() { fn test_span_end_of_line() {
assert_eq!( assert_eq!(
super::span_end_of_line("\n \t \n\nfoo"), super::ignore_new_line_leading_whitespace("\n \t \n\nfoo"),
Ok(("foo", ("\n", " \t \n\n"))) Ok(("foo", IgnoredWhitespace::StartOfLine("\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)))
); );
} }
@ -649,7 +628,7 @@ mod tests {
Ok(( Ok((
"", "",
Span { Span {
contents: vec!["this is just some text"] contents: "this is just some text"
} }
)) ))
); );
@ -658,7 +637,7 @@ mod tests {
Ok(( Ok((
"{~lb}", "{~lb}",
Span { Span {
contents: vec!["this is just some text "] contents: "this is just some text "
} }
)) ))
); );
@ -667,20 +646,44 @@ mod tests {
Err(Error(("{~lb}", ErrorKind::Verify))) Err(Error(("{~lb}", ErrorKind::Verify)))
); );
assert_eq!( 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(( Ok((
"{foo}", "",
Span { Body {
contents: vec!["this is \t ", "multiline text"] 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!( assert_eq!(
super::span("\n leading whitespace"), super::body("\n leading whitespace"),
Ok(( Ok((
"", "",
Span { Body {
contents: vec!["leading whitespace"] elements: vec![
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n "
)),
TemplateElement::TESpan(Span {
contents: "leading whitespace"
}),
]
} }
)) ))
); );
@ -740,9 +743,7 @@ mod tests {
}, },
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span { contents: "hello " }),
contents: vec!["hello "]
}),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
filters: Vec::new() filters: Vec::new()
@ -767,9 +768,7 @@ mod tests {
}, },
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span { contents: "hello " }),
contents: vec!["hello "]
}),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
filters: Vec::new() filters: Vec::new()
@ -779,7 +778,7 @@ mod tests {
else_contents: Some(Body { else_contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span {
contents: vec!["goodbye "] contents: "goodbye "
}), }),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
@ -816,9 +815,7 @@ mod tests {
name: "foo", name: "foo",
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span { contents: "hello " }),
contents: vec!["hello "]
}),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
filters: Vec::new() filters: Vec::new()
@ -854,9 +851,7 @@ mod tests {
name: "foo", name: "foo",
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span { contents: "hello " }),
contents: vec!["hello "]
}),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
filters: Vec::new() filters: Vec::new()
@ -943,15 +938,13 @@ mod tests {
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span {
contents: vec!["Pet the "] contents: "Pet the "
}), }),
TemplateElement::TETag(DustTag::DTReference(Reference { TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec!["name"] }, path: Path { keys: vec!["name"] },
filters: Vec::new() filters: Vec::new()
})), })),
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span { contents: "!" })
contents: vec!["!"]
})
] ]
}), }),
else_contents: None else_contents: None
@ -1002,9 +995,12 @@ mod tests {
contents: Body { contents: Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span {
contents: vec!["- simple -"] contents: "- simple -"
}), }),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)), TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSection(Container { TemplateElement::TETag(DustTag::DTSection(Container {
path: Path { path: Path {
keys: vec!["names"] keys: vec!["names"]
@ -1019,22 +1015,34 @@ mod tests {
}), }),
else_contents: None, else_contents: None,
})), })),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)), TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TESpan(Span { TemplateElement::TESpan(Span {
contents: vec!["- new lines -"] contents: "- new lines -"
}), }),
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)), TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n"
)),
TemplateElement::TETag(DustTag::DTSection(Container { TemplateElement::TETag(DustTag::DTSection(Container {
path: Path { path: Path {
keys: vec!["names"] keys: vec!["names"]
}, },
contents: Some(Body { contents: Some(Body {
elements: vec![TemplateElement::TETag(DustTag::DTReference( elements: vec![
Reference { TemplateElement::TEIgnoredWhitespace(
IgnoredWhitespace::StartOfLine("\n")
),
TemplateElement::TETag(DustTag::DTReference(Reference {
path: Path { keys: vec![] }, path: Path { keys: vec![] },
filters: vec![] filters: vec![]
} })),
))] TemplateElement::TEIgnoredWhitespace(
IgnoredWhitespace::StartOfLine("\n")
)
]
}), }),
else_contents: None, else_contents: None,
})), })),

View File

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