implement @content inside mixins

This commit is contained in:
ConnorSkees 2020-03-22 15:08:13 -04:00
parent 907ac3390c
commit 415b19fc47
6 changed files with 62 additions and 15 deletions

View File

@ -1,6 +1,8 @@
use std::iter::Peekable;
use std::vec::IntoIter;
use super::eat_stmts;
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
use crate::atrule::AtRule;
use crate::common::Symbol;
@ -15,12 +17,18 @@ pub(crate) struct Mixin {
scope: Scope,
args: FuncArgs,
body: Peekable<IntoIter<Token>>,
content: Vec<Stmt>,
}
impl Mixin {
pub fn new(scope: Scope, args: FuncArgs, body: Vec<Token>) -> Self {
pub fn new(scope: Scope, args: FuncArgs, body: Vec<Token>, content: Vec<Stmt>) -> Self {
let body = body.into_iter().peekable();
Mixin { scope, args, body }
Mixin {
scope,
args,
body,
content,
}
}
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
@ -69,7 +77,12 @@ impl Mixin {
}
}
Ok((name, Mixin::new(scope.clone(), args, body)))
Ok((name, Mixin::new(scope.clone(), args, body, Vec::new())))
}
pub fn content(mut self, content: Vec<Stmt>) -> Mixin {
self.content = content;
self
}
pub fn args(mut self, args: &mut CallArgs) -> SassResult<Mixin> {
@ -98,7 +111,7 @@ impl Mixin {
while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector)? {
match expr {
Expr::AtRule(a) => match a {
AtRule::Content => todo!("@content in mixin"),
AtRule::Content => stmts.extend(self.content.clone()),
_ => stmts.push(Stmt::AtRule(a)),
},
Expr::Style(s) => stmts.push(Stmt::Style(s)),
@ -146,6 +159,8 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
devour_whitespace(toks);
let mut has_include = false;
let mut args = if let Some(tok) = toks.next() {
match tok.kind {
TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(),
@ -153,10 +168,18 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
let tmp = eat_call_args(toks, scope, super_selector)?;
devour_whitespace(toks);
if let Some(tok) = toks.next() {
assert_eq!(tok.kind, TokenKind::Symbol(Symbol::SemiColon));
match tok.kind {
TokenKind::Symbol(Symbol::SemiColon) => {}
TokenKind::Symbol(Symbol::OpenCurlyBrace) => has_include = true,
_ => todo!(),
}
}
tmp
}
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
has_include = true;
CallArgs::new()
}
_ => return Err("expected \"{\".".into()),
}
} else {
@ -165,8 +188,24 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
devour_whitespace(toks);
let content = if let Some(tok) = toks.peek() {
if tok.is_symbol(Symbol::OpenCurlyBrace) {
toks.next();
eat_stmts(toks, &mut scope.clone(), super_selector)?
} else if has_include {
eat_stmts(toks, &mut scope.clone(), super_selector)?
} else {
Vec::new()
}
} else {
Vec::new()
};
let mixin = scope.get_mixin(&name)?.clone();
let rules = mixin.args(&mut args)?.call(super_selector)?;
let rules = mixin
.args(&mut args)?
.content(content)
.call(super_selector)?;
Ok(rules)
}

View File

@ -3,8 +3,6 @@ use std::iter::Peekable;
use num_traits::cast::ToPrimitive;
pub(crate) use function::Function;
pub(crate) use mixin::{eat_include, Mixin};
use crate::common::{Keyword, Pos, Symbol};
use crate::error::SassResult;
use crate::scope::Scope;
@ -13,13 +11,15 @@ use crate::unit::Unit;
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
use crate::value::{Number, Value};
use crate::{Stmt, Token, TokenKind};
pub(crate) use function::Function;
pub(crate) use mixin::{eat_include, Mixin};
use parse::eat_stmts;
use unknown::UnknownAtRule;
mod function;
mod parse;
mod mixin;
mod parse;
mod unknown;
#[derive(Debug, Clone)]

View File

@ -164,7 +164,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
} else {
sep1
}
},
}
"comma" => ListSeparator::Comma,
"space" => ListSeparator::Space,
_ => {

View File

@ -84,7 +84,7 @@ use std::io::Write;
use std::iter::{Iterator, Peekable};
use std::path::Path;
use crate::atrule::{AtRule, AtRuleKind, eat_include, Mixin, Function};
use crate::atrule::{eat_include, AtRule, AtRuleKind, Function, Mixin};
use crate::common::{Pos, Symbol, Whitespace};
use crate::css::Css;
use crate::error::SassError;
@ -637,9 +637,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))),
AtRule::Error(pos, err) => Err(SassError::new(err, pos)),
AtRule::Return(_) => Err("This at-rule is not allowed here.".into()),
AtRule::Content => {
return Err("@content is only allowed within mixin declarations.".into())
}
c @ AtRule::Content => Ok(Some(Expr::AtRule(c))),
f @ AtRule::If(..) => Ok(Some(Expr::AtRule(f))),
f @ AtRule::For(..) => Ok(Some(Expr::AtRule(f))),
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),

View File

@ -1,8 +1,8 @@
use std::cell::RefCell;
use std::collections::HashMap;
use crate::error::SassResult;
use crate::atrule::{Function, Mixin};
use crate::error::SassResult;
use crate::value::Value;
thread_local!(pub(crate) static GLOBAL_SCOPE: RefCell<Scope> = RefCell::new(Scope::new()));

View File

@ -174,3 +174,13 @@ test!(
"@mixin foo($x) {\n color: $x;\n}\na {\n @include foo(0px 0px 0px 0px #ef8086 inset !important);\n}\n",
"a {\n color: 0px 0px 0px 0px #ef8086 inset !important;\n}\n"
);
test!(
content_without_variable,
"@mixin foo {\n @content;\n}\n\na {\n @include foo {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n"
);
test!(
content_with_variable,
"@mixin foo($a) {\n @content;\n}\n\na {\n @include foo(red) {\n color: red;\n }\n}\n",
"a {\n color: red;\n}\n"
);