From e6eb3202b58205cba83152c8651611429811b340 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Thu, 9 Sep 2021 12:54:12 +0900 Subject: [PATCH 01/27] Add StyleContext and RuleBlock. --- packages/stylist-core/src/ast/block.rs | 8 +++++- packages/stylist-core/src/ast/context.rs | 12 +++++++++ packages/stylist-core/src/ast/mod.rs | 7 +++++- packages/stylist-core/src/ast/rule_block.rs | 28 +++++++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 packages/stylist-core/src/ast/context.rs create mode 100644 packages/stylist-core/src/ast/rule_block.rs diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 7b4fd30..2f8ed8c 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -1,9 +1,15 @@ use std::borrow::Cow; use std::fmt; -use super::{Selector, StyleAttribute, ToStyleStr}; +use super::{RuleBlock, Selector, StyleAttribute, ToStyleStr}; use crate::Result; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum BlockContent { + StyleAttr(StyleAttribute), + RuleBlock(RuleBlock), +} + /// A block is a set of css properties that apply to elements that /// match the condition. The CSS standard calls these "Qualified rules". /// diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs new file mode 100644 index 0000000..96f03f1 --- /dev/null +++ b/packages/stylist-core/src/ast/context.rs @@ -0,0 +1,12 @@ +// #[derive(Debug)] +// pub enum StyleKind { +// Scoped, +// Global, +// Keyframes, +// } + +#[derive(Debug)] +pub struct StyleContext<'a> { + // pub kind: StyleKind, + pub class_name: Option<&'a str>, +} diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index 393ffa6..ca68684 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -1,7 +1,9 @@ // this module is documented at stylist::ast mod block; +mod context; mod rule; +mod rule_block; mod rule_content; mod scope_content; mod selector; @@ -10,8 +12,11 @@ mod str_frag; mod style_attr; mod to_style_str; -pub use block::Block; +pub use context::StyleContext; + +pub use block::{Block, BlockContent}; pub use rule::Rule; +pub use rule_block::{RuleBlock, RuleBlockContent}; pub use rule_content::RuleContent; pub use scope_content::ScopeContent; pub use selector::Selector; diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs new file mode 100644 index 0000000..a03d5bf --- /dev/null +++ b/packages/stylist-core/src/ast/rule_block.rs @@ -0,0 +1,28 @@ +use std::borrow::Cow; + +use super::{StringFragment, StyleAttribute}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum RuleBlockContent { + StyleAttr(StyleAttribute), + RuleBlock(Box), +} + +/// A declaration block for at-rules. +/// +/// This is used to represent at-rules that contains declaration block (e.g.:`@font-face`) and +/// `@media` and `@supports` inside of a [`Block`](super::Block) which is a non-standard CSS feature. +/// +/// E.g.: +/// ```css +/// .inner { +/// @media screen and (max-width: 500px) { +/// display: flex; +/// } +/// } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RuleBlock { + pub condition: Cow<'static, [StringFragment]>, + pub style_attributes: Cow<'static, [RuleBlockContent]>, +} From 17bd902ef18b4e95e670ae382a7d893f7a13aaba Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Thu, 9 Sep 2021 15:55:12 +0900 Subject: [PATCH 02/27] Add Style Context. --- packages/stylist-core/src/ast/block.rs | 60 ++++++++++++----- packages/stylist-core/src/ast/context.rs | 19 +++++- packages/stylist-core/src/ast/mod.rs | 4 +- packages/stylist-core/src/ast/rule.rs | 65 +++++++++++++++++-- packages/stylist-core/src/ast/rule_content.rs | 57 ---------------- .../stylist-core/src/ast/scope_content.rs | 8 +-- packages/stylist-core/src/ast/selector.rs | 8 +-- packages/stylist-core/src/ast/sheet.rs | 6 +- packages/stylist-core/src/ast/str_frag.rs | 4 +- packages/stylist-core/src/ast/style_attr.rs | 6 +- packages/stylist-core/src/ast/to_style_str.rs | 9 ++- 11 files changed, 145 insertions(+), 101 deletions(-) delete mode 100644 packages/stylist-core/src/ast/rule_content.rs diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 2f8ed8c..43a45b2 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use std::fmt; +use std::fmt::Write; -use super::{RuleBlock, Selector, StyleAttribute, ToStyleStr}; +use super::{RuleBlock, Selector, StyleAttribute, StyleContext, ToStyleStr}; use crate::Result; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -29,27 +30,29 @@ pub struct Block { pub style_attributes: Cow<'static, [StyleAttribute]>, } -impl ToStyleStr for Block { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { - if !self.condition.is_empty() { - for (index, sel) in self.condition.iter().enumerate() { - sel.write_style(w, class_name)?; - if index < self.condition.len() - 1 { - write!(w, ",")?; - } - write!(w, " ")?; +impl Block { + fn cond_str(&self, ctx: &StyleContext<'_>) -> Result> { + if self.condition.is_empty() { + return Ok(None); + } + + let mut cond = "".to_string(); + + for (index, sel) in self.condition.iter().enumerate() { + sel.write_style(&mut cond, ctx)?; + if index < self.condition.len() - 1 { + write!(&mut cond, ", ")?; } - } else if let Some(m) = class_name { - write!(w, ".{} ", m)?; - } else { - // Generates global style for dangling block. - write!(w, "html ")?; } + Ok(Some(cond)) + } + + fn write_content(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { writeln!(w, "{{")?; for attr in self.style_attributes.iter() { - attr.write_style(w, class_name)?; + attr.write_style(w, ctx)?; writeln!(w)?; } @@ -58,3 +61,28 @@ impl ToStyleStr for Block { Ok(()) } } + +impl ToStyleStr for Block { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + if let Some(m) = self.cond_str(ctx)? { + write!(w, "{} ", m)?; + + let block_ctx = ctx.clone().with_condition(&m); + + return self.write_content(w, &block_ctx); + // TODO: nested block. + } + + // Dangling Block. + if let Some(m) = ctx.root_class_name() { + write!(w, ".{} ", m)?; + } else { + // Generates global style for dangling block. + write!(w, "html ")?; + } + + self.write_content(w, ctx)?; + + Ok(()) + } +} diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 96f03f1..63ca154 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -4,9 +4,24 @@ // Global, // Keyframes, // } +// -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StyleContext<'a> { // pub kind: StyleKind, - pub class_name: Option<&'a str>, + pub parent_conditions: Vec<&'a str>, +} + +impl<'a> StyleContext<'a> { + pub fn root_class_name(&self) -> Option<&'a str> { + self.parent_conditions + .first() + .and_then(|m| if m.starts_with('@') { None } else { Some(*m) }) + } + + pub fn with_condition(mut self, condition: &'a str) -> Self { + self.parent_conditions.push(condition); + + self + } } diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index ca68684..72aec97 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -4,7 +4,6 @@ mod block; mod context; mod rule; mod rule_block; -mod rule_content; mod scope_content; mod selector; mod sheet; @@ -15,9 +14,8 @@ mod to_style_str; pub use context::StyleContext; pub use block::{Block, BlockContent}; -pub use rule::Rule; +pub use rule::{Rule, RuleContent}; pub use rule_block::{RuleBlock, RuleBlockContent}; -pub use rule_content::RuleContent; pub use scope_content::ScopeContent; pub use selector::Selector; pub use sheet::Sheet; diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index bfb5051..a9bff91 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -1,9 +1,61 @@ use std::borrow::Cow; use std::fmt; -use super::{RuleContent, StringFragment, ToStyleStr}; +use super::{Block, ScopeContent, StringFragment, StyleContext, ToStyleStr}; use crate::Result; +/// Everything that can be inside a rule. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum RuleContent { + /// A block + Block(Block), + /// A nested rule + Rule(Box), + /// A raw string literal, i.e. something that wasn't parsed. + /// This is an escape-hatch and may get removed in the future + /// for a more meaningful alternative + String(Cow<'static, str>), +} + +impl From for RuleContent { + fn from(scope: ScopeContent) -> Self { + match scope { + ScopeContent::Block(b) => RuleContent::Block(b), + ScopeContent::Rule(r) => RuleContent::Rule(r.into()), + } + } +} + +impl ToStyleStr for RuleContent { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + match self { + RuleContent::Block(ref b) => b.write_style(w, ctx)?, + RuleContent::Rule(ref r) => r.write_style(w, ctx)?, + RuleContent::String(ref s) => write!(w, "{}", s)?, + } + + Ok(()) + } +} + +impl From for RuleContent { + fn from(s: String) -> Self { + Self::String(s.into()) + } +} + +impl From<&'static str> for RuleContent { + fn from(s: &'static str) -> Self { + Self::String(s.into()) + } +} + +impl From> for RuleContent { + fn from(s: Cow<'static, str>) -> Self { + Self::String(s) + } +} + /// An At-Rule can contain both other blocks and in some cases more At-Rules. /// /// E.g.: @@ -26,15 +78,18 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + let mut cond = "".to_string(); for frag in self.condition.iter() { - frag.write_style(w, class_name)?; + frag.write_style(&mut cond, ctx)?; } - writeln!(w, " {{")?; + let rule_ctx = ctx.clone().with_condition(&cond); + + writeln!(w, "{} {{", cond)?; for i in self.content.iter() { - i.write_style(w, class_name)?; + i.write_style(w, &rule_ctx)?; writeln!(w)?; } diff --git a/packages/stylist-core/src/ast/rule_content.rs b/packages/stylist-core/src/ast/rule_content.rs deleted file mode 100644 index 5d8412f..0000000 --- a/packages/stylist-core/src/ast/rule_content.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::borrow::Cow; -use std::fmt; - -use super::{Block, Rule, ScopeContent, ToStyleStr}; -use crate::Result; - -/// Everything that can be inside a rule. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum RuleContent { - /// A block - Block(Block), - /// A nested rule - Rule(Box), - /// A raw string literal, i.e. something that wasn't parsed. - /// This is an escape-hatch and may get removed in the future - /// for a more meaningful alternative - String(Cow<'static, str>), -} - -impl From for RuleContent { - fn from(scope: ScopeContent) -> Self { - match scope { - ScopeContent::Block(b) => RuleContent::Block(b), - ScopeContent::Rule(r) => RuleContent::Rule(r.into()), - } - } -} - -impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { - match self { - RuleContent::Block(ref b) => b.write_style(w, class_name)?, - RuleContent::Rule(ref r) => r.write_style(w, class_name)?, - RuleContent::String(ref s) => write!(w, "{}", s)?, - } - - Ok(()) - } -} - -impl From for RuleContent { - fn from(s: String) -> Self { - Self::String(s.into()) - } -} - -impl From<&'static str> for RuleContent { - fn from(s: &'static str) -> Self { - Self::String(s.into()) - } -} - -impl From> for RuleContent { - fn from(s: Cow<'static, str>) -> Self { - Self::String(s) - } -} diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 8468893..2070c9b 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -1,6 +1,6 @@ use std::fmt; -use super::{Block, Rule, ToStyleStr}; +use super::{Block, Rule, StyleContext, ToStyleStr}; use crate::Result; /// A scope represents a media query or all content not in a media query. @@ -32,10 +32,10 @@ pub enum ScopeContent { } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { match self { - ScopeContent::Block(ref b) => b.write_style(w, class_name)?, - ScopeContent::Rule(ref r) => r.write_style(w, class_name)?, + ScopeContent::Block(ref b) => b.write_style(w, ctx)?, + ScopeContent::Rule(ref r) => r.write_style(w, ctx)?, } Ok(()) diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 2a482a9..d3b396b 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt}; -use super::{StringFragment, ToStyleStr}; +use super::{StringFragment, StyleContext, ToStyleStr}; use crate::Result; /// A CSS Selector. @@ -15,14 +15,14 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { let mut joined_s = "".to_string(); for frag in self.fragments.iter() { - frag.write_style(&mut joined_s, class_name)?; + frag.write_style(&mut joined_s, ctx)?; } - if let Some(m) = class_name { + if let Some(m) = ctx.root_class_name() { // If contains current selector or root pseudo class, replace them with class name. if joined_s.contains('&') || joined_s.contains(":root") { let scoped_class = format!(".{}", m); diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index a261250..25efd0e 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -3,7 +3,7 @@ use std::fmt; use std::ops::Deref; use std::sync::Arc; -use super::{ScopeContent, ToStyleStr}; +use super::{ScopeContent, StyleContext, ToStyleStr}; use crate::Result; @@ -51,9 +51,9 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { for scope in self.0.iter() { - scope.write_style(w, class_name)?; + scope.write_style(w, ctx)?; writeln!(w)?; } diff --git a/packages/stylist-core/src/ast/str_frag.rs b/packages/stylist-core/src/ast/str_frag.rs index fad0342..156961e 100644 --- a/packages/stylist-core/src/ast/str_frag.rs +++ b/packages/stylist-core/src/ast/str_frag.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::fmt; -use super::ToStyleStr; +use super::{StyleContext, ToStyleStr}; use crate::Result; /// A String Fragment @@ -11,7 +11,7 @@ pub struct StringFragment { } impl ToStyleStr for StringFragment { - fn write_style(&self, w: &mut W, _class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, _ctx: &StyleContext<'_>) -> Result<()> { write!(w, "{}", self.inner)?; Ok(()) diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index 760fe74..52d3853 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::fmt; -use super::{StringFragment, ToStyleStr}; +use super::{StringFragment, StyleContext, ToStyleStr}; use crate::Result; /// A simple CSS property in the form of a key value pair. Mirrors what would @@ -15,11 +15,11 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { write!(w, "{}: ", self.key)?; for i in self.value.iter() { - i.write_style(w, class_name)?; + i.write_style(w, ctx)?; } write!(w, ";")?; diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index bfa6789..8d81947 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -1,3 +1,4 @@ +use super::StyleContext; use crate::Result; use std::fmt; @@ -7,11 +8,15 @@ pub trait ToStyleStr { fn to_style_str(&self, class_name: Option<&str>) -> Result { let mut s = String::new(); - self.write_style(&mut s, class_name)?; + let ctx = StyleContext { + parent_conditions: class_name.map(|m| vec![m]).unwrap_or_else(Vec::new), + }; + + self.write_style(&mut s, &ctx)?; Ok(s) } // If None is passed as class_name, it means to write a global style. - fn write_style(&self, w: &mut W, class_name: Option<&str>) -> Result<()>; + fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()>; } From 910173e3b8da634991e8579ee3223a46faa225d5 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Thu, 9 Sep 2021 17:57:48 +0900 Subject: [PATCH 03/27] Padded Style Generation. --- packages/stylist-core/src/ast/block.rs | 52 ++++----- packages/stylist-core/src/ast/context.rs | 107 ++++++++++++++++-- packages/stylist-core/src/ast/mod.rs | 20 ++-- packages/stylist-core/src/ast/rule.rs | 20 ++-- packages/stylist-core/src/ast/rule_block.rs | 41 ++++++- .../stylist-core/src/ast/scope_content.rs | 2 +- packages/stylist-core/src/ast/selector.rs | 4 +- packages/stylist-core/src/ast/sheet.rs | 3 +- packages/stylist-core/src/ast/str_frag.rs | 2 +- packages/stylist-core/src/ast/style_attr.rs | 8 +- packages/stylist-core/src/ast/to_style_str.rs | 8 +- packages/stylist/src/global_style.rs | 20 ++-- packages/stylist/src/style.rs | 18 +-- packages/stylist/tests/macro_css_tests.rs | 12 +- .../macro_integrations/at-supports-pass.rs | 6 +- .../complicated-attributes-pass.rs | 14 +-- .../macro_integrations/nested_at_rule-fail.rs | 8 +- .../uses-display-impl-pass.rs | 2 +- .../at-supports-pass.rs | 6 +- 19 files changed, 239 insertions(+), 114 deletions(-) diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 43a45b2..4fc48ff 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -11,6 +11,17 @@ pub enum BlockContent { RuleBlock(RuleBlock), } +impl ToStyleStr for BlockContent { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + match self { + Self::StyleAttr(ref m) => m.write_style(w, ctx)?, + Self::RuleBlock(ref m) => m.write_style(w, ctx)?, + } + + Ok(()) + } +} + /// A block is a set of css properties that apply to elements that /// match the condition. The CSS standard calls these "Qualified rules". /// @@ -31,7 +42,7 @@ pub struct Block { } impl Block { - fn cond_str(&self, ctx: &StyleContext<'_>) -> Result> { + fn cond_str(&self, ctx: &mut StyleContext<'_>) -> Result> { if self.condition.is_empty() { return Ok(None); } @@ -47,41 +58,26 @@ impl Block { Ok(Some(cond)) } - - fn write_content(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { - writeln!(w, "{{")?; - - for attr in self.style_attributes.iter() { - attr.write_style(w, ctx)?; - writeln!(w)?; - } - - write!(w, "}}")?; - - Ok(()) - } } impl ToStyleStr for Block { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { - if let Some(m) = self.cond_str(ctx)? { - write!(w, "{} ", m)?; + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + // Close last clause. + ctx.write_finishing_clause(w)?; - let block_ctx = ctx.clone().with_condition(&m); + // TODO: nested block, which is not supported at the moment. + let cond_s = self.cond_str(ctx)?; - return self.write_content(w, &block_ctx); - // TODO: nested block. - } + let mut final_ctx = cond_s + .as_ref() + .map(|m| ctx.with_condition(m)) + .unwrap_or_else(|| ctx.to_block_context()); - // Dangling Block. - if let Some(m) = ctx.root_class_name() { - write!(w, ".{} ", m)?; - } else { - // Generates global style for dangling block. - write!(w, "html ")?; + for attr in self.style_attributes.iter() { + attr.write_style(w, &mut final_ctx)?; } - self.write_content(w, ctx)?; + final_ctx.write_finishing_clause(w)?; Ok(()) } diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 63ca154..4f02d0c 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,27 +1,114 @@ +use std::borrow::Cow; +use std::fmt; + +use crate::Result; + // #[derive(Debug)] // pub enum StyleKind { -// Scoped, -// Global, +// Style, // Keyframes, // } // +#[derive(Debug, Clone, PartialEq, Eq)] +enum ContextState { + // Either a finishing clause has been printed, or the starting block is not printed. + Closed, + // A start clause has been printed, but a finishing clause is not printed. + Open, +} + #[derive(Debug, Clone)] pub struct StyleContext<'a> { // pub kind: StyleKind, - pub parent_conditions: Vec<&'a str>, + pub class_name: Option<&'a str>, + pub parent_conditions: Vec>, + state: ContextState, } impl<'a> StyleContext<'a> { - pub fn root_class_name(&self) -> Option<&'a str> { - self.parent_conditions - .first() - .and_then(|m| if m.starts_with('@') { None } else { Some(*m) }) + pub fn new(class_name: Option<&'a str>) -> Self { + Self { + class_name, + parent_conditions: Vec::new(), + state: ContextState::Closed, + } + } + + pub fn with_condition>>(&self, condition: S) -> Self { + let mut self_ = self.clone(); + + self_.parent_conditions.push(condition.into()); + + self_ + } + + pub fn to_block_context(&'a self) -> Self { + // No selectors + if self + .parent_conditions() + .last() + .map(|m| m.starts_with('@')) + .unwrap_or(true) + { + self.with_condition( + self.class_name + .map(|m| Cow::from(format!(".{}", m))) + .unwrap_or_else(|| "html".into()), + ) + } else { + self.clone() + } + } + + pub fn parent_conditions(&self) -> Vec> { + let mut sorted_parents = self.parent_conditions.clone(); + + // @ rules first, then selectors. + sorted_parents.sort_by_cached_key(|m| !m.starts_with('@')); + + sorted_parents + } + + pub fn write_starting_clause(&mut self, w: &mut W) -> Result<()> { + if self.state == ContextState::Open { + return Ok(()); + } + + for (index, cond) in self.parent_conditions().iter().enumerate() { + for _i in 0..index { + write!(w, " ")?; + } + writeln!(w, "{} {{", cond)?; + } + + self.state = ContextState::Open; + + Ok(()) + } + + pub fn write_finishing_clause(&mut self, w: &mut W) -> Result<()> { + if self.state == ContextState::Closed { + return Ok(()); + } + + for i in (0..self.parent_conditions.len()).rev() { + for _i in 0..i { + write!(w, " ")?; + } + writeln!(w, "}}")?; + } + + self.state = ContextState::Closed; + + Ok(()) } - pub fn with_condition(mut self, condition: &'a str) -> Self { - self.parent_conditions.push(condition); + pub fn write_padding(&self, w: &mut W) -> Result<()> { + for _ in 0..self.parent_conditions.len() { + write!(w, " ")?; + } - self + Ok(()) } } diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index 72aec97..886df30 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -66,10 +66,10 @@ width: 200px; assert_eq!( test_block.to_style_str(Some("test"))?, r#".test { -width: 100vw; + width: 100vw; } .test .inner { -background-color: red; + background-color: red; } @keyframes move { from { @@ -126,20 +126,24 @@ width: 200px; assert_eq!( test_block.to_style_str(Some("test"))?, r#"@media only screen and (min-width: 1000px) { -.test { -width: 100vw; + .test { + width: 100vw; + } } -.test .inner { -background-color: red; +@media only screen and (min-width: 1000px) { + .test .inner { + background-color: red; + } } -@keyframes move { +@media only screen and (min-width: 1000px) { + @keyframes move { from { width: 100px; } to { width: 200px; } -} + } } "# ); diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index a9bff91..789bbbc 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -27,11 +27,14 @@ impl From for RuleContent { } impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { match self { RuleContent::Block(ref b) => b.write_style(w, ctx)?, RuleContent::Rule(ref r) => r.write_style(w, ctx)?, - RuleContent::String(ref s) => write!(w, "{}", s)?, + RuleContent::String(ref s) => { + ctx.write_starting_clause(w)?; + writeln!(w, "{}", s)?; + } } Ok(()) @@ -78,22 +81,21 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + ctx.write_finishing_clause(w)?; + let mut cond = "".to_string(); for frag in self.condition.iter() { frag.write_style(&mut cond, ctx)?; } - let rule_ctx = ctx.clone().with_condition(&cond); - - writeln!(w, "{} {{", cond)?; + let mut rule_ctx = ctx.clone().with_condition(&cond); for i in self.content.iter() { - i.write_style(w, &rule_ctx)?; - writeln!(w)?; + i.write_style(w, &mut rule_ctx)?; } - write!(w, "}}")?; + rule_ctx.write_finishing_clause(w)?; Ok(()) } diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs index a03d5bf..ab59c43 100644 --- a/packages/stylist-core/src/ast/rule_block.rs +++ b/packages/stylist-core/src/ast/rule_block.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; +use std::fmt; -use super::{StringFragment, StyleAttribute}; +use super::{StringFragment, StyleAttribute, StyleContext, ToStyleStr}; +use crate::Result; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum RuleBlockContent { @@ -8,10 +10,22 @@ pub enum RuleBlockContent { RuleBlock(Box), } +impl ToStyleStr for RuleBlockContent { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + match self { + Self::StyleAttr(ref b) => b.write_style(w, ctx)?, + Self::RuleBlock(ref r) => r.write_style(w, ctx)?, + } + + Ok(()) + } +} + /// A declaration block for at-rules. /// /// This is used to represent at-rules that contains declaration block (e.g.:`@font-face`) and -/// `@media` and `@supports` inside of a [`Block`](super::Block) which is a non-standard CSS feature. +/// `frame`s inside of a `@keyframes` at rule +/// as well as `@media` and `@supports` inside of a [`Block`](super::Block) which is a non-standard CSS feature. /// /// E.g.: /// ```css @@ -24,5 +38,26 @@ pub enum RuleBlockContent { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RuleBlock { pub condition: Cow<'static, [StringFragment]>, - pub style_attributes: Cow<'static, [RuleBlockContent]>, + pub content: Cow<'static, [RuleBlockContent]>, +} + +impl ToStyleStr for RuleBlock { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + // Finish any previous blocks + ctx.write_finishing_clause(w)?; + + let mut cond = "".to_string(); + for frag in self.condition.iter() { + frag.write_style(&mut cond, ctx)?; + } + + let mut rule_ctx = ctx.with_condition(&cond); + for i in self.content.iter() { + i.write_style(w, &mut rule_ctx)?; + } + + rule_ctx.write_finishing_clause(w)?; + + Ok(()) + } } diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 2070c9b..462007e 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -32,7 +32,7 @@ pub enum ScopeContent { } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { match self { ScopeContent::Block(ref b) => b.write_style(w, ctx)?, ScopeContent::Rule(ref r) => r.write_style(w, ctx)?, diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index d3b396b..40c749e 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -15,14 +15,14 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { let mut joined_s = "".to_string(); for frag in self.fragments.iter() { frag.write_style(&mut joined_s, ctx)?; } - if let Some(m) = ctx.root_class_name() { + if let Some(ref m) = ctx.class_name { // If contains current selector or root pseudo class, replace them with class name. if joined_s.contains('&') || joined_s.contains(":root") { let scoped_class = format!(".{}", m); diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index 25efd0e..4d71238 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -51,10 +51,9 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { for scope in self.0.iter() { scope.write_style(w, ctx)?; - writeln!(w)?; } Ok(()) diff --git a/packages/stylist-core/src/ast/str_frag.rs b/packages/stylist-core/src/ast/str_frag.rs index 156961e..bbee857 100644 --- a/packages/stylist-core/src/ast/str_frag.rs +++ b/packages/stylist-core/src/ast/str_frag.rs @@ -11,7 +11,7 @@ pub struct StringFragment { } impl ToStyleStr for StringFragment { - fn write_style(&self, w: &mut W, _ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, _ctx: &mut StyleContext<'_>) -> Result<()> { write!(w, "{}", self.inner)?; Ok(()) diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index 52d3853..d7e9011 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -15,14 +15,18 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + // Always write starting clause. + ctx.write_starting_clause(w)?; + ctx.write_padding(w)?; + write!(w, "{}: ", self.key)?; for i in self.value.iter() { i.write_style(w, ctx)?; } - write!(w, ";")?; + writeln!(w, ";")?; Ok(()) } diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index 8d81947..e79ddef 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -8,15 +8,13 @@ pub trait ToStyleStr { fn to_style_str(&self, class_name: Option<&str>) -> Result { let mut s = String::new(); - let ctx = StyleContext { - parent_conditions: class_name.map(|m| vec![m]).unwrap_or_else(Vec::new), - }; + let mut ctx = StyleContext::new(class_name); - self.write_style(&mut s, &ctx)?; + self.write_style(&mut s, &mut ctx)?; Ok(s) } // If None is passed as class_name, it means to write a global style. - fn write_style(&self, w: &mut W, ctx: &StyleContext<'_>) -> Result<()>; + fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()>; } diff --git a/packages/stylist/src/global_style.rs b/packages/stylist/src/global_style.rs index 001d1d1..52aef6b 100644 --- a/packages/stylist/src/global_style.rs +++ b/packages/stylist/src/global_style.rs @@ -154,7 +154,7 @@ mod tests { assert_eq!( global_style.get_style_str(), r#"html { -background-color: black; + background-color: black; } "# ); @@ -185,23 +185,23 @@ background-color: black; assert_eq!( global_style.get_style_str(), r#"html { -background-color: black; + background-color: black; } .with-class { -color: red; + color: red; } @media screen and (max-width: 600px) { -html { -color: yellow; -} + html { + color: yellow; + } } @supports (display: grid) { -html { -display: grid; -} + html { + display: grid; + } } header, footer { -border: 1px solid black; + border: 1px solid black; } "#, ) diff --git a/packages/stylist/src/style.rs b/packages/stylist/src/style.rs index 3db3cb2..7252361 100644 --- a/packages/stylist/src/style.rs +++ b/packages/stylist/src/style.rs @@ -357,23 +357,23 @@ mod tests { style.get_style_str(), format!( r#".{style_name} {{ -background-color: black; + background-color: black; }} .{style_name} .with-class {{ -color: red; + color: red; }} @media screen and (max-width: 600px) {{ -.{style_name} {{ -color: yellow; -}} + .{style_name} {{ + color: yellow; + }} }} @supports (display: grid) {{ -.{style_name} {{ -display: grid; -}} + .{style_name} {{ + display: grid; + }} }} .{style_name} header, .{style_name} footer {{ -border: 1px solid black; + border: 1px solid black; }} "#, style_name = style.get_class_name() diff --git a/packages/stylist/tests/macro_css_tests.rs b/packages/stylist/tests/macro_css_tests.rs index 3fdcec7..c1755c0 100644 --- a/packages/stylist/tests/macro_css_tests.rs +++ b/packages/stylist/tests/macro_css_tests.rs @@ -28,18 +28,18 @@ fn test_sheet_interpolation() { let expected = format!( r#".{cls} {{ -color: red; + color: red; }} .{cls} span, .{cls} div.selected {{ -background-color: blue; + background-color: blue; }} :not(.{cls}.highlighted) {{ -background-color: black; + background-color: black; }} @media screen and (max-width: 500px) {{ -.{cls} {{ -display: flex; -}} + .{cls} {{ + display: flex; + }} }} "#, cls = "stylist-testtest" diff --git a/packages/stylist/tests/macro_integrations/at-supports-pass.rs b/packages/stylist/tests/macro_integrations/at-supports-pass.rs index 9f66436..fcc339d 100644 --- a/packages/stylist/tests/macro_integrations/at-supports-pass.rs +++ b/packages/stylist/tests/macro_integrations/at-supports-pass.rs @@ -8,9 +8,9 @@ fn main() { .unwrap(); let expected_result = format!( r#"@supports (display:grid) {{ -.{cls} {{ -background-color: grey; -}} + .{cls} {{ + background-color: grey; + }} }} "#, cls = style.get_class_name() diff --git a/packages/stylist/tests/macro_integrations/complicated-attributes-pass.rs b/packages/stylist/tests/macro_integrations/complicated-attributes-pass.rs index d26faa2..e877c98 100644 --- a/packages/stylist/tests/macro_integrations/complicated-attributes-pass.rs +++ b/packages/stylist/tests/macro_integrations/complicated-attributes-pass.rs @@ -42,25 +42,25 @@ fn main() { let style = stylist::Style::new(sheet).unwrap(); let expected_result = format!( r#".{cls} {{ -border: medium dashed green; + border: medium dashed green; }} .{cls}:checked+label {{ -color: #9799a7; + color: #9799a7; }} .{cls}:nth-child(-n+4)~nav {{ -max-height: 500px; + max-height: 500px; }} .{cls}::first-letter {{ -box-shadow: 3px 3px red,-1rem 0 0.4rem olive; + box-shadow: 3px 3px red,-1rem 0 0.4rem olive; }} .{cls} article span {{ -box-shadow: inset 0 1px 2px rgba(0.32,0,0,15%); + box-shadow: inset 0 1px 2px rgba(0.32,0,0,15%); }} .{cls} a[href*="login"], .{cls} a[href^="https://"], .{cls} a[rel~="tag"], .{cls} a[lang|="en"] {{ -background-image: url("images/pdf.png"); + background-image: url("images/pdf.png"); }} .{cls} #content::after {{ -content: " (" attr(x)")"; + content: " (" attr(x)")"; }} "#, cls = style.get_class_name() diff --git a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs b/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs index b907ea6..ce8fadc 100644 --- a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs +++ b/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs @@ -14,13 +14,13 @@ fn main() { let expected_reusult = format!( r#"@media print {{ .{cls} .outer {{ -background-color: grey; + background-color: grey; }} }} @supports (display:grid) {{ -.{cls} .outer {{ -margin: 2cm; -}} + .{cls} .outer {{ + margin: 2cm; + }} }} "#, cls = style.get_class_name() diff --git a/packages/stylist/tests/macro_integrations/uses-display-impl-pass.rs b/packages/stylist/tests/macro_integrations/uses-display-impl-pass.rs index c18469e..d7d6f14 100644 --- a/packages/stylist/tests/macro_integrations/uses-display-impl-pass.rs +++ b/packages/stylist/tests/macro_integrations/uses-display-impl-pass.rs @@ -19,7 +19,7 @@ fn main() { .unwrap(); let expected_result = format!( r#".{cls} {{ -display: none; + display: none; }} "#, cls = style.get_class_name() diff --git a/packages/stylist/tests/macro_literal_integrations/at-supports-pass.rs b/packages/stylist/tests/macro_literal_integrations/at-supports-pass.rs index 9d35441..620a5e3 100644 --- a/packages/stylist/tests/macro_literal_integrations/at-supports-pass.rs +++ b/packages/stylist/tests/macro_literal_integrations/at-supports-pass.rs @@ -7,9 +7,9 @@ fn main() { .unwrap(); let expected_result = format!( r#"@supports (display:grid) {{ -.{cls} {{ -background-color: grey; -}} + .{cls} {{ + background-color: grey; + }} }} "#, cls = style.get_class_name() From 377d3c505e861b23c86d9c657e61d324ee28d787 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Thu, 9 Sep 2021 18:48:29 +0900 Subject: [PATCH 04/27] Block Content for Block. --- packages/stylist-core/src/ast/block.rs | 10 +- packages/stylist-core/src/ast/mod.rs | 20 +- packages/stylist-core/src/parser.rs | 196 +++++++++++------- .../stylist-macros/src/inline/parse/mod.rs | 27 ++- .../src/literal/to_output_with_args.rs | 90 +++++++- packages/stylist-macros/src/output/block.rs | 14 +- .../src/output/block_content.rs | 27 +++ packages/stylist-macros/src/output/mod.rs | 34 +-- packages/stylist-macros/src/output/rule.rs | 13 +- .../stylist-macros/src/output/rule_block.rs | 46 ++++ .../stylist-macros/src/output/selector.rs | 10 +- packages/stylist-macros/src/output/sheet.rs | 3 +- .../stylist-macros/src/output/style_attr.rs | 13 +- packages/stylist/tests/macro_sheet_tests.rs | 23 +- 14 files changed, 367 insertions(+), 159 deletions(-) create mode 100644 packages/stylist-macros/src/output/block_content.rs create mode 100644 packages/stylist-macros/src/output/rule_block.rs diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 4fc48ff..06a5a74 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -22,6 +22,12 @@ impl ToStyleStr for BlockContent { } } +impl From for BlockContent { + fn from(s: StyleAttribute) -> Self { + BlockContent::StyleAttr(s) + } +} + /// A block is a set of css properties that apply to elements that /// match the condition. The CSS standard calls these "Qualified rules". /// @@ -38,7 +44,7 @@ pub struct Block { /// If the value is set as [`&[]`], it signals to substitute with the classname generated for the /// [`Sheet`](super::Sheet) in which this is contained. pub condition: Cow<'static, [Selector]>, - pub style_attributes: Cow<'static, [StyleAttribute]>, + pub content: Cow<'static, [BlockContent]>, } impl Block { @@ -73,7 +79,7 @@ impl ToStyleStr for Block { .map(|m| ctx.with_condition(m)) .unwrap_or_else(|| ctx.to_block_context()); - for attr in self.style_attributes.iter() { + for attr in self.content.iter() { attr.write_style(w, &mut final_ctx)?; } diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index 886df30..f254a02 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -35,18 +35,20 @@ mod tests { let test_block = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "width".into(), value: vec!["100vw".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec![".inner".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Rule(Rule { @@ -92,18 +94,20 @@ width: 200px; content: vec![ RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "width".into(), value: vec!["100vw".into()].into(), - }] + } + .into()] .into(), }), RuleContent::Block(Block { condition: vec![vec![".inner".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), }), RuleContent::Rule( diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 1bfe66f..94ae1fb 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -1,11 +1,5 @@ use std::borrow::Cow; -use crate::{ - ast::{ - Block, Rule, RuleContent, ScopeContent, Selector, Sheet, StringFragment, StyleAttribute, - }, - Error, Result, -}; use nom::{ branch::alt, bytes::complete::{is_not, tag, take_while, take_while1}, @@ -17,6 +11,12 @@ use nom::{ IResult, }; +use crate::ast::{ + Block, BlockContent, Rule, RuleContent, ScopeContent, Selector, Sheet, StringFragment, + StyleAttribute, +}; +use crate::{Error, Result}; + #[cfg(test)] use log::trace; @@ -374,6 +374,15 @@ impl Parser { result } + fn block_content(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + context( + "BlockContent", + map(Self::attributes, |m: Vec| { + m.into_iter().map(|m| m.into()).collect() + }), + )(i) + } + /// Parse a [`Block`]. fn block(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { #[cfg(test)] @@ -387,12 +396,12 @@ impl Parser { separated_pair( Self::condition, tag("{"), - terminated(Self::trim_cmt(Self::attributes), tag("}")), + terminated(Self::trim_cmt(Self::block_content), tag("}")), ), - |p: (Vec, Vec)| { + |p: (Vec, Vec)| { ScopeContent::Block(Block { condition: p.0.into(), - style_attributes: p.1.into(), + content: p.1.into(), }) }, )), @@ -523,7 +532,11 @@ impl Parser { |attr: Vec| { ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: attr.into(), + content: attr + .into_iter() + .map(|m| m.into()) + .collect::>() + .into(), }) }, )), @@ -720,23 +733,26 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec![".nested".into()].into()].into(), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }, + } + .into(), StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), - }, + } + .into(), ] .into(), }), @@ -761,30 +777,34 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }, + } + .into(), StyleAttribute { key: "content".into(), value: vec![r#"";""#.into()].into(), - }, + } + .into(), ] .into(), }), ScopeContent::Block(Block { condition: vec![vec![r#"[placeholder="someone@example.com"]"#.into()].into()] .into(), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }, + } + .into(), StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), - }, + } + .into(), ] .into(), }), @@ -805,15 +825,17 @@ mod tests { let expected = Sheet::from(vec![ScopeContent::Block(Block { condition: vec![vec![r#"[placeholder="\" {}"]"#.into()].into()].into(), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }, + } + .into(), StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), - }, + } + .into(), ] .into(), })]); @@ -831,10 +853,11 @@ mod tests { let expected = Sheet::from(vec![ScopeContent::Block(Block { condition: vec![vec!["&:hover".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["#d0d0d9".into()].into(), - }] + } + .into()] .into(), })]); assert_eq!(parsed, expected); @@ -861,10 +884,11 @@ mod tests { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -873,10 +897,11 @@ mod tests { condition: vec!["@media ".into(), "screen and (max-width: 200px)".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["yellow".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -909,20 +934,22 @@ mod tests { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), })] .into(), }), ScopeContent::Block(Block { condition: vec![vec![".some-class2".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["yellow".into()].into(), - }] + } + .into()] .into(), }), ]); @@ -951,18 +978,20 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: vec![vec!["div".into()].into(), vec!["span".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["yellow".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec!["&".into()].into(), vec!["& input".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["pink".into()].into(), - }] + } + .into()] .into(), }), ]); @@ -999,19 +1028,22 @@ mod tests { .into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "backdrop-filter".into(), value: vec!["blur(2px)".into()].into(), - }, + } + .into(), StyleAttribute { key: "-webkit-backdrop-filter".into(), value: vec!["blur(2px)".into()].into(), - }, + } + .into(), StyleAttribute { key: "background-color".into(), value: vec!["rgb(0, 0, 0, 0.7)".into()].into(), - }, + } + .into(), ] .into(), })] @@ -1026,10 +1058,11 @@ mod tests { .into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["rgb(25, 25, 25)".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -1069,10 +1102,11 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { @@ -1081,15 +1115,17 @@ mod tests { vec!["${var_a}".into()].into(), ] .into(), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }, + } + .into(), StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), - }, + } + .into(), ] .into(), }), @@ -1105,7 +1141,7 @@ mod tests { let expected = Sheet::from(vec![ScopeContent::Block(Block { condition: vec![vec![".nested".into()].into()].into(), - style_attributes: vec![].into(), + content: vec![].into(), })]); assert_eq!(parsed, expected); } @@ -1156,37 +1192,41 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["${color}".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec!["span".into()].into(), vec!["${sel_div}".into()].into()] .into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec![":not(${sel_root})".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["black".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "display".into(), value: vec!["flex".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -1223,37 +1263,41 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["${color}".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec!["span".into()].into(), vec!["${sel_div}".into()].into()] .into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec![":not(${sel_root})".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["black".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "display".into(), value: vec!["flex".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -1286,37 +1330,41 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "color".into(), value: vec!["\"$${color}\"".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec!["span".into()].into(), vec!["${sel_div}".into()].into()] .into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { condition: vec![vec![":not(${sel_root})".into()].into()].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["black".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), content: vec![RuleContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "display".into(), value: vec!["flex".into()].into(), - }] + } + .into()] .into(), })] .into(), diff --git a/packages/stylist-macros/src/inline/parse/mod.rs b/packages/stylist-macros/src/inline/parse/mod.rs index 6742d9a..e981ff4 100644 --- a/packages/stylist-macros/src/inline/parse/mod.rs +++ b/packages/stylist-macros/src/inline/parse/mod.rs @@ -1,21 +1,25 @@ -use crate::output::{OutputAtRule, OutputAttribute, OutputFragment, OutputQualifiedRule}; use itertools::Itertools; use syn::parse::Error as ParseError; +use crate::output::{ + OutputAtRule, OutputAttribute, OutputBlockContent, OutputFragment, OutputQualifiedRule, +}; + +mod attribute; +mod block; +mod qualifier; mod root; -pub use root::CssRootNode; +mod rule; mod scope; -pub use scope::CssScope; mod scope_content; -pub use scope_content::CssScopeContent; -mod block; + +pub use attribute::{CssAttribute, CssAttributeName, CssAttributeValue}; pub use block::CssQualifiedRule; -mod qualifier; pub use qualifier::CssBlockQualifier; -mod rule; +pub use root::CssRootNode; pub use rule::CssAtRule; -mod attribute; -pub use attribute::{CssAttribute, CssAttributeName, CssAttributeValue}; +pub use scope::CssScope; +pub use scope_content::CssScopeContent; /// We want to normalize the input a bit. For that, we want to pretend that e.g. /// the sample input @@ -93,7 +97,10 @@ fn normalize_hierarchy_impl<'it>( ScopeItem::Attributes(attributes) => { let result = OutputSheetContent::QualifiedRule(OutputQualifiedRule { qualifier: qualifier.clone(), - attributes, + content: attributes + .into_iter() + .map(|m| OutputBlockContent::StyleAttr(m)) + .collect(), }); Box::new(std::iter::once(result)) } diff --git a/packages/stylist-macros/src/literal/to_output_with_args.rs b/packages/stylist-macros/src/literal/to_output_with_args.rs index d5e281f..4022502 100644 --- a/packages/stylist-macros/src/literal/to_output_with_args.rs +++ b/packages/stylist-macros/src/literal/to_output_with_args.rs @@ -5,8 +5,9 @@ use proc_macro_error::abort_call_site; use stylist_core::ast::*; use crate::output::{ - OutputAtRule, OutputAttribute, OutputFragment, OutputQualifiedRule, OutputQualifier, - OutputRuleContent, OutputScopeContent, OutputSelector, OutputSheet, + OutputAtRule, OutputAttribute, OutputBlockContent, OutputFragment, OutputQualifiedRule, + OutputQualifier, OutputRuleBlock, OutputRuleBlockContent, OutputRuleContent, + OutputScopeContent, OutputSelector, OutputSheet, }; use super::{argument::Argument, fstring}; @@ -38,8 +39,79 @@ impl ToOutputWithArgs for Selector { } } +impl ToOutputWithArgs for RuleBlock { + type Output = OutputRuleBlock; + + fn to_output_with_args( + &self, + args: &HashMap, + args_used: &mut HashSet, + ) -> Self::Output { + let mut condition = Vec::new(); + + for i in self.condition.iter() { + condition.extend(i.to_output_with_args(args, args_used)); + } + + let mut contents = Vec::new(); + + for i in self.content.iter() { + contents.push(i.to_output_with_args(args, args_used)); + } + + OutputRuleBlock { + condition, + content: contents, + // errors: Vec::new(), + } + } +} + +impl ToOutputWithArgs for RuleBlockContent { + type Output = OutputRuleBlockContent; + + fn to_output_with_args( + &self, + args: &HashMap, + args_used: &mut HashSet, + ) -> Self::Output { + match self { + Self::RuleBlock(ref m) => { + let block = m.to_output_with_args(args, args_used); + OutputRuleBlockContent::RuleBlock(Box::new(block)) + } + Self::StyleAttr(ref m) => { + let rule = m.to_output_with_args(args, args_used); + OutputRuleBlockContent::StyleAttr(rule) + } + } + } +} + +impl ToOutputWithArgs for BlockContent { + type Output = OutputBlockContent; + + fn to_output_with_args( + &self, + args: &HashMap, + args_used: &mut HashSet, + ) -> Self::Output { + match self { + Self::RuleBlock(ref m) => { + let block = m.to_output_with_args(args, args_used); + OutputBlockContent::RuleBlock(block) + } + Self::StyleAttr(ref m) => { + let rule = m.to_output_with_args(args, args_used); + OutputBlockContent::StyleAttr(rule) + } + } + } +} + impl ToOutputWithArgs for StyleAttribute { type Output = OutputAttribute; + fn to_output_with_args( &self, args: &HashMap, @@ -63,6 +135,7 @@ impl ToOutputWithArgs for StyleAttribute { impl ToOutputWithArgs for Block { type Output = OutputQualifiedRule; + fn to_output_with_args( &self, args: &HashMap, @@ -74,10 +147,10 @@ impl ToOutputWithArgs for Block { selector_list.push(i.to_output_with_args(args, args_used)); } - let mut attributes = Vec::new(); + let mut content = Vec::new(); - for i in self.style_attributes.iter() { - attributes.push(i.to_output_with_args(args, args_used)); + for i in self.content.iter() { + content.push(i.to_output_with_args(args, args_used)); } OutputQualifiedRule { @@ -85,13 +158,14 @@ impl ToOutputWithArgs for Block { selector_list, errors: Vec::new(), }, - attributes, + content, } } } impl ToOutputWithArgs for RuleContent { type Output = OutputRuleContent; + fn to_output_with_args( &self, args: &HashMap, @@ -113,6 +187,7 @@ impl ToOutputWithArgs for RuleContent { impl ToOutputWithArgs for StringFragment { type Output = Vec; + fn to_output_with_args( &self, args: &HashMap, @@ -149,6 +224,7 @@ impl ToOutputWithArgs for StringFragment { impl ToOutputWithArgs for Rule { type Output = OutputAtRule; + fn to_output_with_args( &self, args: &HashMap, @@ -176,6 +252,7 @@ impl ToOutputWithArgs for Rule { impl ToOutputWithArgs for ScopeContent { type Output = OutputScopeContent; + fn to_output_with_args( &self, args: &HashMap, @@ -196,6 +273,7 @@ impl ToOutputWithArgs for ScopeContent { impl ToOutputWithArgs for Sheet { type Output = OutputSheet; + fn to_output_with_args( &self, args: &HashMap, diff --git a/packages/stylist-macros/src/output/block.rs b/packages/stylist-macros/src/output/block.rs index f7b8315..0642ad2 100644 --- a/packages/stylist-macros/src/output/block.rs +++ b/packages/stylist-macros/src/output/block.rs @@ -1,25 +1,21 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputAttribute, OutputQualifier, Reify}; +use super::{ContextRecorder, IntoCowVecTokens, OutputBlockContent, OutputQualifier, Reify}; use proc_macro2::TokenStream; use quote::quote; pub struct OutputQualifiedRule { pub qualifier: OutputQualifier, - pub attributes: Vec, + pub content: Vec, } impl Reify for OutputQualifiedRule { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let Self { - qualifier, - attributes, - } = self; - let qualifier = qualifier.into_token_stream(ctx); - let attributes = attributes.into_cow_vec_tokens(ctx); + let qualifier = self.qualifier.into_token_stream(ctx); + let content = self.content.into_cow_vec_tokens(ctx); quote! { ::stylist::ast::Block { condition: #qualifier, - style_attributes: #attributes, + content: #content, } } } diff --git a/packages/stylist-macros/src/output/block_content.rs b/packages/stylist-macros/src/output/block_content.rs new file mode 100644 index 0000000..f6c6c13 --- /dev/null +++ b/packages/stylist-macros/src/output/block_content.rs @@ -0,0 +1,27 @@ +use super::{ContextRecorder, OutputAttribute, OutputRuleBlock, Reify}; +use proc_macro2::TokenStream; +use quote::quote; +// use syn::Error as ParseError; + +pub enum OutputBlockContent { + RuleBlock(OutputRuleBlock), + StyleAttr(OutputAttribute), + // Err(ParseError), +} + +impl Reify for OutputBlockContent { + fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + match self { + Self::RuleBlock(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::BlockContent::RuleBlock(#tokens) } + } + Self::StyleAttr(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::BlockContent::StyleAttr(#tokens) } + } // Self::Err(err) => err.into_token_stream(ctx), + } + } +} diff --git a/packages/stylist-macros/src/output/mod.rs b/packages/stylist-macros/src/output/mod.rs index bb276a1..af2d672 100644 --- a/packages/stylist-macros/src/output/mod.rs +++ b/packages/stylist-macros/src/output/mod.rs @@ -3,26 +3,32 @@ //! emitted by the different macros. use proc_macro2::TokenStream; -mod sheet; -pub use sheet::OutputSheet; -mod rule; -pub use rule::OutputAtRule; mod block; -pub use block::OutputQualifiedRule; -mod selector; -pub use selector::{OutputQualifier, OutputSelector}; -mod scope_content; -pub use scope_content::OutputScopeContent; +mod block_content; +mod rule; +mod rule_block; mod rule_content; -pub use rule_content::OutputRuleContent; -mod style_attr; -pub use style_attr::OutputAttribute; +mod scope_content; +mod selector; +mod sheet; mod str_frag; -pub use str_frag::{fragment_coalesce, OutputFragment}; +mod style_attr; mod context_recorder; -pub use context_recorder::ContextRecorder; mod maybe_static; + +pub use block::OutputQualifiedRule; +pub use block_content::OutputBlockContent; +pub use rule::OutputAtRule; +pub use rule_block::{OutputRuleBlock, OutputRuleBlockContent}; +pub use rule_content::OutputRuleContent; +pub use scope_content::OutputScopeContent; +pub use selector::{OutputQualifier, OutputSelector}; +pub use sheet::OutputSheet; +pub use str_frag::{fragment_coalesce, OutputFragment}; +pub use style_attr::OutputAttribute; + +pub use context_recorder::ContextRecorder; pub use maybe_static::IntoCowVecTokens; /// Reify a structure into an expression of a specific type. diff --git a/packages/stylist-macros/src/output/rule.rs b/packages/stylist-macros/src/output/rule.rs index cd1a624..b82070c 100644 --- a/packages/stylist-macros/src/output/rule.rs +++ b/packages/stylist-macros/src/output/rule.rs @@ -14,18 +14,13 @@ pub struct OutputAtRule { impl Reify for OutputAtRule { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let Self { - prelude, - contents, - errors, - } = self; - - let condition = prelude + let condition = self + .prelude .into_iter() .coalesce(fragment_coalesce) .into_cow_vec_tokens(ctx); - let content = contents.into_cow_vec_tokens(ctx); - let errors = errors.into_iter().map(|e| e.into_compile_error()); + let content = self.contents.into_cow_vec_tokens(ctx); + let errors = self.errors.into_iter().map(|e| e.into_compile_error()); quote! { ::stylist::ast::Rule { diff --git a/packages/stylist-macros/src/output/rule_block.rs b/packages/stylist-macros/src/output/rule_block.rs new file mode 100644 index 0000000..9214026 --- /dev/null +++ b/packages/stylist-macros/src/output/rule_block.rs @@ -0,0 +1,46 @@ +use super::{ContextRecorder, IntoCowVecTokens, OutputAttribute, OutputFragment, Reify}; +use proc_macro2::TokenStream; +use quote::quote; +// use syn::Error as ParseError; + +pub enum OutputRuleBlockContent { + RuleBlock(Box), + StyleAttr(OutputAttribute), + // Err(ParseError), +} + +impl Reify for OutputRuleBlockContent { + fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + match self { + Self::RuleBlock(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::BlockContent::RuleBlock(::std::boxed::Box::new(#tokens)) } + } + Self::StyleAttr(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::BlockContent::StyleAttr(#tokens) } + } // Self::Err(err) => err.into_token_stream(ctx), + } + } +} + +pub struct OutputRuleBlock { + pub condition: Vec, + pub content: Vec, +} + +impl Reify for OutputRuleBlock { + fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + let condition = self.condition.into_cow_vec_tokens(ctx); + let content = self.content.into_cow_vec_tokens(ctx); + + quote! { + ::stylist::ast::RuleBlock { + condition: #condition, + content: #content, + } + } + } +} diff --git a/packages/stylist-macros/src/output/selector.rs b/packages/stylist-macros/src/output/selector.rs index 262f531..a39141d 100644 --- a/packages/stylist-macros/src/output/selector.rs +++ b/packages/stylist-macros/src/output/selector.rs @@ -33,14 +33,8 @@ pub struct OutputQualifier { impl Reify for OutputQualifier { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let Self { - selector_list: selectors, - errors, - .. - } = self; - - let selectors = selectors.into_iter().into_cow_vec_tokens(ctx); - let errors = errors.into_iter().map(|e| e.into_compile_error()); + let selectors = self.selector_list.into_iter().into_cow_vec_tokens(ctx); + let errors = self.errors.into_iter().map(|e| e.into_compile_error()); quote! { { diff --git a/packages/stylist-macros/src/output/sheet.rs b/packages/stylist-macros/src/output/sheet.rs index 78c4806..cca0ce2 100644 --- a/packages/stylist-macros/src/output/sheet.rs +++ b/packages/stylist-macros/src/output/sheet.rs @@ -8,8 +8,7 @@ pub struct OutputSheet { impl Reify for OutputSheet { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let Self { contents } = self; - let contents = contents.into_cow_vec_tokens(ctx); + let contents = self.contents.into_cow_vec_tokens(ctx); let quoted_sheet = quote! { { diff --git a/packages/stylist-macros/src/output/style_attr.rs b/packages/stylist-macros/src/output/style_attr.rs index 3901d07..b5adc74 100644 --- a/packages/stylist-macros/src/output/style_attr.rs +++ b/packages/stylist-macros/src/output/style_attr.rs @@ -12,18 +12,15 @@ pub struct OutputAttribute { impl Reify for OutputAttribute { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let Self { - key, - values, - errors, - } = self; - let errors = errors.into_iter().map(|e| e.into_compile_error()); + let errors = self.errors.into_iter().map(|e| e.into_compile_error()); - let key = key.into_token_stream(ctx); - let value_parts = values + let key = self.key.into_token_stream(ctx); + let value_parts = self + .values .into_iter() .coalesce(fragment_coalesce) .into_cow_vec_tokens(ctx); + quote! { ::stylist::ast::StyleAttribute { key: #key, diff --git a/packages/stylist/tests/macro_sheet_tests.rs b/packages/stylist/tests/macro_sheet_tests.rs index 0360d85..bda0061 100644 --- a/packages/stylist/tests/macro_sheet_tests.rs +++ b/packages/stylist/tests/macro_sheet_tests.rs @@ -40,10 +40,11 @@ fn test_sheet_interpolation() { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["red".into()].into(), - }] + } + .into()] .into(), }), ScopeContent::Block(Block { @@ -52,15 +53,17 @@ fn test_sheet_interpolation() { vec![".some-selector".into()].into(), ] .into(), - style_attributes: vec![ + content: vec![ StyleAttribute { key: "background-color".into(), value: vec!["blue".into()].into(), - }, + } + .into(), StyleAttribute { key: "width".into(), value: vec!["100".into(), "px".into()].into(), - }, + } + .into(), ] .into(), }), @@ -82,10 +85,11 @@ fn test_sheet_interpolation() { condition: vec!["@media screen and ".into(), "(max-width: 500px)".into()].into(), content: vec![RuleContent::Block(Block { condition: vec![].into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "background-color".into(), value: vec!["brown".into()].into(), - }] + } + .into()] .into(), })] .into(), @@ -112,10 +116,11 @@ fn test_sheet_escaped() { }, ] .into(), - style_attributes: vec![StyleAttribute { + content: vec![StyleAttribute { key: "content".into(), value: vec!["\"${var_b}\"".into()].into(), - }] + } + .into()] .into(), })]); assert_eq!(parsed, expected); From 60f170045a293ac5a364a83fe27a2687c79372b8 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Thu, 9 Sep 2021 23:08:35 +0900 Subject: [PATCH 05/27] Remove folding and perfer native AST generation. --- .../src/inline/component_value/mod.rs | 53 +++----- .../inline/component_value/simple_block.rs | 62 +++++---- packages/stylist-macros/src/inline/mod.rs | 19 ++- .../src/inline/parse/attribute.rs | 11 +- .../stylist-macros/src/inline/parse/block.rs | 66 +++++---- .../stylist-macros/src/inline/parse/mod.rs | 98 +------------- .../src/inline/parse/qualifier.rs | 14 +- .../stylist-macros/src/inline/parse/root.rs | 75 +++++++++-- .../stylist-macros/src/inline/parse/rule.rs | 80 +++++------ .../stylist-macros/src/inline/parse/scope.rs | 126 +++++++++++++++++- .../src/inline/parse/scope_content.rs | 5 +- .../src/literal/to_output_with_args.rs | 6 +- packages/stylist-macros/src/output/block.rs | 1 + .../src/output/block_content.rs | 1 + packages/stylist-macros/src/output/rule.rs | 9 +- .../stylist-macros/src/output/rule_block.rs | 6 +- .../stylist-macros/src/output/rule_content.rs | 7 +- .../src/output/scope_content.rs | 8 +- .../stylist-macros/src/output/selector.rs | 12 +- packages/stylist-macros/src/output/sheet.rs | 1 + .../stylist-macros/src/output/style_attr.rs | 9 +- .../nested_at_rule-fail.stderr | 11 -- ...at_rule-fail.rs => nested_at_rule-pass.rs} | 6 +- 23 files changed, 395 insertions(+), 291 deletions(-) delete mode 100644 packages/stylist/tests/macro_integrations/nested_at_rule-fail.stderr rename packages/stylist/tests/macro_integrations/{nested_at_rule-fail.rs => nested_at_rule-pass.rs} (90%) diff --git a/packages/stylist-macros/src/inline/component_value/mod.rs b/packages/stylist-macros/src/inline/component_value/mod.rs index 8f50182..f673631 100644 --- a/packages/stylist-macros/src/inline/component_value/mod.rs +++ b/packages/stylist-macros/src/inline/component_value/mod.rs @@ -21,7 +21,7 @@ use syn::{ mod preserved_token; pub use preserved_token::PreservedToken; mod simple_block; -pub use simple_block::SimpleBlock; +pub use simple_block::{BlockKind, SimpleBlock}; mod function_token; pub use function_token::FunctionToken; mod stream; @@ -86,23 +86,18 @@ impl ComponentValue { Self::Expr(expr) => vec![expr.to_output_fragment()], - Self::Block(SimpleBlock::Bracketed { contents, .. }) => { - // [ ... ] - let mut output = vec!['['.into()]; - for c in contents { - output.extend(c.to_output_fragments()); + Self::Block(ref m) => { + if let BlockKind::Braced(_) = m.kind { + // this kind of block is not supposed to appear in @-rule preludes, block qualifiers + // or attribute values and as such should not get emitted + unreachable!("braced blocks should not get reified"); } - output.push(']'.into()); - output - } - - Self::Block(SimpleBlock::Paren { contents, .. }) => { - // ( ... ) - let mut output = vec!['('.into()]; - for c in contents { + let (start, end) = m.kind.surround_tokens(); + let mut output = vec![start.into()]; + for c in m.contents.iter() { output.extend(c.to_output_fragments()); } - output.push(')'.into()); + output.push(end.into()); output } @@ -115,12 +110,6 @@ impl ComponentValue { output.push(')'.into()); output } - - Self::Block(SimpleBlock::Braced { .. }) => { - // this kind of block is not supposed to appear in @-rule preludes, block qualifiers - // or attribute values and as such should not get emitted - unreachable!("braced blocks should not get reified"); - } } } @@ -164,19 +153,21 @@ impl ComponentValue { match self { Self::Expr(_) | Self::Function(_) | Self::Token(PreservedToken::Ident(_)) => Ok(vec![]), - Self::Block(SimpleBlock::Bracketed { contents, .. }) => { - let mut collected = vec![]; - for e in contents.iter().map(|e| e.validate_selector_token()) { - collected.extend(e?); + Self::Block(ref m) => { + if let BlockKind::Bracketed(_) = m.kind { + let mut collected = vec![]; + for e in m.contents.iter().map(|e| e.validate_selector_token()) { + collected.extend(e?); + } + Ok(collected) + } else { + Ok(vec![ParseError::new_spanned( + self, + "expected a valid part of a scope qualifier, not a block", + )]) } - Ok(collected) } - Self::Block(_) => Ok(vec![ParseError::new_spanned( - self, - "expected a valid part of a scope qualifier, not a block", - )]), - Self::Token(PreservedToken::Literal(l)) => { let syn_lit = Lit::new(l.clone()); if !matches!(syn_lit, Lit::Str(_)) { diff --git a/packages/stylist-macros/src/inline/component_value/simple_block.rs b/packages/stylist-macros/src/inline/component_value/simple_block.rs index f97ba3b..d51fe76 100644 --- a/packages/stylist-macros/src/inline/component_value/simple_block.rs +++ b/packages/stylist-macros/src/inline/component_value/simple_block.rs @@ -8,36 +8,43 @@ use syn::{ }; #[derive(Debug, Clone)] -pub enum SimpleBlock { - Braced { - brace: token::Brace, - contents: Vec, - }, - Bracketed { - bracket: token::Bracket, - contents: Vec, - }, - Paren { - paren: token::Paren, - contents: Vec, - }, +pub enum BlockKind { + Braced(token::Brace), + Bracketed(token::Bracket), + Paren(token::Paren), +} + +impl BlockKind { + pub fn surround_tokens(&self) -> (char, char) { + match self { + Self::Braced(_) => ('{', '}'), + Self::Bracketed(_) => ('[', ']'), + Self::Paren(_) => ('(', ')'), + } + } +} + +#[derive(Debug, Clone)] +pub struct SimpleBlock { + pub kind: BlockKind, + pub contents: Vec, } impl ToTokens for SimpleBlock { fn to_tokens(&self, toks: &mut TokenStream) { - match self { - Self::Braced { brace, contents } => brace.surround(toks, |toks| { - for c in contents.iter() { + match self.kind { + BlockKind::Braced(ref m) => m.surround(toks, |toks| { + for c in self.contents.iter() { c.to_tokens(toks); } }), - Self::Bracketed { bracket, contents } => bracket.surround(toks, |toks| { - for c in contents.iter() { + BlockKind::Bracketed(ref m) => m.surround(toks, |toks| { + for c in self.contents.iter() { c.to_tokens(toks); } }), - Self::Paren { paren, contents } => paren.surround(toks, |toks| { - for c in contents.iter() { + BlockKind::Paren(ref m) => m.surround(toks, |toks| { + for c in self.contents.iter() { c.to_tokens(toks); } }), @@ -52,17 +59,26 @@ impl Parse for SimpleBlock { let inside; let brace = braced!(inside in input); let contents = ComponentValue::parse_multiple(&inside)?; - Ok(Self::Braced { brace, contents }) + Ok(Self { + kind: BlockKind::Braced(brace), + contents, + }) } else if lookahead.peek(token::Bracket) { let inside; let bracket = bracketed!(inside in input); let contents = ComponentValue::parse_multiple(&inside)?; - Ok(Self::Bracketed { bracket, contents }) + Ok(Self { + kind: BlockKind::Bracketed(bracket), + contents, + }) } else if lookahead.peek(token::Paren) { let inside; let paren = parenthesized!(inside in input); let contents = ComponentValue::parse_multiple(&inside)?; - Ok(Self::Paren { paren, contents }) + Ok(Self { + kind: BlockKind::Paren(paren), + contents, + }) } else { Err(lookahead.error()) } diff --git a/packages/stylist-macros/src/inline/mod.rs b/packages/stylist-macros/src/inline/mod.rs index 0dfe102..45b0b0f 100644 --- a/packages/stylist-macros/src/inline/mod.rs +++ b/packages/stylist-macros/src/inline/mod.rs @@ -7,6 +7,20 @@ use crate::output::{ContextRecorder, Reify}; use log::debug; use parse::CssRootNode; use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::Error as ParseError; + +fn error_to_token_stream(errors: Vec) -> TokenStream { + let tokens: Vec = errors.into_iter().map(|m| m.into_compile_error()).collect(); + + quote! { + { + { #( #tokens )* } + + ::stylist::ast::Sheet::from(Vec::new()) + } + } +} pub fn macro_fn(input: TokenStream) -> TokenStream { let root = match syn::parse2::(input) { @@ -17,5 +31,8 @@ pub fn macro_fn(input: TokenStream) -> TokenStream { debug!("Parsed as: {:?}", root); let mut ctx = ContextRecorder::new(); - root.into_output().into_token_stream(&mut ctx) + match root.into_output() { + Ok(m) => m.into_token_stream(&mut ctx), + Err(e) => error_to_token_stream(e), + } } diff --git a/packages/stylist-macros/src/inline/parse/attribute.rs b/packages/stylist-macros/src/inline/parse/attribute.rs index adc082c..e88b422 100644 --- a/packages/stylist-macros/src/inline/parse/attribute.rs +++ b/packages/stylist-macros/src/inline/parse/attribute.rs @@ -101,7 +101,10 @@ impl ComponentValue { } impl CssAttribute { - pub(super) fn into_output(self) -> OutputAttribute { + pub(super) fn into_output(self) -> Result> { + if !self.value.errors.is_empty() { + return Err(self.value.errors); + } let values = self .value .values @@ -109,11 +112,11 @@ impl CssAttribute { .flat_map(|p| p.to_output_fragments()) .spaced_with(fragment_spacing) .collect(); - OutputAttribute { + Ok(OutputAttribute { key: self.name.into_output(), values, - errors: self.value.errors, - } + // errors: self.value.errors, + }) } } diff --git a/packages/stylist-macros/src/inline/parse/block.rs b/packages/stylist-macros/src/inline/parse/block.rs index 54649a5..f04cdfe 100644 --- a/packages/stylist-macros/src/inline/parse/block.rs +++ b/packages/stylist-macros/src/inline/parse/block.rs @@ -1,9 +1,10 @@ -use super::{normalize_hierarchy_impl, CssBlockQualifier, CssScope, OutputSheetContent}; +use super::{CssAttribute, CssBlockQualifier, CssScope}; +use crate::output::{OutputBlockContent, OutputQualifiedRule, OutputQualifier}; use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; #[derive(Debug)] pub struct CssQualifiedRule { - qualifier: CssBlockQualifier, + pub qualifier: CssBlockQualifier, scope: CssScope, } @@ -16,27 +17,46 @@ impl Parse for CssQualifiedRule { } impl CssQualifiedRule { - pub(super) fn fold_in_context( - self, - ctx: CssBlockQualifier, - ) -> Box> { - let own_ctx = self.qualifier; - if !own_ctx.is_empty() && !ctx.is_empty() { - // TODO: figure out how to combine contexts - // !Warning!: simply duplicating the containing blocks will (if no special care is taken) - // also duplicate injected expressions, which will evaluate them multiple times, which can be - // unexpected and confusing to the user. - // !Warning!: when the qualifiers contain several selectors each, this can lead to an explosion - // of emitted blocks. Consider - // .one, .two, .three { .inner-one, .inner-two, .inner-three { background: ${injected_expr} } } - // Following emotion, this would expand to 9 blocks and evaluate `injected_expr` 9 times. - // A possibility would be collecting appearing expressions once up front and putting replacements - // into the blocks. - return Box::new(std::iter::once(OutputSheetContent::Error( - ParseError::new_spanned(own_ctx, "Can not nest qualified blocks (yet)"), - ))); + pub fn into_output(self) -> Result> { + let qualifier_result = self.qualifier.into_output(); + let scope_result = self.scope.into_block_output(); + + let (qualifier, content) = match (qualifier_result, scope_result) { + (Ok(m), Ok(n)) => (m, n), + (Err(mut e1), Err(e2)) => { + e1.extend(e2); + return Err(e1); + } + (Err(e), _) => return Err(e), + (_, Err(e)) => return Err(e), + }; + + Ok(OutputQualifiedRule { qualifier, content }) + } + + // Into Output for a dangling block + pub fn into_dangling_output( + attrs: &mut Vec, + ) -> Result> { + let mut errors = Vec::new(); + let mut output_attrs = Vec::new(); + + for attr in attrs.drain(0..) { + match attr.into_output() { + Ok(m) => output_attrs.push(OutputBlockContent::StyleAttr(m)), + Err(e) => errors.extend(e), + } + } + + if !errors.is_empty() { + Err(errors) + } else { + Ok(OutputQualifiedRule { + qualifier: OutputQualifier { + selector_list: Vec::new(), + }, + content: output_attrs, + }) } - let relevant_ctx = if !own_ctx.is_empty() { own_ctx } else { ctx }; - Box::new(normalize_hierarchy_impl(relevant_ctx, self.scope.contents)) } } diff --git a/packages/stylist-macros/src/inline/parse/mod.rs b/packages/stylist-macros/src/inline/parse/mod.rs index e981ff4..ead2bd1 100644 --- a/packages/stylist-macros/src/inline/parse/mod.rs +++ b/packages/stylist-macros/src/inline/parse/mod.rs @@ -1,9 +1,4 @@ -use itertools::Itertools; -use syn::parse::Error as ParseError; - -use crate::output::{ - OutputAtRule, OutputAttribute, OutputBlockContent, OutputFragment, OutputQualifiedRule, -}; +use crate::output::OutputFragment; mod attribute; mod block; @@ -21,97 +16,6 @@ pub use rule::CssAtRule; pub use scope::CssScope; pub use scope_content::CssScopeContent; -/// We want to normalize the input a bit. For that, we want to pretend that e.g. -/// the sample input -/// -/// ```css -/// outer-attribute: some; -/// foo-bar: zet; -/// @media print { -/// .nested { -/// only-in-print: foo; -/// } -/// and-always: red; -/// } -/// ``` -/// -/// gets processed as if written in the (more verbose) shallowly nested style: -/// -/// ```css -/// { -/// outer-attribute: some; -/// foo-bar: zet; -/// } -/// @media print { -/// .nested { -/// only-in-print: foo; -/// } -/// { -/// and-always: red; -/// } -/// } -/// ``` -/// -/// Errors in nested items are reported as spanned TokenStreams. -/// -fn normalize_scope_hierarchy<'it>( - it: impl 'it + IntoIterator, -) -> impl 'it + Iterator { - normalize_hierarchy_impl(Default::default(), it) -} - -enum OutputSheetContent { - AtRule(OutputAtRule), - QualifiedRule(OutputQualifiedRule), - Error(ParseError), -} - -// Collect attributes into blocks, also flatten and lift nested blocks. -fn normalize_hierarchy_impl<'it>( - context: CssBlockQualifier, - it: impl 'it + IntoIterator, -) -> impl 'it + Iterator { - let qualifier = context.clone().into_output(); - - // Helper enum appearing in intermediate step - enum ScopeItem { - Attributes(Vec), - AtRule(CssAtRule), - Block(CssQualifiedRule), - } - it.into_iter() - .map(|c| match c { - CssScopeContent::Attribute(a) => ScopeItem::Attributes(vec![a.into_output()]), - CssScopeContent::AtRule(r) => ScopeItem::AtRule(r), - CssScopeContent::Nested(b) => ScopeItem::Block(b), - }) - // collect runs of attributes together into a single item - .coalesce(|l, r| match (l, r) { - (ScopeItem::Attributes(mut ls), ScopeItem::Attributes(rs)) => { - ls.extend(rs); - Ok(ScopeItem::Attributes(ls)) - } - (l, r) => Err((l, r)), - }) - .flat_map(move |w| match w { - ScopeItem::Attributes(attributes) => { - let result = OutputSheetContent::QualifiedRule(OutputQualifiedRule { - qualifier: qualifier.clone(), - content: attributes - .into_iter() - .map(|m| OutputBlockContent::StyleAttr(m)) - .collect(), - }); - Box::new(std::iter::once(result)) - } - ScopeItem::AtRule(r) => { - let result = r.fold_in_context(context.clone()); - Box::new(std::iter::once(result)) - } - ScopeItem::Block(b) => b.fold_in_context(context.clone()), - }) -} - pub fn fragment_spacing(l: &OutputFragment, r: &OutputFragment) -> Option { use super::component_value::PreservedToken::*; use OutputFragment::*; diff --git a/packages/stylist-macros/src/inline/parse/qualifier.rs b/packages/stylist-macros/src/inline/parse/qualifier.rs index 4a7f4a1..fc89be4 100644 --- a/packages/stylist-macros/src/inline/parse/qualifier.rs +++ b/packages/stylist-macros/src/inline/parse/qualifier.rs @@ -64,11 +64,11 @@ impl Default for CssBlockQualifier { } impl CssBlockQualifier { - pub fn is_empty(&self) -> bool { - self.qualifiers.is_empty() - } + pub fn into_output(self) -> Result> { + if !self.qualifier_errors.is_empty() { + return Err(self.qualifier_errors); + } - pub fn into_output(self) -> OutputQualifier { fn is_not_comma(q: &ComponentValue) -> bool { !matches!(q, ComponentValue::Token(PreservedToken::Punct(ref p)) if p.as_char() == ',') } @@ -95,9 +95,9 @@ impl CssBlockQualifier { }) .collect(); - OutputQualifier { + Ok(OutputQualifier { selector_list, - errors: self.qualifier_errors, - } + // errors: self.qualifier_errors, + }) } } diff --git a/packages/stylist-macros/src/inline/parse/root.rs b/packages/stylist-macros/src/inline/parse/root.rs index 6115fd9..01707ed 100644 --- a/packages/stylist-macros/src/inline/parse/root.rs +++ b/packages/stylist-macros/src/inline/parse/root.rs @@ -1,28 +1,75 @@ -use super::{normalize_scope_hierarchy, CssScopeContent, OutputSheetContent}; +use super::{CssAttribute, CssQualifiedRule, CssScopeContent}; use crate::output::{OutputScopeContent, OutputSheet}; -use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; +use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; #[derive(Debug)] pub struct CssRootNode { - root_contents: Vec, + contents: Vec, } impl Parse for CssRootNode { fn parse(input: &ParseBuffer) -> ParseResult { - let root_contents = CssScopeContent::consume_list_of_rules(input)?; - Ok(Self { root_contents }) + let contents = CssScopeContent::consume_list_of_rules(input)?; + Ok(Self { contents }) } } impl CssRootNode { - pub fn into_output(self) -> OutputSheet { - let contents = normalize_scope_hierarchy(self.root_contents) - .map(|c| match c { - OutputSheetContent::QualifiedRule(block) => OutputScopeContent::Block(block), - OutputSheetContent::AtRule(rule) => OutputScopeContent::AtRule(rule), - OutputSheetContent::Error(err) => OutputScopeContent::Err(err), - }) - .collect(); - OutputSheet { contents } + pub fn into_output(self) -> Result> { + let mut errors = Vec::new(); + let mut contents = Vec::new(); + + let mut attrs: Vec = Vec::new(); + + let push_attrs_into_contents = + |attrs: &mut Vec, + contents: &mut Vec, + errors: &mut Vec| { + if attrs.is_empty() { + return; + } + + match CssQualifiedRule::into_dangling_output(attrs) { + Ok(m) => contents.push(OutputScopeContent::Block(m)), + Err(e) => errors.extend(e), + } + }; + + for scope in self.contents { + match scope { + CssScopeContent::Attribute(m) => { + attrs.push(m); + } + CssScopeContent::AtRule(m) => { + push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + + match m.into_rule_output() { + Ok(m) => { + contents.push(OutputScopeContent::AtRule(m)); + } + Err(e) => errors.extend(e), + }; + } + + CssScopeContent::Nested(m) => { + push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + + match m.into_output() { + Ok(m) => { + contents.push(OutputScopeContent::Block(m)); + } + Err(e) => errors.extend(e), + }; + } + } + } + + push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + + if !errors.is_empty() { + Err(errors) + } else { + Ok(OutputSheet { contents }) + } } } diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index 3e715fc..8ac4433 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -1,20 +1,19 @@ +use syn::{ + parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, + token, +}; + use super::{ super::{ component_value::{ComponentValue, ComponentValueStream}, css_ident::CssIdent, }, - fragment_spacing, normalize_hierarchy_impl, CssBlockQualifier, CssScope, OutputSheetContent, + fragment_spacing, CssScope, }; use crate::{ - output::{OutputAtRule, OutputFragment, OutputRuleContent}, + output::{OutputAtRule, OutputFragment, OutputRuleBlock}, spacing_iterator::SpacedIterator, }; -use proc_macro2::TokenStream; -use quote::ToTokens; -use syn::{ - parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, - token, -}; #[derive(Debug)] pub enum CssAtRuleContent { @@ -76,58 +75,45 @@ impl Parse for CssAtRule { } impl CssAtRule { - pub(super) fn fold_in_context(self, ctx: CssBlockQualifier) -> OutputSheetContent { - if !ctx.is_empty() { - return OutputSheetContent::Error(ParseError::new_spanned( - self.prelude_span(), - "Can not nest @-rules (yet)", - )); - } - let contents = match self.contents { - CssAtRuleContent::Empty(_) => Vec::new(), - CssAtRuleContent::Scope(scope) => normalize_hierarchy_impl(ctx, scope.contents) - .map(|c| match c { - OutputSheetContent::AtRule(rule) => OutputRuleContent::AtRule(rule), - OutputSheetContent::QualifiedRule(block) => OutputRuleContent::Block(block), - OutputSheetContent::Error(err) => OutputRuleContent::Err(err), - }) - .collect(), - }; + pub fn condition_output(&self) -> Vec { let mut prelude = vec![OutputFragment::Str(format!( "@{} ", self.name.to_output_string() ))]; prelude.extend( self.prelude + .clone() .into_iter() .flat_map(|p| p.to_output_fragments()) .spaced_with(fragment_spacing), ); - OutputSheetContent::AtRule(OutputAtRule { - prelude, - contents, - errors: self.errors, - }) - } -} -impl CssAtRule { - fn prelude_span(&self) -> PreludeSpan { - PreludeSpan { rule: self } + prelude } -} - -struct PreludeSpan<'a> { - rule: &'a CssAtRule, -} -impl<'a> ToTokens for PreludeSpan<'a> { - fn to_tokens(&self, toks: &mut TokenStream) { - let rule = self.rule; - rule.at.to_tokens(toks); - rule.name.to_tokens(toks); - for c in rule.prelude.iter() { - c.to_tokens(toks); + pub fn into_rule_output(self) -> Result> { + if !self.errors.is_empty() { + return Err(self.errors); } + + let prelude = self.condition_output(); + + Ok(OutputAtRule { + prelude, + contents: match self.contents { + CssAtRuleContent::Scope(m) => m.into_rule_output()?, + CssAtRuleContent::Empty(_) => Vec::new(), + }, + }) + } + + pub fn into_rule_block_output(self) -> Result> { + Ok(OutputRuleBlock { + condition: self.condition_output(), + content: match self.contents { + CssAtRuleContent::Scope(m) => m.into_rule_block_output()?, + CssAtRuleContent::Empty(_) => Vec::new(), + }, + }) } } diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index 33f81f1..acfa08a 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -1,10 +1,15 @@ use super::CssScopeContent; use syn::{ braced, - parse::{Parse, ParseBuffer, Result as ParseResult}, + parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, token, }; +use crate::output::{ + OutputAttribute, OutputBlockContent, OutputQualifiedRule, OutputQualifier, + OutputRuleBlockContent, OutputRuleContent, +}; + #[derive(Debug)] pub struct CssScope { brace: token::Brace, @@ -19,3 +24,122 @@ impl Parse for CssScope { Ok(Self { brace, contents }) } } + +impl CssScope { + pub fn into_rule_output(self) -> Result, Vec> { + let mut attrs = Vec::new(); + let mut errors = Vec::new(); + + let mut contents = Vec::new(); + + let collect_attrs_into_contents = + |attrs: &mut Vec, contents: &mut Vec| { + if attrs.is_empty() { + return; + } + + contents.push(OutputRuleContent::Block(OutputQualifiedRule { + qualifier: OutputQualifier { + selector_list: Vec::new(), + }, + content: attrs + .drain(0..) + .map(OutputBlockContent::StyleAttr) + .collect(), + })); + }; + + for scope in self.contents { + match scope { + CssScopeContent::Attribute(m) => match m.into_output() { + Ok(m) => attrs.push(m), + Err(e) => errors.extend(e), + }, + CssScopeContent::AtRule(m) => { + collect_attrs_into_contents(&mut attrs, &mut contents); + + match m.into_rule_output() { + Ok(m) => contents.push(OutputRuleContent::AtRule(m)), + Err(e) => errors.extend(e), + } + } + CssScopeContent::Nested(m) => { + collect_attrs_into_contents(&mut attrs, &mut contents); + + match m.into_output() { + Ok(m) => contents.push(OutputRuleContent::Block(m)), + Err(e) => errors.extend(e), + } + } + } + } + + collect_attrs_into_contents(&mut attrs, &mut contents); + + if !errors.is_empty() { + Err(errors) + } else { + Ok(contents) + } + } + + pub fn into_rule_block_output(self) -> Result, Vec> { + let mut errors = Vec::new(); + let mut contents = Vec::new(); + + for scope in self.contents { + match scope { + CssScopeContent::Attribute(m) => match m.into_output() { + Ok(m) => contents.push(OutputRuleBlockContent::StyleAttr(m)), + Err(e) => errors.extend(e), + }, + CssScopeContent::AtRule(m) => match m.into_rule_block_output() { + Ok(m) => contents.push(OutputRuleBlockContent::RuleBlock(Box::new(m))), + Err(e) => errors.extend(e), + }, + CssScopeContent::Nested(m) => { + errors.push(ParseError::new_spanned( + m.qualifier, + "Can not nest qualified blocks (yet)", + )); + } + } + } + + if !errors.is_empty() { + Err(errors) + } else { + Ok(contents) + } + } + + pub fn into_block_output(self) -> Result, Vec> { + let mut errors = Vec::new(); + let mut contents = Vec::new(); + + for scope in self.contents { + match scope { + CssScopeContent::Attribute(m) => match m.into_output() { + Ok(m) => contents.push(OutputBlockContent::StyleAttr(m)), + Err(e) => errors.extend(e), + }, + CssScopeContent::AtRule(m) => match m.into_rule_block_output() { + Ok(m) => contents.push(OutputBlockContent::RuleBlock(m)), + Err(e) => errors.extend(e), + }, + CssScopeContent::Nested(m) => { + errors.push(ParseError::new_spanned( + m.qualifier, + "Can not nest qualified blocks (yet)", + )); + } + } + } + + if !errors.is_empty() { + Err(errors) + } else { + Ok(contents) + } + } +} diff --git a/packages/stylist-macros/src/inline/parse/scope_content.rs b/packages/stylist-macros/src/inline/parse/scope_content.rs index c0e7bea..0ba8b2e 100644 --- a/packages/stylist-macros/src/inline/parse/scope_content.rs +++ b/packages/stylist-macros/src/inline/parse/scope_content.rs @@ -1,9 +1,10 @@ +use itertools::Itertools; +use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; + use super::{ super::component_value::{ComponentValue, ComponentValueStream, PreservedToken}, CssAtRule, CssAttribute, CssQualifiedRule, }; -use itertools::Itertools; -use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; #[derive(Debug)] pub enum CssScopeContent { diff --git a/packages/stylist-macros/src/literal/to_output_with_args.rs b/packages/stylist-macros/src/literal/to_output_with_args.rs index 4022502..73643bc 100644 --- a/packages/stylist-macros/src/literal/to_output_with_args.rs +++ b/packages/stylist-macros/src/literal/to_output_with_args.rs @@ -128,7 +128,7 @@ impl ToOutputWithArgs for StyleAttribute { OutputAttribute { key, values, - errors: Vec::new(), + // errors: Vec::new(), } } } @@ -156,7 +156,7 @@ impl ToOutputWithArgs for Block { OutputQualifiedRule { qualifier: OutputQualifier { selector_list, - errors: Vec::new(), + // errors: Vec::new(), }, content, } @@ -245,7 +245,7 @@ impl ToOutputWithArgs for Rule { OutputAtRule { prelude, contents, - errors: Vec::new(), + // errors: Vec::new(), } } } diff --git a/packages/stylist-macros/src/output/block.rs b/packages/stylist-macros/src/output/block.rs index 0642ad2..d8beea2 100644 --- a/packages/stylist-macros/src/output/block.rs +++ b/packages/stylist-macros/src/output/block.rs @@ -2,6 +2,7 @@ use super::{ContextRecorder, IntoCowVecTokens, OutputBlockContent, OutputQualifi use proc_macro2::TokenStream; use quote::quote; +#[derive(Debug)] pub struct OutputQualifiedRule { pub qualifier: OutputQualifier, pub content: Vec, diff --git a/packages/stylist-macros/src/output/block_content.rs b/packages/stylist-macros/src/output/block_content.rs index f6c6c13..ef34544 100644 --- a/packages/stylist-macros/src/output/block_content.rs +++ b/packages/stylist-macros/src/output/block_content.rs @@ -3,6 +3,7 @@ use proc_macro2::TokenStream; use quote::quote; // use syn::Error as ParseError; +#[derive(Debug)] pub enum OutputBlockContent { RuleBlock(OutputRuleBlock), StyleAttr(OutputAttribute), diff --git a/packages/stylist-macros/src/output/rule.rs b/packages/stylist-macros/src/output/rule.rs index b82070c..4844b92 100644 --- a/packages/stylist-macros/src/output/rule.rs +++ b/packages/stylist-macros/src/output/rule.rs @@ -4,12 +4,13 @@ use super::{ use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -use syn::parse::Error as ParseError; +// use syn::parse::Error as ParseError; +#[derive(Debug)] pub struct OutputAtRule { pub prelude: Vec, pub contents: Vec, - pub errors: Vec, + // pub errors: Vec, } impl Reify for OutputAtRule { @@ -20,12 +21,12 @@ impl Reify for OutputAtRule { .coalesce(fragment_coalesce) .into_cow_vec_tokens(ctx); let content = self.contents.into_cow_vec_tokens(ctx); - let errors = self.errors.into_iter().map(|e| e.into_compile_error()); + // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); quote! { ::stylist::ast::Rule { condition: { - #( #errors )* + // #( #errors )* #condition }, content: #content, diff --git a/packages/stylist-macros/src/output/rule_block.rs b/packages/stylist-macros/src/output/rule_block.rs index 9214026..a858363 100644 --- a/packages/stylist-macros/src/output/rule_block.rs +++ b/packages/stylist-macros/src/output/rule_block.rs @@ -3,6 +3,7 @@ use proc_macro2::TokenStream; use quote::quote; // use syn::Error as ParseError; +#[derive(Debug)] pub enum OutputRuleBlockContent { RuleBlock(Box), StyleAttr(OutputAttribute), @@ -15,17 +16,18 @@ impl Reify for OutputRuleBlockContent { Self::RuleBlock(m) => { let tokens = m.into_token_stream(ctx); - quote! { ::stylist::ast::BlockContent::RuleBlock(::std::boxed::Box::new(#tokens)) } + quote! { ::stylist::ast::RuleBlockContent::RuleBlock(::std::boxed::Box::new(#tokens)) } } Self::StyleAttr(m) => { let tokens = m.into_token_stream(ctx); - quote! { ::stylist::ast::BlockContent::StyleAttr(#tokens) } + quote! { ::stylist::ast::RuleBlockContent::StyleAttr(#tokens) } } // Self::Err(err) => err.into_token_stream(ctx), } } } +#[derive(Debug)] pub struct OutputRuleBlock { pub condition: Vec, pub content: Vec, diff --git a/packages/stylist-macros/src/output/rule_content.rs b/packages/stylist-macros/src/output/rule_content.rs index 85251eb..9b943e9 100644 --- a/packages/stylist-macros/src/output/rule_content.rs +++ b/packages/stylist-macros/src/output/rule_content.rs @@ -1,13 +1,13 @@ use super::{ContextRecorder, OutputAtRule, OutputQualifiedRule, Reify}; use proc_macro2::{Literal, TokenStream}; use quote::quote; -use syn::Error as ParseError; +#[derive(Debug)] pub enum OutputRuleContent { AtRule(OutputAtRule), Block(OutputQualifiedRule), String(String), - Err(ParseError), + // Err(ParseError), } impl Reify for OutputRuleContent { @@ -24,8 +24,7 @@ impl Reify for OutputRuleContent { Self::String(ref s) => { let s = Literal::string(s); quote! { ::stylist::ast::RuleContent::String(#s.into()) } - } - Self::Err(err) => err.into_token_stream(ctx), + } // Self::Err(err) => err.into_token_stream(ctx), } } } diff --git a/packages/stylist-macros/src/output/scope_content.rs b/packages/stylist-macros/src/output/scope_content.rs index 75a8229..2811c37 100644 --- a/packages/stylist-macros/src/output/scope_content.rs +++ b/packages/stylist-macros/src/output/scope_content.rs @@ -1,12 +1,13 @@ use super::{ContextRecorder, OutputAtRule, OutputQualifiedRule, Reify}; use proc_macro2::TokenStream; use quote::quote; -use syn::Error as ParseError; +// use syn::Error as ParseError; +#[derive(Debug)] pub enum OutputScopeContent { AtRule(OutputAtRule), Block(OutputQualifiedRule), - Err(ParseError), + // Err(ParseError), } impl Reify for OutputScopeContent { @@ -19,8 +20,7 @@ impl Reify for OutputScopeContent { Self::Block(block) => { let block_tokens = block.into_token_stream(ctx); quote! { ::stylist::ast::ScopeContent::Block(#block_tokens) } - } - Self::Err(err) => err.into_token_stream(ctx), + } // Self::Err(err) => err.into_token_stream(ctx), } } } diff --git a/packages/stylist-macros/src/output/selector.rs b/packages/stylist-macros/src/output/selector.rs index a39141d..e3e2c49 100644 --- a/packages/stylist-macros/src/output/selector.rs +++ b/packages/stylist-macros/src/output/selector.rs @@ -2,9 +2,9 @@ use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -use syn::parse::Error as ParseError; +// use syn::parse::Error as ParseError; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct OutputSelector { pub selectors: Vec, } @@ -25,20 +25,20 @@ impl Reify for OutputSelector { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct OutputQualifier { pub selector_list: Vec, - pub errors: Vec, + // pub errors: Vec, } impl Reify for OutputQualifier { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { let selectors = self.selector_list.into_iter().into_cow_vec_tokens(ctx); - let errors = self.errors.into_iter().map(|e| e.into_compile_error()); + // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); quote! { { - #( #errors )* + // #( #errors )* #selectors } } diff --git a/packages/stylist-macros/src/output/sheet.rs b/packages/stylist-macros/src/output/sheet.rs index cca0ce2..e8241ce 100644 --- a/packages/stylist-macros/src/output/sheet.rs +++ b/packages/stylist-macros/src/output/sheet.rs @@ -2,6 +2,7 @@ use super::{ContextRecorder, IntoCowVecTokens, OutputScopeContent, Reify}; use proc_macro2::TokenStream; use quote::quote; +#[derive(Debug)] pub struct OutputSheet { pub contents: Vec, } diff --git a/packages/stylist-macros/src/output/style_attr.rs b/packages/stylist-macros/src/output/style_attr.rs index b5adc74..b2bbb9d 100644 --- a/packages/stylist-macros/src/output/style_attr.rs +++ b/packages/stylist-macros/src/output/style_attr.rs @@ -2,17 +2,18 @@ use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -use syn::parse::Error as ParseError; +// use syn::parse::Error as ParseError; +#[derive(Debug)] pub struct OutputAttribute { pub key: OutputFragment, pub values: Vec, - pub errors: Vec, + // pub errors: Vec, } impl Reify for OutputAttribute { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let errors = self.errors.into_iter().map(|e| e.into_compile_error()); + // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); let key = self.key.into_token_stream(ctx); let value_parts = self @@ -25,7 +26,7 @@ impl Reify for OutputAttribute { ::stylist::ast::StyleAttribute { key: #key, value: { - #( #errors )* + // #( #errors )* #value_parts }, } diff --git a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.stderr b/packages/stylist/tests/macro_integrations/nested_at_rule-fail.stderr deleted file mode 100644 index 050e0fd..0000000 --- a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Can not nest @-rules (yet) - --> $DIR/nested_at_rule-fail.rs:5:13 - | -5 | @media print { - | ^^^^^^^^^^^^ - -error: Can not nest @-rules (yet) - --> $DIR/nested_at_rule-fail.rs:8:13 - | -8 | @supports (display: grid) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs b/packages/stylist/tests/macro_integrations/nested_at_rule-pass.rs similarity index 90% rename from packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs rename to packages/stylist/tests/macro_integrations/nested_at_rule-pass.rs index ce8fadc..8eb0202 100644 --- a/packages/stylist/tests/macro_integrations/nested_at_rule-fail.rs +++ b/packages/stylist/tests/macro_integrations/nested_at_rule-pass.rs @@ -13,9 +13,9 @@ fn main() { .unwrap(); let expected_reusult = format!( r#"@media print {{ -.{cls} .outer {{ - background-color: grey; -}} + .{cls} .outer {{ + background-color: grey; + }} }} @supports (display:grid) {{ .{cls} .outer {{ From 2e5d54fbea915a8e4bb388eec3dffba14a82b11d Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 00:38:07 +0900 Subject: [PATCH 06/27] Adjust output naming to align with stylist::ast. --- examples/benchmarks/Cargo.toml | 2 +- examples/yew-integration/Cargo.toml | 6 +-- examples/yew-shadow/Cargo.toml | 2 +- examples/yew-theme-yewdux/Cargo.toml | 6 +-- packages/stylist-core/Cargo.toml | 6 +-- packages/stylist-macros/Cargo.toml | 6 +-- .../src/inline/parse/attribute.rs | 6 +-- .../stylist-macros/src/inline/parse/block.rs | 16 ++++---- .../src/inline/parse/qualifier.rs | 12 ++---- .../stylist-macros/src/inline/parse/root.rs | 2 +- .../stylist-macros/src/inline/parse/rule.rs | 12 +++--- .../stylist-macros/src/inline/parse/scope.rs | 11 ++--- .../src/literal/to_output_with_args.rs | 41 +++++++------------ packages/stylist-macros/src/output/block.rs | 12 +++--- .../src/output/block_content.rs | 4 +- packages/stylist-macros/src/output/mod.rs | 6 +-- packages/stylist-macros/src/output/rule.rs | 16 +++----- .../stylist-macros/src/output/rule_block.rs | 4 +- .../stylist-macros/src/output/rule_content.rs | 11 +++-- .../src/output/scope_content.rs | 12 +++--- .../stylist-macros/src/output/selector.rs | 21 ---------- .../stylist-macros/src/output/style_attr.rs | 5 --- packages/stylist/Cargo.toml | 6 +-- 23 files changed, 81 insertions(+), 144 deletions(-) diff --git a/examples/benchmarks/Cargo.toml b/examples/benchmarks/Cargo.toml index 88f849f..9105b91 100644 --- a/examples/benchmarks/Cargo.toml +++ b/examples/benchmarks/Cargo.toml @@ -9,7 +9,7 @@ log = "0.4.14" console_log = { version = "0.2.0", features = ["color"] } yew = "0.18.0" stylist = { path = "../../packages/stylist", features = ["yew_integration"] } -web-sys = { version = "0.3.53", features = [ +web-sys = { version = "0.3.54", features = [ "Window", "Performance", ] } diff --git a/examples/yew-integration/Cargo.toml b/examples/yew-integration/Cargo.toml index a77745e..4c7f2df 100644 --- a/examples/yew-integration/Cargo.toml +++ b/examples/yew-integration/Cargo.toml @@ -11,11 +11,11 @@ yew = "0.18.0" stylist = { path = "../../packages/stylist", features = ["yew_integration"] } [dev-dependencies] -wasm-bindgen-test = "0.3.26" -wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.27" +wasm-bindgen = "0.2.77" [dev-dependencies.web-sys] -version = "0.3.53" +version = "0.3.54" features = [ "Window", "Document", diff --git a/examples/yew-shadow/Cargo.toml b/examples/yew-shadow/Cargo.toml index 750ad3e..250073e 100644 --- a/examples/yew-shadow/Cargo.toml +++ b/examples/yew-shadow/Cargo.toml @@ -12,7 +12,7 @@ stylist = { path = "../../packages/stylist", features = ["yew_integration"] } once_cell = "1.8.0" [dependencies.web-sys] -version = "0.3.53" +version = "0.3.54" features = [ "Window", "Document", diff --git a/examples/yew-theme-yewdux/Cargo.toml b/examples/yew-theme-yewdux/Cargo.toml index 46d1d21..2a26b91 100644 --- a/examples/yew-theme-yewdux/Cargo.toml +++ b/examples/yew-theme-yewdux/Cargo.toml @@ -13,11 +13,11 @@ stylist = { path = "../../packages/stylist", features = ["yew_integration"] } yewdux = "0.6.2" [dev-dependencies] -wasm-bindgen-test = "0.3.26" -wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.27" +wasm-bindgen = "0.2.77" [dev-dependencies.web-sys] -version = "0.3.53" +version = "0.3.54" features = [ "Window", "Document", diff --git a/packages/stylist-core/Cargo.toml b/packages/stylist-core/Cargo.toml index 220b2a6..2cdb1e1 100644 --- a/packages/stylist-core/Cargo.toml +++ b/packages/stylist-core/Cargo.toml @@ -22,8 +22,8 @@ resolver = "2" [dependencies] nom = { version = "7.0.0", optional = true } -thiserror = "1.0.26" -wasm-bindgen = "0.2.76" +thiserror = "1.0.29" +wasm-bindgen = "0.2.77" once_cell = "1.8.0" [dev-dependencies] @@ -31,7 +31,7 @@ log = "0.4.14" env_logger = "0.9.0" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3.26" +wasm-bindgen-test = "0.3.27" [features] parser = ["nom"] diff --git a/packages/stylist-macros/Cargo.toml b/packages/stylist-macros/Cargo.toml index 1aa49f7..b18a3ac 100644 --- a/packages/stylist-macros/Cargo.toml +++ b/packages/stylist-macros/Cargo.toml @@ -28,11 +28,11 @@ stylist-core = { path = "../stylist-core", version = "0.9.0", features = ["parse litrs = "0.2.3" proc-macro-error = "1.0.4" -proc-macro2 = "1.0.28" +proc-macro2 = "1.0.29" quote = "1.0.9" nom = "7.0.0" -syn = { version = "1.0", features = ["full", "extra-traits"] } -itertools = "0.10" +syn = { version = "1.0.76", features = ["full", "extra-traits"] } +itertools = "0.10.1" log = "0.4.14" [dev-dependencies] diff --git a/packages/stylist-macros/src/inline/parse/attribute.rs b/packages/stylist-macros/src/inline/parse/attribute.rs index e88b422..7bfa889 100644 --- a/packages/stylist-macros/src/inline/parse/attribute.rs +++ b/packages/stylist-macros/src/inline/parse/attribute.rs @@ -20,7 +20,7 @@ use syn::{ #[derive(Debug)] pub enum CssAttributeName { Identifier(CssIdent), - InjectedExpr(InterpolatedExpression), + Expr(InterpolatedExpression), } #[derive(Debug)] @@ -94,7 +94,7 @@ impl ComponentValue { ComponentValue::Token(PreservedToken::Ident(i)) => { Some(CssAttributeName::Identifier(i)) } - ComponentValue::Expr(expr) => Some(CssAttributeName::InjectedExpr(expr)), + ComponentValue::Expr(expr) => Some(CssAttributeName::Expr(expr)), _ => None, } } @@ -124,7 +124,7 @@ impl CssAttributeName { fn into_output(self) -> OutputFragment { match self { Self::Identifier(name) => name.into(), - Self::InjectedExpr(expr) => expr.to_output_fragment(), + Self::Expr(expr) => expr.to_output_fragment(), } } } diff --git a/packages/stylist-macros/src/inline/parse/block.rs b/packages/stylist-macros/src/inline/parse/block.rs index f04cdfe..60b9139 100644 --- a/packages/stylist-macros/src/inline/parse/block.rs +++ b/packages/stylist-macros/src/inline/parse/block.rs @@ -1,5 +1,5 @@ use super::{CssAttribute, CssBlockQualifier, CssScope}; -use crate::output::{OutputBlockContent, OutputQualifiedRule, OutputQualifier}; +use crate::output::{OutputBlock, OutputBlockContent}; use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; #[derive(Debug)] @@ -17,11 +17,11 @@ impl Parse for CssQualifiedRule { } impl CssQualifiedRule { - pub fn into_output(self) -> Result> { + pub fn into_output(self) -> Result> { let qualifier_result = self.qualifier.into_output(); let scope_result = self.scope.into_block_output(); - let (qualifier, content) = match (qualifier_result, scope_result) { + let (condition, content) = match (qualifier_result, scope_result) { (Ok(m), Ok(n)) => (m, n), (Err(mut e1), Err(e2)) => { e1.extend(e2); @@ -31,13 +31,13 @@ impl CssQualifiedRule { (_, Err(e)) => return Err(e), }; - Ok(OutputQualifiedRule { qualifier, content }) + Ok(OutputBlock { condition, content }) } // Into Output for a dangling block pub fn into_dangling_output( attrs: &mut Vec, - ) -> Result> { + ) -> Result> { let mut errors = Vec::new(); let mut output_attrs = Vec::new(); @@ -51,10 +51,8 @@ impl CssQualifiedRule { if !errors.is_empty() { Err(errors) } else { - Ok(OutputQualifiedRule { - qualifier: OutputQualifier { - selector_list: Vec::new(), - }, + Ok(OutputBlock { + condition: Vec::new(), content: output_attrs, }) } diff --git a/packages/stylist-macros/src/inline/parse/qualifier.rs b/packages/stylist-macros/src/inline/parse/qualifier.rs index fc89be4..3c02514 100644 --- a/packages/stylist-macros/src/inline/parse/qualifier.rs +++ b/packages/stylist-macros/src/inline/parse/qualifier.rs @@ -2,10 +2,7 @@ use super::{ super::component_value::{ComponentValue, ComponentValueStream, PreservedToken}, fragment_spacing, }; -use crate::{ - output::{OutputQualifier, OutputSelector}, - spacing_iterator::SpacedIterator, -}; +use crate::{output::OutputSelector, spacing_iterator::SpacedIterator}; use itertools::Itertools; use proc_macro2::TokenStream; use quote::ToTokens; @@ -64,7 +61,7 @@ impl Default for CssBlockQualifier { } impl CssBlockQualifier { - pub fn into_output(self) -> Result> { + pub fn into_output(self) -> Result, Vec> { if !self.qualifier_errors.is_empty() { return Err(self.qualifier_errors); } @@ -95,9 +92,6 @@ impl CssBlockQualifier { }) .collect(); - Ok(OutputQualifier { - selector_list, - // errors: self.qualifier_errors, - }) + Ok(selector_list) } } diff --git a/packages/stylist-macros/src/inline/parse/root.rs b/packages/stylist-macros/src/inline/parse/root.rs index 01707ed..4448249 100644 --- a/packages/stylist-macros/src/inline/parse/root.rs +++ b/packages/stylist-macros/src/inline/parse/root.rs @@ -45,7 +45,7 @@ impl CssRootNode { match m.into_rule_output() { Ok(m) => { - contents.push(OutputScopeContent::AtRule(m)); + contents.push(OutputScopeContent::Rule(m)); } Err(e) => errors.extend(e), }; diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index 8ac4433..6f9ebde 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -11,7 +11,7 @@ use super::{ fragment_spacing, CssScope, }; use crate::{ - output::{OutputAtRule, OutputFragment, OutputRuleBlock}, + output::{OutputFragment, OutputRule, OutputRuleBlock}, spacing_iterator::SpacedIterator, }; @@ -91,16 +91,14 @@ impl CssAtRule { prelude } - pub fn into_rule_output(self) -> Result> { + pub fn into_rule_output(self) -> Result> { if !self.errors.is_empty() { return Err(self.errors); } - let prelude = self.condition_output(); - - Ok(OutputAtRule { - prelude, - contents: match self.contents { + Ok(OutputRule { + condition: self.condition_output(), + content: match self.contents { CssAtRuleContent::Scope(m) => m.into_rule_output()?, CssAtRuleContent::Empty(_) => Vec::new(), }, diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index acfa08a..fcfd80a 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -6,8 +6,7 @@ use syn::{ }; use crate::output::{ - OutputAttribute, OutputBlockContent, OutputQualifiedRule, OutputQualifier, - OutputRuleBlockContent, OutputRuleContent, + OutputAttribute, OutputBlock, OutputBlockContent, OutputRuleBlockContent, OutputRuleContent, }; #[derive(Debug)] @@ -38,10 +37,8 @@ impl CssScope { return; } - contents.push(OutputRuleContent::Block(OutputQualifiedRule { - qualifier: OutputQualifier { - selector_list: Vec::new(), - }, + contents.push(OutputRuleContent::Block(OutputBlock { + condition: Vec::new(), content: attrs .drain(0..) .map(OutputBlockContent::StyleAttr) @@ -59,7 +56,7 @@ impl CssScope { collect_attrs_into_contents(&mut attrs, &mut contents); match m.into_rule_output() { - Ok(m) => contents.push(OutputRuleContent::AtRule(m)), + Ok(m) => contents.push(OutputRuleContent::Rule(m)), Err(e) => errors.extend(e), } } diff --git a/packages/stylist-macros/src/literal/to_output_with_args.rs b/packages/stylist-macros/src/literal/to_output_with_args.rs index 73643bc..08c898f 100644 --- a/packages/stylist-macros/src/literal/to_output_with_args.rs +++ b/packages/stylist-macros/src/literal/to_output_with_args.rs @@ -5,9 +5,8 @@ use proc_macro_error::abort_call_site; use stylist_core::ast::*; use crate::output::{ - OutputAtRule, OutputAttribute, OutputBlockContent, OutputFragment, OutputQualifiedRule, - OutputQualifier, OutputRuleBlock, OutputRuleBlockContent, OutputRuleContent, - OutputScopeContent, OutputSelector, OutputSheet, + OutputAttribute, OutputBlock, OutputBlockContent, OutputFragment, OutputRule, OutputRuleBlock, + OutputRuleBlockContent, OutputRuleContent, OutputScopeContent, OutputSelector, OutputSheet, }; use super::{argument::Argument, fstring}; @@ -62,7 +61,6 @@ impl ToOutputWithArgs for RuleBlock { OutputRuleBlock { condition, content: contents, - // errors: Vec::new(), } } } @@ -125,16 +123,12 @@ impl ToOutputWithArgs for StyleAttribute { values.extend(i.to_output_with_args(args, args_used)); } - OutputAttribute { - key, - values, - // errors: Vec::new(), - } + OutputAttribute { key, values } } } impl ToOutputWithArgs for Block { - type Output = OutputQualifiedRule; + type Output = OutputBlock; fn to_output_with_args( &self, @@ -153,11 +147,8 @@ impl ToOutputWithArgs for Block { content.push(i.to_output_with_args(args, args_used)); } - OutputQualifiedRule { - qualifier: OutputQualifier { - selector_list, - // errors: Vec::new(), - }, + OutputBlock { + condition: selector_list, content, } } @@ -178,7 +169,7 @@ impl ToOutputWithArgs for RuleContent { } Self::Rule(ref m) => { let rule = m.to_output_with_args(args, args_used); - OutputRuleContent::AtRule(rule) + OutputRuleContent::Rule(rule) } Self::String(ref m) => OutputRuleContent::String(m.as_ref().to_string()), } @@ -223,30 +214,26 @@ impl ToOutputWithArgs for StringFragment { } impl ToOutputWithArgs for Rule { - type Output = OutputAtRule; + type Output = OutputRule; fn to_output_with_args( &self, args: &HashMap, args_used: &mut HashSet, ) -> Self::Output { - let mut prelude = Vec::new(); + let mut condition = Vec::new(); for i in self.condition.iter() { - prelude.extend(i.to_output_with_args(args, args_used)); + condition.extend(i.to_output_with_args(args, args_used)); } - let mut contents = Vec::new(); + let mut content = Vec::new(); for i in self.content.iter() { - contents.push(i.to_output_with_args(args, args_used)); + content.push(i.to_output_with_args(args, args_used)); } - OutputAtRule { - prelude, - contents, - // errors: Vec::new(), - } + OutputRule { condition, content } } } @@ -265,7 +252,7 @@ impl ToOutputWithArgs for ScopeContent { } Self::Rule(ref m) => { let rule = m.to_output_with_args(args, args_used); - OutputScopeContent::AtRule(rule) + OutputScopeContent::Rule(rule) } } } diff --git a/packages/stylist-macros/src/output/block.rs b/packages/stylist-macros/src/output/block.rs index d8beea2..e4952e6 100644 --- a/packages/stylist-macros/src/output/block.rs +++ b/packages/stylist-macros/src/output/block.rs @@ -1,21 +1,21 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputBlockContent, OutputQualifier, Reify}; +use super::{ContextRecorder, IntoCowVecTokens, OutputBlockContent, OutputSelector, Reify}; use proc_macro2::TokenStream; use quote::quote; #[derive(Debug)] -pub struct OutputQualifiedRule { - pub qualifier: OutputQualifier, +pub struct OutputBlock { + pub condition: Vec, pub content: Vec, } -impl Reify for OutputQualifiedRule { +impl Reify for OutputBlock { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let qualifier = self.qualifier.into_token_stream(ctx); + let condition = self.condition.into_cow_vec_tokens(ctx); let content = self.content.into_cow_vec_tokens(ctx); quote! { ::stylist::ast::Block { - condition: #qualifier, + condition: #condition, content: #content, } } diff --git a/packages/stylist-macros/src/output/block_content.rs b/packages/stylist-macros/src/output/block_content.rs index ef34544..8e8b0a4 100644 --- a/packages/stylist-macros/src/output/block_content.rs +++ b/packages/stylist-macros/src/output/block_content.rs @@ -1,13 +1,11 @@ use super::{ContextRecorder, OutputAttribute, OutputRuleBlock, Reify}; use proc_macro2::TokenStream; use quote::quote; -// use syn::Error as ParseError; #[derive(Debug)] pub enum OutputBlockContent { RuleBlock(OutputRuleBlock), StyleAttr(OutputAttribute), - // Err(ParseError), } impl Reify for OutputBlockContent { @@ -22,7 +20,7 @@ impl Reify for OutputBlockContent { let tokens = m.into_token_stream(ctx); quote! { ::stylist::ast::BlockContent::StyleAttr(#tokens) } - } // Self::Err(err) => err.into_token_stream(ctx), + } } } } diff --git a/packages/stylist-macros/src/output/mod.rs b/packages/stylist-macros/src/output/mod.rs index af2d672..194f9b8 100644 --- a/packages/stylist-macros/src/output/mod.rs +++ b/packages/stylist-macros/src/output/mod.rs @@ -17,13 +17,13 @@ mod style_attr; mod context_recorder; mod maybe_static; -pub use block::OutputQualifiedRule; +pub use block::OutputBlock; pub use block_content::OutputBlockContent; -pub use rule::OutputAtRule; +pub use rule::OutputRule; pub use rule_block::{OutputRuleBlock, OutputRuleBlockContent}; pub use rule_content::OutputRuleContent; pub use scope_content::OutputScopeContent; -pub use selector::{OutputQualifier, OutputSelector}; +pub use selector::OutputSelector; pub use sheet::OutputSheet; pub use str_frag::{fragment_coalesce, OutputFragment}; pub use style_attr::OutputAttribute; diff --git a/packages/stylist-macros/src/output/rule.rs b/packages/stylist-macros/src/output/rule.rs index 4844b92..593eca5 100644 --- a/packages/stylist-macros/src/output/rule.rs +++ b/packages/stylist-macros/src/output/rule.rs @@ -4,29 +4,25 @@ use super::{ use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -// use syn::parse::Error as ParseError; #[derive(Debug)] -pub struct OutputAtRule { - pub prelude: Vec, - pub contents: Vec, - // pub errors: Vec, +pub struct OutputRule { + pub condition: Vec, + pub content: Vec, } -impl Reify for OutputAtRule { +impl Reify for OutputRule { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { let condition = self - .prelude + .condition .into_iter() .coalesce(fragment_coalesce) .into_cow_vec_tokens(ctx); - let content = self.contents.into_cow_vec_tokens(ctx); - // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); + let content = self.content.into_cow_vec_tokens(ctx); quote! { ::stylist::ast::Rule { condition: { - // #( #errors )* #condition }, content: #content, diff --git a/packages/stylist-macros/src/output/rule_block.rs b/packages/stylist-macros/src/output/rule_block.rs index a858363..fed6a7d 100644 --- a/packages/stylist-macros/src/output/rule_block.rs +++ b/packages/stylist-macros/src/output/rule_block.rs @@ -1,13 +1,11 @@ use super::{ContextRecorder, IntoCowVecTokens, OutputAttribute, OutputFragment, Reify}; use proc_macro2::TokenStream; use quote::quote; -// use syn::Error as ParseError; #[derive(Debug)] pub enum OutputRuleBlockContent { RuleBlock(Box), StyleAttr(OutputAttribute), - // Err(ParseError), } impl Reify for OutputRuleBlockContent { @@ -22,7 +20,7 @@ impl Reify for OutputRuleBlockContent { let tokens = m.into_token_stream(ctx); quote! { ::stylist::ast::RuleBlockContent::StyleAttr(#tokens) } - } // Self::Err(err) => err.into_token_stream(ctx), + } } } } diff --git a/packages/stylist-macros/src/output/rule_content.rs b/packages/stylist-macros/src/output/rule_content.rs index 9b943e9..c0eba02 100644 --- a/packages/stylist-macros/src/output/rule_content.rs +++ b/packages/stylist-macros/src/output/rule_content.rs @@ -1,19 +1,18 @@ -use super::{ContextRecorder, OutputAtRule, OutputQualifiedRule, Reify}; +use super::{ContextRecorder, OutputBlock, OutputRule, Reify}; use proc_macro2::{Literal, TokenStream}; use quote::quote; #[derive(Debug)] pub enum OutputRuleContent { - AtRule(OutputAtRule), - Block(OutputQualifiedRule), + Rule(OutputRule), + Block(OutputBlock), String(String), - // Err(ParseError), } impl Reify for OutputRuleContent { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { match self { - Self::AtRule(rule) => { + Self::Rule(rule) => { let block_tokens = rule.into_token_stream(ctx); quote! { ::stylist::ast::RuleContent::Rule(::std::boxed::Box::new(#block_tokens)) } } @@ -24,7 +23,7 @@ impl Reify for OutputRuleContent { Self::String(ref s) => { let s = Literal::string(s); quote! { ::stylist::ast::RuleContent::String(#s.into()) } - } // Self::Err(err) => err.into_token_stream(ctx), + } } } } diff --git a/packages/stylist-macros/src/output/scope_content.rs b/packages/stylist-macros/src/output/scope_content.rs index 2811c37..3041f20 100644 --- a/packages/stylist-macros/src/output/scope_content.rs +++ b/packages/stylist-macros/src/output/scope_content.rs @@ -1,26 +1,24 @@ -use super::{ContextRecorder, OutputAtRule, OutputQualifiedRule, Reify}; +use super::{ContextRecorder, OutputBlock, OutputRule, Reify}; use proc_macro2::TokenStream; use quote::quote; -// use syn::Error as ParseError; #[derive(Debug)] pub enum OutputScopeContent { - AtRule(OutputAtRule), - Block(OutputQualifiedRule), - // Err(ParseError), + Rule(OutputRule), + Block(OutputBlock), } impl Reify for OutputScopeContent { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { match self { - Self::AtRule(rule) => { + Self::Rule(rule) => { let block_tokens = rule.into_token_stream(ctx); quote! { ::stylist::ast::ScopeContent::Rule(#block_tokens) } } Self::Block(block) => { let block_tokens = block.into_token_stream(ctx); quote! { ::stylist::ast::ScopeContent::Block(#block_tokens) } - } // Self::Err(err) => err.into_token_stream(ctx), + } } } } diff --git a/packages/stylist-macros/src/output/selector.rs b/packages/stylist-macros/src/output/selector.rs index e3e2c49..c107b0f 100644 --- a/packages/stylist-macros/src/output/selector.rs +++ b/packages/stylist-macros/src/output/selector.rs @@ -2,7 +2,6 @@ use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -// use syn::parse::Error as ParseError; #[derive(Debug, Clone)] pub struct OutputSelector { @@ -24,23 +23,3 @@ impl Reify for OutputSelector { } } } - -#[derive(Debug, Clone)] -pub struct OutputQualifier { - pub selector_list: Vec, - // pub errors: Vec, -} - -impl Reify for OutputQualifier { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let selectors = self.selector_list.into_iter().into_cow_vec_tokens(ctx); - // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); - - quote! { - { - // #( #errors )* - #selectors - } - } - } -} diff --git a/packages/stylist-macros/src/output/style_attr.rs b/packages/stylist-macros/src/output/style_attr.rs index b2bbb9d..2dca1fe 100644 --- a/packages/stylist-macros/src/output/style_attr.rs +++ b/packages/stylist-macros/src/output/style_attr.rs @@ -2,19 +2,15 @@ use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; -// use syn::parse::Error as ParseError; #[derive(Debug)] pub struct OutputAttribute { pub key: OutputFragment, pub values: Vec, - // pub errors: Vec, } impl Reify for OutputAttribute { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - // let errors = self.errors.into_iter().map(|e| e.into_compile_error()); - let key = self.key.into_token_stream(ctx); let value_parts = self .values @@ -26,7 +22,6 @@ impl Reify for OutputAttribute { ::stylist::ast::StyleAttribute { key: #key, value: { - // #( #errors )* #value_parts }, } diff --git a/packages/stylist/Cargo.toml b/packages/stylist/Cargo.toml index 696c910..f2c7f50 100644 --- a/packages/stylist/Cargo.toml +++ b/packages/stylist/Cargo.toml @@ -29,14 +29,14 @@ stylist-macros = { path = "../stylist-macros", version = "0.9.0", optional = tru once_cell = "1.8.0" rand = { version = "0.8.4", features = ["small_rng"], optional = true } -wasm-bindgen = "0.2.76" +wasm-bindgen = "0.2.77" yew = { version = "0.18.0", optional = true, default-features = false, features = ["web_sys"] } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2.3", features = ["js"], optional = true } [dependencies.web-sys] -version = "0.3.53" +version = "0.3.54" features = [ "Window", "Document", @@ -49,7 +49,7 @@ features = [ [dev-dependencies] log = "0.4.14" env_logger = "0.9.0" -trybuild = "1.0" +trybuild = "1.0.45" [features] random = ["rand", "getrandom"] From c825c278534d68ae5959acaf70db983cb51f8aff Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 11:40:51 +0900 Subject: [PATCH 07/27] Optimise Bundle Size. --- Trunk.toml | 3 ++ packages/stylist-core/src/ast/context.rs | 60 ++++++++++++++---------- packages/stylist-macros/src/sheet.rs | 1 + 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/Trunk.toml b/Trunk.toml index 9e74e8e..77023b3 100644 --- a/Trunk.toml +++ b/Trunk.toml @@ -12,3 +12,6 @@ ignore = [ "examples/yew-theme-agent/target/", "examples/yew-theme-yewdux/target/", ] + +[tools] +wasm_bindgen = "0.2.77" diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 4f02d0c..12023b0 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -10,7 +10,7 @@ use crate::Result; // } // -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] enum ContextState { // Either a finishing clause has been printed, or the starting block is not printed. Closed, @@ -62,44 +62,52 @@ impl<'a> StyleContext<'a> { } pub fn parent_conditions(&self) -> Vec> { - let mut sorted_parents = self.parent_conditions.clone(); - // @ rules first, then selectors. - sorted_parents.sort_by_cached_key(|m| !m.starts_with('@')); - - sorted_parents + // Equivalent to the following line, but would result in a smaller bundle + // sorted_parents.sort_by_cached_key(|m| !m.starts_with('@')); + let (mut rules, mut selectors) = self.parent_conditions.clone().into_iter().fold( + (Vec::new(), Vec::new()), + |(mut rules, mut selectors), item| { + if item.starts_with('@') { + rules.push(item); + } else { + selectors.push(item); + } + + (rules, selectors) + }, + ); + + rules.append(&mut selectors); + rules } pub fn write_starting_clause(&mut self, w: &mut W) -> Result<()> { - if self.state == ContextState::Open { - return Ok(()); - } - - for (index, cond) in self.parent_conditions().iter().enumerate() { - for _i in 0..index { - write!(w, " ")?; + if self.state == ContextState::Closed { + for (index, cond) in self.parent_conditions().iter().enumerate() { + for _i in 0..index { + write!(w, " ")?; + } + writeln!(w, "{} {{", cond)?; } - writeln!(w, "{} {{", cond)?; - } - self.state = ContextState::Open; + self.state = ContextState::Open; + } Ok(()) } pub fn write_finishing_clause(&mut self, w: &mut W) -> Result<()> { - if self.state == ContextState::Closed { - return Ok(()); - } - - for i in (0..self.parent_conditions.len()).rev() { - for _i in 0..i { - write!(w, " ")?; + if self.state == ContextState::Open { + for i in (0..self.parent_conditions.len()).rev() { + for _i in 0..i { + write!(w, " ")?; + } + writeln!(w, "}}")?; } - writeln!(w, "}}")?; - } - self.state = ContextState::Closed; + self.state = ContextState::Closed; + } Ok(()) } diff --git a/packages/stylist-macros/src/sheet.rs b/packages/stylist-macros/src/sheet.rs index 3abb8b9..7066ff7 100644 --- a/packages/stylist-macros/src/sheet.rs +++ b/packages/stylist-macros/src/sheet.rs @@ -1,4 +1,5 @@ use proc_macro2::{TokenStream, TokenTree}; + pub(crate) fn macro_fn(input: TokenStream) -> TokenStream { if let Some(TokenTree::Literal(_)) = input.clone().into_iter().next() { crate::literal::macro_fn(input) From dc699fe35fab9476b3e8ad3046124f2f46e0c3d7 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 12:13:19 +0900 Subject: [PATCH 08/27] Optimise Error Handling. --- packages/stylist-macros/src/inline/mod.rs | 28 ++---- .../src/inline/parse/attribute.rs | 33 +++---- .../stylist-macros/src/inline/parse/block.rs | 38 +++------ .../stylist-macros/src/inline/parse/mod.rs | 43 ++++++++++ .../src/inline/parse/qualifier.rs | 10 +-- .../stylist-macros/src/inline/parse/root.rs | 44 +++------- .../stylist-macros/src/inline/parse/rule.rs | 24 +++--- .../stylist-macros/src/inline/parse/scope.rs | 85 +++++++------------ 8 files changed, 136 insertions(+), 169 deletions(-) diff --git a/packages/stylist-macros/src/inline/mod.rs b/packages/stylist-macros/src/inline/mod.rs index 45b0b0f..a191281 100644 --- a/packages/stylist-macros/src/inline/mod.rs +++ b/packages/stylist-macros/src/inline/mod.rs @@ -5,22 +5,8 @@ mod parse; use crate::output::{ContextRecorder, Reify}; use log::debug; -use parse::CssRootNode; +use parse::{CssRootNode, IntoOutputContext}; use proc_macro2::TokenStream; -use quote::quote; -use syn::parse::Error as ParseError; - -fn error_to_token_stream(errors: Vec) -> TokenStream { - let tokens: Vec = errors.into_iter().map(|m| m.into_compile_error()).collect(); - - quote! { - { - { #( #tokens )* } - - ::stylist::ast::Sheet::from(Vec::new()) - } - } -} pub fn macro_fn(input: TokenStream) -> TokenStream { let root = match syn::parse2::(input) { @@ -30,9 +16,13 @@ pub fn macro_fn(input: TokenStream) -> TokenStream { debug!("Parsed as: {:?}", root); - let mut ctx = ContextRecorder::new(); - match root.into_output() { - Ok(m) => m.into_token_stream(&mut ctx), - Err(e) => error_to_token_stream(e), + let into_output_ctx = IntoOutputContext::new(); + let output_root = root.into_output(&mut into_output_ctx); + + if let Some(m) = into_output_ctx.into_compile_errors() { + m + } else { + let mut ctx = ContextRecorder::new(); + output_root.into_token_stream(&mut ctx) } } diff --git a/packages/stylist-macros/src/inline/parse/attribute.rs b/packages/stylist-macros/src/inline/parse/attribute.rs index 7bfa889..45c07b1 100644 --- a/packages/stylist-macros/src/inline/parse/attribute.rs +++ b/packages/stylist-macros/src/inline/parse/attribute.rs @@ -1,22 +1,17 @@ -use super::{ - super::{ - component_value::{ - ComponentValue, ComponentValueStream, InterpolatedExpression, PreservedToken, - }, - css_ident::CssIdent, - }, - fragment_spacing, -}; -use crate::{ - output::{OutputAttribute, OutputFragment}, - spacing_iterator::SpacedIterator, -}; use syn::{ parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, spanned::Spanned, token, }; +use super::{fragment_spacing, IntoOutputContext}; +use crate::inline::component_value::{ + ComponentValue, ComponentValueStream, InterpolatedExpression, PreservedToken, +}; +use crate::inline::css_ident::CssIdent; +use crate::output::{OutputAttribute, OutputFragment}; +use crate::spacing_iterator::SpacedIterator; + #[derive(Debug)] pub enum CssAttributeName { Identifier(CssIdent), @@ -101,10 +96,9 @@ impl ComponentValue { } impl CssAttribute { - pub(super) fn into_output(self) -> Result> { - if !self.value.errors.is_empty() { - return Err(self.value.errors); - } + pub(super) fn into_output(self, ctx: &mut IntoOutputContext) -> OutputAttribute { + ctx.extend_errors(self.value.errors); + let values = self .value .values @@ -112,11 +106,10 @@ impl CssAttribute { .flat_map(|p| p.to_output_fragments()) .spaced_with(fragment_spacing) .collect(); - Ok(OutputAttribute { + OutputAttribute { key: self.name.into_output(), values, - // errors: self.value.errors, - }) + } } } diff --git a/packages/stylist-macros/src/inline/parse/block.rs b/packages/stylist-macros/src/inline/parse/block.rs index 60b9139..a12c418 100644 --- a/packages/stylist-macros/src/inline/parse/block.rs +++ b/packages/stylist-macros/src/inline/parse/block.rs @@ -1,7 +1,8 @@ -use super::{CssAttribute, CssBlockQualifier, CssScope}; use crate::output::{OutputBlock, OutputBlockContent}; use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; +use super::{CssAttribute, CssBlockQualifier, CssScope, IntoOutputContext}; + #[derive(Debug)] pub struct CssQualifiedRule { pub qualifier: CssBlockQualifier, @@ -17,19 +18,9 @@ impl Parse for CssQualifiedRule { } impl CssQualifiedRule { - pub fn into_output(self) -> Result> { - let qualifier_result = self.qualifier.into_output(); - let scope_result = self.scope.into_block_output(); - - let (condition, content) = match (qualifier_result, scope_result) { - (Ok(m), Ok(n)) => (m, n), - (Err(mut e1), Err(e2)) => { - e1.extend(e2); - return Err(e1); - } - (Err(e), _) => return Err(e), - (_, Err(e)) => return Err(e), - }; + pub fn into_output(self, ctx: &mut IntoOutputContext) -> OutputBlock { + let condition = self.qualifier.into_output(&mut ctx); + let content = self.scope.into_block_output(&mut ctx); Ok(OutputBlock { condition, content }) } @@ -37,24 +28,17 @@ impl CssQualifiedRule { // Into Output for a dangling block pub fn into_dangling_output( attrs: &mut Vec, - ) -> Result> { - let mut errors = Vec::new(); + ctx: &mut IntoOutputContext, + ) -> OutputBlock { let mut output_attrs = Vec::new(); for attr in attrs.drain(0..) { - match attr.into_output() { - Ok(m) => output_attrs.push(OutputBlockContent::StyleAttr(m)), - Err(e) => errors.extend(e), - } + output_attrs.push(attr.into_output(&mut ctx)) } - if !errors.is_empty() { - Err(errors) - } else { - Ok(OutputBlock { - condition: Vec::new(), - content: output_attrs, - }) + OutputBlock { + condition: Vec::new(), + content: output_attrs, } } } diff --git a/packages/stylist-macros/src/inline/parse/mod.rs b/packages/stylist-macros/src/inline/parse/mod.rs index ead2bd1..23da33e 100644 --- a/packages/stylist-macros/src/inline/parse/mod.rs +++ b/packages/stylist-macros/src/inline/parse/mod.rs @@ -1,3 +1,5 @@ +use proc_macro2::TokenStream; + use crate::output::OutputFragment; mod attribute; @@ -16,6 +18,47 @@ pub use rule::CssAtRule; pub use scope::CssScope; pub use scope_content::CssScopeContent; +#[derive(Debug, Default)] +pub struct IntoOutputContext { + errors: Vec, +} + +impl IntoOutputContext { + pub fn new() -> Self { + Self::default() + } + + pub fn extend_errors(&mut self, errors: Vec) { + self.errors.extend(errors); + } + + pub fn push_error(&mut self, error: syn::parse::Error) { + self.errors.push(error); + } + + pub fn into_compile_errors(&mut self) -> Option { + use quote::quote; + + if self.errors.is_empty() { + None + } else { + let tokens: Vec = self + .errors + .into_iter() + .map(|m| m.into_compile_error()) + .collect(); + + Some(quote! { + { + { #( #tokens )* } + + ::stylist::ast::Sheet::from(Vec::new()) + } + }) + } + } +} + pub fn fragment_spacing(l: &OutputFragment, r: &OutputFragment) -> Option { use super::component_value::PreservedToken::*; use OutputFragment::*; diff --git a/packages/stylist-macros/src/inline/parse/qualifier.rs b/packages/stylist-macros/src/inline/parse/qualifier.rs index 3c02514..6e71a07 100644 --- a/packages/stylist-macros/src/inline/parse/qualifier.rs +++ b/packages/stylist-macros/src/inline/parse/qualifier.rs @@ -1,6 +1,6 @@ use super::{ super::component_value::{ComponentValue, ComponentValueStream, PreservedToken}, - fragment_spacing, + fragment_spacing, IntoOutputContext, }; use crate::{output::OutputSelector, spacing_iterator::SpacedIterator}; use itertools::Itertools; @@ -61,10 +61,8 @@ impl Default for CssBlockQualifier { } impl CssBlockQualifier { - pub fn into_output(self) -> Result, Vec> { - if !self.qualifier_errors.is_empty() { - return Err(self.qualifier_errors); - } + pub fn into_output(self, ctx: &mut IntoOutputContext) -> Vec { + ctx.extend_errors(self.qualifier_errors); fn is_not_comma(q: &ComponentValue) -> bool { !matches!(q, ComponentValue::Token(PreservedToken::Punct(ref p)) if p.as_char() == ',') @@ -92,6 +90,6 @@ impl CssBlockQualifier { }) .collect(); - Ok(selector_list) + selector_list } } diff --git a/packages/stylist-macros/src/inline/parse/root.rs b/packages/stylist-macros/src/inline/parse/root.rs index 4448249..b6b15b0 100644 --- a/packages/stylist-macros/src/inline/parse/root.rs +++ b/packages/stylist-macros/src/inline/parse/root.rs @@ -1,6 +1,6 @@ -use super::{CssAttribute, CssQualifiedRule, CssScopeContent}; +use super::{CssAttribute, CssQualifiedRule, CssScopeContent, IntoOutputContext}; use crate::output::{OutputScopeContent, OutputSheet}; -use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; +use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; #[derive(Debug)] pub struct CssRootNode { @@ -15,24 +15,20 @@ impl Parse for CssRootNode { } impl CssRootNode { - pub fn into_output(self) -> Result> { - let mut errors = Vec::new(); + pub fn into_output(self, ctx: &mut IntoOutputContext) -> OutputSheet { let mut contents = Vec::new(); let mut attrs: Vec = Vec::new(); let push_attrs_into_contents = - |attrs: &mut Vec, - contents: &mut Vec, - errors: &mut Vec| { + |attrs: &mut Vec, contents: &mut Vec| { if attrs.is_empty() { return; } - match CssQualifiedRule::into_dangling_output(attrs) { - Ok(m) => contents.push(OutputScopeContent::Block(m)), - Err(e) => errors.extend(e), - } + contents.push(OutputScopeContent::Block( + CssQualifiedRule::into_dangling_output(attrs, ctx), + )); }; for scope in self.contents { @@ -41,35 +37,21 @@ impl CssRootNode { attrs.push(m); } CssScopeContent::AtRule(m) => { - push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + push_attrs_into_contents(&mut attrs, &mut contents); - match m.into_rule_output() { - Ok(m) => { - contents.push(OutputScopeContent::Rule(m)); - } - Err(e) => errors.extend(e), - }; + contents.push(OutputScopeContent::Rule(m.into_rule_output(ctx))); } CssScopeContent::Nested(m) => { - push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + push_attrs_into_contents(&mut attrs, &mut contents); - match m.into_output() { - Ok(m) => { - contents.push(OutputScopeContent::Block(m)); - } - Err(e) => errors.extend(e), - }; + contents.push(OutputScopeContent::Block(m.into_output(ctx))); } } } - push_attrs_into_contents(&mut attrs, &mut contents, &mut errors); + push_attrs_into_contents(&mut attrs, &mut contents); - if !errors.is_empty() { - Err(errors) - } else { - Ok(OutputSheet { contents }) - } + OutputSheet { contents } } } diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index 6f9ebde..125a0c5 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -8,7 +8,7 @@ use super::{ component_value::{ComponentValue, ComponentValueStream}, css_ident::CssIdent, }, - fragment_spacing, CssScope, + fragment_spacing, CssScope, IntoOutputContext, }; use crate::{ output::{OutputFragment, OutputRule, OutputRuleBlock}, @@ -91,27 +91,27 @@ impl CssAtRule { prelude } - pub fn into_rule_output(self) -> Result> { - if !self.errors.is_empty() { - return Err(self.errors); - } + pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> OutputRule { + ctx.extend_errors(self.errors); - Ok(OutputRule { + OutputRule { condition: self.condition_output(), content: match self.contents { - CssAtRuleContent::Scope(m) => m.into_rule_output()?, + CssAtRuleContent::Scope(m) => m.into_rule_output(&mut ctx), CssAtRuleContent::Empty(_) => Vec::new(), }, - }) + } } - pub fn into_rule_block_output(self) -> Result> { - Ok(OutputRuleBlock { + pub fn into_rule_block_output(self, ctx: &mut IntoOutputContext) -> OutputRuleBlock { + ctx.extend_errors(self.errors); + + OutputRuleBlock { condition: self.condition_output(), content: match self.contents { - CssAtRuleContent::Scope(m) => m.into_rule_block_output()?, + CssAtRuleContent::Scope(m) => m.into_rule_block_output(&mut ctx), CssAtRuleContent::Empty(_) => Vec::new(), }, - }) + } } } diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index fcfd80a..ce77df2 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -1,10 +1,10 @@ -use super::CssScopeContent; use syn::{ braced, parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, token, }; +use super::{CssScopeContent, IntoOutputContext}; use crate::output::{ OutputAttribute, OutputBlock, OutputBlockContent, OutputRuleBlockContent, OutputRuleContent, }; @@ -25,9 +25,8 @@ impl Parse for CssScope { } impl CssScope { - pub fn into_rule_output(self) -> Result, Vec> { + pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> Vec { let mut attrs = Vec::new(); - let mut errors = Vec::new(); let mut contents = Vec::new(); @@ -48,54 +47,43 @@ impl CssScope { for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => match m.into_output() { - Ok(m) => attrs.push(m), - Err(e) => errors.extend(e), - }, + CssScopeContent::Attribute(m) => attrs.push(m.into_output(&mut ctx)), CssScopeContent::AtRule(m) => { collect_attrs_into_contents(&mut attrs, &mut contents); - - match m.into_rule_output() { - Ok(m) => contents.push(OutputRuleContent::Rule(m)), - Err(e) => errors.extend(e), - } + contents.push(OutputRuleContent::Rule(m.into_rule_output(&mut ctx))); } CssScopeContent::Nested(m) => { collect_attrs_into_contents(&mut attrs, &mut contents); - - match m.into_output() { - Ok(m) => contents.push(OutputRuleContent::Block(m)), - Err(e) => errors.extend(e), - } + contents.push(OutputRuleContent::Block(m.into_output(&mut ctx))); } } } collect_attrs_into_contents(&mut attrs, &mut contents); - if !errors.is_empty() { - Err(errors) - } else { - Ok(contents) - } + contents } - pub fn into_rule_block_output(self) -> Result, Vec> { - let mut errors = Vec::new(); + pub fn into_rule_block_output( + self, + ctx: &mut IntoOutputContext, + ) -> Vec { let mut contents = Vec::new(); for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => match m.into_output() { - Ok(m) => contents.push(OutputRuleBlockContent::StyleAttr(m)), - Err(e) => errors.extend(e), - }, - CssScopeContent::AtRule(m) => match m.into_rule_block_output() { - Ok(m) => contents.push(OutputRuleBlockContent::RuleBlock(Box::new(m))), - Err(e) => errors.extend(e), - }, + CssScopeContent::Attribute(m) => { + contents.push(OutputRuleBlockContent::StyleAttr(m.into_output(&mut ctx))) + } + + CssScopeContent::AtRule(m) => { + contents.push(OutputRuleBlockContent::RuleBlock(Box::new( + m.into_rule_block_output(&mut ctx), + ))); + } + CssScopeContent::Nested(m) => { - errors.push(ParseError::new_spanned( + ctx.push_error(ParseError::new_spanned( m.qualifier, "Can not nest qualified blocks (yet)", )); @@ -103,29 +91,22 @@ impl CssScope { } } - if !errors.is_empty() { - Err(errors) - } else { - Ok(contents) - } + contents } - pub fn into_block_output(self) -> Result, Vec> { - let mut errors = Vec::new(); + pub fn into_block_output(self, ctx: &mut IntoOutputContext) -> Vec { let mut contents = Vec::new(); for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => match m.into_output() { - Ok(m) => contents.push(OutputBlockContent::StyleAttr(m)), - Err(e) => errors.extend(e), - }, - CssScopeContent::AtRule(m) => match m.into_rule_block_output() { - Ok(m) => contents.push(OutputBlockContent::RuleBlock(m)), - Err(e) => errors.extend(e), - }, + CssScopeContent::Attribute(m) => { + contents.push(OutputBlockContent::StyleAttr(m.into_output(&mut ctx))) + } + CssScopeContent::AtRule(m) => contents.push(OutputBlockContent::RuleBlock( + m.into_rule_block_output(&mut ctx), + )), CssScopeContent::Nested(m) => { - errors.push(ParseError::new_spanned( + ctx.push_error(ParseError::new_spanned( m.qualifier, "Can not nest qualified blocks (yet)", )); @@ -133,10 +114,6 @@ impl CssScope { } } - if !errors.is_empty() { - Err(errors) - } else { - Ok(contents) - } + contents } } From 221dbd9b8b0f7e7f215f5f0d913d3794f8b91a24 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 12:26:40 +0900 Subject: [PATCH 09/27] Fix implementation. --- packages/stylist-macros/src/inline/mod.rs | 2 +- .../stylist-macros/src/inline/parse/block.rs | 14 ++++---- .../stylist-macros/src/inline/parse/mod.rs | 7 ++-- .../src/inline/parse/qualifier.rs | 8 ++--- .../stylist-macros/src/inline/parse/root.rs | 28 ++++++--------- .../stylist-macros/src/inline/parse/rule.rs | 12 +++---- .../stylist-macros/src/inline/parse/scope.rs | 36 +++++++++---------- 7 files changed, 50 insertions(+), 57 deletions(-) diff --git a/packages/stylist-macros/src/inline/mod.rs b/packages/stylist-macros/src/inline/mod.rs index a191281..cbce3e4 100644 --- a/packages/stylist-macros/src/inline/mod.rs +++ b/packages/stylist-macros/src/inline/mod.rs @@ -16,7 +16,7 @@ pub fn macro_fn(input: TokenStream) -> TokenStream { debug!("Parsed as: {:?}", root); - let into_output_ctx = IntoOutputContext::new(); + let mut into_output_ctx = IntoOutputContext::new(); let output_root = root.into_output(&mut into_output_ctx); if let Some(m) = into_output_ctx.into_compile_errors() { diff --git a/packages/stylist-macros/src/inline/parse/block.rs b/packages/stylist-macros/src/inline/parse/block.rs index a12c418..2e0d8e3 100644 --- a/packages/stylist-macros/src/inline/parse/block.rs +++ b/packages/stylist-macros/src/inline/parse/block.rs @@ -1,5 +1,5 @@ use crate::output::{OutputBlock, OutputBlockContent}; -use syn::parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}; +use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; use super::{CssAttribute, CssBlockQualifier, CssScope, IntoOutputContext}; @@ -19,21 +19,21 @@ impl Parse for CssQualifiedRule { impl CssQualifiedRule { pub fn into_output(self, ctx: &mut IntoOutputContext) -> OutputBlock { - let condition = self.qualifier.into_output(&mut ctx); - let content = self.scope.into_block_output(&mut ctx); + let condition = self.qualifier.into_output(ctx); + let content = self.scope.into_block_output(ctx); - Ok(OutputBlock { condition, content }) + OutputBlock { condition, content } } // Into Output for a dangling block pub fn into_dangling_output( - attrs: &mut Vec, + attrs: Vec, ctx: &mut IntoOutputContext, ) -> OutputBlock { let mut output_attrs = Vec::new(); - for attr in attrs.drain(0..) { - output_attrs.push(attr.into_output(&mut ctx)) + for attr in attrs { + output_attrs.push(OutputBlockContent::StyleAttr(attr.into_output(ctx))) } OutputBlock { diff --git a/packages/stylist-macros/src/inline/parse/mod.rs b/packages/stylist-macros/src/inline/parse/mod.rs index 23da33e..6848ef8 100644 --- a/packages/stylist-macros/src/inline/parse/mod.rs +++ b/packages/stylist-macros/src/inline/parse/mod.rs @@ -28,7 +28,10 @@ impl IntoOutputContext { Self::default() } - pub fn extend_errors(&mut self, errors: Vec) { + pub fn extend_errors(&mut self, errors: I) + where + I: IntoIterator, + { self.errors.extend(errors); } @@ -36,7 +39,7 @@ impl IntoOutputContext { self.errors.push(error); } - pub fn into_compile_errors(&mut self) -> Option { + pub fn into_compile_errors(self) -> Option { use quote::quote; if self.errors.is_empty() { diff --git a/packages/stylist-macros/src/inline/parse/qualifier.rs b/packages/stylist-macros/src/inline/parse/qualifier.rs index 6e71a07..5fe5cf1 100644 --- a/packages/stylist-macros/src/inline/parse/qualifier.rs +++ b/packages/stylist-macros/src/inline/parse/qualifier.rs @@ -67,8 +67,8 @@ impl CssBlockQualifier { fn is_not_comma(q: &ComponentValue) -> bool { !matches!(q, ComponentValue::Token(PreservedToken::Punct(ref p)) if p.as_char() == ',') } - let selector_list = self - .qualifiers + + self.qualifiers .into_iter() .peekable() .batching(|it| { @@ -88,8 +88,6 @@ impl CssBlockQualifier { it.next(); // Consume the comma Some(selector) }) - .collect(); - - selector_list + .collect() } } diff --git a/packages/stylist-macros/src/inline/parse/root.rs b/packages/stylist-macros/src/inline/parse/root.rs index b6b15b0..ec1698c 100644 --- a/packages/stylist-macros/src/inline/parse/root.rs +++ b/packages/stylist-macros/src/inline/parse/root.rs @@ -20,38 +20,32 @@ impl CssRootNode { let mut attrs: Vec = Vec::new(); - let push_attrs_into_contents = - |attrs: &mut Vec, contents: &mut Vec| { - if attrs.is_empty() { - return; - } - + let collect_attrs = |attrs: &mut Vec, + contents: &mut Vec, + ctx: &mut IntoOutputContext| { + if !attrs.is_empty() { contents.push(OutputScopeContent::Block( - CssQualifiedRule::into_dangling_output(attrs, ctx), + CssQualifiedRule::into_dangling_output(attrs.drain(0..).collect(), ctx), )); - }; + } + }; for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => { - attrs.push(m); - } + CssScopeContent::Attribute(m) => attrs.push(m), CssScopeContent::AtRule(m) => { - push_attrs_into_contents(&mut attrs, &mut contents); - + collect_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputScopeContent::Rule(m.into_rule_output(ctx))); } CssScopeContent::Nested(m) => { - push_attrs_into_contents(&mut attrs, &mut contents); - + collect_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputScopeContent::Block(m.into_output(ctx))); } } } - push_attrs_into_contents(&mut attrs, &mut contents); - + collect_attrs(&mut attrs, &mut contents, ctx); OutputSheet { contents } } } diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index 125a0c5..82da8ee 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -91,25 +91,25 @@ impl CssAtRule { prelude } - pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> OutputRule { - ctx.extend_errors(self.errors); + pub fn into_rule_output(mut self, ctx: &mut IntoOutputContext) -> OutputRule { + ctx.extend_errors(self.errors.drain(0..)); OutputRule { condition: self.condition_output(), content: match self.contents { - CssAtRuleContent::Scope(m) => m.into_rule_output(&mut ctx), + CssAtRuleContent::Scope(m) => m.into_rule_output(ctx), CssAtRuleContent::Empty(_) => Vec::new(), }, } } - pub fn into_rule_block_output(self, ctx: &mut IntoOutputContext) -> OutputRuleBlock { - ctx.extend_errors(self.errors); + pub fn into_rule_block_output(mut self, ctx: &mut IntoOutputContext) -> OutputRuleBlock { + ctx.extend_errors(self.errors.drain(0..)); OutputRuleBlock { condition: self.condition_output(), content: match self.contents { - CssAtRuleContent::Scope(m) => m.into_rule_block_output(&mut ctx), + CssAtRuleContent::Scope(m) => m.into_rule_block_output(ctx), CssAtRuleContent::Empty(_) => Vec::new(), }, } diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index ce77df2..8c14943 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -30,12 +30,9 @@ impl CssScope { let mut contents = Vec::new(); - let collect_attrs_into_contents = - |attrs: &mut Vec, contents: &mut Vec| { - if attrs.is_empty() { - return; - } - + let collect_attrs = |attrs: &mut Vec, + contents: &mut Vec| { + if !attrs.is_empty() { contents.push(OutputRuleContent::Block(OutputBlock { condition: Vec::new(), content: attrs @@ -43,23 +40,24 @@ impl CssScope { .map(OutputBlockContent::StyleAttr) .collect(), })); - }; + } + }; for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => attrs.push(m.into_output(&mut ctx)), + CssScopeContent::Attribute(m) => attrs.push(m.into_output(ctx)), CssScopeContent::AtRule(m) => { - collect_attrs_into_contents(&mut attrs, &mut contents); - contents.push(OutputRuleContent::Rule(m.into_rule_output(&mut ctx))); + collect_attrs(&mut attrs, &mut contents); + contents.push(OutputRuleContent::Rule(m.into_rule_output(ctx))); } CssScopeContent::Nested(m) => { - collect_attrs_into_contents(&mut attrs, &mut contents); - contents.push(OutputRuleContent::Block(m.into_output(&mut ctx))); + collect_attrs(&mut attrs, &mut contents); + contents.push(OutputRuleContent::Block(m.into_output(ctx))); } } } - collect_attrs_into_contents(&mut attrs, &mut contents); + collect_attrs(&mut attrs, &mut contents); contents } @@ -73,12 +71,12 @@ impl CssScope { for scope in self.contents { match scope { CssScopeContent::Attribute(m) => { - contents.push(OutputRuleBlockContent::StyleAttr(m.into_output(&mut ctx))) + contents.push(OutputRuleBlockContent::StyleAttr(m.into_output(ctx))) } CssScopeContent::AtRule(m) => { contents.push(OutputRuleBlockContent::RuleBlock(Box::new( - m.into_rule_block_output(&mut ctx), + m.into_rule_block_output(ctx), ))); } @@ -100,11 +98,11 @@ impl CssScope { for scope in self.contents { match scope { CssScopeContent::Attribute(m) => { - contents.push(OutputBlockContent::StyleAttr(m.into_output(&mut ctx))) + contents.push(OutputBlockContent::StyleAttr(m.into_output(ctx))) + } + CssScopeContent::AtRule(m) => { + contents.push(OutputBlockContent::RuleBlock(m.into_rule_block_output(ctx))) } - CssScopeContent::AtRule(m) => contents.push(OutputBlockContent::RuleBlock( - m.into_rule_block_output(&mut ctx), - )), CssScopeContent::Nested(m) => { ctx.push_error(ParseError::new_spanned( m.qualifier, From e4e64d9cc97245ed808c2c55b8d544654b9964de Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 12:53:47 +0900 Subject: [PATCH 10/27] Derive default --- .../src/inline/parse/qualifier.rs | 24 +++++-------------- .../stylist-macros/src/inline/parse/scope.rs | 1 - 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/stylist-macros/src/inline/parse/qualifier.rs b/packages/stylist-macros/src/inline/parse/qualifier.rs index 5fe5cf1..f599ede 100644 --- a/packages/stylist-macros/src/inline/parse/qualifier.rs +++ b/packages/stylist-macros/src/inline/parse/qualifier.rs @@ -11,17 +11,17 @@ use syn::{ token, }; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct CssBlockQualifier { qualifiers: Vec, - qualifier_errors: Vec, + errors: Vec, } impl Parse for CssBlockQualifier { fn parse(input: &ParseBuffer) -> ParseResult { let mut component_iter = ComponentValueStream::from(input); let mut qualifiers = vec![]; - let mut qualifier_errors = vec![]; + let mut errors = vec![]; loop { // Consume all tokens till the next '{'-block if input.peek(token::Brace) { @@ -34,12 +34,9 @@ impl Parse for CssBlockQualifier { if token_errors.is_empty() { qualifiers.push(next_token); } - qualifier_errors.extend(token_errors); + errors.extend(token_errors); } - Ok(Self { - qualifiers, - qualifier_errors, - }) + Ok(Self { qualifiers, errors }) } } @@ -51,18 +48,9 @@ impl ToTokens for CssBlockQualifier { } } -impl Default for CssBlockQualifier { - fn default() -> Self { - Self { - qualifiers: vec![], - qualifier_errors: vec![], - } - } -} - impl CssBlockQualifier { pub fn into_output(self, ctx: &mut IntoOutputContext) -> Vec { - ctx.extend_errors(self.qualifier_errors); + ctx.extend_errors(self.errors); fn is_not_comma(q: &ComponentValue) -> bool { !matches!(q, ComponentValue::Token(PreservedToken::Punct(ref p)) if p.as_char() == ',') diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index 8c14943..38a666a 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -27,7 +27,6 @@ impl Parse for CssScope { impl CssScope { pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> Vec { let mut attrs = Vec::new(); - let mut contents = Vec::new(); let collect_attrs = |attrs: &mut Vec, From 52d65f82bb56e89e1505e007b2264500a85a1ef9 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 10 Sep 2021 18:31:37 +0900 Subject: [PATCH 11/27] Reduce code size. --- packages/stylist-core/src/ast/block.rs | 33 ++++------ packages/stylist-core/src/ast/context.rs | 51 ++++++--------- packages/stylist-core/src/ast/mod.rs | 4 +- packages/stylist-core/src/ast/rule.rs | 27 ++++---- packages/stylist-core/src/ast/rule_block.rs | 22 +++---- .../stylist-core/src/ast/scope_content.rs | 11 +--- packages/stylist-core/src/ast/selector.rs | 65 ++++++++----------- packages/stylist-core/src/ast/sheet.rs | 9 +-- packages/stylist-core/src/ast/str_frag.rs | 8 +-- packages/stylist-core/src/ast/style_attr.rs | 17 ++--- packages/stylist-core/src/ast/to_style_str.rs | 10 ++- packages/stylist-core/src/error.rs | 6 -- packages/stylist/src/global_style.rs | 2 +- packages/stylist/src/style.rs | 2 +- packages/stylist/tests/macro_css_tests.rs | 2 +- 15 files changed, 102 insertions(+), 167 deletions(-) diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 06a5a74..87613bc 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -1,9 +1,6 @@ use std::borrow::Cow; -use std::fmt; -use std::fmt::Write; use super::{RuleBlock, Selector, StyleAttribute, StyleContext, ToStyleStr}; -use crate::Result; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum BlockContent { @@ -12,13 +9,11 @@ pub enum BlockContent { } impl ToStyleStr for BlockContent { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { - Self::StyleAttr(ref m) => m.write_style(w, ctx)?, - Self::RuleBlock(ref m) => m.write_style(w, ctx)?, + Self::StyleAttr(ref m) => m.write_style(w, ctx), + Self::RuleBlock(ref m) => m.write_style(w, ctx), } - - Ok(()) } } @@ -48,31 +43,31 @@ pub struct Block { } impl Block { - fn cond_str(&self, ctx: &mut StyleContext<'_>) -> Result> { + fn cond_str(&self, ctx: &mut StyleContext<'_>) -> Option { if self.condition.is_empty() { - return Ok(None); + return None; } let mut cond = "".to_string(); for (index, sel) in self.condition.iter().enumerate() { - sel.write_style(&mut cond, ctx)?; + sel.write_style(&mut cond, ctx); if index < self.condition.len() - 1 { - write!(&mut cond, ", ")?; + cond.push_str(", "); } } - Ok(Some(cond)) + Some(cond) } } impl ToStyleStr for Block { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { // Close last clause. - ctx.write_finishing_clause(w)?; + ctx.write_finishing_clause(w); // TODO: nested block, which is not supported at the moment. - let cond_s = self.cond_str(ctx)?; + let cond_s = self.cond_str(ctx); let mut final_ctx = cond_s .as_ref() @@ -80,11 +75,9 @@ impl ToStyleStr for Block { .unwrap_or_else(|| ctx.to_block_context()); for attr in self.content.iter() { - attr.write_style(w, &mut final_ctx)?; + attr.write_style(w, &mut final_ctx); } - final_ctx.write_finishing_clause(w)?; - - Ok(()) + final_ctx.write_finishing_clause(w); } } diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 12023b0..cde97d5 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,7 +1,4 @@ use std::borrow::Cow; -use std::fmt; - -use crate::Result; // #[derive(Debug)] // pub enum StyleKind { @@ -26,6 +23,8 @@ pub struct StyleContext<'a> { state: ContextState, } +static IDENT: &str = " "; + impl<'a> StyleContext<'a> { pub fn new(class_name: Option<&'a str>) -> Self { Self { @@ -43,7 +42,7 @@ impl<'a> StyleContext<'a> { self_ } - pub fn to_block_context(&'a self) -> Self { + pub fn to_block_context(&self) -> Self { // No selectors if self .parent_conditions() @@ -62,61 +61,53 @@ impl<'a> StyleContext<'a> { } pub fn parent_conditions(&self) -> Vec> { + let (mut rules, mut selectors) = (Vec::new(), Vec::new()); + // @ rules first, then selectors. // Equivalent to the following line, but would result in a smaller bundle // sorted_parents.sort_by_cached_key(|m| !m.starts_with('@')); - let (mut rules, mut selectors) = self.parent_conditions.clone().into_iter().fold( - (Vec::new(), Vec::new()), - |(mut rules, mut selectors), item| { - if item.starts_with('@') { - rules.push(item); - } else { - selectors.push(item); - } - - (rules, selectors) - }, - ); + for item in self.parent_conditions.clone() { + if item.starts_with('@') { + rules.push(item); + } else { + selectors.push(item); + } + } rules.append(&mut selectors); rules } - pub fn write_starting_clause(&mut self, w: &mut W) -> Result<()> { + pub fn write_starting_clause(&mut self, w: &mut String) { if self.state == ContextState::Closed { for (index, cond) in self.parent_conditions().iter().enumerate() { for _i in 0..index { - write!(w, " ")?; + w.push_str(IDENT); } - writeln!(w, "{} {{", cond)?; + w.push_str(cond); + w.push_str(" {\n"); } self.state = ContextState::Open; } - - Ok(()) } - pub fn write_finishing_clause(&mut self, w: &mut W) -> Result<()> { + pub fn write_finishing_clause(&mut self, w: &mut String) { if self.state == ContextState::Open { for i in (0..self.parent_conditions.len()).rev() { for _i in 0..i { - write!(w, " ")?; + w.push_str(IDENT); } - writeln!(w, "}}")?; + w.push_str("}\n"); } self.state = ContextState::Closed; } - - Ok(()) } - pub fn write_padding(&self, w: &mut W) -> Result<()> { + pub fn write_padding(&self, w: &mut String) { for _ in 0..self.parent_conditions.len() { - write!(w, " ")?; + w.push_str(IDENT); } - - Ok(()) } } diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index f254a02..44e25e1 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -66,7 +66,7 @@ width: 200px; }), ]); assert_eq!( - test_block.to_style_str(Some("test"))?, + test_block.to_style_str(Some("test")), r#".test { width: 100vw; } @@ -128,7 +128,7 @@ width: 200px; .into(), })]); assert_eq!( - test_block.to_style_str(Some("test"))?, + test_block.to_style_str(Some("test")), r#"@media only screen and (min-width: 1000px) { .test { width: 100vw; diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index 789bbbc..92f073b 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; -use std::fmt; use super::{Block, ScopeContent, StringFragment, StyleContext, ToStyleStr}; -use crate::Result; /// Everything that can be inside a rule. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -27,17 +25,16 @@ impl From for RuleContent { } impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { - RuleContent::Block(ref b) => b.write_style(w, ctx)?, - RuleContent::Rule(ref r) => r.write_style(w, ctx)?, + RuleContent::Block(ref b) => b.write_style(w, ctx), + RuleContent::Rule(ref r) => r.write_style(w, ctx), RuleContent::String(ref s) => { - ctx.write_starting_clause(w)?; - writeln!(w, "{}", s)?; + ctx.write_starting_clause(w); + w.push_str(s); + w.push('\n'); } } - - Ok(()) } } @@ -81,22 +78,20 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { - ctx.write_finishing_clause(w)?; + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + ctx.write_finishing_clause(w); let mut cond = "".to_string(); for frag in self.condition.iter() { - frag.write_style(&mut cond, ctx)?; + frag.write_style(&mut cond, ctx); } let mut rule_ctx = ctx.clone().with_condition(&cond); for i in self.content.iter() { - i.write_style(w, &mut rule_ctx)?; + i.write_style(w, &mut rule_ctx); } - rule_ctx.write_finishing_clause(w)?; - - Ok(()) + rule_ctx.write_finishing_clause(w); } } diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs index ab59c43..2eeca0e 100644 --- a/packages/stylist-core/src/ast/rule_block.rs +++ b/packages/stylist-core/src/ast/rule_block.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; -use std::fmt; use super::{StringFragment, StyleAttribute, StyleContext, ToStyleStr}; -use crate::Result; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum RuleBlockContent { @@ -11,13 +9,11 @@ pub enum RuleBlockContent { } impl ToStyleStr for RuleBlockContent { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { - Self::StyleAttr(ref b) => b.write_style(w, ctx)?, - Self::RuleBlock(ref r) => r.write_style(w, ctx)?, + Self::StyleAttr(ref b) => b.write_style(w, ctx), + Self::RuleBlock(ref r) => r.write_style(w, ctx), } - - Ok(()) } } @@ -42,22 +38,20 @@ pub struct RuleBlock { } impl ToStyleStr for RuleBlock { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { // Finish any previous blocks - ctx.write_finishing_clause(w)?; + ctx.write_finishing_clause(w); let mut cond = "".to_string(); for frag in self.condition.iter() { - frag.write_style(&mut cond, ctx)?; + frag.write_style(&mut cond, ctx); } let mut rule_ctx = ctx.with_condition(&cond); for i in self.content.iter() { - i.write_style(w, &mut rule_ctx)?; + i.write_style(w, &mut rule_ctx); } - rule_ctx.write_finishing_clause(w)?; - - Ok(()) + rule_ctx.write_finishing_clause(w); } } diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 462007e..9830a6f 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -1,7 +1,4 @@ -use std::fmt; - use super::{Block, Rule, StyleContext, ToStyleStr}; -use crate::Result; /// A scope represents a media query or all content not in a media query. /// The CSS-Syntax-Level-3 standard calls all of these rules, which is used @@ -32,12 +29,10 @@ pub enum ScopeContent { } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { - ScopeContent::Block(ref b) => b.write_style(w, ctx)?, - ScopeContent::Rule(ref r) => r.write_style(w, ctx)?, + ScopeContent::Block(ref b) => b.write_style(w, ctx), + ScopeContent::Rule(ref r) => r.write_style(w, ctx), } - - Ok(()) } } diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 40c749e..9486a3b 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -1,7 +1,6 @@ -use std::{borrow::Cow, fmt}; +use std::borrow::Cow; use super::{StringFragment, StyleContext, ToStyleStr}; -use crate::Result; /// A CSS Selector. /// @@ -15,44 +14,40 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { let mut joined_s = "".to_string(); for frag in self.fragments.iter() { - frag.write_style(&mut joined_s, ctx)?; + frag.write_style(&mut joined_s, ctx); } if let Some(ref m) = ctx.class_name { + let scoped_class = format!(".{}", m); // If contains current selector or root pseudo class, replace them with class name. if joined_s.contains('&') || joined_s.contains(":root") { - let scoped_class = format!(".{}", m); - - write!( - w, - "{}", - joined_s + w.push_str( + &joined_s .replace("&", scoped_class.as_str()) - .replace(":root", scoped_class.as_str()) - )?; - - // If selector starts with a pseudo-class, apply it to the root element. - } else if joined_s.starts_with(':') { - write!(w, ".{}{}", m, joined_s)?; - - // For other selectors, scope it to be the children of the root element. + .replace(":root", scoped_class.as_str()), + ); } else { - write!(w, ".{} {}", m, joined_s)?; + w.push_str(&scoped_class); + + // If selector starts with a pseudo-class, apply it to the root element. + // For other selectors, scope it to be the children of the root element. + if !joined_s.starts_with(':') { + w.push(' '); + } + w.push_str(&joined_s); } // For global styles, if it contains &, it will be replaced with html. } else if joined_s.contains('&') { - write!(w, "{}", joined_s.replace("&", "html"))?; + w.push_str(&joined_s.replace("&", "html")); // For other styles, it will be written as is. } else { - write!(w, "{}", joined_s)?; + w.push_str(&joined_s); } - - Ok(()) } } @@ -69,50 +64,42 @@ mod tests { use super::*; #[test] - fn test_selector_gen_simple() -> Result<()> { + fn test_selector_gen_simple() { let s: Selector = vec![".abc".into()].into(); assert_eq!( - s.to_style_str(Some("stylist-abcdefgh"))?, + s.to_style_str(Some("stylist-abcdefgh")), ".stylist-abcdefgh .abc" ); - - Ok(()) } #[test] - fn test_selector_pseduo() -> Result<()> { + fn test_selector_pseduo() { let s: Selector = vec![":hover".into()].into(); assert_eq!( - s.to_style_str(Some("stylist-abcdefgh"))?, + s.to_style_str(Some("stylist-abcdefgh")), ".stylist-abcdefgh:hover" ); - - Ok(()) } #[test] - fn test_selector_root_pseduo() -> Result<()> { + fn test_selector_root_pseduo() { let s: Selector = vec![":root.big".into()].into(); assert_eq!( - s.to_style_str(Some("stylist-abcdefgh"))?, + s.to_style_str(Some("stylist-abcdefgh")), ".stylist-abcdefgh.big" ); - - Ok(()) } #[test] - fn test_selector_gen_current() -> Result<()> { + fn test_selector_gen_current() { let s: Selector = vec!["&.big".into()].into(); assert_eq!( - s.to_style_str(Some("stylist-abcdefgh"))?, + s.to_style_str(Some("stylist-abcdefgh")), ".stylist-abcdefgh.big" ); - - Ok(()) } } diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index 4d71238..f1c6c1a 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -1,12 +1,9 @@ use std::borrow::Cow; -use std::fmt; use std::ops::Deref; use std::sync::Arc; use super::{ScopeContent, StyleContext, ToStyleStr}; -use crate::Result; - /// The top node of a style string. // Once a sheet is constructed, it becomes immutable. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -51,12 +48,10 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { for scope in self.0.iter() { - scope.write_style(w, ctx)?; + scope.write_style(w, ctx); } - - Ok(()) } } diff --git a/packages/stylist-core/src/ast/str_frag.rs b/packages/stylist-core/src/ast/str_frag.rs index bbee857..a6af079 100644 --- a/packages/stylist-core/src/ast/str_frag.rs +++ b/packages/stylist-core/src/ast/str_frag.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; -use std::fmt; use super::{StyleContext, ToStyleStr}; -use crate::Result; /// A String Fragment #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -11,10 +9,8 @@ pub struct StringFragment { } impl ToStyleStr for StringFragment { - fn write_style(&self, w: &mut W, _ctx: &mut StyleContext<'_>) -> Result<()> { - write!(w, "{}", self.inner)?; - - Ok(()) + fn write_style(&self, w: &mut String, _ctx: &mut StyleContext<'_>) { + w.push_str(&self.inner); } } diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index d7e9011..206d8ab 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; -use std::fmt; use super::{StringFragment, StyleContext, ToStyleStr}; -use crate::Result; /// A simple CSS property in the form of a key value pair. Mirrors what would /// be called a "Declaration" in the CSS standard. @@ -15,19 +13,18 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()> { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { // Always write starting clause. - ctx.write_starting_clause(w)?; - ctx.write_padding(w)?; + ctx.write_starting_clause(w); + ctx.write_padding(w); - write!(w, "{}: ", self.key)?; + w.push_str(&self.key); + w.push_str(": "); for i in self.value.iter() { - i.write_style(w, ctx)?; + i.write_style(w, ctx); } - writeln!(w, ";")?; - - Ok(()) + w.push_str(";\n"); } } diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index e79ddef..b87edd8 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -1,20 +1,18 @@ use super::StyleContext; -use crate::Result; -use std::fmt; /// Structs implementing this trait should be able to turn into /// a part of a CSS style sheet. pub trait ToStyleStr { - fn to_style_str(&self, class_name: Option<&str>) -> Result { + fn to_style_str(&self, class_name: Option<&str>) -> String { let mut s = String::new(); let mut ctx = StyleContext::new(class_name); - self.write_style(&mut s, &mut ctx)?; + self.write_style(&mut s, &mut ctx); - Ok(s) + s } // If None is passed as class_name, it means to write a global style. - fn write_style(&self, w: &mut W, ctx: &mut StyleContext<'_>) -> Result<()>; + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>); } diff --git a/packages/stylist-core/src/error.rs b/packages/stylist-core/src/error.rs index 21f700c..5f27b05 100644 --- a/packages/stylist-core/src/error.rs +++ b/packages/stylist-core/src/error.rs @@ -1,5 +1,3 @@ -use std::fmt; - use thiserror::Error; #[derive(Debug, Error, PartialEq)] @@ -19,10 +17,6 @@ pub enum Error { /// This is usually raised when the style element failed to mount. #[error("Failed to Interact with Web API. Are you running in Browser?")] Web(Option), - - /// Format error when writing Styles. - #[error("Failed to write style!")] - Fmt(#[from] fmt::Error), } pub type Result = std::result::Result; diff --git a/packages/stylist/src/global_style.rs b/packages/stylist/src/global_style.rs index 52aef6b..2b3fca5 100644 --- a/packages/stylist/src/global_style.rs +++ b/packages/stylist/src/global_style.rs @@ -46,7 +46,7 @@ impl GlobalStyle { return Ok(Self { inner: m }); } - let style_str = key.ast.to_style_str(None)?; + let style_str = key.ast.to_style_str(None); // We parse the style str again in debug mode to ensure that interpolated values are // not corrupting the stylesheet. diff --git a/packages/stylist/src/style.rs b/packages/stylist/src/style.rs index 7252361..d2a0c92 100644 --- a/packages/stylist/src/style.rs +++ b/packages/stylist/src/style.rs @@ -176,7 +176,7 @@ impl Style { let id = StyleId(format!("{}-{}", key.prefix, get_entropy())); - let style_str = key.ast.to_style_str(Some(&id))?; + let style_str = key.ast.to_style_str(Some(&id)); // We parse the style str again in debug mode to ensure that interpolated values are // not corrupting the stylesheet. diff --git a/packages/stylist/tests/macro_css_tests.rs b/packages/stylist/tests/macro_css_tests.rs index c1755c0..daec733 100644 --- a/packages/stylist/tests/macro_css_tests.rs +++ b/packages/stylist/tests/macro_css_tests.rs @@ -44,5 +44,5 @@ fn test_sheet_interpolation() { "#, cls = "stylist-testtest" ); - assert_eq!(sheet.to_style_str(Some("stylist-testtest")), Ok(expected)); + assert_eq!(sheet.to_style_str(Some("stylist-testtest")), expected); } From c5afdbb2b80cd2078b14698c7b02969752700df0 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 09:52:34 +0900 Subject: [PATCH 12/27] Add Parser for Rule Block. --- packages/stylist-core/src/parser.rs | 247 ++++++++++++++++++++-------- packages/stylist/src/style.rs | 13 ++ 2 files changed, 194 insertions(+), 66 deletions(-) diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 94ae1fb..007fb9c 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -6,14 +6,14 @@ use nom::{ character::complete::{alpha1, alphanumeric1, anychar, char, none_of}, combinator::{map, map_res, not, opt, recognize}, error::{context, convert_error, ErrorKind, ParseError, VerboseError}, - multi::{many0, many1, separated_list0}, + multi::{many0, many1, separated_list1}, sequence::{delimited, pair, preceded, separated_pair, terminated}, IResult, }; use crate::ast::{ - Block, BlockContent, Rule, RuleContent, ScopeContent, Selector, Sheet, StringFragment, - StyleAttribute, + Block, BlockContent, Rule, RuleBlock, RuleBlockContent, RuleContent, ScopeContent, Selector, + Sheet, StringFragment, StyleAttribute, }; use crate::{Error, Result}; @@ -177,39 +177,6 @@ impl Parser { result } - /// Parse a style attribute such as "width: 10px;" - fn dangling_attribute(i: &str) -> IResult<&str, StyleAttribute, VerboseError<&str>> { - #[cfg(test)] - trace!("Dangling Attribute: {}", i); - - Self::expect_non_empty(i)?; - - let result = context( - "StyleAttribute", - Self::trimmed(map( - separated_pair( - // Key - Self::style_attr_key, - // Separator - tag(":"), - // Value - terminated(Self::style_attr_value, tag(";")), - ), - move |p: (&str, StringFragment)| -> StyleAttribute { - StyleAttribute { - key: p.0.trim().to_string().into(), - value: vec![p.1].into(), - } - }, - )), - )(i); - - #[cfg(test)] - trace!("Dangling Attribute: {:#?}", result); - - result - } - /// Parse a style attribute such as "width: 10px" fn attribute(i: &str) -> IResult<&str, StyleAttribute, VerboseError<&str>> { #[cfg(test)] @@ -240,35 +207,28 @@ impl Parser { result } - /// Parse attributes outside of a { ... }. - fn dangling_attributes(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - #[cfg(test)] - trace!("Dangling Attributes: {}", i); - - Self::expect_non_empty(i)?; - - let result = context( - "StyleAttributes", - Self::trimmed(many1(Self::dangling_attribute)), - )(i); - - #[cfg(test)] - trace!("Dangling Attributes: {:#?}", result); - - result - } - - fn attributes(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + fn attributes( + i: &str, + dangling: bool, + ) -> IResult<&str, Vec, VerboseError<&str>> { #[cfg(test)] trace!("Attributes: {}", i); Self::expect_non_empty(i)?; + let final_semicolon = |i| { + if dangling { + map(tag(";"), |m| Some(m))(i) + } else { + opt(tag(";"))(i) + } + }; + let result = context( "StyleAttributes", Self::trimmed(terminated( - separated_list0(tag(";"), Self::attribute), - preceded(opt(Self::sp), opt(tag(";"))), + separated_list1(tag(";"), Self::attribute), + preceded(opt(Self::sp), final_semicolon), )), )(i); @@ -374,13 +334,32 @@ impl Parser { result } - fn block_content(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - context( - "BlockContent", - map(Self::attributes, |m: Vec| { - m.into_iter().map(|m| m.into()).collect() - }), - )(i) + fn block_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + #[cfg(test)] + trace!("Block Contents: {}", i); + + Self::expect_non_empty(i)?; + + let result = context( + "BlockContents", + Self::trimmed(map( + many0(alt(( + // Either Style Attributes + map( + |i| Parser::attributes(i, false), + |m| m.into_iter().map(BlockContent::StyleAttr).collect(), + ), + // Or an at rule + map(Parser::rule_block, |m| vec![BlockContent::RuleBlock(m)]), + ))), + |m: Vec>| m.into_iter().flatten().collect(), + )), + )(i); + + #[cfg(test)] + trace!("Block Contents: {:#?}", result); + + result } /// Parse a [`Block`]. @@ -396,7 +375,7 @@ impl Parser { separated_pair( Self::condition, tag("{"), - terminated(Self::trim_cmt(Self::block_content), tag("}")), + terminated(Self::trim_cmt(Self::block_contents), tag("}")), ), |p: (Vec, Vec)| { ScopeContent::Block(Block { @@ -413,6 +392,69 @@ impl Parser { result } + fn rule_block_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + #[cfg(test)] + trace!("Rule Block Contents: {}", i); + + Self::expect_non_empty(i)?; + + let result = map( + context( + "RuleBlockContents", + many0(alt(( + // Either Style Attributes + map( + |i| Parser::attributes(i, false), + |m: Vec| { + m.into_iter().map(RuleBlockContent::StyleAttr).collect() + }, + ), + // Or an at rule + map(Parser::rule_block, |m: RuleBlock| { + vec![RuleBlockContent::RuleBlock(Box::new(m))] + }), + ))), + ), + |m: Vec>| m.into_iter().flatten().collect(), + )(i); + + #[cfg(test)] + trace!("Rule Block Contents: {:#?}", result); + + result + } + + /// Parses a Rule Block + fn rule_block(i: &str) -> IResult<&str, RuleBlock, VerboseError<&str>> { + #[cfg(test)] + trace!("Rule Block: {}", i); + + Self::expect_non_empty(i)?; + + let result = context( + "RuleBlock", + Self::trimmed(map( + separated_pair( + // Collect at Rules. + Self::at_rule_condition, + tag("{"), + // Collect contents with-in rules. + terminated(Self::rule_block_contents, tag("}")), + ), + // Map Results into a scope + |p: (Vec, Vec)| RuleBlock { + condition: p.0.into(), + content: p.1.into_iter().map(|i| i.into()).collect(), + }, + )), + )(i); + + #[cfg(test)] + trace!("Rule Block: {:#?}", result); + + result + } + fn rule_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { #[cfg(test)] trace!("Rule contents: {}", i); @@ -528,7 +570,7 @@ impl Parser { let result = context( "StyleDanglingBlock", Self::trimmed(map( - Self::dangling_attributes, + |i| Self::attributes(i, true), |attr: Vec| { ScopeContent::Block(Block { condition: Cow::Borrowed(&[]), @@ -1373,4 +1415,77 @@ mod tests { assert_eq!(parsed, expected); } + + #[test] + fn test_rule_block() { + let test_str = r#" + span { + @media screen and (max-width: 500px) { + background-color: blue; + } + } + + div { + @supports (max-width: 500px) { + @media screen and (max-width: 500px) { + background-color: blue; + } + } + } + + @media screen and ${breakpoint} { + display: flex; + } + "#; + + let parsed = Parser::parse(test_str).expect("Failed to Parse Style"); + + let expected = Sheet::from(vec![ + ScopeContent::Block(Block { + condition: vec![vec!["span".into()].into()].into(), + content: vec![BlockContent::RuleBlock(RuleBlock { + condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()] + .into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "background-color".into(), + value: vec!["blue".into()].into(), + })] + .into(), + })] + .into(), + }), + ScopeContent::Block(Block { + condition: vec![vec!["div".into()].into()].into(), + content: vec![BlockContent::RuleBlock(RuleBlock { + condition: vec!["@supports ".into(), "(max-width: 500px)".into()].into(), + content: vec![RuleBlockContent::RuleBlock(Box::new(RuleBlock { + condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()] + .into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "background-color".into(), + value: vec!["blue".into()].into(), + })] + .into(), + }))] + .into(), + })] + .into(), + }), + ScopeContent::Rule(Rule { + condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), + content: vec![RuleContent::Block(Block { + condition: Cow::Borrowed(&[]), + content: vec![StyleAttribute { + key: "display".into(), + value: vec!["flex".into()].into(), + } + .into()] + .into(), + })] + .into(), + }), + ]); + + assert_eq!(parsed, expected); + } } diff --git a/packages/stylist/src/style.rs b/packages/stylist/src/style.rs index d2a0c92..5b60d3c 100644 --- a/packages/stylist/src/style.rs +++ b/packages/stylist/src/style.rs @@ -348,6 +348,12 @@ mod tests { header, footer { border: 1px solid black; + + @supports (max-width: 500px) { + @media screen and (max-width: 500px) { + display: flex; + } + } } "#, ) @@ -375,6 +381,13 @@ mod tests { .{style_name} header, .{style_name} footer {{ border: 1px solid black; }} +@supports (max-width: 500px) {{ + @media screen and (max-width: 500px) {{ + .{style_name} header, .{style_name} footer {{ + display: flex; + }} + }} +}} "#, style_name = style.get_class_name() ) From 6164ab29ce070b91d72a0bda362eda21b0d78b0a Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 09:57:37 +0900 Subject: [PATCH 13/27] Fix Clippy. --- packages/stylist-core/src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 007fb9c..2a1f26d 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -218,7 +218,7 @@ impl Parser { let final_semicolon = |i| { if dangling { - map(tag(";"), |m| Some(m))(i) + map(tag(";"), Some)(i) } else { opt(tag(";"))(i) } @@ -444,7 +444,7 @@ impl Parser { // Map Results into a scope |p: (Vec, Vec)| RuleBlock { condition: p.0.into(), - content: p.1.into_iter().map(|i| i.into()).collect(), + content: p.1.into(), }, )), )(i); From c34e7cb496d9780e595c3820fda9390cbe748def Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 14:58:02 +0900 Subject: [PATCH 14/27] Keyframes with RuleBlock. --- packages/stylist-core/src/ast/block.rs | 18 +- packages/stylist-core/src/ast/context.rs | 220 ++++++++----- packages/stylist-core/src/ast/mod.rs | 76 +++-- packages/stylist-core/src/ast/rule.rs | 70 +++-- packages/stylist-core/src/ast/rule_block.rs | 12 +- .../stylist-core/src/ast/scope_content.rs | 2 +- packages/stylist-core/src/ast/selector.rs | 2 +- packages/stylist-core/src/ast/sheet.rs | 2 +- packages/stylist-core/src/ast/str_frag.rs | 2 +- packages/stylist-core/src/ast/style_attr.rs | 6 +- packages/stylist-core/src/ast/to_style_str.rs | 2 +- packages/stylist-core/src/lib.rs | 66 ++++ packages/stylist-core/src/parser.rs | 292 +++++++++++------- .../src/literal/to_output_with_args.rs | 7 +- .../stylist-macros/src/output/rule_content.rs | 25 +- packages/stylist/tests/macro_sheet_tests.rs | 24 +- 16 files changed, 531 insertions(+), 295 deletions(-) diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 87613bc..79f08c6 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -9,7 +9,7 @@ pub enum BlockContent { } impl ToStyleStr for BlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { match self { Self::StyleAttr(ref m) => m.write_style(w, ctx), Self::RuleBlock(ref m) => m.write_style(w, ctx), @@ -43,7 +43,7 @@ pub struct Block { } impl Block { - fn cond_str(&self, ctx: &mut StyleContext<'_>) -> Option { + fn cond_str(&self, ctx: &mut StyleContext<'_, '_>) -> Option { if self.condition.is_empty() { return None; } @@ -62,22 +62,16 @@ impl Block { } impl ToStyleStr for Block { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - // Close last clause. - ctx.write_finishing_clause(w); - + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { // TODO: nested block, which is not supported at the moment. let cond_s = self.cond_str(ctx); - let mut final_ctx = cond_s - .as_ref() - .map(|m| ctx.with_condition(m)) - .unwrap_or_else(|| ctx.to_block_context()); + let mut block_ctx = ctx.with_block_condition(cond_s); for attr in self.content.iter() { - attr.write_style(w, &mut final_ctx); + attr.write_style(w, &mut block_ctx); } - final_ctx.write_finishing_clause(w); + block_ctx.finish(w); } } diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index cde97d5..850344a 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,11 +1,5 @@ use std::borrow::Cow; - -// #[derive(Debug)] -// pub enum StyleKind { -// Style, -// Keyframes, -// } -// +use std::sync::Mutex; #[derive(Debug, Clone, PartialEq)] enum ContextState { @@ -15,99 +9,187 @@ enum ContextState { Open, } -#[derive(Debug, Clone)] -pub struct StyleContext<'a> { - // pub kind: StyleKind, +#[derive(Debug)] +pub struct StyleContext<'a, 'b> { pub class_name: Option<&'a str>, - pub parent_conditions: Vec>, - state: ContextState, + parent_ctx: Option<&'b StyleContext<'a, 'b>>, + + rules: Vec>, + selectors: Vec>, + + state: Mutex, } -static IDENT: &str = " "; +static INDENT: &str = " "; -impl<'a> StyleContext<'a> { +impl<'a, 'b> StyleContext<'a, 'b> { pub fn new(class_name: Option<&'a str>) -> Self { Self { + parent_ctx: None, class_name, - parent_conditions: Vec::new(), - state: ContextState::Closed, + rules: Vec::new(), + selectors: Vec::new(), + + state: Mutex::new(ContextState::Closed), } } - pub fn with_condition>>(&self, condition: S) -> Self { - let mut self_ = self.clone(); + pub fn is_open(&self) -> bool { + let state = self.state.try_lock().unwrap(); + *state == ContextState::Open + } - self_.parent_conditions.push(condition.into()); + // We close until we can find a parent that has nothing differs from current path. + pub fn close_until_common_parent(&self, w: &mut String) { + while let Some(m) = self.open_parent() { + if self.differ_conditions().is_empty() { + break; + } + m.finish(w); + } + } - self_ + pub fn open_parent(&self) -> Option<&'b StyleContext<'a, 'b>> { + match self.parent_ctx { + Some(m) => { + if m.is_open() { + Some(m) + } else { + m.open_parent() + } + } + None => None, + } } - pub fn to_block_context(&self) -> Self { - // No selectors - if self - .parent_conditions() - .last() - .map(|m| m.starts_with('@')) - .unwrap_or(true) - { - self.with_condition( - self.class_name - .map(|m| Cow::from(format!(".{}", m))) - .unwrap_or_else(|| "html".into()), - ) - } else { - self.clone() + fn conditions(&self) -> Vec<&str> { + self.rules + .iter() + .chain(self.selectors.iter()) + .map(|m| m.as_ref()) + .collect() + } + + fn common_conditions(&self) -> Vec<&str> { + match self.open_parent() { + Some(m) => self + .conditions() + .into_iter() + .zip(m.conditions()) + .filter_map(|(m1, m2)| if m1 == m2 { Some(m1) } else { None }) + .collect(), + None => Vec::new(), } } - pub fn parent_conditions(&self) -> Vec> { - let (mut rules, mut selectors) = (Vec::new(), Vec::new()); + /// Calculate the layers that current context differs from the parent context + fn unique_conditions(&self) -> Vec<&str> { + self.conditions() + .into_iter() + .skip(self.common_conditions().len()) + .collect() + } - // @ rules first, then selectors. - // Equivalent to the following line, but would result in a smaller bundle - // sorted_parents.sort_by_cached_key(|m| !m.starts_with('@')); - for item in self.parent_conditions.clone() { - if item.starts_with('@') { - rules.push(item); - } else { - selectors.push(item); - } + /// Calculate the layers that parent context differs from current context + fn differ_conditions(&self) -> Vec<&str> { + match self.open_parent() { + Some(m) => m + .conditions() + .into_iter() + .skip(self.common_conditions().len()) + .collect(), + None => Vec::new(), } + } - rules.append(&mut selectors); - rules + fn write_padding_impl(&self, w: &mut String, no: usize) { + for _ in 0..no { + w.push_str(INDENT); + } } - pub fn write_starting_clause(&mut self, w: &mut String) { - if self.state == ContextState::Closed { - for (index, cond) in self.parent_conditions().iter().enumerate() { - for _i in 0..index { - w.push_str(IDENT); - } - w.push_str(cond); - w.push_str(" {\n"); - } + fn write_min_padding(&self, w: &mut String) { + self.write_padding_impl(w, self.common_conditions().len()) + } - self.state = ContextState::Open; + fn write_finish_impl(&self, w: &mut String, no: usize) { + for i in (0..no).rev() { + self.write_min_padding(w); + self.write_padding_impl(w, i); + w.push_str("}\n"); } } - pub fn write_finishing_clause(&mut self, w: &mut String) { - if self.state == ContextState::Open { - for i in (0..self.parent_conditions.len()).rev() { - for _i in 0..i { - w.push_str(IDENT); - } - w.push_str("}\n"); - } + fn write_start_impl(&self, w: &mut String, conds: Vec<&str>) { + for (index, cond) in conds.iter().enumerate() { + self.write_min_padding(w); + self.write_padding_impl(w, index); + w.push_str(cond); + w.push_str(" {\n"); + } + } + + pub fn finish(&self, w: &mut String) { + let mut state = self.state.try_lock().unwrap(); + + if *state == ContextState::Open { + self.write_finish_impl(w, self.unique_conditions().len()); + } + + *state = ContextState::Closed; + } + + pub fn start(&self, w: &mut String) { + let mut state = self.state.try_lock().unwrap(); - self.state = ContextState::Closed; + if *state == ContextState::Closed { + self.close_until_common_parent(w); + self.write_start_impl(w, self.unique_conditions()); } + *state = ContextState::Open; } pub fn write_padding(&self, w: &mut String) { - for _ in 0..self.parent_conditions.len() { - w.push_str(IDENT); + self.write_padding_impl(w, self.conditions().len()); + } + + pub fn with_block_condition(&'b self, cond: Option) -> Self + where + S: Into>, + { + let mut selectors = self.selectors.clone(); + + if let Some(m) = cond { + selectors.push(m.into()); + } else if self.selectors.is_empty() { + selectors.push( + self.class_name + .map(|m| format!(".{}", m).into()) + .unwrap_or_else(|| "html".into()), + ) + } + + Self { + parent_ctx: Some(self), + class_name: self.class_name, + rules: self.rules.clone(), + selectors, + + state: Mutex::new(ContextState::Closed), + } + } + + pub fn with_rule_condition>>(&'b self, cond: S) -> Self { + let mut rules = self.rules.clone(); + rules.push(cond.into()); + + Self { + parent_ctx: Some(self), + class_name: self.class_name, + rules, + selectors: self.selectors.clone(), + + state: Mutex::new(ContextState::Closed), } } } diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index 44e25e1..361a2b6 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -53,15 +53,24 @@ mod tests { }), ScopeContent::Rule(Rule { condition: vec!["@keyframes move".into()].into(), - content: vec![String::from( - r#"from { -width: 100px; -} -to { -width: 200px; -}"#, - ) - .into()] + content: vec![ + RuleContent::RuleBlock(RuleBlock { + condition: vec!["from".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["100px".into()].into(), + })] + .into(), + }), + RuleContent::RuleBlock(RuleBlock { + condition: vec!["to".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["200px".into()].into(), + })] + .into(), + }), + ] .into(), }), ]); @@ -74,12 +83,12 @@ width: 200px; background-color: red; } @keyframes move { -from { -width: 100px; -} -to { -width: 200px; -} + from { + width: 100px; + } + to { + width: 200px; + } } "# ); @@ -113,13 +122,24 @@ width: 200px; RuleContent::Rule( Rule { condition: vec!["@keyframes move".into()].into(), - content: vec![r#"from { -width: 100px; -} -to { -width: 200px; -}"# - .into()] + content: vec![ + RuleContent::RuleBlock(RuleBlock { + condition: vec!["from".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["100px".into()].into(), + })] + .into(), + }), + RuleContent::RuleBlock(RuleBlock { + condition: vec!["to".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["200px".into()].into(), + })] + .into(), + }), + ] .into(), } .into(), @@ -141,12 +161,12 @@ width: 200px; } @media only screen and (min-width: 1000px) { @keyframes move { -from { -width: 100px; -} -to { -width: 200px; -} + from { + width: 100px; + } + to { + width: 200px; + } } } "# diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index 92f073b..6fd1240 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use super::{Block, ScopeContent, StringFragment, StyleContext, ToStyleStr}; +use super::{Block, RuleBlock, ScopeContent, StringFragment, StyleContext, ToStyleStr}; /// Everything that can be inside a rule. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -9,10 +9,12 @@ pub enum RuleContent { Block(Block), /// A nested rule Rule(Box), - /// A raw string literal, i.e. something that wasn't parsed. - /// This is an escape-hatch and may get removed in the future - /// for a more meaningful alternative - String(Cow<'static, str>), + // /// A raw string literal, i.e. something that wasn't parsed. + // /// This is an escape-hatch and may get removed in the future + // /// for a more meaningful alternative + // String(Cow<'static, str>), + /// A RuleBlock + RuleBlock(RuleBlock), } impl From for RuleContent { @@ -25,36 +27,37 @@ impl From for RuleContent { } impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { match self { - RuleContent::Block(ref b) => b.write_style(w, ctx), - RuleContent::Rule(ref r) => r.write_style(w, ctx), - RuleContent::String(ref s) => { - ctx.write_starting_clause(w); - w.push_str(s); - w.push('\n'); - } + RuleContent::Block(ref m) => m.write_style(w, ctx), + RuleContent::Rule(ref m) => m.write_style(w, ctx), + RuleContent::RuleBlock(ref m) => m.write_style(w, ctx), + // RuleContent::String(ref s) => { + // ctx.write_starting_clause(w); + // w.push_str(s); + // w.push('\n'); + // } } } } -impl From for RuleContent { - fn from(s: String) -> Self { - Self::String(s.into()) - } -} +// impl From for RuleContent { +// fn from(s: String) -> Self { +// Self::String(s.into()) +// } +// } -impl From<&'static str> for RuleContent { - fn from(s: &'static str) -> Self { - Self::String(s.into()) - } -} +// impl From<&'static str> for RuleContent { +// fn from(s: &'static str) -> Self { +// Self::String(s.into()) +// } +// } -impl From> for RuleContent { - fn from(s: Cow<'static, str>) -> Self { - Self::String(s) - } -} +// impl From> for RuleContent { +// fn from(s: Cow<'static, str>) -> Self { +// Self::String(s) +// } +// } /// An At-Rule can contain both other blocks and in some cases more At-Rules. /// @@ -78,20 +81,21 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - ctx.write_finishing_clause(w); - + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { let mut cond = "".to_string(); for frag in self.condition.iter() { frag.write_style(&mut cond, ctx); } - let mut rule_ctx = ctx.clone().with_condition(&cond); + let mut rule_ctx = ctx.with_rule_condition(&cond); + if cond.starts_with("@keyframes") { + rule_ctx.start(w); // keyframes should always be printed. + } for i in self.content.iter() { i.write_style(w, &mut rule_ctx); } - rule_ctx.write_finishing_clause(w); + rule_ctx.finish(w); } } diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs index 2eeca0e..af53b5f 100644 --- a/packages/stylist-core/src/ast/rule_block.rs +++ b/packages/stylist-core/src/ast/rule_block.rs @@ -9,7 +9,7 @@ pub enum RuleBlockContent { } impl ToStyleStr for RuleBlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { match self { Self::StyleAttr(ref b) => b.write_style(w, ctx), Self::RuleBlock(ref r) => r.write_style(w, ctx), @@ -38,20 +38,18 @@ pub struct RuleBlock { } impl ToStyleStr for RuleBlock { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - // Finish any previous blocks - ctx.write_finishing_clause(w); - + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { let mut cond = "".to_string(); for frag in self.condition.iter() { frag.write_style(&mut cond, ctx); } - let mut rule_ctx = ctx.with_condition(&cond); + let mut rule_ctx = ctx.with_rule_condition(cond); + // rule_ctx.start(w); for i in self.content.iter() { i.write_style(w, &mut rule_ctx); } - rule_ctx.write_finishing_clause(w); + rule_ctx.finish(w); } } diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 9830a6f..53be274 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -29,7 +29,7 @@ pub enum ScopeContent { } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { match self { ScopeContent::Block(ref b) => b.write_style(w, ctx), ScopeContent::Rule(ref r) => r.write_style(w, ctx), diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 9486a3b..447f90c 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -14,7 +14,7 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { let mut joined_s = "".to_string(); for frag in self.fragments.iter() { diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index f1c6c1a..387ba8e 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -48,7 +48,7 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { for scope in self.0.iter() { scope.write_style(w, ctx); } diff --git a/packages/stylist-core/src/ast/str_frag.rs b/packages/stylist-core/src/ast/str_frag.rs index a6af079..7cebbd1 100644 --- a/packages/stylist-core/src/ast/str_frag.rs +++ b/packages/stylist-core/src/ast/str_frag.rs @@ -9,7 +9,7 @@ pub struct StringFragment { } impl ToStyleStr for StringFragment { - fn write_style(&self, w: &mut String, _ctx: &mut StyleContext<'_>) { + fn write_style(&self, w: &mut String, _ctx: &mut StyleContext<'_, '_>) { w.push_str(&self.inner); } } diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index 206d8ab..b1ce76c 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -13,9 +13,9 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - // Always write starting clause. - ctx.write_starting_clause(w); + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + // Always try to print block + ctx.start(w); ctx.write_padding(w); w.push_str(&self.key); diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index b87edd8..f0b5e38 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -14,5 +14,5 @@ pub trait ToStyleStr { } // If None is passed as class_name, it means to write a global style. - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>); + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>); } diff --git a/packages/stylist-core/src/lib.rs b/packages/stylist-core/src/lib.rs index 6ff137b..1e0db08 100644 --- a/packages/stylist-core/src/lib.rs +++ b/packages/stylist-core/src/lib.rs @@ -13,3 +13,69 @@ pub mod ast; #[cfg_attr(documenting, doc(cfg(feature = "parser")))] #[cfg(feature = "parser")] mod parser; + +#[cfg(test)] +mod tests { + use super::*; + use ast::Sheet; + use ast::ToStyleStr; + + #[test] + fn test_scoped_complex() { + let style: Sheet = r#" + background-color: black; + .with-class { + color: red; + } + @media screen and (max-width: 600px) { + color: yellow; + } + @supports (display: grid) { + display: grid; + } + + header, footer { + border: 1px solid black; + + @supports (max-width: 500px) { + @media screen and (max-width: 500px) { + display: flex; + } + } + } + "# + .parse() + .expect("Failed to create Style."); + + assert_eq!( + style.to_style_str(Some("test-style-cls")), + r#".test-style-cls { + background-color: black; +} +.test-style-cls .with-class { + color: red; +} +@media screen and (max-width: 600px) { + .test-style-cls { + color: yellow; + } +} +@supports (display: grid) { + .test-style-cls { + display: grid; + } +} +.test-style-cls header, .test-style-cls footer { + border: 1px solid black; +} +@supports (max-width: 500px) { + @media screen and (max-width: 500px) { + .test-style-cls header, .test-style-cls footer { + display: flex; + } + } +} +"#, + ) + } +} diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 2a1f26d..4577cfb 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; use nom::{ - branch::alt, + branch::{alt, Alt}, bytes::complete::{is_not, tag, take_while, take_while1}, character::complete::{alpha1, alphanumeric1, anychar, char, none_of}, - combinator::{map, map_res, not, opt, recognize}, + combinator::{fail, map, not, opt, recognize}, // , map_res error::{context, convert_error, ErrorKind, ParseError, VerboseError}, multi::{many0, many1, separated_list1}, sequence::{delimited, pair, preceded, separated_pair, terminated}, @@ -20,6 +20,12 @@ use crate::{Error, Result}; #[cfg(test)] use log::trace; +#[derive(Debug, PartialEq)] +enum RuleBlockKind { + Keyframes, + Other, +} + /// A lightweight CSS Parser. #[derive(Debug)] pub(crate) struct Parser {} @@ -350,7 +356,10 @@ impl Parser { |m| m.into_iter().map(BlockContent::StyleAttr).collect(), ), // Or an at rule - map(Parser::rule_block, |m| vec![BlockContent::RuleBlock(m)]), + map( + |i| Parser::rule_block(i, RuleBlockKind::Other), + |m| vec![BlockContent::RuleBlock(m)], + ), ))), |m: Vec>| m.into_iter().flatten().collect(), )), @@ -410,9 +419,10 @@ impl Parser { }, ), // Or an at rule - map(Parser::rule_block, |m: RuleBlock| { - vec![RuleBlockContent::RuleBlock(Box::new(m))] - }), + map( + |i| Parser::rule_block(i, RuleBlockKind::Other), + |m: RuleBlock| vec![RuleBlockContent::RuleBlock(Box::new(m))], + ), ))), ), |m: Vec>| m.into_iter().flatten().collect(), @@ -425,18 +435,25 @@ impl Parser { } /// Parses a Rule Block - fn rule_block(i: &str) -> IResult<&str, RuleBlock, VerboseError<&str>> { + fn rule_block(i: &str, kind: RuleBlockKind) -> IResult<&str, RuleBlock, VerboseError<&str>> { #[cfg(test)] trace!("Rule Block: {}", i); Self::expect_non_empty(i)?; + let cond = |i| match kind { + RuleBlockKind::Other => Self::at_rule_condition(i, (tag("@media"), tag("@supports"))), + RuleBlockKind::Keyframes => map(recognize(Self::condition), |m| { + vec![m.trim().to_string().into()] + })(i), + }; + let result = context( "RuleBlock", Self::trimmed(map( separated_pair( // Collect at Rules. - Self::at_rule_condition, + cond, tag("{"), // Collect contents with-in rules. terminated(Self::rule_block_contents, tag("}")), @@ -455,110 +472,110 @@ impl Parser { result } - fn rule_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - #[cfg(test)] - trace!("Rule contents: {}", i); - - Self::expect_non_empty(i)?; - - let string_as_contents = map(Parser::rule_string, |s| vec![s]); - let string_or_curlies = alt((Parser::rule_curly_braces, string_as_contents)); - let result = context( - "RuleContents", - map(many0(string_or_curlies), |p: Vec>| { - p.into_iter().flatten().collect() - }), - )(i)?; - - #[cfg(test)] - trace!("Rule contents: {:#?}", result); - - Ok(result) - } - - fn rule(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { - #[cfg(test)] - trace!("Rule: {}", i); - - Self::expect_non_empty(i)?; - - let result = context( - "Rule", - Self::trimmed(map_res( - separated_pair( - recognize(preceded(tag("@"), is_not("{"))), - tag("{"), - terminated(terminated(Self::rule_contents, opt(Parser::sp)), tag("}")), - ), - |p: (&str, Vec)| { - if p.0.starts_with("@media") { - return Err(String::from("Not a media query")); - } - - if p.0.starts_with("@supports") { - return Err(String::from("Not a support at rule")); - } - - Ok(ScopeContent::Rule(Rule { - condition: vec![p.0.trim().to_string().into()].into(), - content: p.1.into(), - })) - }, - )), - )(i); - - #[cfg(test)] - trace!("Rule: {:#?}", result); - - result - } - - /// Parse everything that is not curly braces - fn rule_string(i: &str) -> IResult<&str, RuleContent, VerboseError<&str>> { - #[cfg(test)] - trace!("Rule String: {}", i); - - Self::expect_non_empty(i)?; - - let result = context( - "StyleRuleString", - Self::trimmed(map(is_not("{}"), |p: &str| { - RuleContent::String(p.trim().to_string().into()) - })), - )(i); - - #[cfg(test)] - trace!("Rule String: {:#?}", result); - - result - } - - /// Parse values within curly braces. This is basically just a helper for rules since - /// they may contain braced content. This function is for parsing it all and not - /// returning an incomplete rule at the first appearance of a closed curly brace - fn rule_curly_braces(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - #[cfg(test)] - trace!("Curly Braces: {}", i); - - Self::expect_non_empty(i)?; - - let result = context( - "StyleRuleCurlyBraces", - Self::trimmed(map( - delimited(tag("{"), Self::rule_contents, tag("}")), - |mut m: Vec| { - m.insert(0, RuleContent::String("{".to_string().into())); - m.push(RuleContent::String("}".to_string().into())); - m - }, - )), - )(i); - - #[cfg(test)] - trace!("Curly Braces: {:#?}", result); - - result - } + // fn rule_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + // #[cfg(test)] + // trace!("Rule contents: {}", i); + + // Self::expect_non_empty(i)?; + + // let string_as_contents = map(Parser::rule_string, |s| vec![s]); + // let string_or_curlies = alt((Parser::rule_curly_braces, string_as_contents)); + // let result = context( + // "RuleContents", + // map(many0(string_or_curlies), |p: Vec>| { + // p.into_iter().flatten().collect() + // }), + // )(i)?; + + // #[cfg(test)] + // trace!("Rule contents: {:#?}", result); + + // Ok(result) + // } + + // fn rule(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { + // #[cfg(test)] + // trace!("Rule: {}", i); + + // Self::expect_non_empty(i)?; + + // let result = context( + // "Rule", + // Self::trimmed(map_res( + // separated_pair( + // recognize(preceded(tag("@"), is_not("{"))), + // tag("{"), + // terminated(terminated(Self::rule_contents, opt(Parser::sp)), tag("}")), + // ), + // |p: (&str, Vec)| { + // if p.0.starts_with("@media") { + // return Err(String::from("Not a media query")); + // } + + // if p.0.starts_with("@supports") { + // return Err(String::from("Not a support at rule")); + // } + + // Ok(ScopeContent::Rule(Rule { + // condition: vec![p.0.trim().to_string().into()].into(), + // content: p.1.into(), + // })) + // }, + // )), + // )(i); + + // #[cfg(test)] + // trace!("Rule: {:#?}", result); + + // result + // } + + // /// Parse everything that is not curly braces + // fn rule_string(i: &str) -> IResult<&str, RuleContent, VerboseError<&str>> { + // #[cfg(test)] + // trace!("Rule String: {}", i); + + // Self::expect_non_empty(i)?; + + // let result = context( + // "StyleRuleString", + // Self::trimmed(map(is_not("{}"), |p: &str| { + // RuleContent::String(p.trim().to_string().into()) + // })), + // )(i); + + // #[cfg(test)] + // trace!("Rule String: {:#?}", result); + + // result + // } + + // /// Parse values within curly braces. This is basically just a helper for rules since + // /// they may contain braced content. This function is for parsing it all and not + // /// returning an incomplete rule at the first appearance of a closed curly brace + // fn rule_curly_braces(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + // #[cfg(test)] + // trace!("Curly Braces: {}", i); + + // Self::expect_non_empty(i)?; + + // let result = context( + // "StyleRuleCurlyBraces", + // Self::trimmed(map( + // delimited(tag("{"), Self::rule_contents, tag("}")), + // |mut m: Vec| { + // m.insert(0, RuleContent::String("{".to_string().into())); + // m.push(RuleContent::String("}".to_string().into())); + // m + // }, + // )), + // )(i); + + // #[cfg(test)] + // trace!("Curly Braces: {:#?}", result); + + // result + // } /// Parse anything that is not in a { ... } fn dangling_block(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { @@ -604,17 +621,25 @@ impl Parser { result } - fn at_rule_condition(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + fn at_rule_condition<'a, T>( + i: &'a str, + tags: T, + ) -> IResult<&'a str, Vec, VerboseError<&'a str>> + where + T: Alt<&'a str, &'a str, VerboseError<&'a str>>, + { #[cfg(test)] trace!("At Rule: {}", i); Self::expect_non_empty(i)?; + let tags = recognize(terminated(alt(tags), tag(" "))); + let result = context( "AtRule", Self::trimmed(map( pair( - alt((tag("@supports "), tag("@media "))), + tags, map( recognize(many1(alt((is_not("${"), recognize(Self::interpolation))))), |m: &str| StringFragment { @@ -639,6 +664,37 @@ impl Parser { result } + fn keyframes(i: &str) -> IResult<&str, Rule, VerboseError<&str>> { + #[cfg(test)] + trace!("Keyframes: {}", i); + + let result = context( + "AtRule", + Self::trimmed(map( + separated_pair( + // Collect at Rules. + |i| Self::at_rule_condition(i, (tag("@keyframes"), fail)), + tag("{"), + // Collect contents with-in rules. + terminated( + many0(|i| Parser::rule_block(i, RuleBlockKind::Keyframes)), + tag("}"), + ), + ), + // Map Results into a scope + |mut p: (Vec, Vec)| Rule { + condition: p.0.into(), + content: p.1.drain(..).map(|m| RuleContent::RuleBlock(m)).collect(), + }, + )), + )(i); + + #[cfg(test)] + trace!("Keyframes: {:#?}", result); + + result + } + /// Parse `@supports` and `@media` fn at_rule(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { #[cfg(test)] @@ -651,7 +707,7 @@ impl Parser { Self::trimmed(map( separated_pair( // Collect at Rules. - Self::at_rule_condition, + |i| Self::at_rule_condition(i, (tag("@supports"), tag("@media"))), tag("{"), // Collect contents with-in rules. terminated(Parser::scope_contents, tag("}")), @@ -686,10 +742,10 @@ impl Parser { Parser::dangling_block, // Or a Block Parser::block, - // Or an at rule + // @supports and @media Parser::at_rule, - // Or a Rule - Parser::rule, + // @keyframes + map(Parser::keyframes, |m| ScopeContent::Rule(m)), )))), )(i); diff --git a/packages/stylist-macros/src/literal/to_output_with_args.rs b/packages/stylist-macros/src/literal/to_output_with_args.rs index 08c898f..76d6ca1 100644 --- a/packages/stylist-macros/src/literal/to_output_with_args.rs +++ b/packages/stylist-macros/src/literal/to_output_with_args.rs @@ -167,11 +167,14 @@ impl ToOutputWithArgs for RuleContent { let block = m.to_output_with_args(args, args_used); OutputRuleContent::Block(block) } + Self::RuleBlock(ref m) => { + let rule_block = m.to_output_with_args(args, args_used); + OutputRuleContent::RuleBlock(rule_block) + } Self::Rule(ref m) => { let rule = m.to_output_with_args(args, args_used); OutputRuleContent::Rule(rule) - } - Self::String(ref m) => OutputRuleContent::String(m.as_ref().to_string()), + } // Self::String(ref m) => OutputRuleContent::String(m.as_ref().to_string()), } } } diff --git a/packages/stylist-macros/src/output/rule_content.rs b/packages/stylist-macros/src/output/rule_content.rs index c0eba02..766f34a 100644 --- a/packages/stylist-macros/src/output/rule_content.rs +++ b/packages/stylist-macros/src/output/rule_content.rs @@ -1,28 +1,33 @@ -use super::{ContextRecorder, OutputBlock, OutputRule, Reify}; -use proc_macro2::{Literal, TokenStream}; +use super::{ContextRecorder, OutputBlock, OutputRule, OutputRuleBlock, Reify}; +use proc_macro2::TokenStream; use quote::quote; #[derive(Debug)] pub enum OutputRuleContent { Rule(OutputRule), Block(OutputBlock), - String(String), + // String(String), + RuleBlock(OutputRuleBlock), } impl Reify for OutputRuleContent { fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { match self { Self::Rule(rule) => { - let block_tokens = rule.into_token_stream(ctx); - quote! { ::stylist::ast::RuleContent::Rule(::std::boxed::Box::new(#block_tokens)) } + let tokens = rule.into_token_stream(ctx); + quote! { ::stylist::ast::RuleContent::Rule(::std::boxed::Box::new(#tokens)) } } Self::Block(block) => { - let block_tokens = block.into_token_stream(ctx); - quote! { ::stylist::ast::RuleContent::Block(#block_tokens) } + let tokens = block.into_token_stream(ctx); + quote! { ::stylist::ast::RuleContent::Block(#tokens) } } - Self::String(ref s) => { - let s = Literal::string(s); - quote! { ::stylist::ast::RuleContent::String(#s.into()) } + // Self::String(ref s) => { + // let s = Literal::string(s); + // quote! { ::stylist::ast::RuleContent::String(#s.into()) } + // } + Self::RuleBlock(m) => { + let tokens = m.into_token_stream(ctx); + quote! { ::stylist::ast::RuleContent::RuleBlock(#tokens) } } } } diff --git a/packages/stylist/tests/macro_sheet_tests.rs b/packages/stylist/tests/macro_sheet_tests.rs index bda0061..1fb8858 100644 --- a/packages/stylist/tests/macro_sheet_tests.rs +++ b/packages/stylist/tests/macro_sheet_tests.rs @@ -70,14 +70,22 @@ fn test_sheet_interpolation() { ScopeContent::Rule(Rule { condition: vec!["@keyframes myframe".into()].into(), content: vec![ - "from".into(), - "{".into(), - "width: 100px;".into(), - "}".into(), - "to".into(), - "{".into(), - "width: 200px;".into(), - "}".into(), + RuleContent::RuleBlock(RuleBlock { + condition: vec!["from".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["100px".into()].into(), + })] + .into(), + }), + RuleContent::RuleBlock(RuleBlock { + condition: vec!["to".into()].into(), + content: vec![RuleBlockContent::StyleAttr(StyleAttribute { + key: "width".into(), + value: vec!["200px".into()].into(), + })] + .into(), + }), ] .into(), }), From c1c05dbd68ea08ac5a8dc8eabac6b40683c715fd Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 15:07:54 +0900 Subject: [PATCH 15/27] Fix Clippy. --- packages/stylist-core/src/ast/context.rs | 4 +--- packages/stylist-core/src/parser.rs | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 850344a..6a28d62 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -20,8 +20,6 @@ pub struct StyleContext<'a, 'b> { state: Mutex, } -static INDENT: &str = " "; - impl<'a, 'b> StyleContext<'a, 'b> { pub fn new(class_name: Option<&'a str>) -> Self { Self { @@ -104,7 +102,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { fn write_padding_impl(&self, w: &mut String, no: usize) { for _ in 0..no { - w.push_str(INDENT); + w.push_str(" "); } } diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 4577cfb..8f7bf7c 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -682,9 +682,9 @@ impl Parser { ), ), // Map Results into a scope - |mut p: (Vec, Vec)| Rule { + |p: (Vec, Vec)| Rule { condition: p.0.into(), - content: p.1.drain(..).map(|m| RuleContent::RuleBlock(m)).collect(), + content: p.1.into_iter().map(RuleContent::RuleBlock).collect(), }, )), )(i); @@ -713,10 +713,10 @@ impl Parser { terminated(Parser::scope_contents, tag("}")), ), // Map Results into a scope - |mut p: (Vec, Vec)| { + |p: (Vec, Vec)| { ScopeContent::Rule(Rule { condition: p.0.into(), - content: p.1.drain(..).map(|i| i.into()).collect(), + content: p.1.into_iter().map(|i| i.into()).collect(), }) }, )), @@ -745,7 +745,7 @@ impl Parser { // @supports and @media Parser::at_rule, // @keyframes - map(Parser::keyframes, |m| ScopeContent::Rule(m)), + map(Parser::keyframes, ScopeContent::Rule), )))), )(i); From a5733db9fb88194be738dd5aca8b0f5ea75d62ae Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 15:31:32 +0900 Subject: [PATCH 16/27] Swap for AtomicBool. --- packages/stylist-core/src/ast/context.rs | 34 +++++++----------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 6a28d62..4314335 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,13 +1,5 @@ use std::borrow::Cow; -use std::sync::Mutex; - -#[derive(Debug, Clone, PartialEq)] -enum ContextState { - // Either a finishing clause has been printed, or the starting block is not printed. - Closed, - // A start clause has been printed, but a finishing clause is not printed. - Open, -} +use std::sync::atomic::{AtomicBool, Ordering}; #[derive(Debug)] pub struct StyleContext<'a, 'b> { @@ -17,7 +9,7 @@ pub struct StyleContext<'a, 'b> { rules: Vec>, selectors: Vec>, - state: Mutex, + is_open: AtomicBool, } impl<'a, 'b> StyleContext<'a, 'b> { @@ -28,13 +20,12 @@ impl<'a, 'b> StyleContext<'a, 'b> { rules: Vec::new(), selectors: Vec::new(), - state: Mutex::new(ContextState::Closed), + is_open: AtomicBool::new(false), } } pub fn is_open(&self) -> bool { - let state = self.state.try_lock().unwrap(); - *state == ContextState::Open + self.is_open.load(Ordering::Relaxed) } // We close until we can find a parent that has nothing differs from current path. @@ -128,23 +119,18 @@ impl<'a, 'b> StyleContext<'a, 'b> { } pub fn finish(&self, w: &mut String) { - let mut state = self.state.try_lock().unwrap(); - - if *state == ContextState::Open { + if self.is_open() { self.write_finish_impl(w, self.unique_conditions().len()); } - - *state = ContextState::Closed; + self.is_open.store(false, Ordering::Relaxed); } pub fn start(&self, w: &mut String) { - let mut state = self.state.try_lock().unwrap(); - - if *state == ContextState::Closed { + if !self.is_open() { self.close_until_common_parent(w); self.write_start_impl(w, self.unique_conditions()); } - *state = ContextState::Open; + self.is_open.store(true, Ordering::Relaxed); } pub fn write_padding(&self, w: &mut String) { @@ -173,7 +159,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { rules: self.rules.clone(), selectors, - state: Mutex::new(ContextState::Closed), + is_open: AtomicBool::new(false), } } @@ -187,7 +173,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { rules, selectors: self.selectors.clone(), - state: Mutex::new(ContextState::Closed), + is_open: AtomicBool::new(false), } } } From 2ba1586f19443e24c499dd570f5170bfdc7fbd52 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 15:36:52 +0900 Subject: [PATCH 17/27] Remove extra lifetime. --- packages/stylist-core/src/ast/block.rs | 6 +- packages/stylist-core/src/ast/context.rs | 12 +- packages/stylist-core/src/ast/rule.rs | 31 +----- packages/stylist-core/src/ast/rule_block.rs | 4 +- .../stylist-core/src/ast/scope_content.rs | 4 +- packages/stylist-core/src/ast/selector.rs | 2 +- packages/stylist-core/src/ast/sheet.rs | 4 +- packages/stylist-core/src/ast/str_frag.rs | 2 +- packages/stylist-core/src/ast/style_attr.rs | 2 +- packages/stylist-core/src/ast/to_style_str.rs | 2 +- packages/stylist-core/src/parser.rs | 105 ------------------ 11 files changed, 20 insertions(+), 154 deletions(-) diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index 79f08c6..bea85bf 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -9,7 +9,7 @@ pub enum BlockContent { } impl ToStyleStr for BlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { Self::StyleAttr(ref m) => m.write_style(w, ctx), Self::RuleBlock(ref m) => m.write_style(w, ctx), @@ -43,7 +43,7 @@ pub struct Block { } impl Block { - fn cond_str(&self, ctx: &mut StyleContext<'_, '_>) -> Option { + fn cond_str(&self, ctx: &mut StyleContext<'_>) -> Option { if self.condition.is_empty() { return None; } @@ -62,7 +62,7 @@ impl Block { } impl ToStyleStr for Block { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { // TODO: nested block, which is not supported at the moment. let cond_s = self.cond_str(ctx); diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 4314335..2049ebf 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -2,9 +2,9 @@ use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; #[derive(Debug)] -pub struct StyleContext<'a, 'b> { +pub struct StyleContext<'a> { pub class_name: Option<&'a str>, - parent_ctx: Option<&'b StyleContext<'a, 'b>>, + parent_ctx: Option<&'a StyleContext<'a>>, rules: Vec>, selectors: Vec>, @@ -12,7 +12,7 @@ pub struct StyleContext<'a, 'b> { is_open: AtomicBool, } -impl<'a, 'b> StyleContext<'a, 'b> { +impl<'a> StyleContext<'a> { pub fn new(class_name: Option<&'a str>) -> Self { Self { parent_ctx: None, @@ -38,7 +38,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { } } - pub fn open_parent(&self) -> Option<&'b StyleContext<'a, 'b>> { + pub fn open_parent(&self) -> Option<&StyleContext<'a>> { match self.parent_ctx { Some(m) => { if m.is_open() { @@ -137,7 +137,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { self.write_padding_impl(w, self.conditions().len()); } - pub fn with_block_condition(&'b self, cond: Option) -> Self + pub fn with_block_condition(&'a self, cond: Option) -> Self where S: Into>, { @@ -163,7 +163,7 @@ impl<'a, 'b> StyleContext<'a, 'b> { } } - pub fn with_rule_condition>>(&'b self, cond: S) -> Self { + pub fn with_rule_condition>>(&'a self, cond: S) -> Self { let mut rules = self.rules.clone(); rules.push(cond.into()); diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index 6fd1240..2feb508 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -9,10 +9,6 @@ pub enum RuleContent { Block(Block), /// A nested rule Rule(Box), - // /// A raw string literal, i.e. something that wasn't parsed. - // /// This is an escape-hatch and may get removed in the future - // /// for a more meaningful alternative - // String(Cow<'static, str>), /// A RuleBlock RuleBlock(RuleBlock), } @@ -27,38 +23,15 @@ impl From for RuleContent { } impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { RuleContent::Block(ref m) => m.write_style(w, ctx), RuleContent::Rule(ref m) => m.write_style(w, ctx), RuleContent::RuleBlock(ref m) => m.write_style(w, ctx), - // RuleContent::String(ref s) => { - // ctx.write_starting_clause(w); - // w.push_str(s); - // w.push('\n'); - // } } } } -// impl From for RuleContent { -// fn from(s: String) -> Self { -// Self::String(s.into()) -// } -// } - -// impl From<&'static str> for RuleContent { -// fn from(s: &'static str) -> Self { -// Self::String(s.into()) -// } -// } - -// impl From> for RuleContent { -// fn from(s: Cow<'static, str>) -> Self { -// Self::String(s) -// } -// } - /// An At-Rule can contain both other blocks and in some cases more At-Rules. /// /// E.g.: @@ -81,7 +54,7 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { let mut cond = "".to_string(); for frag in self.condition.iter() { frag.write_style(&mut cond, ctx); diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs index af53b5f..cbe6d33 100644 --- a/packages/stylist-core/src/ast/rule_block.rs +++ b/packages/stylist-core/src/ast/rule_block.rs @@ -9,7 +9,7 @@ pub enum RuleBlockContent { } impl ToStyleStr for RuleBlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { Self::StyleAttr(ref b) => b.write_style(w, ctx), Self::RuleBlock(ref r) => r.write_style(w, ctx), @@ -38,7 +38,7 @@ pub struct RuleBlock { } impl ToStyleStr for RuleBlock { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { let mut cond = "".to_string(); for frag in self.condition.iter() { frag.write_style(&mut cond, ctx); diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 53be274..8147bab 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -24,12 +24,10 @@ use super::{Block, Rule, StyleContext, ToStyleStr}; pub enum ScopeContent { Block(Block), Rule(Rule), - // e.g. media rules nested in support rules and vice versa - // Scope(Scope), } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { match self { ScopeContent::Block(ref b) => b.write_style(w, ctx), ScopeContent::Rule(ref r) => r.write_style(w, ctx), diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 447f90c..9486a3b 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -14,7 +14,7 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { let mut joined_s = "".to_string(); for frag in self.fragments.iter() { diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index 387ba8e..78ef378 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use super::{ScopeContent, StyleContext, ToStyleStr}; -/// The top node of a style string. +/// The top node of a stylesheet. // Once a sheet is constructed, it becomes immutable. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Sheet(Arc>); @@ -48,7 +48,7 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { for scope in self.0.iter() { scope.write_style(w, ctx); } diff --git a/packages/stylist-core/src/ast/str_frag.rs b/packages/stylist-core/src/ast/str_frag.rs index 7cebbd1..a6af079 100644 --- a/packages/stylist-core/src/ast/str_frag.rs +++ b/packages/stylist-core/src/ast/str_frag.rs @@ -9,7 +9,7 @@ pub struct StringFragment { } impl ToStyleStr for StringFragment { - fn write_style(&self, w: &mut String, _ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, _ctx: &mut StyleContext<'_>) { w.push_str(&self.inner); } } diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index b1ce76c..658912f 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -13,7 +13,7 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>) { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { // Always try to print block ctx.start(w); ctx.write_padding(w); diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index f0b5e38..b87edd8 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -14,5 +14,5 @@ pub trait ToStyleStr { } // If None is passed as class_name, it means to write a global style. - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_, '_>); + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>); } diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 8f7bf7c..6a3cb28 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -472,111 +472,6 @@ impl Parser { result } - // fn rule_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - // #[cfg(test)] - // trace!("Rule contents: {}", i); - - // Self::expect_non_empty(i)?; - - // let string_as_contents = map(Parser::rule_string, |s| vec![s]); - // let string_or_curlies = alt((Parser::rule_curly_braces, string_as_contents)); - // let result = context( - // "RuleContents", - // map(many0(string_or_curlies), |p: Vec>| { - // p.into_iter().flatten().collect() - // }), - // )(i)?; - - // #[cfg(test)] - // trace!("Rule contents: {:#?}", result); - - // Ok(result) - // } - - // fn rule(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { - // #[cfg(test)] - // trace!("Rule: {}", i); - - // Self::expect_non_empty(i)?; - - // let result = context( - // "Rule", - // Self::trimmed(map_res( - // separated_pair( - // recognize(preceded(tag("@"), is_not("{"))), - // tag("{"), - // terminated(terminated(Self::rule_contents, opt(Parser::sp)), tag("}")), - // ), - // |p: (&str, Vec)| { - // if p.0.starts_with("@media") { - // return Err(String::from("Not a media query")); - // } - - // if p.0.starts_with("@supports") { - // return Err(String::from("Not a support at rule")); - // } - - // Ok(ScopeContent::Rule(Rule { - // condition: vec![p.0.trim().to_string().into()].into(), - // content: p.1.into(), - // })) - // }, - // )), - // )(i); - - // #[cfg(test)] - // trace!("Rule: {:#?}", result); - - // result - // } - - // /// Parse everything that is not curly braces - // fn rule_string(i: &str) -> IResult<&str, RuleContent, VerboseError<&str>> { - // #[cfg(test)] - // trace!("Rule String: {}", i); - - // Self::expect_non_empty(i)?; - - // let result = context( - // "StyleRuleString", - // Self::trimmed(map(is_not("{}"), |p: &str| { - // RuleContent::String(p.trim().to_string().into()) - // })), - // )(i); - - // #[cfg(test)] - // trace!("Rule String: {:#?}", result); - - // result - // } - - // /// Parse values within curly braces. This is basically just a helper for rules since - // /// they may contain braced content. This function is for parsing it all and not - // /// returning an incomplete rule at the first appearance of a closed curly brace - // fn rule_curly_braces(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { - // #[cfg(test)] - // trace!("Curly Braces: {}", i); - - // Self::expect_non_empty(i)?; - - // let result = context( - // "StyleRuleCurlyBraces", - // Self::trimmed(map( - // delimited(tag("{"), Self::rule_contents, tag("}")), - // |mut m: Vec| { - // m.insert(0, RuleContent::String("{".to_string().into())); - // m.push(RuleContent::String("}".to_string().into())); - // m - // }, - // )), - // )(i); - - // #[cfg(test)] - // trace!("Curly Braces: {:#?}", result); - - // result - // } - /// Parse anything that is not in a { ... } fn dangling_block(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { #[cfg(test)] From 0e9e05b48ffbfe9a03f58f1cc1cfa5428a39f921 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sat, 11 Sep 2021 16:00:11 +0900 Subject: [PATCH 18/27] Optimise for size. --- packages/stylist/Cargo.toml | 2 +- packages/stylist/src/utils.rs | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/stylist/Cargo.toml b/packages/stylist/Cargo.toml index f2c7f50..76d93bb 100644 --- a/packages/stylist/Cargo.toml +++ b/packages/stylist/Cargo.toml @@ -28,7 +28,7 @@ stylist-core = { path = "../stylist-core", version = "0.9.0" } stylist-macros = { path = "../stylist-macros", version = "0.9.0", optional = true } once_cell = "1.8.0" -rand = { version = "0.8.4", features = ["small_rng"], optional = true } +rand = { version = "0.8.4", optional = true } wasm-bindgen = "0.2.77" yew = { version = "0.18.0", optional = true, default-features = false, features = ["web_sys"] } diff --git a/packages/stylist/src/utils.rs b/packages/stylist/src/utils.rs index 5b65ea3..5048608 100644 --- a/packages/stylist/src/utils.rs +++ b/packages/stylist/src/utils.rs @@ -1,16 +1,10 @@ -use std::sync::{Arc, Mutex}; - -use once_cell::sync::Lazy; - #[cfg(feature = "random")] pub fn get_rand_str() -> String { - use rand::{distributions::Alphanumeric, rngs::SmallRng, Rng, SeedableRng}; + use rand::{distributions::Alphanumeric, thread_rng, Rng}; - static RNG: Lazy>> = - Lazy::new(|| Arc::new(Mutex::new(SmallRng::from_entropy()))); - let mut rng = RNG.lock().expect("Failed to lock Rng."); + let mut rng = thread_rng(); - (&mut *rng) + (&mut rng) .sample_iter(Alphanumeric) .take(8) .map(char::from) @@ -19,10 +13,12 @@ pub fn get_rand_str() -> String { #[cfg(any(test, not(feature = "random")))] pub fn get_next_style_id() -> String { - static CTR: Lazy>> = Lazy::new(Arc::default); - let mut ctr = CTR.lock().expect("Failed to lock Rng."); + use once_cell::sync::Lazy; + use std::sync::atomic::{AtomicU64, Ordering}; + + static CTR: Lazy = Lazy::new(AtomicU64::default); - *ctr += 1; + let ctr = CTR.fetch_add(1, Ordering::SeqCst); format!("style-{}", ctr) } From b68f0ab5d6ec68c525c4635992d5c572f8f235e1 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sun, 12 Sep 2021 13:54:52 +0900 Subject: [PATCH 19/27] Reduce code size. --- packages/stylist-core/src/ast/context.rs | 65 ++++++++----------- packages/stylist-core/src/ast/to_style_str.rs | 1 - 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 2049ebf..73a0d82 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; +/// A context to faciliate [`ToStyleStr`](super::ToStyleStr). #[derive(Debug)] pub struct StyleContext<'a> { pub class_name: Option<&'a str>, @@ -13,7 +14,7 @@ pub struct StyleContext<'a> { } impl<'a> StyleContext<'a> { - pub fn new(class_name: Option<&'a str>) -> Self { + pub(crate) fn new(class_name: Option<&'a str>) -> Self { Self { parent_ctx: None, class_name, @@ -24,12 +25,12 @@ impl<'a> StyleContext<'a> { } } - pub fn is_open(&self) -> bool { + fn is_open(&self) -> bool { self.is_open.load(Ordering::Relaxed) } // We close until we can find a parent that has nothing differs from current path. - pub fn close_until_common_parent(&self, w: &mut String) { + fn close_until_common_parent(&self, w: &mut String) { while let Some(m) = self.open_parent() { if self.differ_conditions().is_empty() { break; @@ -38,17 +39,14 @@ impl<'a> StyleContext<'a> { } } - pub fn open_parent(&self) -> Option<&StyleContext<'a>> { - match self.parent_ctx { - Some(m) => { - if m.is_open() { - Some(m) - } else { - m.open_parent() - } + fn open_parent(&self) -> Option<&StyleContext<'a>> { + self.parent_ctx.and_then(|m| { + if m.is_open() { + Some(m) + } else { + m.open_parent() } - None => None, - } + }) } fn conditions(&self) -> Vec<&str> { @@ -101,43 +99,36 @@ impl<'a> StyleContext<'a> { self.write_padding_impl(w, self.common_conditions().len()) } - fn write_finish_impl(&self, w: &mut String, no: usize) { - for i in (0..no).rev() { - self.write_min_padding(w); - self.write_padding_impl(w, i); - w.push_str("}\n"); - } - } - - fn write_start_impl(&self, w: &mut String, conds: Vec<&str>) { - for (index, cond) in conds.iter().enumerate() { - self.write_min_padding(w); - self.write_padding_impl(w, index); - w.push_str(cond); - w.push_str(" {\n"); - } - } - - pub fn finish(&self, w: &mut String) { + pub(crate) fn finish(&self, w: &mut String) { if self.is_open() { - self.write_finish_impl(w, self.unique_conditions().len()); + for i in (0..self.unique_conditions().len()).rev() { + self.write_min_padding(w); + self.write_padding_impl(w, i); + w.push_str("}\n"); + } } self.is_open.store(false, Ordering::Relaxed); } - pub fn start(&self, w: &mut String) { + pub(crate) fn start(&self, w: &mut String) { if !self.is_open() { self.close_until_common_parent(w); - self.write_start_impl(w, self.unique_conditions()); + + for (index, cond) in self.unique_conditions().iter().enumerate() { + self.write_min_padding(w); + self.write_padding_impl(w, index); + w.push_str(cond); + w.push_str(" {\n"); + } } self.is_open.store(true, Ordering::Relaxed); } - pub fn write_padding(&self, w: &mut String) { + pub(crate) fn write_padding(&self, w: &mut String) { self.write_padding_impl(w, self.conditions().len()); } - pub fn with_block_condition(&'a self, cond: Option) -> Self + pub(crate) fn with_block_condition(&'a self, cond: Option) -> Self where S: Into>, { @@ -163,7 +154,7 @@ impl<'a> StyleContext<'a> { } } - pub fn with_rule_condition>>(&'a self, cond: S) -> Self { + pub(crate) fn with_rule_condition>>(&'a self, cond: S) -> Self { let mut rules = self.rules.clone(); rules.push(cond.into()); diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index b87edd8..92bb521 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -5,7 +5,6 @@ use super::StyleContext; pub trait ToStyleStr { fn to_style_str(&self, class_name: Option<&str>) -> String { let mut s = String::new(); - let mut ctx = StyleContext::new(class_name); self.write_style(&mut s, &mut ctx); From 3f8003db4201ecde9e85abc4e51635820a896038 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sun, 12 Sep 2021 15:40:09 +0900 Subject: [PATCH 20/27] Reduce more code size. --- packages/stylist-core/src/ast/block.rs | 25 +----- packages/stylist-core/src/ast/mod.rs | 34 ++++---- packages/stylist-core/src/ast/rule.rs | 34 +------- packages/stylist-core/src/ast/rule_block.rs | 55 ------------ .../src/ast/rule_block_content.rs | 24 ++++++ packages/stylist-core/src/parser.rs | 84 +++++++++++-------- .../stylist-macros/src/inline/parse/block.rs | 6 +- .../stylist-macros/src/inline/parse/rule.rs | 6 +- .../stylist-macros/src/inline/parse/scope.rs | 62 ++++---------- .../src/literal/to_output_with_args.rs | 83 ++---------------- packages/stylist-macros/src/output/block.rs | 4 +- .../src/output/block_content.rs | 26 ------ packages/stylist-macros/src/output/mod.rs | 8 +- packages/stylist-macros/src/output/rule.rs | 5 +- .../stylist-macros/src/output/rule_block.rs | 46 ---------- .../src/output/rule_block_content.rs | 32 +++++++ .../stylist-macros/src/output/rule_content.rs | 34 -------- packages/stylist/src/utils.rs | 4 +- packages/stylist/tests/macro_sheet_tests.rs | 12 +-- 19 files changed, 170 insertions(+), 414 deletions(-) delete mode 100644 packages/stylist-core/src/ast/rule_block.rs create mode 100644 packages/stylist-core/src/ast/rule_block_content.rs delete mode 100644 packages/stylist-macros/src/output/block_content.rs delete mode 100644 packages/stylist-macros/src/output/rule_block.rs create mode 100644 packages/stylist-macros/src/output/rule_block_content.rs delete mode 100644 packages/stylist-macros/src/output/rule_content.rs diff --git a/packages/stylist-core/src/ast/block.rs b/packages/stylist-core/src/ast/block.rs index bea85bf..32bdae1 100644 --- a/packages/stylist-core/src/ast/block.rs +++ b/packages/stylist-core/src/ast/block.rs @@ -1,27 +1,6 @@ use std::borrow::Cow; -use super::{RuleBlock, Selector, StyleAttribute, StyleContext, ToStyleStr}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum BlockContent { - StyleAttr(StyleAttribute), - RuleBlock(RuleBlock), -} - -impl ToStyleStr for BlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - match self { - Self::StyleAttr(ref m) => m.write_style(w, ctx), - Self::RuleBlock(ref m) => m.write_style(w, ctx), - } - } -} - -impl From for BlockContent { - fn from(s: StyleAttribute) -> Self { - BlockContent::StyleAttr(s) - } -} +use super::{RuleBlockContent, Selector, StyleContext, ToStyleStr}; /// A block is a set of css properties that apply to elements that /// match the condition. The CSS standard calls these "Qualified rules". @@ -39,7 +18,7 @@ pub struct Block { /// If the value is set as [`&[]`], it signals to substitute with the classname generated for the /// [`Sheet`](super::Sheet) in which this is contained. pub condition: Cow<'static, [Selector]>, - pub content: Cow<'static, [BlockContent]>, + pub content: Cow<'static, [RuleBlockContent]>, } impl Block { diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index 361a2b6..cc17c85 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -3,7 +3,7 @@ mod block; mod context; mod rule; -mod rule_block; +mod rule_block_content; mod scope_content; mod selector; mod sheet; @@ -13,9 +13,9 @@ mod to_style_str; pub use context::StyleContext; -pub use block::{Block, BlockContent}; -pub use rule::{Rule, RuleContent}; -pub use rule_block::{RuleBlock, RuleBlockContent}; +pub use block::Block; +pub use rule::Rule; +pub use rule_block_content::RuleBlockContent; pub use scope_content::ScopeContent; pub use selector::Selector; pub use sheet::Sheet; @@ -54,22 +54,22 @@ mod tests { ScopeContent::Rule(Rule { condition: vec!["@keyframes move".into()].into(), content: vec![ - RuleContent::RuleBlock(RuleBlock { + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["from".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), })] .into(), - }), - RuleContent::RuleBlock(RuleBlock { + })), + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["to".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["200px".into()].into(), })] .into(), - }), + })), ] .into(), }), @@ -101,7 +101,7 @@ mod tests { let test_block = Sheet::from(vec![ScopeContent::Rule(Rule { condition: vec!["@media only screen and (min-width: 1000px)".into()].into(), content: vec![ - RuleContent::Block(Block { + RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "width".into(), @@ -109,8 +109,8 @@ mod tests { } .into()] .into(), - }), - RuleContent::Block(Block { + })), + RuleBlockContent::Block(Box::new(Block { condition: vec![vec![".inner".into()].into()].into(), content: vec![StyleAttribute { key: "background-color".into(), @@ -118,27 +118,27 @@ mod tests { } .into()] .into(), - }), - RuleContent::Rule( + })), + RuleBlockContent::Rule( Rule { condition: vec!["@keyframes move".into()].into(), content: vec![ - RuleContent::RuleBlock(RuleBlock { + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["from".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), })] .into(), - }), - RuleContent::RuleBlock(RuleBlock { + })), + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["to".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["200px".into()].into(), })] .into(), - }), + })), ] .into(), } diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index 2feb508..e971a44 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -1,36 +1,6 @@ use std::borrow::Cow; -use super::{Block, RuleBlock, ScopeContent, StringFragment, StyleContext, ToStyleStr}; - -/// Everything that can be inside a rule. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum RuleContent { - /// A block - Block(Block), - /// A nested rule - Rule(Box), - /// A RuleBlock - RuleBlock(RuleBlock), -} - -impl From for RuleContent { - fn from(scope: ScopeContent) -> Self { - match scope { - ScopeContent::Block(b) => RuleContent::Block(b), - ScopeContent::Rule(r) => RuleContent::Rule(r.into()), - } - } -} - -impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - match self { - RuleContent::Block(ref m) => m.write_style(w, ctx), - RuleContent::Rule(ref m) => m.write_style(w, ctx), - RuleContent::RuleBlock(ref m) => m.write_style(w, ctx), - } - } -} +use super::{RuleBlockContent, StringFragment, StyleContext, ToStyleStr}; /// An At-Rule can contain both other blocks and in some cases more At-Rules. /// @@ -50,7 +20,7 @@ pub struct Rule { pub condition: Cow<'static, [StringFragment]>, /// Note that not all At-Rules allow arbitrary other At-Rules to appear /// inside them, or arbitrary blocks. No safeguards at this point! - pub content: Cow<'static, [RuleContent]>, + pub content: Cow<'static, [RuleBlockContent]>, } impl ToStyleStr for Rule { diff --git a/packages/stylist-core/src/ast/rule_block.rs b/packages/stylist-core/src/ast/rule_block.rs deleted file mode 100644 index cbe6d33..0000000 --- a/packages/stylist-core/src/ast/rule_block.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::borrow::Cow; - -use super::{StringFragment, StyleAttribute, StyleContext, ToStyleStr}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum RuleBlockContent { - StyleAttr(StyleAttribute), - RuleBlock(Box), -} - -impl ToStyleStr for RuleBlockContent { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - match self { - Self::StyleAttr(ref b) => b.write_style(w, ctx), - Self::RuleBlock(ref r) => r.write_style(w, ctx), - } - } -} - -/// A declaration block for at-rules. -/// -/// This is used to represent at-rules that contains declaration block (e.g.:`@font-face`) and -/// `frame`s inside of a `@keyframes` at rule -/// as well as `@media` and `@supports` inside of a [`Block`](super::Block) which is a non-standard CSS feature. -/// -/// E.g.: -/// ```css -/// .inner { -/// @media screen and (max-width: 500px) { -/// display: flex; -/// } -/// } -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RuleBlock { - pub condition: Cow<'static, [StringFragment]>, - pub content: Cow<'static, [RuleBlockContent]>, -} - -impl ToStyleStr for RuleBlock { - fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { - let mut cond = "".to_string(); - for frag in self.condition.iter() { - frag.write_style(&mut cond, ctx); - } - - let mut rule_ctx = ctx.with_rule_condition(cond); - // rule_ctx.start(w); - for i in self.content.iter() { - i.write_style(w, &mut rule_ctx); - } - - rule_ctx.finish(w); - } -} diff --git a/packages/stylist-core/src/ast/rule_block_content.rs b/packages/stylist-core/src/ast/rule_block_content.rs new file mode 100644 index 0000000..102dd8a --- /dev/null +++ b/packages/stylist-core/src/ast/rule_block_content.rs @@ -0,0 +1,24 @@ +use super::{Block, Rule, StyleAttribute, StyleContext, ToStyleStr}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum RuleBlockContent { + StyleAttr(StyleAttribute), + Rule(Box), + Block(Box), +} + +impl From for RuleBlockContent { + fn from(s: StyleAttribute) -> Self { + Self::StyleAttr(s) + } +} + +impl ToStyleStr for RuleBlockContent { + fn write_style(&self, w: &mut String, ctx: &mut StyleContext<'_>) { + match self { + Self::StyleAttr(ref m) => m.write_style(w, ctx), + Self::Rule(ref m) => m.write_style(w, ctx), + Self::Block(ref m) => m.write_style(w, ctx), + } + } +} diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 6a3cb28..161a778 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -12,8 +12,7 @@ use nom::{ }; use crate::ast::{ - Block, BlockContent, Rule, RuleBlock, RuleBlockContent, RuleContent, ScopeContent, Selector, - Sheet, StringFragment, StyleAttribute, + Block, Rule, RuleBlockContent, ScopeContent, Selector, Sheet, StringFragment, StyleAttribute, }; use crate::{Error, Result}; @@ -340,7 +339,7 @@ impl Parser { result } - fn block_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { + fn block_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { #[cfg(test)] trace!("Block Contents: {}", i); @@ -353,15 +352,15 @@ impl Parser { // Either Style Attributes map( |i| Parser::attributes(i, false), - |m| m.into_iter().map(BlockContent::StyleAttr).collect(), + |m| m.into_iter().map(RuleBlockContent::StyleAttr).collect(), ), // Or an at rule map( |i| Parser::rule_block(i, RuleBlockKind::Other), - |m| vec![BlockContent::RuleBlock(m)], + |m| vec![RuleBlockContent::Rule(Box::new(m))], ), ))), - |m: Vec>| m.into_iter().flatten().collect(), + |m: Vec>| m.into_iter().flatten().collect(), )), )(i); @@ -386,7 +385,7 @@ impl Parser { tag("{"), terminated(Self::trim_cmt(Self::block_contents), tag("}")), ), - |p: (Vec, Vec)| { + |p: (Vec, Vec)| { ScopeContent::Block(Block { condition: p.0.into(), content: p.1.into(), @@ -421,7 +420,7 @@ impl Parser { // Or an at rule map( |i| Parser::rule_block(i, RuleBlockKind::Other), - |m: RuleBlock| vec![RuleBlockContent::RuleBlock(Box::new(m))], + |m: Rule| vec![RuleBlockContent::Rule(Box::new(m))], ), ))), ), @@ -435,7 +434,7 @@ impl Parser { } /// Parses a Rule Block - fn rule_block(i: &str, kind: RuleBlockKind) -> IResult<&str, RuleBlock, VerboseError<&str>> { + fn rule_block(i: &str, kind: RuleBlockKind) -> IResult<&str, Rule, VerboseError<&str>> { #[cfg(test)] trace!("Rule Block: {}", i); @@ -459,7 +458,7 @@ impl Parser { terminated(Self::rule_block_contents, tag("}")), ), // Map Results into a scope - |p: (Vec, Vec)| RuleBlock { + |p: (Vec, Vec)| Rule { condition: p.0.into(), content: p.1.into(), }, @@ -489,7 +488,7 @@ impl Parser { content: attr .into_iter() .map(|m| m.into()) - .collect::>() + .collect::>() .into(), }) }, @@ -577,9 +576,13 @@ impl Parser { ), ), // Map Results into a scope - |p: (Vec, Vec)| Rule { + |p: (Vec, Vec)| Rule { condition: p.0.into(), - content: p.1.into_iter().map(RuleContent::RuleBlock).collect(), + content: p + .1 + .into_iter() + .map(|m| RuleBlockContent::Rule(Box::new(m))) + .collect(), }, )), )(i); @@ -611,7 +614,14 @@ impl Parser { |p: (Vec, Vec)| { ScopeContent::Rule(Rule { condition: p.0.into(), - content: p.1.into_iter().map(|i| i.into()).collect(), + content: p + .1 + .into_iter() + .map(|m| match m { + ScopeContent::Block(m) => RuleBlockContent::Block(Box::new(m)), + ScopeContent::Rule(m) => RuleBlockContent::Rule(Box::new(m)), + }) + .collect(), }) }, )), @@ -875,7 +885,7 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "background-color".into(), @@ -883,12 +893,12 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and (max-width: 200px)".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "color".into(), @@ -896,7 +906,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); @@ -925,7 +935,7 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "background-color".into(), @@ -933,7 +943,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ScopeContent::Block(Block { @@ -1019,7 +1029,7 @@ mod tests { "(backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))".into(), ] .into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![ StyleAttribute { @@ -1039,7 +1049,7 @@ mod tests { .into(), ] .into(), - })] + }))] .into(), }), ScopeContent::Rule(Rule { @@ -1049,7 +1059,7 @@ mod tests { .into(), ] .into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "background-color".into(), @@ -1057,7 +1067,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); @@ -1213,7 +1223,7 @@ mod tests { }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "display".into(), @@ -1221,7 +1231,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); @@ -1284,7 +1294,7 @@ mod tests { }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "display".into(), @@ -1292,7 +1302,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); @@ -1351,7 +1361,7 @@ mod tests { }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "display".into(), @@ -1359,7 +1369,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); @@ -1394,7 +1404,7 @@ mod tests { let expected = Sheet::from(vec![ ScopeContent::Block(Block { condition: vec![vec!["span".into()].into()].into(), - content: vec![BlockContent::RuleBlock(RuleBlock { + content: vec![RuleBlockContent::Rule(Box::new(Rule { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()] .into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { @@ -1402,14 +1412,14 @@ mod tests { value: vec!["blue".into()].into(), })] .into(), - })] + }))] .into(), }), ScopeContent::Block(Block { condition: vec![vec!["div".into()].into()].into(), - content: vec![BlockContent::RuleBlock(RuleBlock { + content: vec![RuleBlockContent::Rule(Box::new(Rule { condition: vec!["@supports ".into(), "(max-width: 500px)".into()].into(), - content: vec![RuleBlockContent::RuleBlock(Box::new(RuleBlock { + content: vec![RuleBlockContent::Rule(Box::new(Rule { condition: vec!["@media ".into(), "screen and (max-width: 500px)".into()] .into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { @@ -1419,12 +1429,12 @@ mod tests { .into(), }))] .into(), - })] + }))] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media ".into(), "screen and ${breakpoint}".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: Cow::Borrowed(&[]), content: vec![StyleAttribute { key: "display".into(), @@ -1432,7 +1442,7 @@ mod tests { } .into()] .into(), - })] + }))] .into(), }), ]); diff --git a/packages/stylist-macros/src/inline/parse/block.rs b/packages/stylist-macros/src/inline/parse/block.rs index 2e0d8e3..797cb33 100644 --- a/packages/stylist-macros/src/inline/parse/block.rs +++ b/packages/stylist-macros/src/inline/parse/block.rs @@ -1,4 +1,4 @@ -use crate::output::{OutputBlock, OutputBlockContent}; +use crate::output::{OutputBlock, OutputRuleBlockContent}; use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; use super::{CssAttribute, CssBlockQualifier, CssScope, IntoOutputContext}; @@ -20,7 +20,7 @@ impl Parse for CssQualifiedRule { impl CssQualifiedRule { pub fn into_output(self, ctx: &mut IntoOutputContext) -> OutputBlock { let condition = self.qualifier.into_output(ctx); - let content = self.scope.into_block_output(ctx); + let content = self.scope.into_rule_block_output(ctx); OutputBlock { condition, content } } @@ -33,7 +33,7 @@ impl CssQualifiedRule { let mut output_attrs = Vec::new(); for attr in attrs { - output_attrs.push(OutputBlockContent::StyleAttr(attr.into_output(ctx))) + output_attrs.push(OutputRuleBlockContent::StyleAttr(attr.into_output(ctx))) } OutputBlock { diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index 82da8ee..dbec29d 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -11,7 +11,7 @@ use super::{ fragment_spacing, CssScope, IntoOutputContext, }; use crate::{ - output::{OutputFragment, OutputRule, OutputRuleBlock}, + output::{OutputFragment, OutputRule}, spacing_iterator::SpacedIterator, }; @@ -103,10 +103,10 @@ impl CssAtRule { } } - pub fn into_rule_block_output(mut self, ctx: &mut IntoOutputContext) -> OutputRuleBlock { + pub fn into_rule_block_output(mut self, ctx: &mut IntoOutputContext) -> OutputRule { ctx.extend_errors(self.errors.drain(0..)); - OutputRuleBlock { + OutputRule { condition: self.condition_output(), content: match self.contents { CssAtRuleContent::Scope(m) => m.into_rule_block_output(ctx), diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index 38a666a..e7f52b9 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -4,10 +4,8 @@ use syn::{ token, }; -use super::{CssScopeContent, IntoOutputContext}; -use crate::output::{ - OutputAttribute, OutputBlock, OutputBlockContent, OutputRuleBlockContent, OutputRuleContent, -}; +use super::{CssAttribute, CssQualifiedRule, CssScopeContent, IntoOutputContext}; +use crate::output::OutputRuleBlockContent; #[derive(Debug)] pub struct CssScope { @@ -25,38 +23,37 @@ impl Parse for CssScope { } impl CssScope { - pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> Vec { + pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> Vec { let mut attrs = Vec::new(); let mut contents = Vec::new(); - let collect_attrs = |attrs: &mut Vec, - contents: &mut Vec| { + let collect_attrs = |attrs: &mut Vec, + contents: &mut Vec, + ctx: &mut IntoOutputContext| { if !attrs.is_empty() { - contents.push(OutputRuleContent::Block(OutputBlock { - condition: Vec::new(), - content: attrs - .drain(0..) - .map(OutputBlockContent::StyleAttr) - .collect(), - })); + contents.push(OutputRuleBlockContent::Block(Box::new( + CssQualifiedRule::into_dangling_output(attrs.drain(0..).collect(), ctx), + ))); } }; for scope in self.contents { match scope { - CssScopeContent::Attribute(m) => attrs.push(m.into_output(ctx)), + CssScopeContent::Attribute(m) => attrs.push(m), CssScopeContent::AtRule(m) => { - collect_attrs(&mut attrs, &mut contents); - contents.push(OutputRuleContent::Rule(m.into_rule_output(ctx))); + collect_attrs(&mut attrs, &mut contents, ctx); + contents.push(OutputRuleBlockContent::Rule(Box::new( + m.into_rule_output(ctx), + ))); } CssScopeContent::Nested(m) => { - collect_attrs(&mut attrs, &mut contents); - contents.push(OutputRuleContent::Block(m.into_output(ctx))); + collect_attrs(&mut attrs, &mut contents, ctx); + contents.push(OutputRuleBlockContent::Block(Box::new(m.into_output(ctx)))); } } } - collect_attrs(&mut attrs, &mut contents); + collect_attrs(&mut attrs, &mut contents, ctx); contents } @@ -74,7 +71,7 @@ impl CssScope { } CssScopeContent::AtRule(m) => { - contents.push(OutputRuleBlockContent::RuleBlock(Box::new( + contents.push(OutputRuleBlockContent::Rule(Box::new( m.into_rule_block_output(ctx), ))); } @@ -90,27 +87,4 @@ impl CssScope { contents } - - pub fn into_block_output(self, ctx: &mut IntoOutputContext) -> Vec { - let mut contents = Vec::new(); - - for scope in self.contents { - match scope { - CssScopeContent::Attribute(m) => { - contents.push(OutputBlockContent::StyleAttr(m.into_output(ctx))) - } - CssScopeContent::AtRule(m) => { - contents.push(OutputBlockContent::RuleBlock(m.into_rule_block_output(ctx))) - } - CssScopeContent::Nested(m) => { - ctx.push_error(ParseError::new_spanned( - m.qualifier, - "Can not nest qualified blocks (yet)", - )); - } - } - } - - contents - } } diff --git a/packages/stylist-macros/src/literal/to_output_with_args.rs b/packages/stylist-macros/src/literal/to_output_with_args.rs index 76d6ca1..0f281ff 100644 --- a/packages/stylist-macros/src/literal/to_output_with_args.rs +++ b/packages/stylist-macros/src/literal/to_output_with_args.rs @@ -5,8 +5,8 @@ use proc_macro_error::abort_call_site; use stylist_core::ast::*; use crate::output::{ - OutputAttribute, OutputBlock, OutputBlockContent, OutputFragment, OutputRule, OutputRuleBlock, - OutputRuleBlockContent, OutputRuleContent, OutputScopeContent, OutputSelector, OutputSheet, + OutputAttribute, OutputBlock, OutputFragment, OutputRule, OutputRuleBlockContent, + OutputScopeContent, OutputSelector, OutputSheet, }; use super::{argument::Argument, fstring}; @@ -38,33 +38,6 @@ impl ToOutputWithArgs for Selector { } } -impl ToOutputWithArgs for RuleBlock { - type Output = OutputRuleBlock; - - fn to_output_with_args( - &self, - args: &HashMap, - args_used: &mut HashSet, - ) -> Self::Output { - let mut condition = Vec::new(); - - for i in self.condition.iter() { - condition.extend(i.to_output_with_args(args, args_used)); - } - - let mut contents = Vec::new(); - - for i in self.content.iter() { - contents.push(i.to_output_with_args(args, args_used)); - } - - OutputRuleBlock { - condition, - content: contents, - } - } -} - impl ToOutputWithArgs for RuleBlockContent { type Output = OutputRuleBlockContent; @@ -74,34 +47,17 @@ impl ToOutputWithArgs for RuleBlockContent { args_used: &mut HashSet, ) -> Self::Output { match self { - Self::RuleBlock(ref m) => { + Self::Rule(ref m) => { let block = m.to_output_with_args(args, args_used); - OutputRuleBlockContent::RuleBlock(Box::new(block)) + OutputRuleBlockContent::Rule(Box::new(block)) } - Self::StyleAttr(ref m) => { - let rule = m.to_output_with_args(args, args_used); - OutputRuleBlockContent::StyleAttr(rule) - } - } - } -} - -impl ToOutputWithArgs for BlockContent { - type Output = OutputBlockContent; - - fn to_output_with_args( - &self, - args: &HashMap, - args_used: &mut HashSet, - ) -> Self::Output { - match self { - Self::RuleBlock(ref m) => { + Self::Block(ref m) => { let block = m.to_output_with_args(args, args_used); - OutputBlockContent::RuleBlock(block) + OutputRuleBlockContent::Block(Box::new(block)) } Self::StyleAttr(ref m) => { let rule = m.to_output_with_args(args, args_used); - OutputBlockContent::StyleAttr(rule) + OutputRuleBlockContent::StyleAttr(rule) } } } @@ -154,31 +110,6 @@ impl ToOutputWithArgs for Block { } } -impl ToOutputWithArgs for RuleContent { - type Output = OutputRuleContent; - - fn to_output_with_args( - &self, - args: &HashMap, - args_used: &mut HashSet, - ) -> Self::Output { - match self { - Self::Block(ref m) => { - let block = m.to_output_with_args(args, args_used); - OutputRuleContent::Block(block) - } - Self::RuleBlock(ref m) => { - let rule_block = m.to_output_with_args(args, args_used); - OutputRuleContent::RuleBlock(rule_block) - } - Self::Rule(ref m) => { - let rule = m.to_output_with_args(args, args_used); - OutputRuleContent::Rule(rule) - } // Self::String(ref m) => OutputRuleContent::String(m.as_ref().to_string()), - } - } -} - impl ToOutputWithArgs for StringFragment { type Output = Vec; diff --git a/packages/stylist-macros/src/output/block.rs b/packages/stylist-macros/src/output/block.rs index e4952e6..3c06baa 100644 --- a/packages/stylist-macros/src/output/block.rs +++ b/packages/stylist-macros/src/output/block.rs @@ -1,11 +1,11 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputBlockContent, OutputSelector, Reify}; +use super::{ContextRecorder, IntoCowVecTokens, OutputRuleBlockContent, OutputSelector, Reify}; use proc_macro2::TokenStream; use quote::quote; #[derive(Debug)] pub struct OutputBlock { pub condition: Vec, - pub content: Vec, + pub content: Vec, } impl Reify for OutputBlock { diff --git a/packages/stylist-macros/src/output/block_content.rs b/packages/stylist-macros/src/output/block_content.rs deleted file mode 100644 index 8e8b0a4..0000000 --- a/packages/stylist-macros/src/output/block_content.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::{ContextRecorder, OutputAttribute, OutputRuleBlock, Reify}; -use proc_macro2::TokenStream; -use quote::quote; - -#[derive(Debug)] -pub enum OutputBlockContent { - RuleBlock(OutputRuleBlock), - StyleAttr(OutputAttribute), -} - -impl Reify for OutputBlockContent { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - match self { - Self::RuleBlock(m) => { - let tokens = m.into_token_stream(ctx); - - quote! { ::stylist::ast::BlockContent::RuleBlock(#tokens) } - } - Self::StyleAttr(m) => { - let tokens = m.into_token_stream(ctx); - - quote! { ::stylist::ast::BlockContent::StyleAttr(#tokens) } - } - } - } -} diff --git a/packages/stylist-macros/src/output/mod.rs b/packages/stylist-macros/src/output/mod.rs index 194f9b8..af3d965 100644 --- a/packages/stylist-macros/src/output/mod.rs +++ b/packages/stylist-macros/src/output/mod.rs @@ -4,10 +4,8 @@ use proc_macro2::TokenStream; mod block; -mod block_content; mod rule; -mod rule_block; -mod rule_content; +mod rule_block_content; mod scope_content; mod selector; mod sheet; @@ -18,10 +16,8 @@ mod context_recorder; mod maybe_static; pub use block::OutputBlock; -pub use block_content::OutputBlockContent; pub use rule::OutputRule; -pub use rule_block::{OutputRuleBlock, OutputRuleBlockContent}; -pub use rule_content::OutputRuleContent; +pub use rule_block_content::OutputRuleBlockContent; pub use scope_content::OutputScopeContent; pub use selector::OutputSelector; pub use sheet::OutputSheet; diff --git a/packages/stylist-macros/src/output/rule.rs b/packages/stylist-macros/src/output/rule.rs index 593eca5..1169d2f 100644 --- a/packages/stylist-macros/src/output/rule.rs +++ b/packages/stylist-macros/src/output/rule.rs @@ -1,5 +1,6 @@ use super::{ - fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment, OutputRuleContent, Reify, + fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment, OutputRuleBlockContent, + Reify, }; use itertools::Itertools; use proc_macro2::TokenStream; @@ -8,7 +9,7 @@ use quote::quote; #[derive(Debug)] pub struct OutputRule { pub condition: Vec, - pub content: Vec, + pub content: Vec, } impl Reify for OutputRule { diff --git a/packages/stylist-macros/src/output/rule_block.rs b/packages/stylist-macros/src/output/rule_block.rs deleted file mode 100644 index fed6a7d..0000000 --- a/packages/stylist-macros/src/output/rule_block.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputAttribute, OutputFragment, Reify}; -use proc_macro2::TokenStream; -use quote::quote; - -#[derive(Debug)] -pub enum OutputRuleBlockContent { - RuleBlock(Box), - StyleAttr(OutputAttribute), -} - -impl Reify for OutputRuleBlockContent { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - match self { - Self::RuleBlock(m) => { - let tokens = m.into_token_stream(ctx); - - quote! { ::stylist::ast::RuleBlockContent::RuleBlock(::std::boxed::Box::new(#tokens)) } - } - Self::StyleAttr(m) => { - let tokens = m.into_token_stream(ctx); - - quote! { ::stylist::ast::RuleBlockContent::StyleAttr(#tokens) } - } - } - } -} - -#[derive(Debug)] -pub struct OutputRuleBlock { - pub condition: Vec, - pub content: Vec, -} - -impl Reify for OutputRuleBlock { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - let condition = self.condition.into_cow_vec_tokens(ctx); - let content = self.content.into_cow_vec_tokens(ctx); - - quote! { - ::stylist::ast::RuleBlock { - condition: #condition, - content: #content, - } - } - } -} diff --git a/packages/stylist-macros/src/output/rule_block_content.rs b/packages/stylist-macros/src/output/rule_block_content.rs new file mode 100644 index 0000000..20d1f4d --- /dev/null +++ b/packages/stylist-macros/src/output/rule_block_content.rs @@ -0,0 +1,32 @@ +use super::{ContextRecorder, OutputAttribute, OutputBlock, OutputRule, Reify}; +use proc_macro2::TokenStream; +use quote::quote; + +#[derive(Debug)] +pub enum OutputRuleBlockContent { + Rule(Box), + Block(Box), + StyleAttr(OutputAttribute), +} + +impl Reify for OutputRuleBlockContent { + fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + match self { + Self::Rule(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::RuleBlockContent::Rule(::std::boxed::Box::new(#tokens)) } + } + Self::Block(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::RuleBlockContent::Block(::std::boxed::Box::new(#tokens)) } + } + Self::StyleAttr(m) => { + let tokens = m.into_token_stream(ctx); + + quote! { ::stylist::ast::RuleBlockContent::StyleAttr(#tokens) } + } + } + } +} diff --git a/packages/stylist-macros/src/output/rule_content.rs b/packages/stylist-macros/src/output/rule_content.rs deleted file mode 100644 index 766f34a..0000000 --- a/packages/stylist-macros/src/output/rule_content.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::{ContextRecorder, OutputBlock, OutputRule, OutputRuleBlock, Reify}; -use proc_macro2::TokenStream; -use quote::quote; - -#[derive(Debug)] -pub enum OutputRuleContent { - Rule(OutputRule), - Block(OutputBlock), - // String(String), - RuleBlock(OutputRuleBlock), -} - -impl Reify for OutputRuleContent { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { - match self { - Self::Rule(rule) => { - let tokens = rule.into_token_stream(ctx); - quote! { ::stylist::ast::RuleContent::Rule(::std::boxed::Box::new(#tokens)) } - } - Self::Block(block) => { - let tokens = block.into_token_stream(ctx); - quote! { ::stylist::ast::RuleContent::Block(#tokens) } - } - // Self::String(ref s) => { - // let s = Literal::string(s); - // quote! { ::stylist::ast::RuleContent::String(#s.into()) } - // } - Self::RuleBlock(m) => { - let tokens = m.into_token_stream(ctx); - quote! { ::stylist::ast::RuleContent::RuleBlock(#tokens) } - } - } - } -} diff --git a/packages/stylist/src/utils.rs b/packages/stylist/src/utils.rs index 5048608..5300021 100644 --- a/packages/stylist/src/utils.rs +++ b/packages/stylist/src/utils.rs @@ -1,5 +1,5 @@ #[cfg(feature = "random")] -pub fn get_rand_str() -> String { +pub(crate) fn get_rand_str() -> String { use rand::{distributions::Alphanumeric, thread_rng, Rng}; let mut rng = thread_rng(); @@ -12,7 +12,7 @@ pub fn get_rand_str() -> String { } #[cfg(any(test, not(feature = "random")))] -pub fn get_next_style_id() -> String { +pub(crate) fn get_next_style_id() -> String { use once_cell::sync::Lazy; use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/packages/stylist/tests/macro_sheet_tests.rs b/packages/stylist/tests/macro_sheet_tests.rs index 1fb8858..e3ae8b2 100644 --- a/packages/stylist/tests/macro_sheet_tests.rs +++ b/packages/stylist/tests/macro_sheet_tests.rs @@ -70,28 +70,28 @@ fn test_sheet_interpolation() { ScopeContent::Rule(Rule { condition: vec!["@keyframes myframe".into()].into(), content: vec![ - RuleContent::RuleBlock(RuleBlock { + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["from".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["100px".into()].into(), })] .into(), - }), - RuleContent::RuleBlock(RuleBlock { + })), + RuleBlockContent::Rule(Box::new(Rule { condition: vec!["to".into()].into(), content: vec![RuleBlockContent::StyleAttr(StyleAttribute { key: "width".into(), value: vec!["200px".into()].into(), })] .into(), - }), + })), ] .into(), }), ScopeContent::Rule(Rule { condition: vec!["@media screen and ".into(), "(max-width: 500px)".into()].into(), - content: vec![RuleContent::Block(Block { + content: vec![RuleBlockContent::Block(Box::new(Block { condition: vec![].into(), content: vec![StyleAttribute { key: "background-color".into(), @@ -99,7 +99,7 @@ fn test_sheet_interpolation() { } .into()] .into(), - })] + }))] .into(), }), ]); From 6f86cbde3c136e47e174cfadcb41809ddf1e4f75 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sun, 12 Sep 2021 16:24:38 +0900 Subject: [PATCH 21/27] ContextRecorder -> ReifyContext. --- .../src/inline/component_value/mod.rs | 13 ++++++----- .../{context_recorder.rs => context.rs} | 6 ++--- .../stylist-macros/src/output/maybe_static.rs | 6 ++--- packages/stylist-macros/src/output/mod.rs | 8 +++---- packages/stylist-macros/src/output/rule.rs | 6 ++--- .../src/output/rule_block_content.rs | 4 ++-- .../src/output/scope_content.rs | 4 ++-- .../stylist-macros/src/output/selector.rs | 4 ++-- packages/stylist-macros/src/output/sheet.rs | 4 ++-- .../stylist-macros/src/output/str_frag.rs | 4 ++-- .../stylist-macros/src/output/style_attr.rs | 4 ++-- packages/stylist/src/ast.rs | 22 ++++++++++--------- 12 files changed, 44 insertions(+), 41 deletions(-) rename packages/stylist-macros/src/output/{context_recorder.rs => context.rs} (94%) diff --git a/packages/stylist-macros/src/inline/component_value/mod.rs b/packages/stylist-macros/src/inline/component_value/mod.rs index f673631..908f3c6 100644 --- a/packages/stylist-macros/src/inline/component_value/mod.rs +++ b/packages/stylist-macros/src/inline/component_value/mod.rs @@ -18,16 +18,17 @@ use syn::{ token, Lit, }; +mod function_token; +mod interpolated_expression; mod preserved_token; -pub use preserved_token::PreservedToken; mod simple_block; -pub use simple_block::{BlockKind, SimpleBlock}; -mod function_token; -pub use function_token::FunctionToken; mod stream; -pub use stream::ComponentValueStream; -mod interpolated_expression; + +pub use function_token::FunctionToken; pub use interpolated_expression::InterpolatedExpression; +pub use preserved_token::PreservedToken; +pub use simple_block::{BlockKind, SimpleBlock}; +pub use stream::ComponentValueStream; #[derive(Debug, Clone)] pub enum ComponentValue { diff --git a/packages/stylist-macros/src/output/context_recorder.rs b/packages/stylist-macros/src/output/context.rs similarity index 94% rename from packages/stylist-macros/src/output/context_recorder.rs rename to packages/stylist-macros/src/output/context.rs index c9486a0..72aeae9 100644 --- a/packages/stylist-macros/src/output/context_recorder.rs +++ b/packages/stylist-macros/src/output/context.rs @@ -24,11 +24,11 @@ enum AllowedUsage { } #[derive(Debug, Clone)] -pub struct ContextRecorder { +pub struct ReifyContext { usage: AllowedUsage, } -impl Default for ContextRecorder { +impl Default for ReifyContext { fn default() -> Self { Self { usage: AllowedUsage::Static, @@ -36,7 +36,7 @@ impl Default for ContextRecorder { } } -impl ContextRecorder { +impl ReifyContext { pub fn new() -> Self { Self::default() } diff --git a/packages/stylist-macros/src/output/maybe_static.rs b/packages/stylist-macros/src/output/maybe_static.rs index 6895142..9dc8bf4 100644 --- a/packages/stylist-macros/src/output/maybe_static.rs +++ b/packages/stylist-macros/src/output/maybe_static.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, Reify}; +use super::{Reify, ReifyContext}; use proc_macro2::TokenStream; use quote::quote; @@ -10,7 +10,7 @@ where // as elements the values formed by the expressions in this stream. // Depending on the context in which the expression can be expanded, // uses either Cow::Owned or Cow::Borrowed (currently always Cow::Owned). - fn into_cow_vec_tokens(self, ctx: &mut ContextRecorder) -> TokenStream; + fn into_cow_vec_tokens(self, ctx: &mut ReifyContext) -> TokenStream; } impl IntoCowVecTokens for I @@ -18,7 +18,7 @@ where I: IntoIterator, I::Item: Reify, { - fn into_cow_vec_tokens(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_cow_vec_tokens(self, ctx: &mut ReifyContext) -> TokenStream { let contents: Vec = self.into_iter().map(|m| m.into_token_stream(ctx)).collect(); diff --git a/packages/stylist-macros/src/output/mod.rs b/packages/stylist-macros/src/output/mod.rs index af3d965..051b8fd 100644 --- a/packages/stylist-macros/src/output/mod.rs +++ b/packages/stylist-macros/src/output/mod.rs @@ -12,7 +12,7 @@ mod sheet; mod str_frag; mod style_attr; -mod context_recorder; +mod context; mod maybe_static; pub use block::OutputBlock; @@ -24,16 +24,16 @@ pub use sheet::OutputSheet; pub use str_frag::{fragment_coalesce, OutputFragment}; pub use style_attr::OutputAttribute; -pub use context_recorder::ContextRecorder; +pub use context::ReifyContext; pub use maybe_static::IntoCowVecTokens; /// Reify a structure into an expression of a specific type. pub trait Reify { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream; + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream; } impl Reify for syn::Error { - fn into_token_stream(self, _ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, _ctx: &mut ReifyContext) -> TokenStream { self.into_compile_error() } } diff --git a/packages/stylist-macros/src/output/rule.rs b/packages/stylist-macros/src/output/rule.rs index 1169d2f..d73bc5e 100644 --- a/packages/stylist-macros/src/output/rule.rs +++ b/packages/stylist-macros/src/output/rule.rs @@ -1,6 +1,6 @@ use super::{ - fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment, OutputRuleBlockContent, - Reify, + fragment_coalesce, IntoCowVecTokens, OutputFragment, OutputRuleBlockContent, Reify, + ReifyContext, }; use itertools::Itertools; use proc_macro2::TokenStream; @@ -13,7 +13,7 @@ pub struct OutputRule { } impl Reify for OutputRule { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { let condition = self .condition .into_iter() diff --git a/packages/stylist-macros/src/output/rule_block_content.rs b/packages/stylist-macros/src/output/rule_block_content.rs index 20d1f4d..17a7ecf 100644 --- a/packages/stylist-macros/src/output/rule_block_content.rs +++ b/packages/stylist-macros/src/output/rule_block_content.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, OutputAttribute, OutputBlock, OutputRule, Reify}; +use super::{OutputAttribute, OutputBlock, OutputRule, Reify, ReifyContext}; use proc_macro2::TokenStream; use quote::quote; @@ -10,7 +10,7 @@ pub enum OutputRuleBlockContent { } impl Reify for OutputRuleBlockContent { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { match self { Self::Rule(m) => { let tokens = m.into_token_stream(ctx); diff --git a/packages/stylist-macros/src/output/scope_content.rs b/packages/stylist-macros/src/output/scope_content.rs index 3041f20..717f8fe 100644 --- a/packages/stylist-macros/src/output/scope_content.rs +++ b/packages/stylist-macros/src/output/scope_content.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, OutputBlock, OutputRule, Reify}; +use super::{OutputBlock, OutputRule, Reify, ReifyContext}; use proc_macro2::TokenStream; use quote::quote; @@ -9,7 +9,7 @@ pub enum OutputScopeContent { } impl Reify for OutputScopeContent { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { match self { Self::Rule(rule) => { let block_tokens = rule.into_token_stream(ctx); diff --git a/packages/stylist-macros/src/output/selector.rs b/packages/stylist-macros/src/output/selector.rs index c107b0f..ea469f5 100644 --- a/packages/stylist-macros/src/output/selector.rs +++ b/packages/stylist-macros/src/output/selector.rs @@ -1,4 +1,4 @@ -use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment, Reify}; +use super::{fragment_coalesce, IntoCowVecTokens, OutputFragment, Reify, ReifyContext}; use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; @@ -9,7 +9,7 @@ pub struct OutputSelector { } impl Reify for OutputSelector { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { let parts = self .selectors .into_iter() diff --git a/packages/stylist-macros/src/output/sheet.rs b/packages/stylist-macros/src/output/sheet.rs index e8241ce..bbfef22 100644 --- a/packages/stylist-macros/src/output/sheet.rs +++ b/packages/stylist-macros/src/output/sheet.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputScopeContent, Reify}; +use super::{IntoCowVecTokens, OutputScopeContent, Reify, ReifyContext}; use proc_macro2::TokenStream; use quote::quote; @@ -8,7 +8,7 @@ pub struct OutputSheet { } impl Reify for OutputSheet { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { let contents = self.contents.into_cow_vec_tokens(ctx); let quoted_sheet = quote! { diff --git a/packages/stylist-macros/src/output/str_frag.rs b/packages/stylist-macros/src/output/str_frag.rs index 5607650..00df251 100644 --- a/packages/stylist-macros/src/output/str_frag.rs +++ b/packages/stylist-macros/src/output/str_frag.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, Reify}; +use super::{Reify, ReifyContext}; use crate::{ inline::{component_value::PreservedToken, css_ident::CssIdent}, literal::argument::Argument, @@ -101,7 +101,7 @@ impl OutputFragment { } impl Reify for OutputFragment { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { match self.try_into_string() { Err(t) => { ctx.uses_dynamic_argument(); diff --git a/packages/stylist-macros/src/output/style_attr.rs b/packages/stylist-macros/src/output/style_attr.rs index 2dca1fe..883cc45 100644 --- a/packages/stylist-macros/src/output/style_attr.rs +++ b/packages/stylist-macros/src/output/style_attr.rs @@ -1,4 +1,4 @@ -use super::{fragment_coalesce, ContextRecorder, IntoCowVecTokens, OutputFragment, Reify}; +use super::{fragment_coalesce, IntoCowVecTokens, OutputFragment, Reify, ReifyContext}; use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; @@ -10,7 +10,7 @@ pub struct OutputAttribute { } impl Reify for OutputAttribute { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { let key = self.key.into_token_stream(ctx); let value_parts = self .values diff --git a/packages/stylist/src/ast.rs b/packages/stylist/src/ast.rs index 7e668d7..20dbe8f 100644 --- a/packages/stylist/src/ast.rs +++ b/packages/stylist/src/ast.rs @@ -3,16 +3,18 @@ //! ```text //! struct Sheet //! └── Vec -//! ├── struct Block -//! │ ├── condition: Vec -//! │ └── Vec -//! │ ├── key: String -//! │ └── value: Vec -//! └── struct Rule -//! ├── condition: Vec -//! └── Vec -//! ├── Block (*) -//! └── Rule (*) +//! ├── struct Block +//! │ ├── condition: Vec +//! │ │ └── fragments: Vec +//! │ └── content: Vec +//! │ ├── StyleAttr (*) +//! │ │ ├── key: String +//! │ │ └── value: Vec +//! │ ├── Block (*) +//! │ └── Rule (*) +//! └── content: struct Rule +//! ├── condition: Vec +//! └── Vec //! ``` //! //! # Note From d3a3a8593dfd0e231c367f38a919d061b4a26607 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Sun, 12 Sep 2021 16:38:22 +0900 Subject: [PATCH 22/27] Expose some methods on StyleContext. --- packages/stylist-core/src/ast/context.rs | 20 ++++++++++++-------- packages/stylist-macros/src/inline/mod.rs | 4 ++-- packages/stylist-macros/src/literal/mod.rs | 4 ++-- packages/stylist-macros/src/output/block.rs | 4 ++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 73a0d82..4a89747 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -14,7 +14,7 @@ pub struct StyleContext<'a> { } impl<'a> StyleContext<'a> { - pub(crate) fn new(class_name: Option<&'a str>) -> Self { + pub fn new(class_name: Option<&'a str>) -> Self { Self { parent_ctx: None, class_name, @@ -29,6 +29,10 @@ impl<'a> StyleContext<'a> { self.is_open.load(Ordering::Relaxed) } + fn set_open(&self, value: bool) { + self.is_open.store(value, Ordering::Relaxed); + } + // We close until we can find a parent that has nothing differs from current path. fn close_until_common_parent(&self, w: &mut String) { while let Some(m) = self.open_parent() { @@ -99,7 +103,7 @@ impl<'a> StyleContext<'a> { self.write_padding_impl(w, self.common_conditions().len()) } - pub(crate) fn finish(&self, w: &mut String) { + pub fn finish(&self, w: &mut String) { if self.is_open() { for i in (0..self.unique_conditions().len()).rev() { self.write_min_padding(w); @@ -107,10 +111,10 @@ impl<'a> StyleContext<'a> { w.push_str("}\n"); } } - self.is_open.store(false, Ordering::Relaxed); + self.set_open(false); } - pub(crate) fn start(&self, w: &mut String) { + pub fn start(&self, w: &mut String) { if !self.is_open() { self.close_until_common_parent(w); @@ -121,14 +125,14 @@ impl<'a> StyleContext<'a> { w.push_str(" {\n"); } } - self.is_open.store(true, Ordering::Relaxed); + self.set_open(true); } - pub(crate) fn write_padding(&self, w: &mut String) { + pub fn write_padding(&self, w: &mut String) { self.write_padding_impl(w, self.conditions().len()); } - pub(crate) fn with_block_condition(&'a self, cond: Option) -> Self + pub fn with_block_condition(&'a self, cond: Option) -> Self where S: Into>, { @@ -154,7 +158,7 @@ impl<'a> StyleContext<'a> { } } - pub(crate) fn with_rule_condition>>(&'a self, cond: S) -> Self { + pub fn with_rule_condition>>(&'a self, cond: S) -> Self { let mut rules = self.rules.clone(); rules.push(cond.into()); diff --git a/packages/stylist-macros/src/inline/mod.rs b/packages/stylist-macros/src/inline/mod.rs index cbce3e4..c723907 100644 --- a/packages/stylist-macros/src/inline/mod.rs +++ b/packages/stylist-macros/src/inline/mod.rs @@ -3,7 +3,7 @@ pub mod css_ident; mod parse; -use crate::output::{ContextRecorder, Reify}; +use crate::output::{Reify, ReifyContext}; use log::debug; use parse::{CssRootNode, IntoOutputContext}; use proc_macro2::TokenStream; @@ -22,7 +22,7 @@ pub fn macro_fn(input: TokenStream) -> TokenStream { if let Some(m) = into_output_ctx.into_compile_errors() { m } else { - let mut ctx = ContextRecorder::new(); + let mut ctx = ReifyContext::new(); output_root.into_token_stream(&mut ctx) } } diff --git a/packages/stylist-macros/src/literal/mod.rs b/packages/stylist-macros/src/literal/mod.rs index 2acc80a..53dbef7 100644 --- a/packages/stylist-macros/src/literal/mod.rs +++ b/packages/stylist-macros/src/literal/mod.rs @@ -15,7 +15,7 @@ mod to_output_with_args; use argument::Argument; use to_output_with_args::ToOutputWithArgs; -use crate::output::{ContextRecorder, Reify}; +use crate::output::{Reify, ReifyContext}; pub(crate) fn macro_fn(input: TokenStream) -> TokenStream { let mut tokens = input.into_iter(); @@ -129,6 +129,6 @@ pub(crate) fn macro_fn(input: TokenStream) -> TokenStream { } } - let mut ctx = ContextRecorder::new(); + let mut ctx = ReifyContext::new(); output.into_token_stream(&mut ctx) } diff --git a/packages/stylist-macros/src/output/block.rs b/packages/stylist-macros/src/output/block.rs index 3c06baa..c0c595b 100644 --- a/packages/stylist-macros/src/output/block.rs +++ b/packages/stylist-macros/src/output/block.rs @@ -1,4 +1,4 @@ -use super::{ContextRecorder, IntoCowVecTokens, OutputRuleBlockContent, OutputSelector, Reify}; +use super::{IntoCowVecTokens, OutputRuleBlockContent, OutputSelector, Reify, ReifyContext}; use proc_macro2::TokenStream; use quote::quote; @@ -9,7 +9,7 @@ pub struct OutputBlock { } impl Reify for OutputBlock { - fn into_token_stream(self, ctx: &mut ContextRecorder) -> TokenStream { + fn into_token_stream(self, ctx: &mut ReifyContext) -> TokenStream { let condition = self.condition.into_cow_vec_tokens(ctx); let content = self.content.into_cow_vec_tokens(ctx); From b9e54a30e1a3537a2e88ea2127faf05e37eb44b6 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 14 Sep 2021 20:06:21 +0900 Subject: [PATCH 23/27] Address some review comments. --- packages/stylist-core/src/ast/context.rs | 85 +++++++++---------- .../stylist-macros/src/inline/parse/rule.rs | 14 +-- .../stylist-macros/src/inline/parse/scope.rs | 4 +- 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 4a89747..1e83ec6 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::iter::empty; use std::sync::atomic::{AtomicBool, Ordering}; /// A context to faciliate [`ToStyleStr`](super::ToStyleStr). @@ -8,7 +9,8 @@ pub struct StyleContext<'a> { parent_ctx: Option<&'a StyleContext<'a>>, rules: Vec>, - selectors: Vec>, + // selectors: Vec>>, + selector: Option>, is_open: AtomicBool, } @@ -19,7 +21,7 @@ impl<'a> StyleContext<'a> { parent_ctx: None, class_name, rules: Vec::new(), - selectors: Vec::new(), + selector: None, is_open: AtomicBool::new(false), } @@ -36,7 +38,7 @@ impl<'a> StyleContext<'a> { // We close until we can find a parent that has nothing differs from current path. fn close_until_common_parent(&self, w: &mut String) { while let Some(m) = self.open_parent() { - if self.differ_conditions().is_empty() { + if self.differ_conditions().next().is_none() { break; } m.finish(w); @@ -53,44 +55,34 @@ impl<'a> StyleContext<'a> { }) } - fn conditions(&self) -> Vec<&str> { + fn conditions(&self) -> impl Iterator { self.rules .iter() - .chain(self.selectors.iter()) + .chain(self.selector.iter()) .map(|m| m.as_ref()) - .collect() - } - - fn common_conditions(&self) -> Vec<&str> { - match self.open_parent() { - Some(m) => self - .conditions() - .into_iter() - .zip(m.conditions()) - .filter_map(|(m1, m2)| if m1 == m2 { Some(m1) } else { None }) - .collect(), - None => Vec::new(), - } + } + + fn common_conditions(&self) -> impl Iterator { + self.open_parent() + .map(|m| Box::new(m.conditions()) as Box>) + .unwrap_or_else(|| Box::new(empty())) + .zip(self.conditions()) + .filter_map(|(m1, m2)| if m1 == m2 { Some(m1) } else { None }) } /// Calculate the layers that current context differs from the parent context - fn unique_conditions(&self) -> Vec<&str> { + fn unique_conditions(&self) -> impl Iterator { self.conditions() .into_iter() - .skip(self.common_conditions().len()) - .collect() + .skip(self.common_conditions().count()) } /// Calculate the layers that parent context differs from current context - fn differ_conditions(&self) -> Vec<&str> { - match self.open_parent() { - Some(m) => m - .conditions() - .into_iter() - .skip(self.common_conditions().len()) - .collect(), - None => Vec::new(), - } + fn differ_conditions(&self) -> impl Iterator { + self.open_parent() + .map(|m| Box::new(m.conditions()) as Box>) + .unwrap_or_else(|| Box::new(empty())) + .skip(self.common_conditions().count()) } fn write_padding_impl(&self, w: &mut String, no: usize) { @@ -100,12 +92,12 @@ impl<'a> StyleContext<'a> { } fn write_min_padding(&self, w: &mut String) { - self.write_padding_impl(w, self.common_conditions().len()) + self.write_padding_impl(w, self.common_conditions().count()) } pub fn finish(&self, w: &mut String) { if self.is_open() { - for i in (0..self.unique_conditions().len()).rev() { + for i in (0..self.unique_conditions().count()).rev() { self.write_min_padding(w); self.write_padding_impl(w, i); w.push_str("}\n"); @@ -118,7 +110,7 @@ impl<'a> StyleContext<'a> { if !self.is_open() { self.close_until_common_parent(w); - for (index, cond) in self.unique_conditions().iter().enumerate() { + for (index, cond) in self.unique_conditions().enumerate() { self.write_min_padding(w); self.write_padding_impl(w, index); w.push_str(cond); @@ -129,30 +121,29 @@ impl<'a> StyleContext<'a> { } pub fn write_padding(&self, w: &mut String) { - self.write_padding_impl(w, self.conditions().len()); + self.write_padding_impl(w, self.conditions().count()); } pub fn with_block_condition(&'a self, cond: Option) -> Self where S: Into>, { - let mut selectors = self.selectors.clone(); - - if let Some(m) = cond { - selectors.push(m.into()); - } else if self.selectors.is_empty() { - selectors.push( - self.class_name - .map(|m| format!(".{}", m).into()) - .unwrap_or_else(|| "html".into()), - ) - } + // This logic is find as long as there's no + let selector = cond + // Use condition provided + .map(|m| m.into()) + // Use the selector of parent context + .or_else(|| self.selector.clone()) + // Use class name of scope context + .or_else(|| self.class_name.map(|m| format!(".{}", m).into())) + // Use html + .or_else(|| Some("html".into())); Self { parent_ctx: Some(self), class_name: self.class_name, rules: self.rules.clone(), - selectors, + selector, is_open: AtomicBool::new(false), } @@ -166,7 +157,7 @@ impl<'a> StyleContext<'a> { parent_ctx: Some(self), class_name: self.class_name, rules, - selectors: self.selectors.clone(), + selector: self.selector.clone(), is_open: AtomicBool::new(false), } diff --git a/packages/stylist-macros/src/inline/parse/rule.rs b/packages/stylist-macros/src/inline/parse/rule.rs index dbec29d..0d25aa2 100644 --- a/packages/stylist-macros/src/inline/parse/rule.rs +++ b/packages/stylist-macros/src/inline/parse/rule.rs @@ -91,11 +91,12 @@ impl CssAtRule { prelude } - pub fn into_rule_output(mut self, ctx: &mut IntoOutputContext) -> OutputRule { - ctx.extend_errors(self.errors.drain(0..)); + pub fn into_rule_output(self, ctx: &mut IntoOutputContext) -> OutputRule { + let condition = self.condition_output(); + ctx.extend_errors(self.errors); OutputRule { - condition: self.condition_output(), + condition, content: match self.contents { CssAtRuleContent::Scope(m) => m.into_rule_output(ctx), CssAtRuleContent::Empty(_) => Vec::new(), @@ -103,11 +104,12 @@ impl CssAtRule { } } - pub fn into_rule_block_output(mut self, ctx: &mut IntoOutputContext) -> OutputRule { - ctx.extend_errors(self.errors.drain(0..)); + pub fn into_rule_block_output(self, ctx: &mut IntoOutputContext) -> OutputRule { + let condition = self.condition_output(); + ctx.extend_errors(self.errors); OutputRule { - condition: self.condition_output(), + condition, content: match self.contents { CssAtRuleContent::Scope(m) => m.into_rule_block_output(ctx), CssAtRuleContent::Empty(_) => Vec::new(), diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index e7f52b9..37fc1c7 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -1,3 +1,5 @@ +use std::mem; + use syn::{ braced, parse::{Error as ParseError, Parse, ParseBuffer, Result as ParseResult}, @@ -32,7 +34,7 @@ impl CssScope { ctx: &mut IntoOutputContext| { if !attrs.is_empty() { contents.push(OutputRuleBlockContent::Block(Box::new( - CssQualifiedRule::into_dangling_output(attrs.drain(0..).collect(), ctx), + CssQualifiedRule::into_dangling_output(mem::take(attrs), ctx), ))); } }; From 757cea639bc11a2917083a6835de51839ac19361 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 14 Sep 2021 20:14:04 +0900 Subject: [PATCH 24/27] Address Parser Comments. --- packages/stylist-core/src/parser.rs | 35 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/stylist-core/src/parser.rs b/packages/stylist-core/src/parser.rs index 4cb3f4e..a691d18 100644 --- a/packages/stylist-core/src/parser.rs +++ b/packages/stylist-core/src/parser.rs @@ -178,7 +178,7 @@ impl Parser { fn attribute(i: &str) -> IResult<&str, StyleAttribute, VerboseError<&str>> { traced_context( "StyleAttribute", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( separated_pair( // Key Self::style_attr_key, @@ -208,7 +208,7 @@ impl Parser { traced_context( "StyleAttributes", - expect_non_empty(Self::trimmed(terminated( + Self::trimmed(expect_non_empty(terminated( separated_list1(tag(";"), Self::attribute), preceded(opt(Self::sp), final_semicolon), ))), @@ -226,7 +226,7 @@ impl Parser { terminated(many0(alt((is_not(r#"\""#), escaped_char))), tag("\"")), )); - traced_context("String", expect_non_empty(Self::trimmed(parse_str)))(i) + traced_context("String", Self::trimmed(expect_non_empty(parse_str)))(i) } /// Parse a string interpolation. @@ -235,7 +235,7 @@ impl Parser { fn interpolation(i: &str) -> IResult<&str, &str, VerboseError<&str>> { traced_context( "Interpolation", - expect_non_empty(Self::trimmed(delimited( + Self::trimmed(expect_non_empty(delimited( tag("${"), Self::trimmed(recognize(preceded( alpha1, @@ -252,7 +252,7 @@ impl Parser { fn selector(i: &str) -> IResult<&str, Selector, VerboseError<&str>> { traced_context( "Selector", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( recognize(many1(alt(( recognize(preceded(none_of("$,}@{\""), opt(is_not("$,\"{")))), Self::string, @@ -267,7 +267,7 @@ impl Parser { fn condition(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { traced_context( "Condition", - expect_non_empty(Self::trimmed(many1(terminated( + Self::trimmed(expect_non_empty(many1(terminated( Self::selector, opt(tag(",")), )))), @@ -277,7 +277,7 @@ impl Parser { fn block_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { traced_context( "BlockContents", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( many0(alt(( // Either Style Attributes map( @@ -298,12 +298,11 @@ impl Parser { /// Parse a [`Block`]. fn block(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { traced_context( - "StyleBlock", - expect_non_empty(Self::trimmed(map( - separated_pair( + "Block", + Self::trimmed(expect_non_empty(map( + pair( Self::condition, - tag("{"), - terminated(Self::trimmed(Self::block_contents), tag("}")), + delimited(tag("{"), Self::trimmed(Self::block_contents), tag("}")), ), |p: (Vec, Vec)| { ScopeContent::Block(Block { @@ -349,7 +348,7 @@ impl Parser { traced_context( "RuleBlock", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( separated_pair( // Collect at Rules. cond, @@ -370,7 +369,7 @@ impl Parser { fn dangling_block(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { traced_context( "DanglingBlock", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( |i| Self::attributes(i, true), |attr: Vec| { ScopeContent::Block(Block { @@ -390,7 +389,7 @@ impl Parser { fn scope(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { traced_context( "Scope", - expect_non_empty(Self::trimmed(Parser::scope_contents)), + Self::trimmed(expect_non_empty(Parser::scope_contents)), )(i) } @@ -405,7 +404,7 @@ impl Parser { traced_context( "AtRuleCondition", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( pair( tags, map( @@ -458,7 +457,7 @@ impl Parser { fn at_rule(i: &str) -> IResult<&str, ScopeContent, VerboseError<&str>> { traced_context( "AtRule", - expect_non_empty(Self::trimmed(map( + Self::trimmed(expect_non_empty(map( separated_pair( // Collect at Rules. |i| Self::at_rule_condition(i, (tag("@supports"), tag("@media"))), @@ -488,7 +487,7 @@ impl Parser { fn scope_contents(i: &str) -> IResult<&str, Vec, VerboseError<&str>> { traced_context( "ScopeContents", - expect_non_empty(Self::trimmed(many0(alt(( + Self::trimmed(expect_non_empty(many0(alt(( // Either a dangling block Parser::dangling_block, // Or a Block From 215eac86bd1cea96c84217c5d3ad353294680900 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 14 Sep 2021 21:20:09 +0900 Subject: [PATCH 25/27] Address more comments. --- packages/stylist-core/src/ast/rule.rs | 7 +++++-- packages/stylist-core/src/lib.rs | 9 +++++++++ packages/stylist-macros/src/inline/parse/mod.rs | 7 ++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index e971a44..0583256 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -31,8 +31,11 @@ impl ToStyleStr for Rule { } let mut rule_ctx = ctx.with_rule_condition(&cond); - if cond.starts_with("@keyframes") { - rule_ctx.start(w); // keyframes should always be printed. + + // keyframes should always be printed as they contain a global name. + let always_print = cond.starts_with("@keyframes"); + if always_print { + rule_ctx.start(w); } for i in self.content.iter() { diff --git a/packages/stylist-core/src/lib.rs b/packages/stylist-core/src/lib.rs index 1e0db08..7059feb 100644 --- a/packages/stylist-core/src/lib.rs +++ b/packages/stylist-core/src/lib.rs @@ -38,8 +38,11 @@ mod tests { border: 1px solid black; @supports (max-width: 500px) { + max-width: 500px; + @media screen and (max-width: 500px) { display: flex; + flex-direction: row; } } } @@ -68,10 +71,16 @@ mod tests { .test-style-cls header, .test-style-cls footer { border: 1px solid black; } +@supports (max-width: 500px) { + .test-style-cls header, .test-style-cls footer { + max-width: 500px; + } +} @supports (max-width: 500px) { @media screen and (max-width: 500px) { .test-style-cls header, .test-style-cls footer { display: flex; + flex-direction: row; } } } diff --git a/packages/stylist-macros/src/inline/parse/mod.rs b/packages/stylist-macros/src/inline/parse/mod.rs index 6848ef8..b539de7 100644 --- a/packages/stylist-macros/src/inline/parse/mod.rs +++ b/packages/stylist-macros/src/inline/parse/mod.rs @@ -1,4 +1,5 @@ use proc_macro2::TokenStream; +use syn::parse::Error as ParseError; use crate::output::OutputFragment; @@ -20,7 +21,7 @@ pub use scope_content::CssScopeContent; #[derive(Debug, Default)] pub struct IntoOutputContext { - errors: Vec, + errors: Vec, } impl IntoOutputContext { @@ -30,12 +31,12 @@ impl IntoOutputContext { pub fn extend_errors(&mut self, errors: I) where - I: IntoIterator, + I: IntoIterator, { self.errors.extend(errors); } - pub fn push_error(&mut self, error: syn::parse::Error) { + pub fn push_error(&mut self, error: ParseError) { self.errors.push(error); } From 7bd17934cfd150096035df67b8bddb25f0eb2cb2 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 14 Sep 2021 22:46:48 +0900 Subject: [PATCH 26/27] Address comments. --- .../stylist-macros/src/inline/parse/root.rs | 17 ++++++------ .../stylist-macros/src/inline/parse/scope.rs | 12 ++++----- packages/stylist/src/ast.rs | 26 +++++++++---------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/packages/stylist-macros/src/inline/parse/root.rs b/packages/stylist-macros/src/inline/parse/root.rs index ec1698c..c3b854d 100644 --- a/packages/stylist-macros/src/inline/parse/root.rs +++ b/packages/stylist-macros/src/inline/parse/root.rs @@ -1,3 +1,5 @@ +use std::mem; + use super::{CssAttribute, CssQualifiedRule, CssScopeContent, IntoOutputContext}; use crate::output::{OutputScopeContent, OutputSheet}; use syn::parse::{Parse, ParseBuffer, Result as ParseResult}; @@ -19,13 +21,12 @@ impl CssRootNode { let mut contents = Vec::new(); let mut attrs: Vec = Vec::new(); - - let collect_attrs = |attrs: &mut Vec, - contents: &mut Vec, - ctx: &mut IntoOutputContext| { + let flush_attrs = |attrs: &mut Vec, + contents: &mut Vec, + ctx: &mut IntoOutputContext| { if !attrs.is_empty() { contents.push(OutputScopeContent::Block( - CssQualifiedRule::into_dangling_output(attrs.drain(0..).collect(), ctx), + CssQualifiedRule::into_dangling_output(mem::take(attrs), ctx), )); } }; @@ -34,18 +35,18 @@ impl CssRootNode { match scope { CssScopeContent::Attribute(m) => attrs.push(m), CssScopeContent::AtRule(m) => { - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputScopeContent::Rule(m.into_rule_output(ctx))); } CssScopeContent::Nested(m) => { - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputScopeContent::Block(m.into_output(ctx))); } } } - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); OutputSheet { contents } } } diff --git a/packages/stylist-macros/src/inline/parse/scope.rs b/packages/stylist-macros/src/inline/parse/scope.rs index 37fc1c7..51f42e2 100644 --- a/packages/stylist-macros/src/inline/parse/scope.rs +++ b/packages/stylist-macros/src/inline/parse/scope.rs @@ -29,9 +29,9 @@ impl CssScope { let mut attrs = Vec::new(); let mut contents = Vec::new(); - let collect_attrs = |attrs: &mut Vec, - contents: &mut Vec, - ctx: &mut IntoOutputContext| { + let flush_attrs = |attrs: &mut Vec, + contents: &mut Vec, + ctx: &mut IntoOutputContext| { if !attrs.is_empty() { contents.push(OutputRuleBlockContent::Block(Box::new( CssQualifiedRule::into_dangling_output(mem::take(attrs), ctx), @@ -43,19 +43,19 @@ impl CssScope { match scope { CssScopeContent::Attribute(m) => attrs.push(m), CssScopeContent::AtRule(m) => { - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputRuleBlockContent::Rule(Box::new( m.into_rule_output(ctx), ))); } CssScopeContent::Nested(m) => { - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); contents.push(OutputRuleBlockContent::Block(Box::new(m.into_output(ctx)))); } } } - collect_attrs(&mut attrs, &mut contents, ctx); + flush_attrs(&mut attrs, &mut contents, ctx); contents } diff --git a/packages/stylist/src/ast.rs b/packages/stylist/src/ast.rs index 20dbe8f..0b2579b 100644 --- a/packages/stylist/src/ast.rs +++ b/packages/stylist/src/ast.rs @@ -1,20 +1,20 @@ //! This module contains the semantic representation of a CSS StyleSheet. //! //! ```text -//! struct Sheet +//! Sheet //! └── Vec -//! ├── struct Block -//! │ ├── condition: Vec -//! │ │ └── fragments: Vec -//! │ └── content: Vec -//! │ ├── StyleAttr (*) -//! │ │ ├── key: String -//! │ │ └── value: Vec -//! │ ├── Block (*) -//! │ └── Rule (*) -//! └── content: struct Rule -//! ├── condition: Vec -//! └── Vec +//! ├── Block +//! │ ├── condition: Vec +//! │ │ └── fragments: Vec +//! │ └── content: Vec +//! │ ├── StyleAttr +//! │ │ ├── key: String +//! │ │ └── value: Vec +//! │ ├── Block (*) +//! │ └── Rule (*) +//! └── Rule +//! ├── condition: Vec +//! └── Vec //! ``` //! //! # Note From 6bdafe2cf2e340e10855f195e42125c650d1f6b3 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 14 Sep 2021 23:17:05 +0900 Subject: [PATCH 27/27] Apply to `:root`. --- packages/stylist-core/src/ast/context.rs | 2 +- packages/stylist-core/src/ast/selector.rs | 2 +- packages/stylist/src/global_style.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/stylist-core/src/ast/context.rs b/packages/stylist-core/src/ast/context.rs index 1e83ec6..14ce7f1 100644 --- a/packages/stylist-core/src/ast/context.rs +++ b/packages/stylist-core/src/ast/context.rs @@ -137,7 +137,7 @@ impl<'a> StyleContext<'a> { // Use class name of scope context .or_else(|| self.class_name.map(|m| format!(".{}", m).into())) // Use html - .or_else(|| Some("html".into())); + .or_else(|| Some(":root".into())); Self { parent_ctx: Some(self), diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 9486a3b..2fc470e 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -43,7 +43,7 @@ impl ToStyleStr for Selector { // For global styles, if it contains &, it will be replaced with html. } else if joined_s.contains('&') { - w.push_str(&joined_s.replace("&", "html")); + w.push_str(&joined_s.replace("&", ":root")); // For other styles, it will be written as is. } else { w.push_str(&joined_s); diff --git a/packages/stylist/src/global_style.rs b/packages/stylist/src/global_style.rs index 2b3fca5..bbfeeb4 100644 --- a/packages/stylist/src/global_style.rs +++ b/packages/stylist/src/global_style.rs @@ -15,8 +15,8 @@ use crate::{Result, StyleSource}; /// /// This class is equivalent to [`Style`](crate::Style) but for global styles. /// -/// It will replace Current Selectors (`&`) with `html` and apply dangling style attributes to -/// html. +/// It will replace Current Selectors (`&`) with `:root` and apply dangling style attributes to +/// the root element (`html` when style is not applied in a Shadow DOM). #[derive(Debug, Clone)] pub struct GlobalStyle { inner: Rc, @@ -153,7 +153,7 @@ mod tests { GlobalStyle::new("background-color: black;").expect("Failed to create Style."); assert_eq!( global_style.get_style_str(), - r#"html { + r#":root { background-color: black; } "# @@ -184,19 +184,19 @@ mod tests { assert_eq!( global_style.get_style_str(), - r#"html { + r#":root { background-color: black; } .with-class { color: red; } @media screen and (max-width: 600px) { - html { + :root { color: yellow; } } @supports (display: grid) { - html { + :root { display: grid; } }