diff --git a/Cargo.toml b/Cargo.toml index 5d8ecfc5e..2c712a804 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ spectest = ["yaml-rust", "deunicode", "hrx-get", "regex"] unimplemented_args = [] [dependencies] +arc-swap = "1.5.0" +fastrand = "1.4" lazy_static = "1.0" nom = { version = "7.0", default-features = false } nom_locate = "4.0" @@ -35,12 +37,11 @@ num-bigint = { version = "0.4.0", default-features = false, features = ["std"] } num-integer = "0.1.42" num-rational = { version = "0.4.0", default-features = false } num-traits = "^0.2.0" -fastrand = "1.4" +tracing = "0.1.34" clap = { version = "3.0.0", features = ["derive", "wrap_help"], optional = true } deunicode = { version = "1.0", optional = true } hrx-get = { version = "0.2.0", optional = true } regex = { version = "1.1.0", optional = true } -tracing = "0.1.34" yaml-rust = { version = "0.4", optional = true } [badges] diff --git a/src/output/transform.rs b/src/output/transform.rs index 0b606134f..39235e731 100644 --- a/src/output/transform.rs +++ b/src/output/transform.rs @@ -10,7 +10,7 @@ use crate::css::{BodyItem, Comment, Import, Property, Rule, Selectors}; use crate::error::{Error, Invalid}; use crate::file_context::FileContext; use crate::parser::Parsed; -use crate::sass::{get_global_module, Expose, Item, Name, UseAs}; +use crate::sass::{get_global_module, Expose, Item, UseAs}; use crate::value::ValueRange; use crate::ScopeRef; use std::io::Write; @@ -364,9 +364,7 @@ fn handle_item( } } Item::Content(args, pos) => { - if let Some(content) = - scope.get_mixin(&Name::from_static("%%BODY%%")) - { + if let Some(content) = scope.get_content() { let mixin = content.get( "@content", scope, diff --git a/src/sass/functions/meta.rs b/src/sass/functions/meta.rs index 40e8dca20..ac749a876 100644 --- a/src/sass/functions/meta.rs +++ b/src/sass/functions/meta.rs @@ -3,7 +3,7 @@ use super::{ CheckedArg, Error, FunctionMap, }; use crate::css::{CallArgs, CssString, Value}; -use crate::sass::{Function, MixinDecl, Name}; +use crate::sass::{Function, MixinDecl}; use crate::value::Quotes; use crate::{Format, Scope, ScopeRef}; @@ -66,8 +66,7 @@ pub fn create_module() -> Scope { } }); def!(f, content_exists(), |s| { - let content = call_scope(s).get_mixin(&Name::from_static("%%BODY%%")); - if let Some(content) = content { + if let Some(content) = call_scope(s).get_content() { Ok((!content.is_no_body()).into()) } else { Err(Error::error( diff --git a/src/sass/mixin.rs b/src/sass/mixin.rs index 35b3fd3d5..ce2fd10ce 100644 --- a/src/sass/mixin.rs +++ b/src/sass/mixin.rs @@ -143,13 +143,10 @@ impl Mixin { scope: &ScopeRef, body: &Option, ) { - self.scope.define_mixin( - Name::from_static("%%BODY%%"), - match body { - Some(body) => body.closure(scope).into(), - None => MixinDecl::NoBody, - }, - ) + self.scope.define_content(match body { + Some(body) => body.closure(scope).into(), + None => MixinDecl::NoBody, + }) } } diff --git a/src/variablescope.rs b/src/variablescope.rs index d34622690..2574771ff 100644 --- a/src/variablescope.rs +++ b/src/variablescope.rs @@ -4,6 +4,7 @@ use crate::error::Invalid; use crate::output::Format; use crate::sass::{Expose, Function, Item, MixinDecl, Name, UseAs}; use crate::{Error, SourcePos}; +use arc_swap::ArcSwapOption; use lazy_static::lazy_static; use std::collections::BTreeMap; use std::ops::Deref; @@ -218,6 +219,8 @@ pub struct Scope { selectors: Option, forward: Mutex>, format: Format, + /// The thing to use for `@content` in a mixin. + content: ArcSwapOption, // Set of files currently loading. Only used in the root scope. loading: Mutex>, } @@ -238,6 +241,7 @@ impl<'a> Scope { selectors: None, forward: Default::default(), format, + content: None.into(), loading: Default::default(), } } @@ -270,6 +274,7 @@ impl<'a> Scope { selectors: None, forward: Default::default(), format, + content: None.into(), loading: Default::default(), } } @@ -285,6 +290,7 @@ impl<'a> Scope { selectors: Some(selectors), forward: Default::default(), format, + content: None.into(), loading: Default::default(), } } @@ -633,6 +639,16 @@ impl<'a> Scope { self.loading.lock().unwrap().remove(name); } } + + pub(crate) fn define_content(&self, body: MixinDecl) { + self.content.store(Some(Arc::new(body))); + } + pub(crate) fn get_content(&self) -> Option { + self.content + .load_full() + .map(|c| (*c).clone()) + .or_else(|| self.parent.as_ref().and_then(|p| p.get_content())) + } } #[cfg(test)]