Initial implementation of nested styles
This commit is contained in:
parent
b1d887ee3d
commit
8e42d73c1e
165
src/lib.rs
165
src/lib.rs
@ -58,7 +58,10 @@ use crate::lexer::Lexer;
|
||||
use crate::mixin::{eat_include, Mixin};
|
||||
use crate::selector::{Attribute, Selector};
|
||||
use crate::style::Style;
|
||||
use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl};
|
||||
use crate::utils::{
|
||||
devour_whitespace, eat_variable_value, parse_interpolation, IsComment, IsWhitespace,
|
||||
VariableDecl,
|
||||
};
|
||||
use crate::value::Value;
|
||||
|
||||
mod args;
|
||||
@ -198,6 +201,8 @@ pub(crate) struct RuleSet {
|
||||
enum Expr {
|
||||
/// A style: `color: red`
|
||||
Style(Style),
|
||||
/// Several styles
|
||||
Styles(Vec<Style>),
|
||||
/// A collection of styles, from a mixin or function
|
||||
// Styles(Vec<Style>),
|
||||
/// A full selector `a > h1`
|
||||
@ -450,6 +455,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
{
|
||||
match expr {
|
||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||
Expr::Styles(s) => stmts.extend(s.into_iter().map(|s| Stmt::Style(s))),
|
||||
Expr::MixinDecl(name, mixin) => {
|
||||
scope.mixins.insert(name, mixin);
|
||||
}
|
||||
@ -487,6 +493,149 @@ impl<'a> StyleSheetParser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
super_property: String,
|
||||
) -> Result<Option<Expr>, (Pos, String)> {
|
||||
let mut styles = Vec::new();
|
||||
devour_whitespace(toks);
|
||||
while let Some(tok) = toks.peek() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
loop {
|
||||
let property =
|
||||
parse_property(toks, scope, super_selector, super_property.clone());
|
||||
if let Some(tok) = toks.peek() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
if let Some(Expr::Styles(s)) =
|
||||
eat_style_group(toks, scope, super_selector, property)?
|
||||
{
|
||||
styles.extend(s);
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
if let Some(tok) = toks.peek() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
return Ok(Some(Expr::Styles(styles)));
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let value = parse_style_value(toks, scope, super_selector);
|
||||
styles.push(Style { property, value });
|
||||
if let Some(tok) = toks.peek() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
return Ok(Some(Expr::Styles(styles)));
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let val = parse_style_value(toks, scope, super_selector);
|
||||
return Ok(Some(Expr::Style(Style {
|
||||
property: super_property,
|
||||
value: val,
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(Expr::Styles(styles)))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> Value {
|
||||
let mut style = Vec::new();
|
||||
let mut n = 0;
|
||||
devour_whitespace(toks);
|
||||
while let Some(tok) = toks.peek() {
|
||||
dbg!(&tok.pos);
|
||||
match tok.kind {
|
||||
TokenKind::MultilineComment(_) => {
|
||||
toks.next();
|
||||
continue;
|
||||
}
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) | TokenKind::Interpolation => n += 1,
|
||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
||||
if n == 0 {
|
||||
break;
|
||||
} else {
|
||||
// todo: toks.next() and push
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
TokenKind::Symbol(Symbol::SemiColon) => {
|
||||
toks.next();
|
||||
break;
|
||||
}
|
||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||
style.push(Token {
|
||||
kind: TokenKind::Ident(super_selector.to_string()),
|
||||
pos: Pos::new(),
|
||||
});
|
||||
toks.next();
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
style.push(toks.next().unwrap());
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
Value::from_tokens(&mut style.into_iter().peekable(), scope).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn parse_property<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
mut super_property: String,
|
||||
) -> String {
|
||||
let mut property = String::new();
|
||||
while let Some(Token { kind, .. }) = toks.next() {
|
||||
match kind {
|
||||
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
||||
TokenKind::Ident(ref s) => property.push_str(s),
|
||||
TokenKind::Interpolation => property.push_str(
|
||||
&parse_interpolation(toks, scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
),
|
||||
TokenKind::Symbol(Symbol::Colon) => break,
|
||||
TokenKind::Symbol(Symbol::BitAnd) => property.push_str(&super_selector.to_string()),
|
||||
_ => property.push_str(&kind.to_string()),
|
||||
};
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
if !super_property.is_empty() {
|
||||
super_property.reserve(1 + property.len());
|
||||
super_property.push('-');
|
||||
super_property.push_str(&property);
|
||||
super_property
|
||||
} else {
|
||||
property
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
@ -495,6 +644,20 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
let mut values = Vec::with_capacity(5);
|
||||
while let Some(tok) = toks.peek() {
|
||||
match &tok.kind {
|
||||
TokenKind::Symbol(Symbol::Colon) => {
|
||||
let tok = toks.next();
|
||||
if devour_whitespace(toks) {
|
||||
let prop = parse_property(
|
||||
&mut values.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
String::new(),
|
||||
);
|
||||
return eat_style_group(toks, scope, super_selector, prop);
|
||||
} else {
|
||||
values.push(tok.unwrap());
|
||||
}
|
||||
}
|
||||
TokenKind::Symbol(Symbol::SemiColon) => {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
|
@ -94,6 +94,7 @@ impl Mixin {
|
||||
while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? {
|
||||
match expr {
|
||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||
Expr::Styles(s) => stmts.extend(s.into_iter().map(|s| Stmt::Style(s))),
|
||||
Expr::Include(s) => stmts.extend(s),
|
||||
Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => {
|
||||
todo!()
|
||||
|
@ -9,8 +9,8 @@ use std::vec::IntoIter;
|
||||
/// A style: `color: red`
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Style {
|
||||
property: String,
|
||||
value: Value,
|
||||
pub property: String,
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
impl Display for Style {
|
||||
|
@ -130,21 +130,6 @@ mod test_variables {
|
||||
"a {\n $a: red\n}\n\nb {\n color: blue;\n}\n",
|
||||
"b {\n color: blue;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_following,
|
||||
"a + {\n b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_preceding,
|
||||
"a {\n + b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_alone,
|
||||
"a {\n + {\n b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
}
|
||||
|
||||
mod test_selectors {
|
||||
@ -363,6 +348,21 @@ mod test_selectors {
|
||||
"div,, , span, ,, {\n color: red;\n}\n",
|
||||
"div, span {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_following,
|
||||
"a + {\n b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_preceding,
|
||||
"a {\n + b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
combinator_alone,
|
||||
"a {\n + {\n b {\n color: red;\n }\n}\n",
|
||||
"a + b {\n color: red;\n}\n"
|
||||
);
|
||||
}
|
||||
|
||||
mod test_units {
|
||||
@ -538,6 +538,16 @@ mod test_styles {
|
||||
"a {\n b {\n c {}\n foo: bar;\n }\n}\n",
|
||||
"a b {\n foo: bar;\n}\n"
|
||||
);
|
||||
test!(
|
||||
single_nested_styles,
|
||||
"a {\n webkit: {\n color: red;\n color: orange\n }\n}\n",
|
||||
"a {\n webkit-color: red;\n webkit-color: orange;\n}\n"
|
||||
);
|
||||
test!(
|
||||
multiple_nested_styles,
|
||||
"a {\n webkit: {\n webkit: {\n color: red;\n }\n }\n}\n",
|
||||
"a {\n webkit-webkit-color: red;\n}\n"
|
||||
);
|
||||
}
|
||||
|
||||
mod test_misc {
|
||||
|
Loading…
x
Reference in New Issue
Block a user