Skip to content

Commit a433731

Browse files
committed
Minify selectors
1 parent 2b2b4c2 commit a433731

File tree

3 files changed

+444
-239
lines changed

3 files changed

+444
-239
lines changed

src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern crate itertools;
99

1010
mod parser;
1111
mod media_query;
12+
mod selector;
1213
mod properties;
1314
mod values;
1415
mod printer;
@@ -65,7 +66,7 @@ fn compile(code: &str, minify: bool) -> String {
6566
} else {
6667
printer.newline();
6768
}
68-
println!("{:?}", rule);
69+
// println!("{:?}", rule);
6970
let rule = match rule {
7071
parser::CssRule::Import(import) => {
7172
parser::CssRule::Import(parser::ImportRule {
@@ -1020,4 +1021,15 @@ mod tests {
10201021
"#
10211022
});
10221023
}
1024+
1025+
#[test]
1026+
fn test_selectors() {
1027+
minify_test("[foo=\"baz\"] {}", "[foo=baz]{}");
1028+
minify_test("[foo=\"foo bar\"] {}", "[foo=foo\\ bar]{}");
1029+
minify_test("[foo=\"foo bar baz\"] {}", "[foo=\"foo bar baz\"]{}");
1030+
minify_test(".test:not([foo=\"bar\"]) {}", ".test:not([foo=bar]){}");
1031+
minify_test(".test + .foo {}", ".test+.foo{}");
1032+
minify_test(".test ~ .foo {}", ".test~.foo{}");
1033+
minify_test(".test .foo {}", ".test .foo{}");
1034+
}
10231035
}

src/parser.rs

Lines changed: 12 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use cssparser::*;
2-
use selectors::{SelectorList, parser::SelectorImpl};
2+
use selectors::SelectorList;
33
use std::fmt;
44
use std::cell::RefCell;
55
use crate::media_query::*;
@@ -8,9 +8,7 @@ use crate::values::traits::PropertyHandler;
88
use crate::printer::Printer;
99
use crate::values::traits::ToCss;
1010
use std::fmt::Write;
11-
12-
#[derive(Debug, Clone, PartialEq)]
13-
pub struct Selectors;
11+
use crate::selector::{Selectors, SelectorParser};
1412

1513
#[derive(Eq, PartialEq, Clone)]
1614
pub struct CssString(RefCell<String>);
@@ -19,6 +17,16 @@ impl CssString {
1917
pub fn replace(&self, x: String) {
2018
self.0.replace(x);
2119
}
20+
21+
pub fn write_identifier<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
22+
serialize_identifier(self.0.borrow().as_ref(), dest)
23+
}
24+
25+
pub fn write_string<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
26+
let b = self.0.borrow();
27+
let s: &str = b.as_ref();
28+
write!(CssStringWriter::new(dest), "{}", s)
29+
}
2230
}
2331

2432
impl cssparser::ToCss for CssString {
@@ -51,123 +59,6 @@ impl std::cmp::PartialEq<str> for CssString {
5159
}
5260
}
5361

54-
impl SelectorImpl for Selectors {
55-
type AttrValue = CssString;
56-
type Identifier = CssString;
57-
type LocalName = CssString;
58-
type NamespacePrefix = CssString;
59-
type NamespaceUrl = String;
60-
type BorrowedNamespaceUrl = String;
61-
type BorrowedLocalName = String;
62-
63-
type NonTSPseudoClass = PseudoClass;
64-
type PseudoElement = PseudoElement;
65-
66-
type ExtraMatchingData = ();
67-
}
68-
69-
/// https://drafts.csswg.org/selectors-4/#structural-pseudos
70-
#[derive(Clone, Eq, PartialEq)]
71-
pub enum PseudoClass {
72-
Active,
73-
AnyLink,
74-
Checked,
75-
Defined,
76-
Disabled,
77-
Enabled,
78-
Focus,
79-
Fullscreen,
80-
Hover,
81-
Indeterminate,
82-
Lang(Box<str>),
83-
Link,
84-
PlaceholderShown,
85-
ReadWrite,
86-
ReadOnly,
87-
Target,
88-
Visited,
89-
}
90-
91-
impl selectors::parser::NonTSPseudoClass for PseudoClass {
92-
type Impl = Selectors;
93-
94-
fn is_active_or_hover(&self) -> bool {
95-
matches!(*self, PseudoClass::Active | PseudoClass::Hover)
96-
}
97-
98-
fn is_user_action_state(&self) -> bool {
99-
matches!(*self, PseudoClass::Active | PseudoClass::Hover | PseudoClass::Focus)
100-
}
101-
}
102-
103-
impl cssparser::ToCss for PseudoClass {
104-
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
105-
where
106-
W: fmt::Write,
107-
{
108-
use PseudoClass::*;
109-
if let Lang(ref lang) = *self {
110-
dest.write_str(":lang(")?;
111-
serialize_identifier(lang, dest)?;
112-
return dest.write_str(")");
113-
}
114-
115-
dest.write_str(match *self {
116-
Active => ":active",
117-
AnyLink => ":any-link",
118-
Checked => ":checked",
119-
Defined => ":defined",
120-
Disabled => ":disabled",
121-
Enabled => ":enabled",
122-
Focus => ":focus",
123-
Fullscreen => ":fullscreen",
124-
Hover => ":hover",
125-
Indeterminate => ":indeterminate",
126-
Link => ":link",
127-
PlaceholderShown => ":placeholder-shown",
128-
ReadWrite => ":read-write",
129-
ReadOnly => ":read-only",
130-
Target => ":target",
131-
Visited => ":visited",
132-
Lang(_) => unreachable!(),
133-
})
134-
}
135-
}
136-
137-
138-
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
139-
pub enum PseudoElement {
140-
After,
141-
Before,
142-
FirstLine,
143-
FirstLetter,
144-
Selection,
145-
}
146-
147-
impl cssparser::ToCss for PseudoElement {
148-
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
149-
where
150-
W: fmt::Write,
151-
{
152-
use PseudoElement::*;
153-
dest.write_str(match *self {
154-
// CSS2 pseudo elements support a single colon syntax in addition
155-
// to the more correct double colon for other pseudo elements.
156-
// We use that here because it's supported everywhere and is shorter.
157-
After => ":after",
158-
Before => ":before",
159-
FirstLine => ":first-line",
160-
FirstLetter => ":first-letter",
161-
Selection => "::selection"
162-
})
163-
}
164-
}
165-
166-
impl selectors::parser::PseudoElement for PseudoElement {
167-
type Impl = Selectors;
168-
}
169-
170-
17162
/// The parser for the top-level rules in a stylesheet.
17263
pub struct TopLevelRuleParser {
17364
}
@@ -765,108 +656,6 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser {
765656
}
766657
}
767658

