Skip to content

Commit 70edb78

Browse files
committed
Add list-style properties
1 parent adc9e3d commit 70edb78

File tree

6 files changed

+277
-8
lines changed

6 files changed

+277
-8
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ A WIP CSS parser, transformer, and minifier written in Rust.
2424

2525
```
2626
$ node bench.js bootstrap-4.css
27-
cssnano: 537.776ms
27+
cssnano: 545.799ms
2828
159636 bytes
2929
30-
esbuild: 17.3ms
30+
esbuild: 17.003ms
3131
160332 bytes
3232
33-
parcel-css: 4.947ms
34-
143732 bytes
33+
parcel-css: 4.964ms
34+
143688 bytes
3535
3636
3737
$ node bench.js animate.css
@@ -46,12 +46,12 @@ parcel-css: 2.039ms
4646
4747
4848
$ node bench.js tailwind.css
49-
cssnano: 2.188s
49+
cssnano: 2.198s
5050
1925626 bytes
5151
52-
esbuild: 112.382ms
52+
esbuild: 107.668ms
5353
1961642 bytes
5454
55-
parcel-css: 42.701ms
56-
1905333 bytes
55+
parcel-css: 42.568ms
56+
1905068 bytes
5757
```

src/declaration.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::properties::{
1818
text::TextDecorationHandler,
1919
position::PositionHandler,
2020
overflow::OverflowHandler,
21+
list::ListStyleHandler,
2122
};
2223
use crate::properties::prefixes::Browsers;
2324

