Handle curly braces in quotes in styles
This commit is contained in:
parent
c2e339fb5b
commit
8c6be57872
@ -100,6 +100,13 @@ impl Token {
|
|||||||
pub fn equals_symbol(&self, s: Symbol) -> bool {
|
pub fn equals_symbol(&self, s: Symbol) -> bool {
|
||||||
self.kind.equals_symbol(s)
|
self.kind.equals_symbol(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_string(s: String) -> Self {
|
||||||
|
Token {
|
||||||
|
kind: TokenKind::Ident(s),
|
||||||
|
pos: Pos::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsWhitespace for Token {
|
impl IsWhitespace for Token {
|
||||||
|
28
src/style.rs
28
src/style.rs
@ -1,7 +1,7 @@
|
|||||||
use crate::common::{Pos, Scope, Symbol};
|
use crate::common::{Pos, Scope, Symbol};
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
use crate::selector::Selector;
|
use crate::selector::Selector;
|
||||||
use crate::utils::{devour_whitespace, parse_interpolation};
|
use crate::utils::{devour_whitespace, parse_interpolation, parse_quoted_string};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{Expr, Token, TokenKind};
|
use crate::{Expr, Token, TokenKind};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
@ -35,7 +35,7 @@ impl Style {
|
|||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
) -> SassResult<Value> {
|
) -> SassResult<Value> {
|
||||||
StyleParser::new(scope, super_selector).parse_style_value(toks)
|
StyleParser::new(scope, super_selector).parse_style_value(toks, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||||
@ -44,7 +44,7 @@ impl Style {
|
|||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
super_property: String,
|
super_property: String,
|
||||||
) -> SassResult<Expr> {
|
) -> SassResult<Expr> {
|
||||||
StyleParser::new(scope, super_selector).eat_style_group(toks, super_property)
|
StyleParser::new(scope, super_selector).eat_style_group(toks, super_property, scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +64,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
|
pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
|
||||||
&self,
|
&self,
|
||||||
toks: &mut Peekable<I>,
|
toks: &mut Peekable<I>,
|
||||||
|
scope: &Scope,
|
||||||
) -> SassResult<Value> {
|
) -> SassResult<Value> {
|
||||||
let mut style = Vec::new();
|
let mut style = Vec::new();
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
@ -83,6 +84,16 @@ impl<'a> StyleParser<'a> {
|
|||||||
n -= 1;
|
n -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ref q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
||||||
|
| ref q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||||
|
let q = q.clone();
|
||||||
|
let tok = toks.next().unwrap();
|
||||||
|
style.push(tok.clone());
|
||||||
|
style.push(Token::from_string(
|
||||||
|
parse_quoted_string(toks, scope, q)?.unquote().to_string(),
|
||||||
|
));
|
||||||
|
style.push(tok);
|
||||||
|
}
|
||||||
TokenKind::Symbol(Symbol::OpenCurlyBrace)
|
TokenKind::Symbol(Symbol::OpenCurlyBrace)
|
||||||
| TokenKind::Symbol(Symbol::SemiColon) => break,
|
| TokenKind::Symbol(Symbol::SemiColon) => break,
|
||||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||||
@ -104,6 +115,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
&self,
|
&self,
|
||||||
toks: &mut Peekable<I>,
|
toks: &mut Peekable<I>,
|
||||||
super_property: String,
|
super_property: String,
|
||||||
|
scope: &Scope,
|
||||||
) -> SassResult<Expr> {
|
) -> SassResult<Expr> {
|
||||||
let mut styles = Vec::new();
|
let mut styles = Vec::new();
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
@ -116,7 +128,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
let property = self.parse_property(toks, super_property.clone())?;
|
let property = self.parse_property(toks, super_property.clone())?;
|
||||||
if let Some(tok) = toks.peek() {
|
if let Some(tok) = toks.peek() {
|
||||||
if tok.equals_symbol(Symbol::OpenCurlyBrace) {
|
if tok.equals_symbol(Symbol::OpenCurlyBrace) {
|
||||||
match self.eat_style_group(toks, property)? {
|
match self.eat_style_group(toks, property, scope)? {
|
||||||
Expr::Styles(s) => styles.extend(s),
|
Expr::Styles(s) => styles.extend(s),
|
||||||
Expr::Style(s) => styles.push(*s),
|
Expr::Style(s) => styles.push(*s),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -134,7 +146,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let value = self.parse_style_value(toks)?;
|
let value = self.parse_style_value(toks, scope)?;
|
||||||
match toks.peek().unwrap().kind {
|
match toks.peek().unwrap().kind {
|
||||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
||||||
styles.push(Style { property, value });
|
styles.push(Style { property, value });
|
||||||
@ -149,7 +161,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
property: property.clone(),
|
property: property.clone(),
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
match self.eat_style_group(toks, property)? {
|
match self.eat_style_group(toks, property, scope)? {
|
||||||
Expr::Style(s) => styles.push(*s),
|
Expr::Style(s) => styles.push(*s),
|
||||||
Expr::Styles(s) => styles.extend(s),
|
Expr::Styles(s) => styles.extend(s),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -173,7 +185,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let val = self.parse_style_value(toks)?;
|
let val = self.parse_style_value(toks, scope)?;
|
||||||
match toks.peek().unwrap().kind {
|
match toks.peek().unwrap().kind {
|
||||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {}
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {}
|
||||||
TokenKind::Symbol(Symbol::SemiColon) => {
|
TokenKind::Symbol(Symbol::SemiColon) => {
|
||||||
@ -185,7 +197,7 @@ impl<'a> StyleParser<'a> {
|
|||||||
property: super_property.clone(),
|
property: super_property.clone(),
|
||||||
value: val,
|
value: val,
|
||||||
}];
|
}];
|
||||||
match self.eat_style_group(toks, super_property)? {
|
match self.eat_style_group(toks, super_property, scope)? {
|
||||||
Expr::Style(s) => v.push(*s),
|
Expr::Style(s) => v.push(*s),
|
||||||
Expr::Styles(s) => v.extend(s),
|
Expr::Styles(s) => v.extend(s),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
22
src/utils.rs
22
src/utils.rs
@ -171,14 +171,26 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
|||||||
{
|
{
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
TokenKind::Symbol(Symbol::DoubleQuote) if is_escaped => {
|
||||||
|
s.push('\\');
|
||||||
|
s.push('"');
|
||||||
|
is_escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
TokenKind::Symbol(Symbol::SingleQuote)
|
TokenKind::Symbol(Symbol::SingleQuote)
|
||||||
if !is_escaped && q == TokenKind::Symbol(Symbol::SingleQuote) =>
|
if !is_escaped && q == TokenKind::Symbol(Symbol::SingleQuote) =>
|
||||||
{
|
{
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
TokenKind::Symbol(Symbol::SingleQuote) if is_escaped => {
|
||||||
|
s.push('\\');
|
||||||
|
s.push('\'');
|
||||||
|
is_escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
TokenKind::Symbol(Symbol::BackSlash) if !is_escaped => is_escaped = true,
|
TokenKind::Symbol(Symbol::BackSlash) if !is_escaped => is_escaped = true,
|
||||||
TokenKind::Symbol(Symbol::BackSlash) => s.push('\\'),
|
TokenKind::Symbol(Symbol::BackSlash) => s.push('\\'),
|
||||||
TokenKind::Interpolation => {
|
TokenKind::Interpolation if !is_escaped => {
|
||||||
found_interpolation = true;
|
found_interpolation = true;
|
||||||
s.push_str(
|
s.push_str(
|
||||||
&parse_interpolation(toks, scope)?
|
&parse_interpolation(toks, scope)?
|
||||||
@ -188,13 +200,21 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
TokenKind::Interpolation => {
|
||||||
|
s.push('#');
|
||||||
|
s.push('{');
|
||||||
|
is_escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
if is_escaped && tok.kind != TokenKind::Symbol(Symbol::BackSlash) {
|
if is_escaped && tok.kind != TokenKind::Symbol(Symbol::BackSlash) {
|
||||||
is_escaped = false;
|
is_escaped = false;
|
||||||
}
|
}
|
||||||
|
if tok.kind != TokenKind::Symbol(Symbol::BackSlash) {
|
||||||
s.push_str(&tok.kind.to_string());
|
s.push_str(&tok.kind.to_string());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let quotes = if found_interpolation {
|
let quotes = if found_interpolation {
|
||||||
QuoteKind::Double
|
QuoteKind::Double
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,3 +141,9 @@ test!(
|
|||||||
"foo {\n a: b {\n c: d {\n e: f;\n }\n }\n}\n",
|
"foo {\n a: b {\n c: d {\n e: f;\n }\n }\n}\n",
|
||||||
"foo {\n a: b;\n a-c: d;\n a-c-e: f;\n}\n"
|
"foo {\n a: b;\n a-c: d;\n a-c-e: f;\n}\n"
|
||||||
);
|
);
|
||||||
|
test!(curly_braces_in_quotes, "a {\n color: \"{foo}\";\n}\n");
|
||||||
|
test!(
|
||||||
|
escaped_interpolation,
|
||||||
|
"a {\n color: \"\\#{foo}\";\n}\n",
|
||||||
|
"a {\n color: \"#{foo}\";\n}\n"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user