Skip to content

Commit aa0d3c5

Browse files
committed
Support content: attr() and content: counter() (#559)
* Support attr() * Support counter * Support counter * Inline ppxlib, goodbye label * Remove old properties from ms * Remove useless code * Remove useles polyvars on list-style-type * Remove useles polyvars on list-style-type
1 parent 4156733 commit aa0d3c5

File tree

9 files changed

+334
-365
lines changed

9 files changed

+334
-365
lines changed

packages/css-property-parser/lib/Parser.re

Lines changed: 50 additions & 288 deletions
Large diffs are not rendered by default.

packages/css-property-parser/lib/Standard.re

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,23 @@ let custom_ident_without_span_or_auto =
344344

345345
// TODO: workarounds
346346
let invalid = expect(STRING("not-implemented"));
347-
let attr_name = invalid;
348-
let attr_fallback = invalid;
349-
let string_token = invalid;
350-
let ident_token = invalid;
347+
348+
let string_token =
349+
token(
350+
fun
351+
| STRING(string) => Ok(string)
352+
| _ => Error(["expected a string."]),
353+
);
354+
355+
let ident_token =
356+
token(
357+
fun
358+
| IDENT(string) => Ok(string)
359+
| _ => Error(["expected an identifier."]),
360+
);
361+
351362
let declaration_value = invalid;
363+
352364
let positive_integer = integer;
353365
let function_token = invalid;
354366
let any_value = invalid;

packages/css-property-parser/lib/Standard.rei

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,9 @@ let flex_value: Rule.rule([> | `Fr(float)]);
121121

122122
let invalid: Rule.rule(unit);
123123

124-
let attr_name: Rule.rule(unit);
124+
let string_token: Rule.rule(string);
125125

126-
let attr_fallback: Rule.rule(unit);
127-
128-
let string_token: Rule.rule(unit);
129-
130-
let ident_token: Rule.rule(unit);
126+
let ident_token: Rule.rule(string);
131127

132128
let declaration_value: Rule.rule(unit);
133129

packages/css-property-parser/ppx/Generate.re

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,19 @@ module Make = (Builder: Ppxlib.Ast_builder.S) => {
6666
kebab_case_to_snake_case(str);
6767
};
6868

69+
let keyword_to_css = str => {
70+
switch (str) {
71+
| "%" => "percent"
72+
| _ => kebab_case_to_snake_case(str)
73+
};
74+
};
75+
6976
// TODO: multiplier name
7077
let rec variant_name = value => {
7178
let value_name =
7279
switch (value) {
7380
| Terminal(Delim(name), _) => value_of_delimiter(name)
74-
| Terminal(Keyword(name), _) => kebab_case_to_snake_case(name)
81+
| Terminal(Keyword(name), _) => keyword_to_css(name)
7582
| Terminal(Data_type(name), _) => value_name_of_css(name)
7683
| Terminal(Property_type(name), _) =>
7784
property_value_name(name) |> value_name_of_css
@@ -353,10 +360,10 @@ module Make = (Builder: Ppxlib.Ast_builder.S) => {
353360
type_("flex_value", [%type: [ | `Fr(float)]]),
354361
type_("media_type", [%type: string]),
355362
type_("container_name", [%type: string]),
363+
type_("ident_token", [%type: string]),
364+
type_("string_token", [%type: string]),
356365
// From Parser_helper, those are `invalid` represented here as unit
357-
type_("ident_token", [%type: unit]),
358366
type_("function_token", [%type: unit]),
359-
type_("string_token", [%type: unit]),
360367
type_("hash_token", [%type: unit]),
361368
type_("any_value", [%type: unit]),
362369
type_("declaration_value", [%type: unit]),
@@ -543,18 +550,6 @@ module Make = (Builder: Ppxlib.Ast_builder.S) => {
543550
kebab_case_to_snake_case(str);
544551
};
545552

