2020-06-16 22:34:01 -04:00
|
|
|
use codemap::Spanned;
|
|
|
|
|
2020-06-30 06:53:17 -04:00
|
|
|
use peekmore::PeekMore;
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
use crate::{
|
|
|
|
args::{CallArgs, FuncArgs},
|
2020-07-02 10:31:32 -04:00
|
|
|
atrule::{Content, Mixin},
|
2020-06-16 22:34:01 -04:00
|
|
|
error::SassResult,
|
|
|
|
utils::read_until_closing_curly_brace,
|
|
|
|
Token,
|
|
|
|
};
|
|
|
|
|
2020-07-05 10:13:49 -04:00
|
|
|
use super::{common::ContextFlags, NeverEmptyVec, Parser, Stmt};
|
2020-06-16 22:34:01 -04:00
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
pub(super) fn parse_mixin(&mut self) -> SassResult<()> {
|
|
|
|
self.whitespace();
|
|
|
|
let Spanned { node: name, span } = self.parse_identifier()?;
|
|
|
|
self.whitespace();
|
|
|
|
let args = match self.toks.next() {
|
|
|
|
Some(Token { kind: '(', .. }) => self.parse_func_args()?,
|
|
|
|
Some(Token { kind: '{', .. }) => FuncArgs::new(),
|
|
|
|
Some(t) => return Err(("expected \"{\".", t.pos()).into()),
|
|
|
|
None => return Err(("expected \"{\".", span).into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
self.whitespace();
|
|
|
|
|
|
|
|
let mut body = read_until_closing_curly_brace(self.toks)?;
|
2020-06-18 03:09:24 -04:00
|
|
|
body.push(match self.toks.next() {
|
|
|
|
Some(tok) => tok,
|
|
|
|
None => return Err(("expected \"}\".", self.span_before).into()),
|
|
|
|
});
|
2020-06-16 22:34:01 -04:00
|
|
|
|
|
|
|
// todo: `@include` can only give content when `@content` is present within the body
|
|
|
|
// if `@content` is *not* present and `@include` attempts to give a body, we throw an error
|
|
|
|
// `Error: Mixin doesn't accept a content block.`
|
|
|
|
//
|
|
|
|
// this is blocked on figuring out just how to check for this. presumably we could have a check
|
|
|
|
// not when parsing initially, but rather when `@include`ing to see if an `@content` was found.
|
|
|
|
|
2020-06-18 18:14:35 -04:00
|
|
|
let mixin = Mixin::new(self.scopes.last().clone(), args, body, false);
|
2020-06-16 22:34:01 -04:00
|
|
|
|
|
|
|
if self.at_root {
|
|
|
|
self.global_scope.insert_mixin(name, mixin);
|
|
|
|
} else {
|
|
|
|
self.scopes.last_mut().insert_mixin(name, mixin);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn parse_include(&mut self) -> SassResult<Vec<Stmt>> {
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
let name = self.parse_identifier()?;
|
|
|
|
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
let args = if let Some(Token { kind: '(', .. }) = self.toks.peek() {
|
|
|
|
self.toks.next();
|
|
|
|
self.parse_call_args()?
|
|
|
|
} else {
|
|
|
|
CallArgs::new(name.span)
|
|
|
|
};
|
2020-06-16 22:34:01 -04:00
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
self.whitespace_or_comment();
|
|
|
|
|
|
|
|
let content_args = if let Some(Token { kind: 'u', .. }) | Some(Token { kind: 'U', .. }) =
|
|
|
|
self.toks.peek()
|
|
|
|
{
|
|
|
|
let mut ident = self.parse_identifier_no_interpolation(false)?;
|
|
|
|
ident.node.make_ascii_lowercase();
|
|
|
|
if ident.node == "using" {
|
2020-06-16 22:34:01 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-02 10:31:32 -04:00
|
|
|
if !matches!(self.toks.next(), Some(Token { kind: '(', .. })) {
|
|
|
|
return Err(("expected \"(\".", ident.span).into());
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
2020-07-02 10:31:32 -04:00
|
|
|
|
|
|
|
Some(self.parse_func_args()?)
|
|
|
|
} else {
|
|
|
|
return Err(("expected keyword \"using\".", ident.span).into());
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
2020-07-02 10:31:32 -04:00
|
|
|
} else {
|
|
|
|
None
|
2020-06-16 22:34:01 -04:00
|
|
|
};
|
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-06-16 22:34:01 -04:00
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
let content = if content_args.is_some()
|
|
|
|
|| matches!(self.toks.peek(), Some(Token { kind: '{', .. }))
|
|
|
|
{
|
|
|
|
if matches!(self.toks.peek(), Some(Token { kind: '{', .. })) {
|
|
|
|
self.toks.next();
|
|
|
|
}
|
|
|
|
let mut toks = read_until_closing_curly_brace(self.toks)?;
|
|
|
|
if let Some(tok) = self.toks.peek() {
|
|
|
|
toks.push(*tok);
|
|
|
|
self.toks.next();
|
|
|
|
}
|
|
|
|
Some(toks)
|
2020-06-16 22:34:01 -04:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
|
|
|
|
self.toks.next();
|
|
|
|
}
|
|
|
|
|
|
|
|
let Mixin {
|
|
|
|
mut scope,
|
|
|
|
body,
|
|
|
|
args: fn_args,
|
|
|
|
..
|
|
|
|
} = self.scopes.last().get_mixin(name, self.global_scope)?;
|
|
|
|
self.eval_args(fn_args, args, &mut scope)?;
|
2020-06-16 22:34:01 -04:00
|
|
|
|
2020-07-02 15:22:15 -04:00
|
|
|
self.content.push(Content {
|
|
|
|
content,
|
|
|
|
content_args,
|
2020-07-02 15:43:11 -04:00
|
|
|
scope: self.scopes.last().clone(),
|
2020-07-02 15:22:15 -04:00
|
|
|
});
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
let body = Parser {
|
2020-07-02 10:31:32 -04:00
|
|
|
toks: &mut body.into_iter().peekmore(),
|
2020-06-16 22:34:01 -04:00
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
2020-07-02 10:31:32 -04:00
|
|
|
scopes: &mut NeverEmptyVec::new(scope),
|
2020-06-16 22:34:01 -04:00
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
2020-07-05 10:13:49 -04:00
|
|
|
flags: self.flags | ContextFlags::IN_MIXIN,
|
2020-07-02 15:22:15 -04:00
|
|
|
content: self.content,
|
2020-06-16 22:34:01 -04:00
|
|
|
at_root: false,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
2020-06-18 16:56:03 -04:00
|
|
|
extender: self.extender,
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
|
|
|
.parse()?;
|
|
|
|
|
2020-07-02 15:22:15 -04:00
|
|
|
self.content.pop();
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
Ok(body)
|
|
|
|
}
|
|
|
|
|
2020-07-02 10:31:32 -04:00
|
|
|
pub(super) fn parse_content_rule(&mut self) -> SassResult<Vec<Stmt>> {
|
2020-07-05 10:13:49 -04:00
|
|
|
if self.flags.in_mixin() {
|
2020-07-02 15:43:11 -04:00
|
|
|
let mut scope = self
|
|
|
|
.content
|
|
|
|
.last()
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_else(Content::new)
|
|
|
|
.scope;
|
2020-07-02 10:31:32 -04:00
|
|
|
if let Some(Token { kind: '(', .. }) = self.toks.peek() {
|
|
|
|
self.toks.next();
|
|
|
|
let args = self.parse_call_args()?;
|
2020-07-02 15:22:15 -04:00
|
|
|
if let Some(Some(content_args)) =
|
|
|
|
self.content.last().map(|v| v.content_args.clone())
|
|
|
|
{
|
2020-07-02 10:31:32 -04:00
|
|
|
args.max_args(content_args.len())?;
|
|
|
|
|
|
|
|
self.eval_args(content_args, args, &mut scope)?;
|
|
|
|
} else {
|
|
|
|
args.max_args(0)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 15:22:15 -04:00
|
|
|
Ok(if let Some(content) = &self.content.pop() {
|
|
|
|
let stmts = if let Some(body) = content.content.clone() {
|
|
|
|
Parser {
|
|
|
|
toks: &mut body.into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: &mut NeverEmptyVec::new(scope),
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
2020-07-05 19:16:44 +08:00
|
|
|
flags: self.flags,
|
2020-07-02 15:22:15 -04:00
|
|
|
content: self.content,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
|
|
|
}
|
|
|
|
.parse()?
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
|
|
|
self.content.push(content.clone());
|
|
|
|
stmts
|
2020-07-02 10:31:32 -04:00
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Err((
|
|
|
|
"@content is only allowed within mixin declarations.",
|
|
|
|
self.span_before,
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
}
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
|
|
|
}
|