768-
#[derive(Debug)]
769-
pub enum Value {
770-
Url(String)
771-
}
772-
773-
// fn exhaust<'i>(input: &mut cssparser::Parser<'i, '_>) -> Vec<Value> {
774-
// let start = input.position();
775-
// while input.next().is_ok() {}
776-
// input.slice_from(start)
777-
// let mut val = vec![];
778-
// loop {
779-
// if let Ok(tok) = input.next() {
780-
// println!("{:?}", tok);
781-
// match tok {
782-
// Token::UnquotedUrl(ref url) => val.push(Value::Url((&**url).into())),
783-
// Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {
784-
// let url = input.parse_nested_block(|input| {
785-
// input.expect_string().map_err(Into::into).map(|s| s.clone())
786-
// })
787-
// .map_err(ParseError::<()>::basic);
788-
// if let Ok(url) = url {
789-
// val.push(Value::Url((&*url).into()));
790-
// }
791-
// },
792-
// _ => {}
793-
// }
794-
// } else {
795-
// break
796-
// }
797-
// }
798-
// val
799-
// }
800-
801-
struct SelectorParser;
802-
impl<'i> selectors::parser::Parser<'i> for SelectorParser {
803-
type Impl = Selectors;
804-
type Error = selectors::parser::SelectorParseErrorKind<'i>;
805-
806-
fn parse_non_ts_pseudo_class(
807-
&self,
808-
location: SourceLocation,
809-
name: CowRcStr<'i>,
810-
) -> Result<PseudoClass, ParseError<'i, Self::Error>> {
811-
use PseudoClass::*;
812-
let pseudo_class = match_ignore_ascii_case! { &name,
813-
"active" => Active,
814-
"any-link" => AnyLink,
815-
"checked" => Checked,
816-
"defined" => Defined,
817-
"disabled" => Disabled,
818-
"enabled" => Enabled,
819-
"focus" => Focus,
820-
"fullscreen" => Fullscreen,
821-
"hover" => Hover,
822-
"indeterminate" => Indeterminate,
823-
"link" => Link,
824-
"placeholder-shown" => PlaceholderShown,
825-
"read-write" => ReadWrite,
826-
"read-only" => ReadOnly,
827-
"target" => Target,
828-
"visited" => Visited,
829-
_ => return Err(location.new_custom_error(selectors::parser::SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
830-
};
831-
832-
Ok(pseudo_class)
833-
}
834-
835-
fn parse_non_ts_functional_pseudo_class<'t>(
836-
&self,
837-
name: CowRcStr<'i>,
838-
parser: &mut cssparser::Parser<'i, 't>,
839-
) -> Result<PseudoClass, ParseError<'i, Self::Error>> {
840-
use PseudoClass::*;
841-
let pseudo_class = match_ignore_ascii_case! { &name,
842-
"lang" => {
843-
Lang(parser.expect_ident_or_string()?.as_ref().into())
844-
},
845-
_ => return Err(parser.new_custom_error(selectors::parser::SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
846-
};
847-
848-
Ok(pseudo_class)
849-
}
850-
851-
fn parse_pseudo_element(
852-
&self,
853-
location: SourceLocation,
854-
name: CowRcStr<'i>,
855-
) -> Result<PseudoElement, ParseError<'i, Self::Error>> {
856-
use PseudoElement::*;
857-
let pseudo_element = match_ignore_ascii_case! { &name,
858-
"before" => Before,
859-
"after" => After,
860-
"first-line" => FirstLine,
861-
"first-letter" => FirstLetter,
862-
"selection" => Selection,
863-
_ => return Err(location.new_custom_error(selectors::parser::SelectorParseErrorKind::UnexpectedIdent(name.clone())))
864-
};
865-
866-
Ok(pseudo_element)
867-
}
868-
}
869-
870659
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser {
871660
type Prelude = SelectorList<Selectors>;
872661
type QualifiedRule = CssRule;
@@ -911,21 +700,6 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser {
911700
}
912701
}
913702

914-
impl ToCss for SelectorList<Selectors> {
915-
fn to_css<W>(&self, dest: &mut Printer<W>) -> fmt::Result where W: fmt::Write {
916-
use cssparser::ToCss;
917-
let mut first = true;
918-
for selector in &self.0 {
919-
if !first {
920-
dest.delim(',', false)?;
921-
}
922-
first = false;
923-
selector.to_css(dest)?;
924-
}
925-
Ok(())
926-
}
927-
}
928-
929703
struct PropertyDeclarationParser;
930704

931705
/// Parse a declaration within {} block: `color: blue`

0 commit comments

Comments
 (0)