diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs index 100cb3a..227ef72 100644 --- a/src/parse/control_flow.rs +++ b/src/parse/control_flow.rs @@ -155,8 +155,7 @@ impl<'a> Parser<'a> { } pub(super) fn parse_for(&mut self) -> SassResult> { - // todo: whitespace or comment - self.whitespace(); + self.whitespace_or_comment(); // todo: test for error here self.expect_char('$')?; @@ -164,7 +163,7 @@ impl<'a> Parser<'a> { .parse_identifier_no_interpolation(false)? .map_node(|n| n.into()); - self.whitespace(); + self.whitespace_or_comment(); self.span_before = match self.toks.peek() { Some(tok) => tok.pos, 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" { return Err(("Expected \"from\".", var.span).into()); } - self.whitespace(); + self.whitespace_or_comment(); let mut from_toks = Vec::new(); let mut through = 0; 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 = match from_val.node { 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)?; self.toks.next(); - self.whitespace(); - let (mut x, mut y); // we can't use an inclusive range here #[allow(clippy::range_plus_one)] @@ -348,7 +345,10 @@ impl<'a> Parser<'a> { } pub(super) fn parse_while(&mut self) -> SassResult> { - 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)?; if cond.is_empty() { @@ -364,8 +364,6 @@ impl<'a> Parser<'a> { None => return Err(("expected \"}\".", self.span_before).into()), }); - self.whitespace(); - let mut stmts = Vec::new(); let mut val = self.parse_value_from_vec(cond.clone(), true)?; self.scopes.enter_new_scope(); @@ -424,16 +422,15 @@ impl<'a> Parser<'a> { } pub(super) fn parse_each(&mut self) -> SassResult> { - self.whitespace(); let mut vars: Vec> = Vec::new(); + self.whitespace_or_comment(); loop { self.expect_char('$')?; vars.push(self.parse_identifier()?.map_node(|i| i.into())); - // todo: whitespace or comment - self.whitespace(); + self.whitespace_or_comment(); if self .toks .peek() @@ -442,7 +439,7 @@ impl<'a> Parser<'a> { == ',' { self.toks.next(); - self.whitespace(); + self.whitespace_or_comment(); } else { break; } @@ -451,7 +448,7 @@ impl<'a> Parser<'a> { if i.node.to_ascii_lowercase() != "in" { 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 = self .parse_value_from_vec(iter_val_toks, true)? diff --git a/src/parse/import.rs b/src/parse/import.rs index cd17e8c..81db734 100644 --- a/src/parse/import.rs +++ b/src/parse/import.rs @@ -121,7 +121,7 @@ impl<'a> Parser<'a> { return Err(("This at-rule is not allowed here.", self.span_before).into()); } - self.whitespace(); + self.whitespace_or_comment(); match self.toks.peek() { Some(Token { kind: '\'', .. }) diff --git a/src/parse/media.rs b/src/parse/media.rs index 61f124a..7b443fd 100644 --- a/src/parse/media.rs +++ b/src/parse/media.rs @@ -35,6 +35,7 @@ impl<'a> Parser<'a> { false } + // todo: replace with predicate pub fn expression_until_comparison(&mut self) -> SassResult> { let mut toks = Vec::new(); 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 { let mut buf = String::new(); loop { - self.whitespace(); + self.whitespace_or_comment(); buf.push_str(&self.parse_single_media_query()?); if !self.scan_char(',') { break; @@ -94,13 +95,13 @@ impl<'a> Parser<'a> { let mut buf = String::with_capacity(2); self.expect_char('(')?; buf.push('('); - self.whitespace(); + self.whitespace_or_comment(); buf.push_str(&self.expression_until_comparison()?); if let Some(Token { kind: ':', .. }) = self.toks.peek() { self.toks.next(); - self.whitespace(); + self.whitespace_or_comment(); buf.push(':'); buf.push(' '); @@ -112,7 +113,7 @@ impl<'a> Parser<'a> { } buf.push_str(&self.parse_value_as_string_from_vec(toks, true)?); - self.whitespace(); + self.whitespace_or_comment(); buf.push(')'); return Ok(buf); } else { @@ -127,14 +128,14 @@ impl<'a> Parser<'a> { } buf.push(' '); - self.whitespace(); + self.whitespace_or_comment(); buf.push_str(&self.expression_until_comparison()?); } } self.expect_char(')')?; - self.whitespace(); + self.whitespace_or_comment(); buf.push(')'); Ok(buf) } @@ -145,7 +146,7 @@ impl<'a> Parser<'a> { if !matches!(self.toks.peek(), Some(Token { kind: '(', .. })) { buf.push_str(&self.parse_identifier()?); - self.whitespace(); + self.whitespace_or_comment(); if let Some(tok) = self.toks.peek() { if !is_name_start(tok.kind) { @@ -156,7 +157,7 @@ impl<'a> Parser<'a> { buf.push(' '); let ident = self.parse_identifier()?; - self.whitespace(); + self.whitespace_or_comment(); if ident.to_ascii_lowercase() == "and" { buf.push_str("and "); @@ -164,7 +165,7 @@ impl<'a> Parser<'a> { buf.push_str(&ident); if self.scan_identifier("and")? { - self.whitespace(); + self.whitespace_or_comment(); buf.push_str(" and "); } else { return Ok(buf); @@ -173,9 +174,9 @@ impl<'a> Parser<'a> { } loop { - self.whitespace(); + self.whitespace_or_comment(); buf.push_str(&self.parse_media_feature()?); - self.whitespace(); + self.whitespace_or_comment(); if !self.scan_identifier("and")? { break; } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 13c8a5e..f47aa5a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -616,7 +616,7 @@ impl<'a> Parser<'a> { } let mut params = String::new(); - self.whitespace(); + self.whitespace_or_comment(); if let Some(Token { kind: ';', .. }) | None = self.toks.peek() { self.toks.next(); return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule { diff --git a/src/parse/style.rs b/src/parse/style.rs index 0e10bd1..ff42866 100644 --- a/src/parse/style.rs +++ b/src/parse/style.rs @@ -109,7 +109,7 @@ impl<'a> Parser<'a> { } 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() { Some(Token { kind: ':', .. }) => { diff --git a/src/parse/variable.rs b/src/parse/variable.rs index a5a77e4..08c0ed0 100644 --- a/src/parse/variable.rs +++ b/src/parse/variable.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_variable_declaration(&mut self) -> SassResult<()> { assert!(matches!(self.toks.next(), Some(Token { kind: '$', .. }))); let ident: Identifier = self.parse_identifier_no_interpolation(false)?.node.into(); - self.whitespace(); + self.whitespace_or_comment(); self.expect_char(':')?; diff --git a/tests/each.rs b/tests/each.rs index a47e4b5..e6cad28 100644 --- a/tests/each.rs +++ b/tests/each.rs @@ -77,6 +77,17 @@ test!( }", "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!( list_of_single_map, "a { diff --git a/tests/for.rs b/tests/for.rs index 7b6fa39..820591a 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -113,3 +113,8 @@ test!( }", "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" +); diff --git a/tests/if.rs b/tests/if.rs index ae9d4a3..417514d 100644 --- a/tests/if.rs +++ b/tests/if.rs @@ -163,6 +163,16 @@ test!( }", "a {\n a: \\}}}{{{#;\n b: \\}}}{{{#;\n c: \\}}}{{{#;\n}\n" ); +test!( + multiline_comments_everywhere, + " /**/ @if /**/ false /**/ {} /**/ + /**/ + /**/ @else /**/ if /**/ false /**/ {} /**/ + /**/ + /**/ @else /**/ {} /**/ + /**/ ", + "/**/\n/**/\n/**/\n" +); error!( nothing_after_escape, "@if \\", "Error: Expected expression." diff --git a/tests/imports.rs b/tests/imports.rs index 9210585..ed52eae 100644 --- a/tests/imports.rs +++ b/tests/imports.rs @@ -188,6 +188,11 @@ test!( "@import url(#{1+1}..);", "@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 absolute paths (how?) diff --git a/tests/media.rs b/tests/media.rs index 3b70082..33d751b 100644 --- a/tests/media.rs +++ b/tests/media.rs @@ -63,3 +63,12 @@ test!( }", "@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" +); diff --git a/tests/styles.rs b/tests/styles.rs index d0d9fb7..24b6ebd 100644 --- a/tests/styles.rs +++ b/tests/styles.rs @@ -170,3 +170,8 @@ test!( "a {\n color: unquote(\"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" +); diff --git a/tests/unknown-at-rule.rs b/tests/unknown-at-rule.rs index c635ba8..032684b 100644 --- a/tests/unknown-at-rule.rs +++ b/tests/unknown-at-rule.rs @@ -29,3 +29,4 @@ test!( }", "@foo (a: b) {\n a {\n color: red;\n }\n}\na {\n color: green;\n}\n" ); +test!(contains_multiline_comment, "@foo /**/;\n", "@foo;\n"); diff --git a/tests/variables.rs b/tests/variables.rs index 4ca5f30..555f275 100644 --- a/tests/variables.rs +++ b/tests/variables.rs @@ -149,6 +149,11 @@ test!( }", "a {\n color: red;\n}\n" ); +test!( + multiline_comments_everywhere, + " /**/ $a /**/ : /**/ red /**/ ; /**/ ", + "/**/\n/**/\n" +); // https://github.com/Kixiron/lasso/issues/7 test!( regression_test_for_lasso_0_3_0, diff --git a/tests/while.rs b/tests/while.rs index 3af9ac5..eec83b7 100644 --- a/tests/while.rs +++ b/tests/while.rs @@ -125,6 +125,11 @@ test!( }", "a {\n color: red;\n}\n\na {\n color: blue;\n}\n" ); +test!( + multiline_comments_everywhere, + " /**/ @while /**/ false /**/ {} /**/ ", + "/**/\n/**/\n" +); error!( missing_closing_curly_brace, "@while true {", "Error: expected \"}\"."