Skip to content

Commit

Permalink
Connect sass atmedia with css atmedia.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaj committed Jun 19, 2023
1 parent 611a10a commit eea6ee5
Show file tree
Hide file tree
Showing 38 changed files with 698 additions and 284 deletions.
99 changes: 7 additions & 92 deletions rsass/src/css/atrule.rs
Original file line number Diff line number Diff line change
@@ -1,98 +1,10 @@
use super::{
BodyItem, Comment, CustomProperty, Import, Property, Rule, Value,
BodyItem, Comment, CustomProperty, Import, MediaRule, Property, Rule,
Value,
};
use crate::output::CssBuf;
use std::io::{self, Write};

/// An `@media rule in css.
#[derive(Clone, Debug)]
pub struct MediaRule {
args: MediaArgs,
body: Vec<AtRuleBodyItem>,
}

impl MediaRule {
pub(crate) fn new(args: MediaArgs, body: Vec<AtRuleBodyItem>) -> Self {
MediaRule { args, body }
}
pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
buf.do_indent_no_nl();
buf.add_str("@media ");
self.args.write(buf)?;
//if !self.args.is_null() {
//write!(buf, " {}", self.args.format(buf.format()))?;
//}
buf.start_block();
for item in &self.body {
item.write(buf)?;
}
buf.end_block();
Ok(())
}
}

/// The media selection argument of an `@media` rule.
#[derive(Clone, Debug)]
pub enum MediaArgs {
/// `all` media.
Name(String),
/// `(cond: valud)` media.
Condition(String, Value),
/// unary logic
Only(Box<MediaArgs>),
/// Any media subquery in parenthesis.
Paren(Box<MediaArgs>),
/// unary logic
Not(Box<MediaArgs>),
/// a and b and c media.
And(Vec<MediaArgs>),
/// a or b or c media.
Or(Vec<MediaArgs>),
}

impl MediaArgs {
pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
match self {
MediaArgs::Name(name) => write!(buf, "{name}")?,
MediaArgs::Only(a) => {
buf.add_str("only ");
a.write(buf)?;
}
MediaArgs::Not(a) => {
buf.add_str("not ");
a.write(buf)?;
}
MediaArgs::Condition(c, v) => {
write!(buf, "({c}: {})", v.format(buf.format()))?
}
MediaArgs::And(args) => {
if let Some((first, rest)) = args.split_first() {
first.write(buf)?;
for arg in rest {
buf.add_str(" and ");
arg.write(buf)?;
}
}
}
MediaArgs::Or(args) => {
if let Some((first, rest)) = args.split_first() {
first.write(buf)?;
for arg in rest {
buf.add_str(" or ");
arg.write(buf)?;
}
}
}
MediaArgs::Paren(a) => {
buf.add_str("(");
a.write(buf)?;
buf.add_str(")");
}
}
Ok(())
}
}

/// An `@something` rule in css.
///
/// Note that some well-known at rules (`@media`, `@keyframes`, ...)
Expand Down Expand Up @@ -148,6 +60,8 @@ pub enum AtRuleBodyItem {
Property(Property),
/// A custom property declaration with a name and a value.
CustomProperty(CustomProperty),
/// An `@media` rule.
MediaRule(MediaRule),
/// An `@` rule.
AtRule(AtRule),
}
Expand All @@ -160,6 +74,7 @@ impl AtRuleBodyItem {
AtRuleBodyItem::Rule(rule) => rule.write(buf)?,
AtRuleBodyItem::Property(property) => property.write(buf),
AtRuleBodyItem::CustomProperty(cp) => cp.write(buf),
AtRuleBodyItem::MediaRule(rule) => rule.write(buf)?,
AtRuleBodyItem::AtRule(rule) => rule.write(buf)?,
}
Ok(())
Expand Down Expand Up @@ -191,8 +106,8 @@ impl From<AtRule> for AtRuleBodyItem {
}
}
impl From<MediaRule> for AtRuleBodyItem {
fn from(_: MediaRule) -> Self {
todo!("This should not be needed")
fn from(rule: MediaRule) -> Self {
AtRuleBodyItem::MediaRule(rule)
}
}
impl From<BodyItem> for AtRuleBodyItem {
Expand Down
4 changes: 3 additions & 1 deletion rsass/src/css/binop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ impl Display for Formatted<'_, BinOp> {
}
}
(op, Value::BinOp(op2))
if (op2.op < op) || (op == Minus && op2.op == Minus) =>
if ((op2.op < op)
|| (op == Minus && op2.op == Minus))
&& !(op.is_cmp() && op2.op.is_cmp()) =>
{
(op, Value::Paren(Box::new(self.value.b.clone())))
}
Expand Down
139 changes: 139 additions & 0 deletions rsass/src/css/mediarule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use super::atrule::AtRuleBodyItem;
use super::Value;
use crate::output::{CssBuf, Format};
use crate::parser::{css::media, input_span};
use crate::value::Operator;
use crate::ParseError;
use std::io::{self, Write};

/// An `@media` rule in css.
#[derive(Clone, Debug)]
pub struct MediaRule {
args: MediaArgs,
body: Vec<AtRuleBodyItem>,
}

