From b8e8f197243893d3e155f6d86e5b703f456939cb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 3 May 2020 12:28:55 -0400 Subject: [PATCH] Implemented a new span to trim leading whitespace. Through my experiements I have concluded that DustJS trims whitespace at the front of a line. To handle this, I implement a new parser that matches a newline followed by any amount of whitespace. This should allow me to trim whitespace at the head of the line because spans start immediately after a tag is closed. So, for example: ``` {foo} bar {/foo} ``` Would be: ``` {foo}\n bar{/foo} ^ ^ \____/ span ``` So while there is no magical "start of line" detector like in regular expressions, the first start of a line in a span will always be preceded with a newline character except for the opening of the document. For handling the opening of the document I am already trimming the whitespace in the `template()` parser. --- src/parser/parser.rs | 101 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 480cd68..5d0b7ab 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -88,6 +88,11 @@ pub struct Span<'a> { pub contents: &'a str, } +#[derive(Clone, Debug, PartialEq)] +pub struct NewSpan<'a> { + pub contents: Vec<&'a str>, +} + #[derive(Clone, Debug, PartialEq)] pub struct Container<'a> { pub path: Path<'a>, @@ -475,6 +480,34 @@ 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) +} + +fn span_line(i: &str) -> IResult<&str, &str> { + 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 new_span(i: &str) -> IResult<&str, NewSpan> { + let (remaining, lines) = preceded( + opt(span_end_of_line), + many1(terminated(span_line, opt(span_end_of_line))), + )(i)?; + Ok((remaining, NewSpan { contents: lines })) +} + /// Any text that is not a Dust element fn span(i: &str) -> IResult<&str, Span> { let (remaining, body) = verify( @@ -620,6 +653,74 @@ 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))) + ); + } + + #[test] + fn test_new_span() { + assert_eq!( + super::new_span("this is just some text"), + Ok(( + "", + NewSpan { + contents: vec!["this is just some text"] + } + )) + ); + assert_eq!( + super::new_span("this is just some text {~lb}"), + Ok(( + "{~lb}", + NewSpan { + contents: vec!["this is just some text "] + } + )) + ); + assert_eq!( + super::new_span("{~lb}"), + Err(Error(("{~lb}", ErrorKind::Verify))) + ); + assert_eq!( + super::new_span("this is \t \n\n \t \n \t multiline text\n {foo}"), + Ok(( + "{foo}", + NewSpan { + contents: vec!["this is \t ", "multiline text"] + } + )) + ); + assert_eq!( + super::new_span("\n leading whitespace"), + Ok(( + "", + NewSpan { + contents: vec!["leading whitespace"] + } + )) + ); + } + #[test] fn test_section_mismatched_paths() { assert_eq!(