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.
This commit is contained in:
Tom Alexander 2020-05-03 12:28:55 -04:00
parent 31029bf50b
commit b8e8f19724
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

View File

@ -88,6 +88,11 @@ pub struct Span<'a> {
pub contents: &'a str, pub contents: &'a str,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct NewSpan<'a> {
pub contents: Vec<&'a str>,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Container<'a> { pub struct Container<'a> {
pub path: Path<'a>, pub path: Path<'a>,
@ -475,6 +480,34 @@ fn filter(i: &str) -> IResult<&str, Filter> {
)(i) )(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 /// Any text that is not a Dust element
fn span(i: &str) -> IResult<&str, Span> { fn span(i: &str) -> IResult<&str, Span> {
let (remaining, body) = verify( 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] #[test]
fn test_section_mismatched_paths() { fn test_section_mismatched_paths() {
assert_eq!( assert_eq!(