fix(css/lexer): Exclude whitespace from spans (#2702)

This commit is contained in:
Alexander Akait 2021-11-11 08:15:01 +03:00 committed by GitHub
parent 5db7bdc133
commit 0b1042354c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 339 additions and 90 deletions

View File

@ -88,25 +88,7 @@ where
I: Input,
{
fn read_token(&mut self) -> LexResult<Token> {
// Consume comments.
// If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A
// ASTERISK (*), consume them and all following code points up to and including
// the first U+002A ASTERISK (*) followed by a U+002F SOLIDUS (/), or up to an
// EOF code point. Return to the start of this step.
if self.input.cur() == Some('/') {
if self.input.peek() == Some('*') {
self.skip_block_comment()?;
self.skip_ws()?;
self.start_pos = self.input.cur_pos();
return self.read_token();
} else if self.config.allow_wrong_line_comments && self.input.peek() == Some('/') {
self.skip_line_comment()?;
self.start_pos = self.input.cur_pos();
return self.read_token();
}
}
self.read_comments()?;
let start = self.input.cur_pos();
let next = self.input.cur();
@ -116,7 +98,10 @@ where
// whitespace
// Consume as much whitespace as possible. Return a <whitespace-token>.
Some(c) if is_whitespace(c) => {
self.input.bump();
let mut value = String::new();
value.push(c);
loop {
let c = self.input.cur();
@ -133,13 +118,6 @@ where
}
}
if self.config.allow_wrong_line_comments {
if self.input.is_byte(b'/') && self.input.peek() == Some('/') {
self.skip_line_comment()?;
self.start_pos = self.input.cur_pos();
}
}
return Ok(Token::WhiteSpace {
value: value.into(),
});
@ -430,6 +408,81 @@ where
}
}
// Consume comments.
// This section describes how to consume comments from a stream of code points.
// It returns nothing.
fn read_comments(&mut self) -> LexResult<()> {
// If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A
// ASTERISK (*), consume them and all following code points up to and including
// the first U+002A ASTERISK (*) followed by a U+002F SOLIDUS (/), or up to an
// EOF code point. Return to the start of this step.
// NOTE: We allow to parse line comments under the option.
if self.input.cur() == Some('/') && self.input.peek() == Some('*') {
while self.input.cur() == Some('/') && self.input.peek() == Some('*') {
self.input.bump(); // '*'
self.input.bump(); // '/'
loop {
let cur = self.input.cur();
if cur.is_some() {
self.input.bump();
}
match cur {
Some('*') if self.input.cur() == Some('/') => {
self.input.bump(); // '/'
break;
}
Some(_) => {}
None => {
return Err(ErrorKind::UnterminatedBlockComment);
}
}
}
// TODO: invalid
self.skip_ws()?;
}
self.start_pos = self.input.cur_pos();
} else if self.config.allow_wrong_line_comments
&& self.input.cur() == Some('/')
&& self.input.peek() == Some('/')
{
while self.input.cur() == Some('/') && self.input.peek() == Some('/') {
self.input.bump(); // '/'
self.input.bump(); // '/'
loop {
let cur = self.input.cur();
if cur.is_some() {
self.input.bump();
}
match cur {
Some(c) if is_newline(c) => {
break;
}
Some(_) => {}
None => {
return Err(ErrorKind::UnterminatedBlockComment);
}
}
}
// TODO: invalid
self.skip_ws()?;
}
self.start_pos = self.input.cur_pos();
}
Ok(())
}
// This section describes how to consume a numeric token from a stream of code
// points. It returns either a <number-token>, <percentage-token>, or
// <dimension-token>.
@ -1242,69 +1295,6 @@ where
}
}
if self.config.allow_wrong_line_comments {
if self.input.is_byte(b'/') && self.input.peek() == Some('/') {
self.skip_line_comment()?;
self.start_pos = self.input.cur_pos();
return Ok(());
}
}
Ok(())
}
/// Expects current char to be '/' and next char to be '*'.
fn skip_block_comment(&mut self) -> LexResult<()> {
debug_assert_eq!(self.input.cur(), Some('/'));
debug_assert_eq!(self.input.peek(), Some('*'));
self.input.bump();
self.input.bump();
// let slice_start = self.input.cur_pos();
let mut was_star = if self.input.is_byte(b'*') {
self.input.bump();
true
} else {
false
};
while let Some(c) = self.input.cur() {
if was_star && c == '/' {
debug_assert_eq!(self.input.cur(), Some('/'));
self.input.bump(); // '/'
return Ok(());
}
was_star = c == '*';
self.input.bump();
}
Err(ErrorKind::UnterminatedBlockComment)
}
fn skip_line_comment(&mut self) -> LexResult<()> {
debug_assert_eq!(self.input.cur(), Some('/'));
debug_assert_eq!(self.input.peek(), Some('/'));
self.input.bump();
self.input.bump();
debug_assert!(
self.config.allow_wrong_line_comments,
"Line comments are wrong and should be lexed only if it's explicitly requested"
);
while let Some(c) = self.input.cur() {
if is_newline(c) {
break;
}
self.input.bump();
}
Ok(())
}
}