@@ -81,6 +82,7 @@ pub struct DeclarationHandler {
8182
scroll_padding: ScrollPaddingHandler,
8283
font: FontHandler,
8384
text: TextDecorationHandler,
85+
list: ListStyleHandler,
8486
transition: TransitionHandler,
8587
animation: AnimationHandler,
8688
display: DisplayHandler,
@@ -124,6 +126,7 @@ impl DeclarationHandler{
124126
self.scroll_padding.handle_property(property, &mut self.decls) ||
125127
self.font.handle_property(property, &mut self.decls) ||
126128
self.text.handle_property(property, &mut self.decls) ||
129+
self.list.handle_property(property, &mut self.decls) ||
127130
self.transition.handle_property(property, &mut self.decls) ||
128131
self.animation.handle_property(property, &mut self.decls) ||
129132
self.display.handle_property(property, &mut self.decls) ||
@@ -146,6 +149,7 @@ impl DeclarationHandler{
146149
self.scroll_padding.finalize(&mut self.decls);
147150
self.font.finalize(&mut self.decls);
148151
self.text.finalize(&mut self.decls);
152+
self.list.finalize(&mut self.decls);
149153
self.transition.finalize(&mut self.decls);
150154
self.animation.finalize(&mut self.decls);
151155
self.display.finalize(&mut self.decls);

src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4831,4 +4831,40 @@ mod tests {
48314831
..Browsers::default()
48324832
});
48334833
}
4834+
4835+
#[test]
4836+
fn test_list() {
4837+
minify_test(".foo { list-style-type: disc; }", ".foo{list-style-type:disc}");
4838+
minify_test(".foo { list-style-type: \"\"; }", ".foo{list-style-type:\"\"}");
4839+
minify_test(".foo { list-style-type: symbols(cyclic '○' '●'); }", ".foo{list-style-type:symbols(cyclic \"\" \"\")}");
4840+
minify_test(".foo { list-style-type: symbols('○' '●'); }", ".foo{list-style-type:symbols(\"\" \"\")}");
4841+
minify_test(".foo { list-style-type: symbols(symbolic '○' '●'); }", ".foo{list-style-type:symbols(\"\" \"\")}");
4842+
minify_test(".foo { list-style-type: symbols(symbolic url('ellipse.png')); }", ".foo{list-style-type:symbols(url(ellipse.png))}");
4843+
minify_test(".foo { list-style-image: url('ellipse.png'); }", ".foo{list-style-image:url(ellipse.png)}");
4844+
minify_test(".foo { list-style-position: outside; }", ".foo{list-style-position:outside}");
4845+
minify_test(".foo { list-style: \"\" url(ellipse.png) outside; }", ".foo{list-style:\"\" url(ellipse.png)}");
4846+
4847+
test(r#"
4848+
.foo {
4849+
list-style-type: disc;
4850+
list-style-image: url(ellipse.png);
4851+
list-style-position: outside;
4852+
}
4853+
"#, indoc! {r#"
4854+
.foo {
4855+
list-style: url(ellipse.png);
4856+
}
4857+
"#});
4858+
4859+
test(r#"
4860+
.foo {
4861+
list-style: \"★\" url(ellipse.png) outside;
4862+
list-style-image: none;
4863+
}
4864+
"#, indoc! {r#"
4865+
.foo {
4866+
list-style: \"★\";
4867+
}
4868+
"#});
4869+
}
48344870
}

src/macros.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,57 @@ macro_rules! shorthand_property {
164164
}
165165

166166
pub (crate) use shorthand_property;
167+
168+
macro_rules! shorthand_handler {
169+
(
170+
$name: ident -> $shorthand: ident
171+
{ $( $key: ident: $prop: ident($type: ty), )+ }
172+
) => {
173+
#[derive(Default)]
174+
pub struct $name {
175+
$(
176+
pub $key: Option<$type>,
177+
)*
178+
}
179+
180+
impl PropertyHandler for $name {
181+
fn handle_property(&mut self, property: &Property, _: &mut DeclarationList) -> bool {
182+
match property {
183+
$(
184+
Property::$prop(val) => self.$key = Some(val.clone()),
185+
)+
186+
Property::$shorthand(val) => {
187+
$(
188+
self.$key = Some(val.$key.clone());
189+
)+
190+
}
191+
_ => return false
192+
}
193+
194+
true
195+
}
196+
197+
fn finalize(&mut self, dest: &mut DeclarationList) {
198+
$(
199+
let $key = std::mem::take(&mut self.$key);
200+
)+
201+
202+
if $( $key.is_some() && )* true {
203+
dest.push(Property::$shorthand($shorthand {
204+
$(
205+
$key: $key.unwrap(),
206+
)+
207+
}))
208+
} else {
209+
$(
210+
if let Some(val) = $key {
211+
dest.push(Property::$prop(val))
212+
}
213+
)+
214+
}
215+
}
216+
}
217+
};
218+
}
219+
220+
pub (crate) use shorthand_handler;

src/properties/list.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use cssparser::*;
2+
use crate::traits::{Parse, ToCss, PropertyHandler};
3+
use super::Property;
4+
use crate::values::{image::Image, ident::CustomIdent};
5+
use crate::declaration::DeclarationList;
6+
use crate::macros::{enum_property, shorthand_property, shorthand_handler};
7+
use crate::printer::Printer;
8+
use std::fmt::Write;
9+
10+
/// https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#text-markers
11+
#[derive(Debug, Clone, PartialEq)]
12+
pub enum ListStyleType {
13+
None,
14+
CounterStyle(CounterStyle),
15+
String(String)
16+
}
17+
18+
impl Default for ListStyleType {
19+
fn default() -> ListStyleType {
20+
ListStyleType::CounterStyle(CounterStyle::Name(CustomIdent("disc".into())))
21+
}
22+
}
23+
24+
impl Parse for ListStyleType {
25+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
26+
if let Ok(val) = input.try_parse(CounterStyle::parse) {
27+
return Ok(ListStyleType::CounterStyle(val))
28+
}
29+
30+
if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
31+
return Ok(ListStyleType::None)
32+
}
33+
34+
let s = input.expect_string()?.as_ref().to_owned();
35+
Ok(ListStyleType::String(s))
36+
}
37+
}
38+
39+
impl ToCss for ListStyleType {
40+
fn to_css<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
41+
match self {
42+
ListStyleType::None => dest.write_str("none"),
43+
ListStyleType::CounterStyle(style) => style.to_css(dest),
44+
ListStyleType::String(s) => serialize_string(&s, dest)
45+
}
46+
}
47+
}
48+
49+
/// https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style
50+
#[derive(Debug, Clone, PartialEq)]
51+
pub enum CounterStyle {
52+
Name(CustomIdent),
53+
Symbols(SymbolsType, Vec<Symbol>)
54+
}
55+
56+
impl Parse for CounterStyle {
57+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
58+
if input.try_parse(|input| input.expect_function_matching("symbols")).is_ok() {
59+
return input.parse_nested_block(|input| {
60+
let t = input.try_parse(SymbolsType::parse).unwrap_or(SymbolsType::Symbolic);
61+
println!("{:?}", t);
62+
63+
let mut symbols = Vec::new();
64+
while let Ok(s) = input.try_parse(Symbol::parse) {
65+
symbols.push(s);
66+
}
67+
68+
Ok(CounterStyle::Symbols(t, symbols))
69+
})
70+
}
71+
72+
let name = CustomIdent::parse(input)?;
73+
Ok(CounterStyle::Name(name))
74+
}
75+
}
76+
77+
impl ToCss for CounterStyle {
78+
fn to_css<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
79+
match self {
80+
CounterStyle::Name(name) => name.to_css(dest),
81+
CounterStyle::Symbols(t, symbols) => {
82+
dest.write_str("symbols(")?;
83+
let mut needs_space = false;
84+
if *t != SymbolsType::Symbolic {
85+
t.to_css(dest)?;
86+
needs_space = true;
87+
}
88+
89+
for symbol in symbols {
90+
if needs_space {
91+
dest.write_char(' ')?;
92+
}
93+
symbol.to_css(dest)?;
94+
needs_space = true;
95+
}
96+
dest.write_char(')')
97+
}
98+
}
99+
}
100+
}
101+
102+
enum_property!(SymbolsType,
103+
Cyclic,
104+
Numeric,
105+
Alphabetic,
106+
Symbolic,
107+
Fixed
108+
);
109+
110+
/// https://www.w3.org/TR/css-counter-styles-3/#funcdef-symbols
111+
#[derive(Debug, Clone, PartialEq)]
112+
pub enum Symbol {
113+
String(String),
114+
Image(Image)
115+
}
116+
117+
impl Parse for Symbol {
118+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
119+
if let Ok(img) = input.try_parse(Image::parse) {
120+
return Ok(Symbol::Image(img))
121+
}
122+
123+
let s = input.expect_string()?.as_ref().to_owned();
124+
Ok(Symbol::String(s))
125+
}
126+
}
127+
128+
impl ToCss for Symbol {
129+
fn to_css<W>(&self, dest: &mut Printer<W>) -> std::fmt::Result where W: std::fmt::Write {
130+
match self {
131+
Symbol::String(s) => serialize_string(&s, dest),
132+
Symbol::Image(img) => img.to_css(dest)
133+
}
134+
}
135+
}
136+
137+
// https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-position-property
138+
enum_property!(ListStylePosition,
139+
Inside,
140+
Outside
141+
);
142+
143+
impl Default for ListStylePosition {
144+
fn default() -> ListStylePosition {
145+
ListStylePosition::Outside
146+
}
147+
}
148+
149+
// https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#marker-side
150+
enum_property!(MarkerSide,
151+
("match-self", MatchSelf),
152+
("match-parent", MatchParent)
153+
);
154+
155+
// https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-property
156+
shorthand_property!(ListStyle {
157+
list_style_type: ListStyleType,
158+
image: Image,
159+
position: ListStylePosition,
160+
});
161+
162+
shorthand_handler!(ListStyleHandler -> ListStyle {
163+
list_style_type: ListStyleType(ListStyleType),
164+
image: ListStyleImage(Image),
165+
position: ListStylePosition(ListStylePosition),
166+
});

