Refactor handling of styles to handle spaces and quotes

This commit is contained in:
ConnorSkees 2020-01-05 20:51:14 -05:00
parent aa4cacec78
commit 61a6b5eeeb
3 changed files with 52 additions and 138 deletions

View File

@ -1,6 +1,6 @@
use std::io::{self, Write}; use std::io::{self, Write};
use crate::{RuleSet, Stmt, Style, StyleSheet}; use crate::{RuleSet, Stmt, StyleSheet};
pub(crate) struct PrettyPrinter<W: Write> { pub(crate) struct PrettyPrinter<W: Write> {
buf: W, buf: W,
@ -28,18 +28,8 @@ impl<W: Write> PrettyPrinter<W> {
writeln!(self.buf, "{}}}", padding)?; writeln!(self.buf, "{}}}", padding)?;
self.scope -= 1; self.scope -= 1;
} }
Stmt::Style(Style { property, value }) => { Stmt::Style(s) => {
writeln!( writeln!(self.buf, "{}{}", padding, s)?;
self.buf,
"{}{}: {};",
padding,
property,
value
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join(" ")
)?;
} }
} }
Ok(()) Ok(())
@ -79,18 +69,8 @@ impl<W: Write> PrettyPrinter<W> {
writeln!(self.buf, "{}}}", padding)?; writeln!(self.buf, "{}}}", padding)?;
self.scope -= 1; self.scope -= 1;
} }
Stmt::Style(Style { property, value }) => { Stmt::Style(s) => {
writeln!( writeln!(self.buf, "{}{}", padding, s)?;
self.buf,
"{}{}: {};",
padding,
property,
value
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join(" ")
)?;
} }
} }
Ok(()) Ok(())
@ -161,6 +141,8 @@ mod test_scss {
test!(selector_universal_el_descendant, "* a {\n}\n"); test!(selector_universal_el_descendant, "* a {\n}\n");
test!(selector_attribute_any, "[attr] {\n}\n"); test!(selector_attribute_any, "[attr] {\n}\n");
test!(selector_attribute_equals, "[attr=val] {\n}\n"); test!(selector_attribute_equals, "[attr=val] {\n}\n");
test!(selector_attribute_single_quotes, "[attr='val'] {\n}\n");
test!(selector_attribute_double_quotes, "[attr=\"val\"] {\n}\n");
test!(selector_attribute_in, "[attr~=val] {\n}\n"); test!(selector_attribute_in, "[attr~=val] {\n}\n");
test!( test!(
selector_attribute_begins_hyphen_or_exact, selector_attribute_begins_hyphen_or_exact,
@ -192,17 +174,15 @@ mod test_scss {
space_separated_style_value, space_separated_style_value,
"a {\n border: solid red;\n}\n" "a {\n border: solid red;\n}\n"
); );
test!( test!(single_quoted_style_value, "a {\n font: 'Open-Sans';\n}\n");
single_quoted_style_value,
"a {\n font: 'Open-Sans';\n}\n",
"a {\n font: Open-Sans;\n}\n"
);
test!( test!(
double_quoted_style_value, double_quoted_style_value,
"a {\n font: \"Open-Sans\";\n}\n", "a {\n font: \"Open-Sans\";\n}\n"
"a {\n font: Open-Sans;\n}\n" );
test!(
comma_style_value,
"a {\n font: Open-Sans, sans-serif;\n}\n"
); );
// test!(comma_style_value, "a {\n font: Open-Sans, sans-serif;\n}\n");
test!( test!(
nested_style_in_parent, nested_style_in_parent,
"a {\n color: red;\n b {\n }\n}\n" "a {\n color: red;\n b {\n }\n}\n"

View File

@ -214,22 +214,11 @@ impl<'a> Lexer<'a> {
self.devour_whitespace(); self.devour_whitespace();
match self
.buf
.peek()
.expect("todo! expected either value or quote")
{
'\'' | '"' => {
self.buf.next();
}
_ => {}
}
let mut value = String::with_capacity(99); let mut value = String::with_capacity(99);
let mut case_sensitive = true; let mut case_sensitive = true;
while let Some(c) = self.buf.peek() { while let Some(c) = self.buf.peek() {
if !c.is_alphabetic() && c != &'-' && c != &'_' { if !c.is_alphabetic() && c != &'-' && c != &'_' && c != &'"' && c != &'\'' {
break; break;
} }
@ -260,17 +249,6 @@ impl<'a> Lexer<'a> {
value.push(tok); value.push(tok);
} }
match self
.buf
.peek()
.expect("todo! expected either value or quote")
{
'\'' | '"' => {
self.buf.next();
}
_ => {}
}
self.devour_whitespace(); self.devour_whitespace();
assert!(self.buf.next() == Some(']')); assert!(self.buf.next() == Some(']'));

View File

@ -82,27 +82,6 @@ impl Display for TokenKind {
} }
} }
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StyleToken {
Ident(String),
Function(String, Vec<String>),
Keyword(Keyword),
Symbol(Symbol),
Dimension(String, Unit),
}
impl Display for StyleToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StyleToken::Ident(s) => write!(f, "{}", s),
StyleToken::Symbol(s) => write!(f, "{}", s),
StyleToken::Function(name, args) => write!(f, "{}({})", name, args.join(", ")),
StyleToken::Keyword(kw) => write!(f, "{}", kw),
StyleToken::Dimension(val, unit) => write!(f, "{}{}", val, unit),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Token { pub struct Token {
pos: Pos, pos: Pos,
@ -117,27 +96,18 @@ pub struct StyleSheet {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Style { pub struct Style {
property: String, property: String,
value: Vec<StyleToken>, value: String,
} }
impl Display for Style { impl Display for Style {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(f, "{}:{};", self.property, self.value)
f,
"{}: {};",
self.property,
self.value
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<String>>()
.join(" ")
)
} }
} }
impl Style { impl Style {
pub fn from_tokens(tokens: &[Token], vars: &HashMap<String, Vec<Token>>) -> Result<Self, ()> { pub fn from_tokens(tokens: &[Token], vars: &HashMap<String, Vec<Token>>) -> Result<Self, ()> {
StyleParser::new(tokens, vars).parse() Ok(StyleParser::new(tokens, vars)?.parse())
} }
} }
@ -147,8 +117,11 @@ struct StyleParser<'a> {
} }
impl<'a> StyleParser<'a> { impl<'a> StyleParser<'a> {
const fn new(tokens: &'a [Token], vars: &'a HashMap<String, Vec<Token>>) -> Self { fn new(tokens: &'a [Token], vars: &'a HashMap<String, Vec<Token>>) -> Result<Self, ()> {
StyleParser { tokens, vars } if tokens.is_empty() {
return Err(());
}
Ok(StyleParser { tokens, vars })
} }
fn deref_variable(&mut self, variable: &TokenKind) -> String { fn deref_variable(&mut self, variable: &TokenKind) -> String {
@ -180,22 +153,18 @@ impl<'a> StyleParser<'a> {
val val
} }
fn parse(&mut self) -> Result<Style, ()> { fn parse(&mut self) -> Style {
let mut iter = self.tokens.iter(); let mut iter = self.tokens.iter().peekable();
let property: String; let mut property = String::new();
loop { while let Some(tok) = iter.next() {
if let Some(tok) = iter.next() { match tok.kind {
match tok.kind { TokenKind::Whitespace(_) => continue,
TokenKind::Whitespace(_) => continue, TokenKind::Ident(ref s) => {
TokenKind::Ident(ref s) => { property = s.clone();
property = s.clone(); break;
break; }
} _ => todo!(),
_ => todo!(), };
};
} else {
return Err(());
}
} }
while let Some(tok) = iter.next() { while let Some(tok) = iter.next() {
@ -206,42 +175,25 @@ impl<'a> StyleParser<'a> {
} }
} }
let mut value = Vec::new(); let mut value = String::new();
while let Some(tok) = iter.next() { while let Some(tok) = iter.next() {
match tok.kind { match &tok.kind {
TokenKind::Whitespace(_) TokenKind::Whitespace(_) => {
| TokenKind::Symbol(Symbol::SingleQuote) while let Some(w) = iter.peek() {
| TokenKind::Symbol(Symbol::DoubleQuote) => continue, if let TokenKind::Whitespace(_) = w.kind {
TokenKind::Ident(ref s) => value.push(StyleToken::Ident(s.clone())), iter.next();
TokenKind::Symbol(s) => value.push(StyleToken::Symbol(s)), } else {
TokenKind::Unit(u) => value.push(StyleToken::Ident(u.into())), value.push(' ');
TokenKind::Variable(_) => { break;
value.push(StyleToken::Ident(self.deref_variable(&tok.kind)))
}
TokenKind::Number(ref num) => {
if let Some(t) = iter.next() {
match &t.kind {
&TokenKind::Unit(unit) => {
value.push(StyleToken::Dimension(num.clone(), unit))
}
TokenKind::Ident(ref s) => {
value.push(StyleToken::Dimension(num.clone(), Unit::None));
value.push(StyleToken::Ident(s.clone()));
}
TokenKind::Whitespace(_) => {
value.push(StyleToken::Dimension(num.clone(), Unit::None))
}
_ => todo!(),
} }
} else {
value.push(StyleToken::Dimension(num.clone(), Unit::None))
} }
} }
_ => todo!("style value not ident or dimension"), TokenKind::Variable(_) => value.push_str(&self.deref_variable(&tok.kind)),
_ => value.push_str(&tok.kind.to_string()),
} }
} }
Ok(Style { property, value }) Style { property, value }
} }
} }
@ -462,7 +414,11 @@ mod test_css {
}; };
} }
test!(nesting_el_mul_el, "a, b {\n a, b {\n color: red\n}\n}\n", "a a, b a, a b, b b {\n color: red;\n}\n"); test!(
nesting_el_mul_el,
"a, b {\n a, b {\n color: red\n}\n}\n",
"a a, b a, a b, b b {\n color: red;\n}\n"
);
test!(basic_style, "a {\n color: red;\n}\n"); test!(basic_style, "a {\n color: red;\n}\n");
test!(two_styles, "a {\n color: red;\n color: blue;\n}\n"); test!(two_styles, "a {\n color: red;\n color: blue;\n}\n");
test!(selector_mul, "a, b {\n color: red;\n}\n"); test!(selector_mul, "a, b {\n color: red;\n}\n");