546-
let value_of_keyword = str => {
547-
switch (str) {
548-
| "," => "comma"
549-
| "+" => "cross"
550-
| "-" => "dash"
551-
| "*" => "asterisk"
552-
| "/" => "bar"
553-
| "@" => "at"
554-
| s => kebab_case_to_snake_case(s)
555-
};
556-
};
557-
558553
let apply_modifier = {
559554
let option_int_to_expr =
560555
fun

packages/css-property-parser/test/snapshots/Spec.expected.re

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ module Types = {
9595
and flex_value = [ | `Fr(float)]
9696
and media_type = string
9797
and container_name = string
98-
and ident_token = unit
98+
and ident_token = string
99+
and string_token = string
99100
and function_token = unit
100-
and string_token = unit
101101
and hash_token = unit
102102
and any_value = unit
103103
and declaration_value = unit

packages/ppx/src/Property_to_runtime.re

Lines changed: 147 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
open Ppxlib;
2-
1+
module Location = Ppxlib.Location;
2+
module Parsetree = Ppxlib.Parsetree;
33
module Builder = Ppxlib.Ast_builder.Default;
44

55
module Standard = Css_property_parser.Standard;
@@ -79,7 +79,8 @@ let render_option = (~loc, f) =>
7979
| Some(v) => [%expr Some([%e f(~loc, v)])]
8080
| None => [%expr None];
8181

82-
let list_to_longident = vars => vars |> String.concat(".") |> Longident.parse;
82+
let list_to_longident = vars =>
83+
vars |> String.concat(".") |> Ppxlib.Longident.parse;
8384

8485
let render_variable = (~loc, name) =>
8586
list_to_longident(name) |> txt(~loc) |> Builder.pexp_ident(~loc);
@@ -1303,7 +1304,7 @@ let render_box_shadow = (~loc, shadow) => {
13031304
);
13041305

13051306
let args =
1306-
[
1307+
Ppxlib.Asttypes.[
13071308
(Labelled("x"), Some(x)),
13081309
(Labelled("y"), Some(y)),
13091310
(Labelled("blur"), blur),
@@ -2909,7 +2910,7 @@ let render_text_shadow = (~loc, shadow) => {
29092910
};
29102911

29112912
let args =
2912-
[
2913+
Ppxlib.Asttypes.[
29132914
(Labelled("x"), Some(render_length_interp(~loc, x))),
29142915
(Labelled("y"), Some(render_length_interp(~loc, y))),
29152916
(Labelled("blur"), Option.map(render_length_interp(~loc), blur)),
@@ -5199,13 +5200,13 @@ let filter =
51995200
monomorphic(
52005201
Property_parser.property_filter,
52015202
(~loc) => [%expr CSS.filter],
5202-
(~loc, value) =>
5203+
(~loc, value) => {
52035204
switch (value) {
52045205
| `None => [%expr [|`none|]]
52055206
| `Interpolation(v) => render_variable(~loc, v)
52065207
| `Filter_function_list(ffl) => render_filter_function_list(~loc, ffl)
5207-
| `_ms_filter_function_list(_) => raise(Unsupported_feature)
5208-
},
5208+
}
5209+
},
52095210
);
52105211

52115212
let backdrop_filter =
@@ -5474,20 +5475,21 @@ let render_quote = (~loc, quote: Types.quote) => {
54745475

54755476
let render_content_string = (~loc, str) => {
54765477
let length = String.length(str);
5478+
let get = String.get;
54775479
let str =
54785480
if (length == 0) {
54795481
[%expr {js|''|js}];
5480-
} else if (length == 1 && str.[0] == '"') {
5482+
} else if (length == 1 && get(str, 0) == '"') {
54815483
[%expr {js|'"'|js}];
5482-
} else if (length == 1 && str.[0] == ' ') {
5484+
} else if (length == 1 && get(str, 0) == ' ') {
54835485
[%expr {js|' '|js}];
5484-
} else if (length == 1 && str.[0] == '\'') {
5486+
} else if (length == 1 && get(str, 0) == '\'') {
54855487
[%expr {js|"'"|js}];
5486-
} else if (length == 2 && str.[0] == '"' && str.[1] == '"') {
5488+
} else if (length == 2 && get(str, 0) == '"' && get(str, 1) == '"') {
54875489
[%expr {js|""|js}];
54885490
} else {
5489-
let first = str.[0];
5490-
let last = str.[length - 1];
5491+
let first = get(str, 0);
5492+
let last = get(str, length - 1);
54915493
switch (first, last) {
54925494
| ('\'', '\'') => [%expr [%e render_string(~loc, str)]]
54935495
| ('"', '"') => [%expr [%e render_string(~loc, str)]]
@@ -5497,6 +5499,127 @@ let render_content_string = (~loc, str) => {
54975499
[%expr `text([%e str])];
54985500
};
54995501

5502+
let render_attr_name = (~loc, attr_name: Types.attr_name) => {
5503+
switch (attr_name) {
5504+
| (Some((Some(label), _)), _label) => [%expr
5505+
[%e render_string(~loc, label)]
5506+
]
5507+
| (Some((None, _)), label) => [%expr [%e render_string(~loc, label)]]
5508+
| (None, label) => [%expr [%e render_string(~loc, label)]]
5509+
};
5510+
};
5511+
5512+
let render_attr_unit = (~loc, attr_unit: Types.attr_unit) => {
5513+
switch (attr_unit) {
5514+
| `Percent => [%expr [%e render_string(~loc, "%")]]
5515+
| `Em => [%expr [%e render_string(~loc, "em")]]
5516+
| `Vmin => [%expr [%e render_string(~loc, "vmin")]]
5517+
| `In => [%expr [%e render_string(~loc, "in")]]
5518+
| `Vw => [%expr [%e render_string(~loc, "vw")]]
5519+
| `Mm => [%expr [%e render_string(~loc, "mm")]]
5520+
| `Deg => [%expr [%e render_string(~loc, "deg")]]
5521+
| `Cm => [%expr [%e render_string(~loc, "cm")]]
5522+
| `Grad => [%expr [%e render_string(~loc, "grad")]]
5523+
| `Px => [%expr [%e render_string(~loc, "px")]]
5524+
| `KHz => [%expr [%e render_string(~loc, "kHz")]]
5525+
| `Ex => [%expr [%e render_string(~loc, "ex")]]
5526+
| `Rad => [%expr [%e render_string(~loc, "rad")]]
5527+
| `Ch => [%expr [%e render_string(~loc, "ch")]]
5528+
| `Rem => [%expr [%e render_string(~loc, "rem")]]
5529+
| `Pt => [%expr [%e render_string(~loc, "pt")]]
5530+
| `Hz => [%expr [%e render_string(~loc, "Hz")]]
5531+
| `Pc => [%expr [%e render_string(~loc, "pc")]]
5532+
| `Turn => [%expr [%e render_string(~loc, "turn")]]
5533+
| `S => [%expr [%e render_string(~loc, "s")]]
5534+
| `Vmax => [%expr [%e render_string(~loc, "vmax")]]
5535+
| `Ms => [%expr [%e render_string(~loc, "ms")]]
5536+
| `Vh => [%expr [%e render_string(~loc, "vh")]]
5537+
};
5538+
};
5539+
5540+
let render_attr_type = (~loc, attr_type: Types.attr_type) => {
5541+
switch (attr_type) {
5542+
| `Raw_string => [%expr [%e render_string(~loc, "raw-string")]]
5543+
| `Attr_unit(attr_unit) => [%expr [%e render_attr_unit(~loc, attr_unit)]]
5544+
};
5545+
};
5546+
5547+
let render_function_attr =
5548+
(~loc, attr_name: Types.attr_name, attr_type: option(Types.attr_type)) => {
5549+
switch (attr_type) {
5550+
| Some(attr_type) => [%expr
5551+
`attrWithType((
5552+
[%e render_attr_name(~loc, attr_name)],
5553+
[%e render_attr_type(~loc, attr_type)],
5554+
))
5555+
]
5556+
| None => [%expr `attr([%e render_attr_name(~loc, attr_name)])]
5557+
};
5558+
};
5559+
5560+
let render_symbols_type = (~loc, symbols_type: Types.symbols_type) => {
5561+
switch (symbols_type) {
5562+
| `Cyclic => [%expr `cyclic]
5563+
| `Numeric => [%expr `numeric]
5564+
| `Alphabetic => [%expr `alphabetic]
5565+
| `Symbolic => [%expr `symbolic]
5566+
| `Fixed => [%expr `fixed]
5567+
};
5568+
};
5569+
5570+
let render_list_image_or_string = (~loc, list_image_or_string) => {
5571+
list_image_or_string
5572+
|> List.map(image_or_string =>
5573+
switch (image_or_string) {
5574+
| `Image(image) => render_image(~loc, image)
5575+
| `String(str) => render_string(~loc, str)
5576+
}
5577+
)
5578+
|> Builder.pexp_array(~loc);
5579+
};
5580+
5581+
let render_symbols =
5582+
(~loc, symbols_type: option(Types.symbols_type), list_image_or_string) => {
5583+
switch (symbols_type) {
5584+
| Some(symbols_type) => [%expr
5585+
`symbols((
5586+
[%e render_symbols_type(~loc, symbols_type)],
5587+
[%e render_list_image_or_string(~loc, list_image_or_string)],
5588+
))
5589+
]
5590+
| None => [%expr
5591+
`symbols((
5592+
None,
5593+
[%e render_list_image_or_string(~loc, list_image_or_string)],
5594+
))
5595+
]
5596+
};
5597+
};
5598+
5599+
let render_counter_style = (~loc, counter_style: Types.counter_style) => {
5600+
switch (counter_style) {
5601+
| `Counter_style_name(label) => [%expr
5602+
`Custom([%e render_string(~loc, label)])
5603+
]
5604+
| `Function_symbols(symbols_type, list_image_or_string) => [%expr
5605+
[%e render_symbols(~loc, symbols_type, list_image_or_string)]
5606+
]
5607+
};
5608+
};
5609+
5610+
let render_counter =
5611+
(~loc, label: string, style: option(Types.counter_style)) => {
5612+
switch (style) {
5613+
| Some(counter_style) => [%expr
5614+
`counter((
5615+
[%e render_string(~loc, label)],
5616+
Some([%e render_counter_style(~loc, counter_style)]),
5617+
))
5618+
]
5619+
| None => [%expr `counter(([%e render_string(~loc, label)], None))]
5620+
};
5621+
};
5622+
55005623
let render_content_list = (~loc, content_list: Types.content_list) => {
55015624
content_list
55025625
|> List.map(content_item =>
@@ -5505,8 +5628,16 @@ let render_content_list = (~loc, content_list: Types.content_list) => {
55055628
| `Quote(quote) => render_quote(~loc, quote)
55065629
| `String(str) => render_content_string(~loc, str)
55075630
| `Url(u) => render_url(~loc, u)
5508-
| `Counter(_label, _, _style) => raise(Unsupported_feature)
5509-
| `Function_attr(_attr) => raise(Unsupported_feature)
5631+
| `Counter(counter_name, _, list_style_type_opt) =>
5632+
let counter_style_opt =
5633+
switch (list_style_type_opt) {
5634+
| Some(`Counter_style(cs)) => Some(cs)
5635+
| Some(`None | `String(_)) => None
5636+
| None => None
5637+
};
5638+
render_counter(~loc, counter_name, counter_style_opt);
5639+
| `Function_attr((attr_name, attr_type): Types.function_attr) =>
5640+
render_function_attr(~loc, attr_name, attr_type)
55105641
}
55115642
)
55125643
|> Builder.pexp_array(~loc);

packages/ppx/test/css-support/content.t/input.re

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
[%css {|content: "";|}];
77
/* [%css {|content: counter(ol);|}]; */
8+
[%css {|content: counter(count, decimal);|}];
9+
[%css {|content: counter(count, decimal) ") ";|}];
810
[%css {|content: unset;|}];
911

1012
/* Keywords that cannot be combined with other values */
@@ -29,7 +31,10 @@
2931
/* [%css {|content: counters(section_counter, ".", decimal-leading-zero);|}]; */
3032

3133
/* attr() value linked to the HTML attribute value */
32-
/* [%css {|content: attr(href);|}]; */
34+
[%css {|content: attr(href);|}];
35+
[%css {|content: attr(data-width px);|}];
36+
/* inherit is a declaration value, and current spec parser does not support it */
37+
/* [%css {|content: attr(data-width px, inherit);|}]; */
3338

3439
/* <quote> values */
3540
[%css {|content: open-quote;|}];
@@ -61,3 +66,22 @@
6166
[%css {|content: '';|}];
6267
[%css {|content: "'";|}];
6368
[%css {|content: '"';|}];
69+
70+
/* Test attr() with attr-type */
71+
[%css {|content: attr(href);|}];
72+
[%css {|content: attr(data-value);|}];
73+
/* [%css {|content: attr(data-value type(string));|}]; */
74+
/* [%css {|content: attr(data-value type(color));|}]; */
75+
/* [%css {|content: attr(data-value type(url));|}]; */
76+
/* [%css {|content: attr(data-value type(integer));|}]; */
77+
/* [%css {|content: attr(data-value type(number));|}]; */
78+
/* [%css {|content: attr(data-value type(length));|}]; */
79+
/* [%css {|content: attr(data-value type(angle));|}]; */
80+
/* [%css {|content: attr(data-value type(time));|}]; */
81+
/* [%css {|content: attr(data-value type(percentage));|}]; */
82+
[%css {|content: attr(data-value raw-string);|}];
83+
[%css {|content: attr(data-value em);|}];
84+
[%css {|content: attr(data-value px);|}];
85+
/* [%css {|content: attr(data-value type(string), "fallback");|}]; */
86+
/* [%css {|content: attr(data-value type(color), red);|}]; */
87+
/* [%css {|content: attr(href type(url), "#");|}]; */

0 commit comments

Comments
 (0)