styleable_helpers.rs

  1use proc_macro::TokenStream;
  2use proc_macro2::TokenStream as TokenStream2;
  3use quote::{format_ident, quote};
  4use syn::{
  5    parse::{Parse, ParseStream, Result},
  6    parse_macro_input,
  7};
  8
  9struct StyleableMacroInput;
 10
 11impl Parse for StyleableMacroInput {
 12    fn parse(_input: ParseStream) -> Result<Self> {
 13        Ok(StyleableMacroInput)
 14    }
 15}
 16
 17pub fn styleable_helpers(input: TokenStream) -> TokenStream {
 18    let _ = parse_macro_input!(input as StyleableMacroInput);
 19    let methods = generate_methods();
 20    let output = quote! {
 21        #(#methods)*
 22    };
 23
 24    output.into()
 25}
 26
 27fn generate_methods() -> Vec<TokenStream2> {
 28    let mut methods = Vec::new();
 29
 30    for (prefix, auto_allowed, fields) in box_prefixes() {
 31        methods.push(generate_custom_value_setter(
 32            prefix,
 33            if auto_allowed {
 34                quote! { Length }
 35            } else {
 36                quote! { DefiniteLength }
 37            },
 38            &fields,
 39        ));
 40
 41        for (suffix, length_tokens, doc_string) in box_suffixes() {
 42            if suffix != "auto" || auto_allowed {
 43                methods.push(generate_predefined_setter(
 44                    prefix,
 45                    suffix,
 46                    &fields,
 47                    &length_tokens,
 48                    false,
 49                    doc_string,
 50                ));
 51            }
 52
 53            if suffix != "auto" {
 54                methods.push(generate_predefined_setter(
 55                    prefix,
 56                    suffix,
 57                    &fields,
 58                    &length_tokens,
 59                    true,
 60                    doc_string,
 61                ));
 62            }
 63        }
 64    }
 65
 66    for (prefix, fields) in corner_prefixes() {
 67        methods.push(generate_custom_value_setter(
 68            prefix,
 69            quote! { AbsoluteLength },
 70            &fields,
 71        ));
 72
 73        for (suffix, radius_tokens, doc_string) in corner_suffixes() {
 74            methods.push(generate_predefined_setter(
 75                prefix,
 76                suffix,
 77                &fields,
 78                &radius_tokens,
 79                false,
 80                doc_string,
 81            ));
 82        }
 83    }
 84
 85    for (prefix, fields) in border_prefixes() {
 86        for (suffix, width_tokens, doc_string) in border_suffixes() {
 87            methods.push(generate_predefined_setter(
 88                prefix,
 89                suffix,
 90                &fields,
 91                &width_tokens,
 92                false,
 93                doc_string,
 94            ));
 95        }
 96    }
 97    methods
 98}
 99
