allow multiline comments in more contexts

This commit is contained in:
Connor Skees 2020-08-07 11:39:14 -04:00
parent 484409761d
commit d5b2410a8c
15 changed files with 84 additions and 30 deletions

View File

@ -155,8 +155,7 @@ impl<'a> Parser<'a> {
} }
pub(super) fn parse_for(&mut self) -> SassResult<Vec<Stmt>> { pub(super) fn parse_for(&mut self) -> SassResult<Vec<Stmt>> {
// todo: whitespace or comment self.whitespace_or_comment();
self.whitespace();
// todo: test for error here // todo: test for error here
self.expect_char('$')?; self.expect_char('$')?;
@ -164,7 +163,7 @@ impl<'a> Parser<'a> {
.parse_identifier_no_interpolation(false)? .parse_identifier_no_interpolation(false)?
.map_node(|n| n.into()); .map_node(|n| n.into());
self.whitespace(); self.whitespace_or_comment();
self.span_before = match self.toks.peek() { self.span_before = match self.toks.peek() {
Some(tok) => tok.pos, Some(tok) => tok.pos,
None => return Err(("Expected \"from\".", var.span).into()), None => return Err(("Expected \"from\".", var.span).into()),
@ -172,7 +171,7 @@ impl<'a> Parser<'a> {
if self.parse_identifier()?.node.to_ascii_lowercase() != "from" { if self.parse_identifier()?.node.to_ascii_lowercase() != "from" {
return Err(("Expected \"from\".", var.span).into()); return Err(("Expected \"from\".", var.span).into());
} }
self.whitespace(); self.whitespace_or_comment();
let mut from_toks = Vec::new(); let mut from_toks = Vec::new();
let mut through = 0; let mut through = 0;
while let Some(tok) = self.toks.peek().cloned() { while let Some(tok) = self.toks.peek().cloned() {
@ -234,7 +233,7 @@ impl<'a> Parser<'a> {
} }
} }
} }
self.whitespace(); self.whitespace_or_comment();
let from_val = self.parse_value_from_vec(from_toks, true)?; let from_val = self.parse_value_from_vec(from_toks, true)?;
let from = match from_val.node { let from = match from_val.node {
Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() { Value::Dimension(Some(n), ..) => match n.to_integer().to_isize() {
@ -272,8 +271,6 @@ impl<'a> Parser<'a> {
let body = read_until_closing_curly_brace(self.toks)?; let body = read_until_closing_curly_brace(self.toks)?;
self.toks.next(); self.toks.next();
self.whitespace();
let (mut x, mut y); let (mut x, mut y);
// we can't use an inclusive range here // we can't use an inclusive range here
#[allow(clippy::range_plus_one)] #[allow(clippy::range_plus_one)]
@ -348,7 +345,10 @@ impl<'a> Parser<'a> {
} }
pub(super) fn parse_while(&mut self) -> SassResult<Vec<Stmt>> { pub(super) fn parse_while(&mut self) -> SassResult<Vec<Stmt>> {
self.whitespace(); // technically not necessary to eat whitespace here, but since we
// operate on raw tokens rather than an AST, it potentially saves a lot of
// time in re-parsing
self.whitespace_or_comment();
let cond = read_until_open_curly_brace(self.toks)?; let cond = read_until_open_curly_brace(self.toks)?;
if cond.is_empty() { if cond.is_empty() {
@ -364,8 +364,6 @@ impl<'a> Parser<'a> {
None => return Err(("expected \"}\".", self.span_before).into()), None => return Err(("expected \"}\".", self.span_before).into()),
}); });
self.whitespace();
let mut stmts = Vec::new(); let mut stmts = Vec::new();
let mut val = self.parse_value_from_vec(cond.clone(), true)?; let mut val = self.parse_value_from_vec(cond.clone(), true)?;
self.scopes.enter_new_scope(); self.scopes.enter_new_scope();
@ -424,16 +422,15 @@ impl<'a> Parser<'a> {
} }
pub(super) fn parse_each(&mut self) -> SassResult<Vec<Stmt>> { pub(super) fn parse_each(&mut self) -> SassResult<Vec<Stmt>> {
self.whitespace();
let mut vars: Vec<Spanned<Identifier>> = Vec::new(); let mut vars: Vec<Spanned<Identifier>> = Vec::new();
self.whitespace_or_comment();
loop { loop {
self.expect_char('$')?; self.expect_char('$')?;
vars.push(self.parse_identifier()?.map_node(|i| i.into())); vars.push(self.parse_identifier()?.map_node(|i| i.into()));
// todo: whitespace or comment self.whitespace_or_comment();
self.whitespace();
if self if self
.toks .toks
.peek() .peek()
@ -442,7 +439,7 @@ impl<'a> Parser<'a> {
== ',' == ','
{ {
self.toks.next(); self.toks.next();
self.whitespace(); self.whitespace_or_comment();
} else { } else {
break; break;
} }
@ -451,7 +448,7 @@ impl<'a> Parser<'a> {
if i.node.to_ascii_lowercase() != "in" { if i.node.to_ascii_lowercase() != "in" {
return Err(("Expected \"in\".", i.span).into()); return Err(("Expected \"in\".", i.span).into());
} }
self.whitespace(); self.whitespace_or_comment();
let iter_val_toks = read_until_open_curly_brace(self.toks)?; let iter_val_toks = read_until_open_curly_brace(self.toks)?;
let iter = self let iter = self
.parse_value_from_vec(iter_val_toks, true)? .parse_value_from_vec(iter_val_toks, true)?

View File

@ -121,7 +121,7 @@ impl<'a> Parser<'a> {
return Err(("This at-rule is not allowed here.", self.span_before).into()); return Err(("This at-rule is not allowed here.", self.span_before).into());
} }
self.whitespace(); self.whitespace_or_comment();
match self.toks.peek() { match self.toks.peek() {
Some(Token { kind: '\'', .. }) Some(Token { kind: '\'', .. })

View File

@ -35,6 +35,7 @@ impl<'a> Parser<'a> {
false false
} }
// todo: replace with predicate
pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> { pub fn expression_until_comparison(&mut self) -> SassResult<Cow<'static, str>> {
let mut toks = Vec::new(); let mut toks = Vec::new();
while let Some(tok) = self.toks.peek().cloned() { while let Some(tok) = self.toks.peek().cloned() {
@ -71,7 +72,7 @@ impl<'a> Parser<'a> {
pub(super) fn parse_media_query_list(&mut self) -> SassResult<String> { pub(super) fn parse_media_query_list(&mut self) -> SassResult<String> {
let mut buf = String::new(); let mut buf = String::new();
loop { loop {
self.whitespace(); self.whitespace_or_comment();
buf.push_str(&self.parse_single_media_query()?); buf.push_str(&self.parse_single_media_query()?);
if !self.scan_char(',') { if !self.scan_char(',') {
break; break;
@ -94,13 +95,13 @@ impl<'a> Parser<'a> {
let mut buf = String::with_capacity(2); let mut buf = String::with_capacity(2);
self.expect_char('(')?; self.expect_char('(')?;
buf.push('('); buf.push('(');
self.whitespace(); self.whitespace_or_comment();
buf.push_str(&self.expression_until_comparison()?); buf.push_str(&self.expression_until_comparison()?);
if let Some(Token { kind: ':', .. }) = self.toks.peek() { if let Some(Token { kind: ':', .. }) = self.toks.peek() {
self.toks.next(); self.toks.next();
self.whitespace(); self.whitespace_or_comment();
buf.push(':'); buf.push(':');
buf.push(' '); buf.push(' ');
@ -112,7 +113,7 @@ impl<'a> Parser<'a> {
} }
buf.push_str(&self.parse_value_as_string_from_vec(toks, true)?); buf.push_str(&self.parse_value_as_string_from_vec(toks, true)?);
self.whitespace(); self.whitespace_or_comment();
buf.push(')'); buf.push(')');
return Ok(buf); return Ok(buf);
} else { } else {
@ -127,14 +128,14 @@ impl<'a> Parser<'a> {
} }
buf.push(' '); buf.push(' ');
self.whitespace(); self.whitespace_or_comment();
buf.push_str(&self.expression_until_comparison()?); buf.push_str(&self.expression_until_comparison()?);
} }
} }
self.expect_char(')')?; self.expect_char(')')?;
self.whitespace(); self.whitespace_or_comment();
buf.push(')'); buf.push(')');
Ok(buf) Ok(buf)
} }
@ -145,7 +146,7 @@ impl<'a> Parser<'a> {
if !matches!(self.toks.peek(), Some(Token { kind: '(', .. })) { if !matches!(self.toks.peek(), Some(Token { kind: '(', .. })) {
buf.push_str(&self.parse_identifier()?); buf.push_str(&self.parse_identifier()?);
self.whitespace(); self.whitespace_or_comment();
if let Some(tok) = self.toks.peek() { if let Some(tok) = self.toks.peek() {
if !is_name_start(tok.kind) { if !is_name_start(tok.kind) {
@ -156,7 +157,7 @@ impl<'a> Parser<'a> {
buf.push(' '); buf.push(' ');
let ident = self.parse_identifier()?; let ident = self.parse_identifier()?;
self.whitespace(); self.whitespace_or_comment();
if ident.to_ascii_lowercase() == "and" { if ident.to_ascii_lowercase() == "and" {
buf.push_str("and "); buf.push_str("and ");
@ -164,7 +165,7 @@ impl<'a> Parser<'a> {
buf.push_str(&ident); buf.push_str(&ident);
if self.scan_identifier("and")? { if self.scan_identifier("and")? {
self.whitespace(); self.whitespace_or_comment();
buf.push_str(" and "); buf.push_str(" and ");
} else { } else {
return Ok(buf); return Ok(buf);
@ -173,9 +174,9 @@ impl<'a> Parser<'a> {
} }
loop { loop {
self.whitespace(); self.whitespace_or_comment();
buf.push_str(&self.parse_media_feature()?); buf.push_str(&self.parse_media_feature()?);
self.whitespace(); self.whitespace_or_comment();
if !self.scan_identifier("and")? { if !self.scan_identifier("and")? {
break; break;
} }

View File

@ -616,7 +616,7 @@ impl<'a> Parser<'a> {
} }
let mut params = String::new(); let mut params = String::new();
self.whitespace(); self.whitespace_or_comment();
if let Some(Token { kind: ';', .. }) | None = self.toks.peek() { if let Some(Token { kind: ';', .. }) | None = self.toks.peek() {
self.toks.next(); self.toks.next();
return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule { return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule {

View File

@ -109,7 +109,7 @@ impl<'a> Parser<'a> {
} }
let mut property = self.parse_identifier()?.node; let mut property = self.parse_identifier()?.node;
let whitespace_after_property = self.whitespace(); let whitespace_after_property = self.whitespace_or_comment();
match self.toks.peek() { match self.toks.peek() {
Some(Token { kind: ':', .. }) => { Some(Token { kind: ':', .. }) => {

View File

@ -28,7 +28,7 @@ impl<'a> Parser<'a> {
pub(super) fn parse_variable_declaration(&mut self) -> SassResult<()> { pub(super) fn parse_variable_declaration(&mut self) -> SassResult<()> {
assert!(matches!(self.toks.next(), Some(Token { kind: '$', .. }))); assert!(matches!(self.toks.next(), Some(Token { kind: '$', .. })));
let ident: Identifier = self.parse_identifier_no_interpolation(false)?.node.into(); let ident: Identifier = self.parse_identifier_no_interpolation(false)?.node.into();
self.whitespace(); self.whitespace_or_comment();
self.expect_char(':')?; self.expect_char(':')?;

View File

@ -77,6 +77,17 @@ test!(
}", }",
"a {\n color: a;\n color: b;\n color: 1;\n color: b;\n}\n" "a {\n color: a;\n color: b;\n color: 1;\n color: b;\n}\n"
); );
// todo: newlines are not correct
test!(
multiline_comments_everywhere,
" /**/ @each /**/ $a /**/ , /**/ $b /**/ in /**/ ( /**/ a /**/ , /**/ b /**/ ) /**/ { /**/
a {
color: $a;
color: $b;
}
} /**/ ",
"/**/\n/**/\na {\n color: a;\n}\n/**/\n\na {\n color: b;\n}\n/**/\n"
);
error!( error!(
list_of_single_map, list_of_single_map,
"a { "a {

View File

@ -113,3 +113,8 @@ test!(
}", }",
"a {\n color: 1;\n color: 2;\n color: 1;\n color: 2;\n}\n" "a {\n color: 1;\n color: 2;\n color: 1;\n color: 2;\n}\n"
); );
test!(
multiline_comments_everywhere,
" /**/ @for /**/ $i /**/ from /**/ 0 /**/ to /**/ 2 /**/ {} /**/ ",
"/**/\n/**/\n"
);

View File

@ -163,6 +163,16 @@ test!(
}", }",
"a {\n a: \\}}}{{{#;\n b: \\}}}{{{#;\n c: \\}}}{{{#;\n}\n" "a {\n a: \\}}}{{{#;\n b: \\}}}{{{#;\n c: \\}}}{{{#;\n}\n"
); );
test!(
multiline_comments_everywhere,
" /**/ @if /**/ false /**/ {} /**/
/**/
/**/ @else /**/ if /**/ false /**/ {} /**/
/**/
/**/ @else /**/ {} /**/
/**/ ",
"/**/\n/**/\n/**/\n"
);
error!( error!(
nothing_after_escape, nothing_after_escape,
"@if \\", "Error: Expected expression." "@if \\", "Error: Expected expression."

View File

@ -188,6 +188,11 @@ test!(
"@import url(#{1+1}..);", "@import url(#{1+1}..);",
"@import url(2..);\n" "@import url(2..);\n"
); );
test!(
import_multiline_comments_everywhere,
" /**/ @import /**/ url(foo) /**/ ;",
"/**/\n@import url(foo);\n"
);
// todo: test for calling paths, e.g. `grass b\index.scss` // todo: test for calling paths, e.g. `grass b\index.scss`
// todo: test for absolute paths (how?) // todo: test for absolute paths (how?)

View File

@ -63,3 +63,12 @@ test!(
}", }",
"@media screen and (:) {\n a {\n color: red;\n }\n}\n" "@media screen and (:) {\n a {\n color: red;\n }\n}\n"
); );
test!(
multiline_comments_everywhere,
"@media/**/foo/**/and/**/(/**/bar/**/)/**/{
a {
color: red;
}
}",
"@media foo and (bar) {\n a {\n color: red;\n }\n}\n"
);

View File

@ -170,3 +170,8 @@ test!(
"a {\n color: unquote(\"foo \");\n}\n", "a {\n color: unquote(\"foo \");\n}\n",
"a {\n color: foo ;\n}\n" "a {\n color: foo ;\n}\n"
); );
test!(
multiline_comment_after_style_property,
"a {\n color /**/ : red;\n}\n",
"a {\n color: red;\n}\n"
);

View File

@ -29,3 +29,4 @@ test!(
}", }",
"@foo (a: b) {\n a {\n color: red;\n }\n}\na {\n color: green;\n}\n" "@foo (a: b) {\n a {\n color: red;\n }\n}\na {\n color: green;\n}\n"
); );
test!(contains_multiline_comment, "@foo /**/;\n", "@foo;\n");

View File

@ -149,6 +149,11 @@ test!(
}", }",
"a {\n color: red;\n}\n" "a {\n color: red;\n}\n"
); );
test!(
multiline_comments_everywhere,
" /**/ $a /**/ : /**/ red /**/ ; /**/ ",
"/**/\n/**/\n"
);
// https://github.com/Kixiron/lasso/issues/7 // https://github.com/Kixiron/lasso/issues/7
test!( test!(
regression_test_for_lasso_0_3_0, regression_test_for_lasso_0_3_0,

View File

@ -125,6 +125,11 @@ test!(
}", }",
"a {\n color: red;\n}\n\na {\n color: blue;\n}\n" "a {\n color: red;\n}\n\na {\n color: blue;\n}\n"
); );
test!(
multiline_comments_everywhere,
" /**/ @while /**/ false /**/ {} /**/ ",
"/**/\n/**/\n"
);
error!( error!(
missing_closing_curly_brace, missing_closing_curly_brace,
"@while true {", "Error: expected \"}\"." "@while true {", "Error: expected \"}\"."