Initial implementation of !global (some issues remain)
This commit is contained in:
parent
d560f13289
commit
061694bd63
@ -1,8 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::common::Symbol;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
||||
use crate::value::Value;
|
||||
|
@ -3,10 +3,11 @@ use std::iter::Peekable;
|
||||
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
use crate::common::{Keyword, Pos, Scope, Symbol};
|
||||
use crate::common::{Keyword, Pos, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::function::Function;
|
||||
use crate::mixin::Mixin;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::units::Unit;
|
||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::common::Scope;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::{eat_expr, Expr, RuleSet, Stmt, Token};
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::iter::Peekable;
|
||||
|
||||
use super::parse::eat_stmts;
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::common::Symbol;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, parse_interpolation};
|
||||
use crate::{RuleSet, Stmt, Token, TokenKind};
|
||||
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::args::CallArgs;
|
||||
use crate::common::Scope;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::value::Value;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -1,12 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::error::SassResult;
|
||||
use crate::function::Function;
|
||||
use crate::mixin::Mixin;
|
||||
use crate::value::Value;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Symbol {
|
||||
/// .
|
||||
@ -220,6 +214,7 @@ pub enum Keyword {
|
||||
False,
|
||||
Null,
|
||||
Default,
|
||||
Global,
|
||||
From(String),
|
||||
To(String),
|
||||
Through(String),
|
||||
@ -243,7 +238,7 @@ impl Display for Keyword {
|
||||
Self::False => write!(f, "false"),
|
||||
Self::Null => write!(f, "null"),
|
||||
Self::Default => write!(f, "!default"),
|
||||
// todo!(maintain casing for keywords)
|
||||
Self::Global => write!(f, "!global"),
|
||||
Self::From(s) => write!(f, "{}", s),
|
||||
Self::To(s) => write!(f, "{}", s),
|
||||
Self::Through(s) => write!(f, "{}", s),
|
||||
@ -269,6 +264,7 @@ impl Into<&'static str> for Keyword {
|
||||
Self::False => "false",
|
||||
Self::Null => "null",
|
||||
Self::Default => "!default",
|
||||
Self::Global => "!global",
|
||||
Self::From(_) => "from",
|
||||
Self::To(_) => "to",
|
||||
Self::Through(_) => "through",
|
||||
@ -296,6 +292,7 @@ impl TryFrom<&str> for Keyword {
|
||||
"false" => Ok(Self::False),
|
||||
"null" => Ok(Self::Null),
|
||||
"default" => Ok(Self::Default),
|
||||
"global" => Ok(Self::Global),
|
||||
"from" => Ok(Self::From(kw.to_owned())),
|
||||
"to" => Ok(Self::To(kw.to_owned())),
|
||||
"through" => Ok(Self::Through(kw.to_owned())),
|
||||
@ -353,75 +350,6 @@ impl Display for Pos {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Scope {
|
||||
vars: HashMap<String, Value>,
|
||||
mixins: HashMap<String, Mixin>,
|
||||
functions: HashMap<String, Function>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vars: HashMap::new(),
|
||||
mixins: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_var(&self, v: &str) -> SassResult<&Value> {
|
||||
match self.vars.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err("Undefined variable.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_var(&mut self, s: &str, v: Value) -> SassResult<Option<Value>> {
|
||||
Ok(self.vars.insert(s.replace('_', "-"), v.eval()?))
|
||||
}
|
||||
|
||||
pub fn var_exists(&self, v: &str) -> bool {
|
||||
self.vars.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn get_mixin(&self, v: &str) -> SassResult<&Mixin> {
|
||||
match self.mixins.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err("Undefined mixin.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
||||
self.mixins.insert(s.replace('_', "-"), v)
|
||||
}
|
||||
|
||||
pub fn mixin_exists(&self, v: &str) -> bool {
|
||||
self.mixins.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, v: &str) -> SassResult<&Function> {
|
||||
match self.functions.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err("Undefined function.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
||||
self.functions.insert(s.replace('_', "-"), v)
|
||||
}
|
||||
|
||||
pub fn fn_exists(&self, v: &str) -> bool {
|
||||
self.functions.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: Scope) {
|
||||
self.vars.extend(other.vars);
|
||||
self.mixins.extend(other.mixins);
|
||||
self.functions.extend(other.functions);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum QuoteKind {
|
||||
Single,
|
||||
|
@ -2,8 +2,9 @@ use std::iter::Peekable;
|
||||
|
||||
use crate::args::{eat_func_args, CallArgs, FuncArgs};
|
||||
use crate::atrule::AtRule;
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::common::Symbol;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::devour_whitespace;
|
||||
use crate::value::Value;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::common::Scope;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::{Stmt, StyleSheet};
|
||||
|
||||
pub(crate) fn import<P: AsRef<Path>>(path: P) -> SassResult<(Vec<Stmt>, Scope)> {
|
||||
|
@ -180,6 +180,11 @@ impl<'a> Lexer<'a> {
|
||||
assert_char!(self, 'e' 'f' 'a' 'u' 'l' 't');
|
||||
TokenKind::Keyword(Keyword::Default)
|
||||
}
|
||||
Some('g') | Some('G') => {
|
||||
self.buf.next();
|
||||
assert_char!(self, 'l' 'o' 'b' 'a' 'l');
|
||||
TokenKind::Keyword(Keyword::Global)
|
||||
}
|
||||
Some('=') => {
|
||||
self.buf.next();
|
||||
TokenKind::Op(Op::NotEqual)
|
||||
|
57
src/lib.rs
57
src/lib.rs
@ -85,7 +85,7 @@ use std::iter::{Iterator, Peekable};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::atrule::{AtRule, AtRuleKind};
|
||||
use crate::common::{Keyword, Op, Pos, Scope, Symbol, Whitespace};
|
||||
use crate::common::{Keyword, Op, Pos, Symbol, Whitespace};
|
||||
use crate::css::Css;
|
||||
use crate::error::SassError;
|
||||
pub use crate::error::SassResult;
|
||||
@ -94,6 +94,7 @@ use crate::function::Function;
|
||||
use crate::imports::import;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::mixin::{eat_include, Mixin};
|
||||
use crate::scope::{insert_global_var, Scope, GLOBAL_SCOPE};
|
||||
use crate::selector::Selector;
|
||||
use crate::style::Style;
|
||||
use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl};
|
||||
@ -111,12 +112,18 @@ mod function;
|
||||
mod imports;
|
||||
mod lexer;
|
||||
mod mixin;
|
||||
mod scope;
|
||||
mod selector;
|
||||
mod style;
|
||||
mod units;
|
||||
mod utils;
|
||||
mod value;
|
||||
|
||||
pub(crate) fn error<E: Into<String>>(msg: E) -> ! {
|
||||
eprintln!("Error: {}", msg.into());
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Token {
|
||||
pos: Pos,
|
||||
@ -391,7 +398,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
| TokenKind::Symbol(Symbol::Mul)
|
||||
| TokenKind::Symbol(Symbol::Percent)
|
||||
| TokenKind::Symbol(Symbol::Period) => rules
|
||||
.extend(self.eat_rules(&Selector::new(), &mut self.global_scope.clone())?),
|
||||
.extend(self.eat_rules(&Selector::new(), &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()))?),
|
||||
TokenKind::Whitespace(_) => {
|
||||
self.lexer.next();
|
||||
continue;
|
||||
@ -415,11 +422,16 @@ impl<'a> StyleSheetParser<'a> {
|
||||
{
|
||||
self.error(pos, "unexpected variable use at toplevel");
|
||||
}
|
||||
let VariableDecl { val, default } =
|
||||
eat_variable_value(&mut self.lexer, &self.global_scope, &Selector::new())?;
|
||||
if !default || self.global_scope.get_var(&name).is_err() {
|
||||
self.global_scope.insert_var(&name, val)?;
|
||||
}
|
||||
let VariableDecl { val, default, .. } =
|
||||
eat_variable_value(&mut self.lexer, &GLOBAL_SCOPE.with(|s| s.borrow().clone()), &Selector::new())?;
|
||||
GLOBAL_SCOPE.with(|s| {
|
||||
if !default || s.borrow().get_var(&name).is_err() {
|
||||
match s.borrow_mut().insert_var(&name, val) {
|
||||
Ok(..) => {},
|
||||
Err(e) => error(e),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
TokenKind::MultilineComment(_) => {
|
||||
let comment = match self
|
||||
@ -435,7 +447,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
}
|
||||
TokenKind::AtRule(AtRuleKind::Include) => rules.extend(eat_include(
|
||||
&mut self.lexer,
|
||||
&self.global_scope,
|
||||
&GLOBAL_SCOPE.with(|s| s.borrow().clone()),
|
||||
&Selector::new(),
|
||||
)?),
|
||||
TokenKind::AtRule(AtRuleKind::Import) => {
|
||||
@ -479,7 +491,9 @@ impl<'a> StyleSheetParser<'a> {
|
||||
|
||||
let (new_rules, new_scope) = import(file_name)?;
|
||||
rules.extend(new_rules);
|
||||
self.global_scope.extend(new_scope);
|
||||
GLOBAL_SCOPE.with(|s| {
|
||||
s.borrow_mut().extend(new_scope);
|
||||
});
|
||||
}
|
||||
TokenKind::AtRule(_) => {
|
||||
if let Some(Token {
|
||||
@ -487,12 +501,16 @@ impl<'a> StyleSheetParser<'a> {
|
||||
pos,
|
||||
}) = self.lexer.next()
|
||||
{
|
||||
match AtRule::from_tokens(rule, pos, &mut self.lexer, &mut self.global_scope, &Selector::new())? {
|
||||
match AtRule::from_tokens(rule, pos, &mut self.lexer, &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()), &Selector::new())? {
|
||||
AtRule::Mixin(name, mixin) => {
|
||||
self.global_scope.insert_mixin(&name, *mixin);
|
||||
GLOBAL_SCOPE.with(|s| {
|
||||
s.borrow_mut().insert_mixin(&name, *mixin);
|
||||
});
|
||||
}
|
||||
AtRule::Function(name, func) => {
|
||||
self.global_scope.insert_fn(&name, *func);
|
||||
GLOBAL_SCOPE.with(|s| {
|
||||
s.borrow_mut().insert_fn(&name, *func);
|
||||
});
|
||||
}
|
||||
AtRule::Charset => continue,
|
||||
AtRule::Error(pos, message) => self.error(pos, &message),
|
||||
@ -518,7 +536,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
},
|
||||
};
|
||||
}
|
||||
Ok((rules, self.global_scope))
|
||||
Ok((rules, GLOBAL_SCOPE.with(|s| s.borrow().clone())))
|
||||
}
|
||||
|
||||
fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> SassResult<Vec<Stmt>> {
|
||||
@ -556,7 +574,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
Expr::VariableDecl(name, val) => {
|
||||
if self.scope == 0 {
|
||||
scope.insert_var(&name, *val.clone())?;
|
||||
self.global_scope.insert_var(&name, *val)?;
|
||||
insert_global_var(&name, *val)?;
|
||||
} else {
|
||||
scope.insert_var(&name, *val)?;
|
||||
}
|
||||
@ -648,9 +666,14 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
{
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
let VariableDecl { val, default } =
|
||||
eat_variable_value(toks, scope, super_selector)?;
|
||||
if !default || scope.get_var(&name).is_err() {
|
||||
let VariableDecl {
|
||||
val,
|
||||
default,
|
||||
global,
|
||||
} = eat_variable_value(toks, scope, super_selector)?;
|
||||
if global {
|
||||
insert_global_var(&name, val)?;
|
||||
} else if !default || scope.get_var(&name).is_err() {
|
||||
return Ok(Some(Expr::VariableDecl(name, Box::new(val))));
|
||||
}
|
||||
} else {
|
||||
|
@ -3,8 +3,9 @@ use std::vec::IntoIter;
|
||||
|
||||
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
|
||||
use crate::atrule::AtRule;
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::common::Symbol;
|
||||
use crate::error::{SassError, SassResult};
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::devour_whitespace;
|
||||
use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind};
|
||||
|
94
src/scope.rs
Normal file
94
src/scope.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::error::SassResult;
|
||||
use crate::function::Function;
|
||||
use crate::mixin::Mixin;
|
||||
use crate::value::Value;
|
||||
|
||||
thread_local!(pub(crate) static GLOBAL_SCOPE: RefCell<Scope> = RefCell::new(Scope::new()));
|
||||
|
||||
pub(crate) fn get_global_var(s: &str) -> SassResult<Value> {
|
||||
GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(s) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err("Undefined variable.".into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn insert_global_var(s: &str, v: Value) -> SassResult<Option<Value>> {
|
||||
GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s, v))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Scope {
|
||||
vars: HashMap<String, Value>,
|
||||
mixins: HashMap<String, Mixin>,
|
||||
functions: HashMap<String, Function>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vars: HashMap::new(),
|
||||
mixins: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vars(&self) -> &HashMap<String, Value> {
|
||||
&self.vars
|
||||
}
|
||||
|
||||
pub fn get_var(&self, v: &str) -> SassResult<Value> {
|
||||
let v = &v.replace('_', "-");
|
||||
match self.vars.get(v) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => get_global_var(v),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_var(&mut self, s: &str, v: Value) -> SassResult<Option<Value>> {
|
||||
Ok(self.vars.insert(s.replace('_', "-"), v.eval()?))
|
||||
}
|
||||
|
||||
pub fn var_exists(&self, v: &str) -> bool {
|
||||
self.vars.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn get_mixin(&self, v: &str) -> SassResult<&Mixin> {
|
||||
match self.mixins.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err("Undefined mixin.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
||||
self.mixins.insert(s.replace('_', "-"), v)
|
||||
}
|
||||
|
||||
pub fn mixin_exists(&self, v: &str) -> bool {
|
||||
self.mixins.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn get_fn(&self, v: &str) -> SassResult<&Function> {
|
||||
match self.functions.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err("Undefined function.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
||||
self.functions.insert(s.replace('_', "-"), v)
|
||||
}
|
||||
|
||||
pub fn fn_exists(&self, v: &str) -> bool {
|
||||
self.functions.contains_key(&v.replace('_', "-"))
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: Scope) {
|
||||
self.vars.extend(other.vars);
|
||||
self.mixins.extend(other.mixins);
|
||||
self.functions.extend(other.functions);
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ use std::fmt::{self, Display, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::string::ToString;
|
||||
|
||||
use crate::common::{Scope, Symbol, Whitespace};
|
||||
use crate::common::{Symbol, Whitespace};
|
||||
use crate::error::SassResult;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::scope::Scope;
|
||||
use crate::utils::{
|
||||
devour_whitespace, devour_whitespace_or_comment, flatten_ident, parse_interpolation,
|
||||
parse_quoted_string, IsWhitespace,
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::common::{Pos, QuoteKind, Scope, Symbol};
|
||||
use crate::common::{Pos, QuoteKind, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, parse_interpolation, parse_quoted_string};
|
||||
use crate::value::Value;
|
||||
|
16
src/utils.rs
16
src/utils.rs
@ -83,11 +83,16 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
pub(crate) struct VariableDecl {
|
||||
pub val: Value,
|
||||
pub default: bool,
|
||||
pub global: bool,
|
||||
}
|
||||
|
||||
impl VariableDecl {
|
||||
pub const fn new(val: Value, default: bool) -> VariableDecl {
|
||||
VariableDecl { val, default }
|
||||
pub const fn new(val: Value, default: bool, global: bool) -> VariableDecl {
|
||||
VariableDecl {
|
||||
val,
|
||||
default,
|
||||
global,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +103,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
) -> SassResult<VariableDecl> {
|
||||
devour_whitespace(toks);
|
||||
let mut default = false;
|
||||
let mut global = false;
|
||||
let mut raw: Vec<Token> = Vec::new();
|
||||
let mut nesting = 0;
|
||||
while let Some(tok) = toks.peek() {
|
||||
@ -110,6 +116,10 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
toks.next();
|
||||
default = true
|
||||
}
|
||||
TokenKind::Keyword(Keyword::Global) => {
|
||||
toks.next();
|
||||
global = true
|
||||
}
|
||||
TokenKind::Interpolation | TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
nesting += 1;
|
||||
raw.push(toks.next().unwrap());
|
||||
@ -127,7 +137,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope, super_selector).unwrap();
|
||||
Ok(VariableDecl::new(val, default))
|
||||
Ok(VariableDecl::new(val, default, global))
|
||||
}
|
||||
|
||||
pub(crate) fn flatten_ident<I: Iterator<Item = Token>>(
|
||||
|
@ -8,8 +8,9 @@ use num_traits::pow;
|
||||
use crate::args::eat_call_args;
|
||||
use crate::builtin::GLOBAL_FUNCTIONS;
|
||||
use crate::color::Color;
|
||||
use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Scope, Symbol};
|
||||
use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::units::Unit;
|
||||
use crate::utils::{
|
||||
|
15
tests/scope.rs
Normal file
15
tests/scope.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
test!(
|
||||
scoping_var_decl_inner_ruleset,
|
||||
"a {\n $color: red;\n b {\n $color: blue;\n }\n color: $color;\n}\n",
|
||||
"a {\n color: blue;\n}\n"
|
||||
);
|
||||
test!(
|
||||
basic_global,
|
||||
"a {\n $color: red !global;\n}\n\nb {\n color: $color;\n}\n",
|
||||
"b {\n color: red;\n}\n"
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user