src/properties/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod text;
1919
pub mod position;
2020
pub mod overflow;
2121
pub mod ui;
22+
pub mod list;
2223

2324
use cssparser::*;
2425
use custom::*;
@@ -38,6 +39,7 @@ use display::*;
3839
use text::*;
3940
use overflow::*;
4041
use ui::*;
42+
use list::*;
4143
use crate::values::{image::*, length::*, position::*, alpha::*, size::*, rect::*, color::*, time::Time, ident::CustomIdent, easing::EasingFunction};
4244
use crate::traits::{Parse, ToCss};
4345
use crate::printer::Printer;
@@ -492,6 +494,13 @@ define_properties! {
492494
"user-select": UserSelect(UserSelect, VendorPrefix) / "webkit" / "moz" / "ms",
493495
"accent-color": AccentColor(ColorOrAuto),
494496
"appearance": Appearance(Appearance, VendorPrefix) / "webkit" / "moz" / "ms",
497+
498+
// https://www.w3.org/TR/2020/WD-css-lists-3-20201117
499+
"list-style-type": ListStyleType(ListStyleType),
500+
"list-style-image": ListStyleImage(Image),
501+
"list-style-position": ListStylePosition(ListStylePosition),
502+
"list-style": ListStyle(ListStyle),
503+
"marker-side": MarkerSide(MarkerSide),
495504
}
496505

497506
impl<T: smallvec::Array<Item = V>, V: Parse> Parse for SmallVec<T> {

0 commit comments

Comments
 (0)