add support for unicode ranges
This commit is contained in:
parent
5133d580de
commit
45508a7665
@ -14,7 +14,7 @@ use crate::{
|
|||||||
common::{unvendor, Brackets, Identifier, ListSeparator, Op, QuoteKind},
|
common::{unvendor, Brackets, Identifier, ListSeparator, Op, QuoteKind},
|
||||||
error::SassResult,
|
error::SassResult,
|
||||||
unit::Unit,
|
unit::Unit,
|
||||||
utils::{eat_whole_number, IsWhitespace, ParsedNumber},
|
utils::{eat_whole_number, is_name, IsWhitespace, ParsedNumber},
|
||||||
value::{Number, SassFunction, SassMap, Value},
|
value::{Number, SassFunction, SassMap, Value},
|
||||||
Token,
|
Token,
|
||||||
};
|
};
|
||||||
@ -525,6 +525,102 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_interpolated_identifier_body(&mut self) -> bool {
|
||||||
|
match self.toks.peek() {
|
||||||
|
Some(Token { kind: '\\', .. }) => true,
|
||||||
|
Some(Token { kind, .. }) if is_name(*kind) => true,
|
||||||
|
Some(Token { kind: '#', .. }) => {
|
||||||
|
let next_is_curly = matches!(self.toks.peek_next(), Some(Token { kind: '{', .. }));
|
||||||
|
self.toks.reset_cursor();
|
||||||
|
next_is_curly
|
||||||
|
}
|
||||||
|
Some(..) => false,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// single codepoint: U+26
|
||||||
|
/// Codepoint range: U+0-7F
|
||||||
|
/// Wildcard range: U+4??
|
||||||
|
fn parse_unicode_range(&mut self, kind: char) -> SassResult<Spanned<IntermediateValue>> {
|
||||||
|
let mut buf = String::with_capacity(4);
|
||||||
|
let mut span = self.span_before;
|
||||||
|
buf.push(kind);
|
||||||
|
buf.push('+');
|
||||||
|
|
||||||
|
for _ in 0..6 {
|
||||||
|
if let Some(Token { kind, pos }) = self.toks.peek() {
|
||||||
|
if kind.is_ascii_hexdigit() {
|
||||||
|
span = span.merge(*pos);
|
||||||
|
self.span_before = *pos;
|
||||||
|
buf.push(*kind);
|
||||||
|
self.toks.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.consume_char_if_exists('?') {
|
||||||
|
buf.push('?');
|
||||||
|
for _ in 0..(8_usize.saturating_sub(buf.len())) {
|
||||||
|
if let Some(Token { kind: '?', pos }) = self.toks.peek() {
|
||||||
|
span = span.merge(*pos);
|
||||||
|
self.span_before = *pos;
|
||||||
|
buf.push('?');
|
||||||
|
self.toks.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(Spanned {
|
||||||
|
node: IntermediateValue::Value(HigherIntermediateValue::Literal(Value::String(
|
||||||
|
buf,
|
||||||
|
QuoteKind::None,
|
||||||
|
))),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.len() == 2 {
|
||||||
|
return Err(("Expected hex digit or \"?\".", self.span_before).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.consume_char_if_exists('-') {
|
||||||
|
buf.push('-');
|
||||||
|
let mut found_hex_digit = false;
|
||||||
|
for _ in 0..6 {
|
||||||
|
found_hex_digit = true;
|
||||||
|
if let Some(Token { kind, pos }) = self.toks.peek() {
|
||||||
|
if kind.is_ascii_hexdigit() {
|
||||||
|
span = span.merge(*pos);
|
||||||
|
self.span_before = *pos;
|
||||||
|
buf.push(*kind);
|
||||||
|
self.toks.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_hex_digit {
|
||||||
|
return Err(("Expected hex digit.", self.span_before).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.in_interpolated_identifier_body() {
|
||||||
|
return Err(("Expected end of identifier.", self.span_before).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Spanned {
|
||||||
|
node: IntermediateValue::Value(HigherIntermediateValue::Literal(Value::String(
|
||||||
|
buf,
|
||||||
|
QuoteKind::None,
|
||||||
|
))),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_intermediate_value(
|
fn parse_intermediate_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
predicate: &dyn Fn(&mut PeekMoreIterator<IntoIter<Token>>) -> bool,
|
predicate: &dyn Fn(&mut PeekMoreIterator<IntoIter<Token>>) -> bool,
|
||||||
@ -553,6 +649,15 @@ impl<'a> Parser<'a> {
|
|||||||
|| (!kind.is_ascii() && !kind.is_control())
|
|| (!kind.is_ascii() && !kind.is_control())
|
||||||
|| (kind == '-' && self.next_is_hypen()) =>
|
|| (kind == '-' && self.next_is_hypen()) =>
|
||||||
{
|
{
|
||||||
|
if kind == 'U' || kind == 'u' {
|
||||||
|
if matches!(self.toks.peek_next(), Some(Token { kind: '+', .. })) {
|
||||||
|
self.toks.next();
|
||||||
|
self.toks.next();
|
||||||
|
return Some(self.parse_unicode_range(kind));
|
||||||
|
} else {
|
||||||
|
self.toks.reset_cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
return Some(self.parse_ident_value(predicate));
|
return Some(self.parse_ident_value(predicate));
|
||||||
}
|
}
|
||||||
'0'..='9' | '.' => {
|
'0'..='9' | '.' => {
|
||||||
|
20
tests/unicode-range.rs
Normal file
20
tests/unicode-range.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
single_codepoint,
|
||||||
|
"a {\n color: U+26;\n}\n",
|
||||||
|
"a {\n color: U+26;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
simple_range,
|
||||||
|
"a {\n color: U+0-7F;\n}\n",
|
||||||
|
"a {\n color: U+0-7F;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
simple_wildcard_range,
|
||||||
|
"a {\n color: U+45????;\n}\n",
|
||||||
|
"a {\n color: U+45????;\n}\n"
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user