impl MediaRule {
pub(crate) fn new(args: MediaArgs, body: Vec<AtRuleBodyItem>) -> Self {
MediaRule { args, body }
}
pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
if !self.body.is_empty() {
buf.do_indent_no_nl();
buf.add_str("@media ");
self.args.write(buf)?;
buf.start_block();
for item in &self.body {
item.write(buf)?;
}
buf.end_block();
}
Ok(())
}
}

/// The media selection argument of an `@media` rule.
#[derive(Clone, Debug)]
pub enum MediaArgs {
/// A named media, such as `all` or `screen`.
Name(String),
/// `(cond: value)` media.
Condition(String, Value),
/// A condition / range, such as `(witdh < 14em)` or
/// `(14em <= width < 80em)`.
Range(Vec<(Operator, Value)>),
/// Any media subquery in parenthesis.
Paren(Box<MediaArgs>),
/// Any media subquery in square brackets.
Bracket(Box<MediaArgs>),
/// unary operation.
/// The operator (`not`, `only`) is a string to preserve case.
UnaryOp(String, Box<MediaArgs>),
/// a and b and c media.
Comma(Vec<MediaArgs>),
/// a and b and c media.
And(Vec<MediaArgs>),
/// a or b or c media.
Or(Vec<MediaArgs>),
}

impl MediaArgs {
pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
match self {
MediaArgs::Name(name) => write!(buf, "{name}")?,
MediaArgs::UnaryOp(op, a) => {
buf.add_str(op);
buf.add_str(" ");
a.write(buf)?;
}
MediaArgs::Condition(c, v) => {
write!(buf, "({c}: {})", v.format(buf.format()))?
}
MediaArgs::Comma(args) => {
if let Some((first, rest)) = args.split_first() {
first.write(buf)?;
for arg in rest {
buf.add_one(", ", ",");
arg.write(buf)?;
}
}
}
MediaArgs::And(args) => {
if let Some((first, rest)) = args.split_first() {
first.write(buf)?;
for arg in rest {
buf.add_str(" and ");
arg.write(buf)?;
}
}
}
MediaArgs::Or(args) => {
if let Some((first, rest)) = args.split_first() {
first.write(buf)?;
for arg in rest {
buf.add_str(" or ");
arg.write(buf)?;
}
}
}
MediaArgs::Paren(a) => {
buf.add_str("(");
a.write(buf)?;
buf.add_str(")");
}
MediaArgs::Bracket(a) => {
buf.add_str("[");
a.write(buf)?;
buf.add_str("]");
}
MediaArgs::Range(v) => {
buf.add_str("(");
if let Some(((_op, first), rest)) = v.split_first() {
buf.add_str(
&first.to_string(buf.format()).replace('\n', " "),
);
for (op, val) in rest {
write!(buf, " {} ", op)?;
buf.add_str(
&val.to_string(buf.format()).replace('\n', " "),
);
}
}
buf.add_str(")");
}
}
Ok(())
}
}

// Note: I'm not sure printing and parsing is the best way to do this,
// but then, I'm not sure media arguments in scss are best represented
// as a Value either.
impl TryFrom<Value> for MediaArgs {
type Error = crate::Error;

fn try_from(value: Value) -> Result<Self, Self::Error> {
let value = value.format(Format::default()).to_string();
Ok(ParseError::check(media::args(input_span(value).borrow()))?)
}
}
4 changes: 3 additions & 1 deletion rsass/src/css/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ mod binop;
mod call_args;
mod comment;
mod item;
mod mediarule;
mod rule;
mod selectors;
mod string;
mod util;
mod value;
mod valueformat;

pub use self::atrule::{AtRule, AtRuleBodyItem, MediaArgs, MediaRule};
pub use self::atrule::{AtRule, AtRuleBodyItem};
pub use self::binop::BinOp;
pub use self::call_args::CallArgs;
pub use self::comment::Comment;
pub use self::item::{Import, Item};
pub use self::mediarule::{MediaArgs, MediaRule};
pub use self::rule::{BodyItem, CustomProperty, Property, Rule};
pub use self::selectors::{BadSelector, Selector, SelectorPart, Selectors};
pub use self::string::CssString;
Expand Down
11 changes: 9 additions & 2 deletions rsass/src/output/cssdata.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use super::cssdest::{AtRuleDest, CssDestination, NsRuleDest, RuleDest};
use super::cssdest::{
AtMediaDest, AtRuleDest, CssDestination, NsRuleDest, RuleDest,
};
use super::{CssBuf, Format};
use crate::css::{Comment, CssString, Import, Item, Selectors, Value};
use crate::css::{
Comment, CssString, Import, Item, MediaArgs, Selectors, Value,
};
use crate::{Error, Invalid, ScopeRef};
use std::collections::BTreeMap;

Expand Down Expand Up @@ -85,6 +89,9 @@ impl CssDestination for CssData {
fn start_rule(&mut self, selectors: Selectors) -> Result<RuleDest> {
Ok(RuleDest::new(self, selectors))
}
fn start_atmedia(&mut self, args: MediaArgs) -> AtMediaDest {
AtMediaDest::new(self, args)
}
fn start_atrule(&mut self, name: String, args: Value) -> AtRuleDest {
AtRuleDest::new(self, name, args)
}
Expand Down
Loading

0 comments on commit eea6ee5

Please sign in to comment.