View File

@ -0,0 +1,6 @@
a {
background: url(
/* test */
"test.png"
);
}

View File

@ -0,0 +1,10 @@
error: Expected Declaration value
--> $DIR/tests/errors/comments/bad-comment-4/input.css:2:17
|
2 | background: url(
| _________________^
3 | | /* test */
4 | | "test.png"
5 | | );
| |_____^

View File

@ -0,0 +1,6 @@
a {
background: url(
/* test */
test.png
);
}

View File

@ -0,0 +1,10 @@
error: Expected Declaration value
--> $DIR/tests/errors/comments/bad-comment-5/input.css:2:17
|
2 | background: url(
| _________________^
3 | | /* test */
4 | | test.png
5 | | );
| |_____^

View File

@ -33,3 +33,4 @@ st*/
/*!te**st*/
/****************************/
/*************** FOO *****************/
/* comment *//* comment */

View File

@ -1,7 +1,7 @@
{
"type": "Stylesheet",
"span": {
"start": 15,
"start": 16,
"end": 40,
"ctxt": 0
},

View File

@ -0,0 +1,5 @@
// Line comment
// Line comment
foo {
color: red;
}

View File

@ -0,0 +1,108 @@
{
"type": "Stylesheet",
"span": {
"start": 32,
"end": 56,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 32,
"end": 55,
"ctxt": 0
},
"selectors": {
"type": "SelectorList",
"span": {
"start": 32,
"end": 35,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 32,
"end": 35,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 32,
"end": 35,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TypeSelector",
"span": {
"start": 32,
"end": 35,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 32,
"end": 35,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
},
"subclassSelectors": []
}
]
}
]
},
"block": {
"type": "Block",
"span": {
"start": 36,
"end": 55,
"ctxt": 0
},
"items": [
{
"type": "Declaration",
"span": {
"start": 42,
"end": 52,
"ctxt": 0
},
"property": {
"type": "Text",
"span": {
"start": 42,
"end": 47,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"value": [
{
"type": "Text",
"span": {
"start": 49,
"end": 52,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}

View File

@ -0,0 +1,5 @@
foo {
// Line comment
// Line comment
color: red;
}

View File

@ -0,0 +1,108 @@
{
"type": "Stylesheet",
"span": {
"start": 0,
"end": 64,
"ctxt": 0
},
"rules": [
{
"type": "StyleRule",
"span": {
"start": 0,
"end": 63,
"ctxt": 0
},
"selectors": {
"type": "SelectorList",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "ComplexSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"children": [
{
"type": "CompoundSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"nestingSelector": null,
"typeSelector": {
"type": "TypeSelector",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"prefix": null,
"name": {
"type": "Text",
"span": {
"start": 0,
"end": 3,
"ctxt": 0
},
"value": "foo",
"raw": "foo"
}
},
"subclassSelectors": []
}
]
}
]
},
"block": {
"type": "Block",
"span": {
"start": 4,
"end": 63,
"ctxt": 0
},
"items": [
{
"type": "Declaration",
"span": {
"start": 50,
"end": 60,
"ctxt": 0
},
"property": {
"type": "Text",
"span": {
"start": 50,
"end": 55,
"ctxt": 0
},
"value": "color",
"raw": "color"
},
"value": [
{
"type": "Text",
"span": {
"start": 57,
"end": 60,
"ctxt": 0
},
"value": "red",
"raw": "red"
}
],
"important": null
}
]
}
}
]
}