100fn generate_predefined_setter(
101    name: &'static str,
102    length: &'static str,
103    fields: &Vec<TokenStream2>,
104    length_tokens: &TokenStream2,
105    negate: bool,
106    doc_string: &'static str,
107) -> TokenStream2 {
108    let (negation_prefix, negation_token) = if negate {
109        ("neg_", quote! { - })
110    } else {
111        ("", quote! {})
112    };
113
114    let method_name = if length.is_empty() {
115        format_ident!("{}{}", negation_prefix, name)
116    } else {
117        format_ident!("{}{}_{}", negation_prefix, name, length)
118    };
119
120    let field_assignments = fields
121        .iter()
122        .map(|field_tokens| {
123            quote! {
124                style.#field_tokens = Some((#negation_token gpui2::geometry::#length_tokens).into());
125            }
126        })
127        .collect::<Vec<_>>();
128
129    let method = quote! {
130        #[doc = #doc_string]
131        fn #method_name(mut self) -> Self where Self: std::marker::Sized {
132            let mut style = self.declared_style();
133            #(#field_assignments)*
134            self
135        }
136    };
137
138    method
139}
140
141fn generate_custom_value_setter(
142    prefix: &'static str,
143    length_type: TokenStream2,
144    fields: &Vec<TokenStream2>,
145) -> TokenStream2 {
146    let method_name = format_ident!("{}", prefix);
147
148    let mut iter = fields.into_iter();
149    let last = iter.next_back().unwrap();
150    let field_assignments = iter
151        .map(|field_tokens| {
152            quote! {
153                style.#field_tokens = Some(length.clone().into());
154            }
155        })
156        .chain(std::iter::once(quote! {
157            style.#last = Some(length.into());
158        }))
159        .collect::<Vec<_>>();
160
161    let method = quote! {
162        fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui2::geometry::#length_type>) -> Self where Self: std::marker::Sized {
163            let mut style = self.declared_style();
164            #(#field_assignments)*
165            self
166        }
167    };
168
169    method
170}
171
172fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
173    vec![
174        ("w", true, vec![quote! { size.width }]),
175        ("h", true, vec![quote! { size.height }]),
176        (
177            "size",
178            true,
179            vec![quote! {size.width}, quote! {size.height}],
180        ),
181        ("min_w", true, vec![quote! { min_size.width }]),
182        ("min_h", true, vec![quote! { min_size.height }]),
183        ("max_w", true, vec![quote! { max_size.width }]),
184        ("max_h", true, vec![quote! { max_size.height }]),
185        (
186            "m",
187            true,
188            vec![
189                quote! { margin.top },
190                quote! { margin.bottom },
191                quote! { margin.left },
192                quote! { margin.right },
193            ],
194        ),
195        ("mt", true, vec![quote! { margin.top }]),
196        ("mb", true, vec![quote! { margin.bottom }]),
197        (
198            "my",
199            true,
200            vec![quote! { margin.top }, quote! { margin.bottom }],
201        ),
202        (
203            "mx",
204            true,
205            vec![quote! { margin.left }, quote! { margin.right }],
206        ),
207        ("ml", true, vec![quote! { margin.left }]),
208        ("mr", true, vec![quote! { margin.right }]),
209        (
210            "p",
211            false,
212            vec![
213                quote! { padding.top },
214                quote! { padding.bottom },
215                quote! { padding.left },
216                quote! { padding.right },
217            ],
218        ),
219        ("pt", false, vec![quote! { padding.top }]),
220        ("pb", false, vec![quote! { padding.bottom }]),
221        (
222            "px",
223            false,
224            vec![quote! { padding.left }, quote! { padding.right }],
225        ),
226        (
227            "py",
228            false,
229            vec![quote! { padding.top }, quote! { padding.bottom }],
230        ),
231        ("pl", false, vec![quote! { padding.left }]),
232        ("pr", false, vec![quote! { padding.right }]),
233        ("top", true, vec![quote! { inset.top }]),
234        ("bottom", true, vec![quote! { inset.bottom }]),
235        ("left", true, vec![quote! { inset.left }]),
236        ("right", true, vec![quote! { inset.right }]),
237        (
238            "gap",
239            false,
240            vec![quote! { gap.width }, quote! { gap.height }],
241        ),
242        ("gap_x", false, vec![quote! { gap.width }]),
243        ("gap_y", false, vec![quote! { gap.height }]),
244    ]
245}
246
247fn box_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
248    vec![
249        ("0", quote! { pixels(0.) }, "0px"),
250        ("0p5", quote! { rems(0.125) }, "2px (0.125rem)"),
251        ("1", quote! { rems(0.25) }, "4px (0.25rem)"),
252        ("1p5", quote! { rems(0.375) }, "6px (0.375rem)"),
253        ("2", quote! { rems(0.5) }, "8px (0.5rem)"),
254        ("2p5", quote! { rems(0.625) }, "10px (0.625rem)"),
255        ("3", quote! { rems(0.75) }, "12px (0.75rem)"),
256        ("3p5", quote! { rems(0.875) }, "14px (0.875rem)"),
257        ("4", quote! { rems(1.) }, "16px (1rem)"),
258        ("5", quote! { rems(1.25) }, "20px (1.25rem)"),
259        ("6", quote! { rems(1.5) }, "24px (1.5rem)"),
260        ("7", quote! { rems(1.75) }, "28px (1.75rem)"),
261        ("8", quote! { rems(2.0) }, "32px (2rem)"),
262        ("9", quote! { rems(2.25) }, "36px (2.25rem)"),
263        ("10", quote! { rems(2.5) }, "40px (2.5rem)"),
264        ("11", quote! { rems(2.75) }, "44px (2.75rem)"),
265        ("12", quote! { rems(3.) }, "48px (3rem)"),
266        ("16", quote! { rems(4.) }, "64px (4rem)"),
267        ("20", quote! { rems(5.) }, "80px (5rem)"),
268        ("24", quote! { rems(6.) }, "96px (6rem)"),
269        ("32", quote! { rems(8.) }, "128px (8rem)"),
270        ("40", quote! { rems(10.) }, "160px (10rem)"),
271        ("48", quote! { rems(12.) }, "192px (12rem)"),
272        ("56", quote! { rems(14.) }, "224px (14rem)"),
273        ("64", quote! { rems(16.) }, "256px (16rem)"),
274        ("72", quote! { rems(18.) }, "288px (18rem)"),
275        ("80", quote! { rems(20.) }, "320px (20rem)"),
276        ("96", quote! { rems(24.) }, "384px (24rem)"),
277        ("auto", quote! { auto() }, "Auto"),
278        ("px", quote! { pixels(1.) }, "1px"),
279        ("full", quote! { relative(1.) }, "100%"),
280        ("1_2", quote! { relative(0.5) }, "50% (1/2)"),
281        ("1_3", quote! { relative(1./3.) }, "33% (1/3)"),
282        ("2_3", quote! { relative(2./3.) }, "66% (2/3)"),
283        ("1_4", quote! { relative(0.25) }, "25% (1/4)"),
284        ("2_4", quote! { relative(0.5) }, "50% (2/4)"),
285        ("3_4", quote! { relative(0.75) }, "75% (3/4)"),
286        ("1_5", quote! { relative(0.2) }, "20% (1/5)"),
287        ("2_5", quote! { relative(0.4) }, "40% (2/5)"),
288        ("3_5", quote! { relative(0.6) }, "60% (3/5)"),
289        ("4_5", quote! { relative(0.8) }, "80% (4/5)"),
290        ("1_6", quote! { relative(1./6.) }, "16% (1/6)"),
291        ("5_6", quote! { relative(5./6.) }, "80% (5/6)"),
292        ("1_12", quote! { relative(1./12.) }, "8% (1/12)"),
293    ]
294}
295
296fn corner_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
297    vec![
298        (
299            "rounded",
300            vec![
301                quote! { corner_radii.top_left },
302                quote! { corner_radii.top_right },
303                quote! { corner_radii.bottom_right },
304                quote! { corner_radii.bottom_left },
305            ],
306        ),
307        (
308            "rounded_t",
309            vec![
310                quote! { corner_radii.top_left },
311                quote! { corner_radii.top_right },
312            ],
313        ),
314        (
315            "rounded_b",
316            vec![
317                quote! { corner_radii.bottom_left },
318                quote! { corner_radii.bottom_right },
319            ],
320        ),
321        (
322            "rounded_r",
323            vec![
324                quote! { corner_radii.top_right },
325                quote! { corner_radii.bottom_right },
326            ],
327        ),
328        (
329            "rounded_l",
330            vec![
331                quote! { corner_radii.top_left },
332                quote! { corner_radii.bottom_left },
333            ],
334        ),
335        ("rounded_tl", vec![quote! { corner_radii.top_left }]),
336        ("rounded_tr", vec![quote! { corner_radii.top_right }]),
337        ("rounded_bl", vec![quote! { corner_radii.bottom_left }]),
338        ("rounded_br", vec![quote! { corner_radii.bottom_right }]),
339    ]
340}
341
342fn corner_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
343    vec![
344        ("none", quote! { pixels(0.) }, "0px"),
345        ("sm", quote! { rems(0.125) }, "2px (0.125rem)"),
346        ("md", quote! { rems(0.25) }, "4px (0.25rem)"),
347        ("lg", quote! { rems(0.5) }, "8px (0.5rem)"),
348        ("xl", quote! { rems(0.75) }, "12px (0.75rem)"),
349        ("2xl", quote! { rems(1.) }, "16px (1rem)"),
350        ("3xl", quote! { rems(1.5) }, "24px (1.5rem)"),
351        ("full", quote! {  pixels(9999.) }, "9999px"),
352    ]
353}
354
355fn border_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
356    vec![
357        (
358            "border",
359            vec![
360                quote! { border_widths.top },
361                quote! { border_widths.right },
362                quote! { border_widths.bottom },
363                quote! { border_widths.left },
364            ],
365        ),
366        ("border_t", vec![quote! { border_widths.top }]),
367        ("border_b", vec![quote! { border_widths.bottom }]),
368        ("border_r", vec![quote! { border_widths.right }]),
369        ("border_l", vec![quote! { border_widths.left }]),
370        (
371            "border_x",
372            vec![
373                quote! { border_widths.left },
374                quote! { border_widths.right },
375            ],
376        ),
377        (
378            "border_y",
379            vec![
380                quote! { border_widths.top },
381                quote! { border_widths.bottom },
382            ],
383        ),
384    ]
385}
386
387fn border_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
388    vec![
389        ("", quote! { pixels(1.)}, "1px"),
390        ("0", quote! { pixels(0.)}, "0px"),
391        ("1", quote! { pixels(1.) }, "1px"),
392        ("2", quote! { pixels(2.) }, "2px"),
393        ("3", quote! { pixels(3.) }, "3px"),
394        ("4", quote! { pixels(4.) }, "4px"),
395        ("5", quote! { pixels(5.) }, "5px"),
396        ("6", quote! { pixels(6.) }, "6px"),
397        ("7", quote! { pixels(7.) }, "7px"),
398        ("8", quote! { pixels(8.) }, "8px"),
399        ("9", quote! { pixels(9.) }, "9px"),
400        ("10", quote! { pixels(10.) }, "10px"),
401        ("11", quote! { pixels(11.) }, "11px"),
402        ("12", quote! { pixels(12.) }, "12px"),
403        ("16", quote! { pixels(16.) }, "16px"),
404        ("20", quote! { pixels(20.) }, "20px"),
405        ("24", quote! { pixels(24.) }, "24px"),
406        ("32", quote! { pixels(32.) }, "32px"),
407    ]
408}