Refactor handling of styles to handle spaces and quotes
This commit is contained in:
parent
aa4cacec78
commit
61a6b5eeeb
@ -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"
|
||||||
|
24
src/lexer.rs
24
src/lexer.rs
@ -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(']'));
|
||||||
|
120
src/main.rs
120
src/main.rs
@ -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,8 +414,12 @@ 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");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user