style_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 style_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, prefix_doc_string) 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            prefix_doc_string,
 40        ));
 41
 42        for (suffix, length_tokens, suffix_doc_string) in box_suffixes() {
 43            if suffix != "auto" || auto_allowed {
 44                methods.push(generate_predefined_setter(
 45                    prefix,
 46                    suffix,
 47                    &fields,
 48                    &length_tokens,
 49                    false,
 50                    &format!("{prefix_doc_string}\n\n{suffix_doc_string}"),
 51                ));
 52            }
 53
 54            if suffix != "auto" {
 55                methods.push(generate_predefined_setter(
 56                    prefix,
 57                    suffix,
 58                    &fields,
 59                    &length_tokens,
 60                    true,
 61                    &format!("{prefix_doc_string}\n\n{suffix_doc_string}"),
 62                ));
 63            }
 64        }
 65    }
 66
 67    for (prefix, fields, prefix_doc_string) in corner_prefixes() {
 68        methods.push(generate_custom_value_setter(
 69            prefix,
 70            quote! { AbsoluteLength },
 71            &fields,
 72            prefix_doc_string,
 73        ));
 74
 75        for (suffix, radius_tokens, suffix_doc_string) in corner_suffixes() {
 76            methods.push(generate_predefined_setter(
 77                prefix,
 78                suffix,
 79                &fields,
 80                &radius_tokens,
 81                false,
 82                &format!("{prefix_doc_string}\n\n{suffix_doc_string}"),
 83            ));
 84        }
 85    }
 86
 87    for (prefix, fields, prefix_doc_string) in border_prefixes() {
 88        for (suffix, width_tokens, suffix_doc_string) in border_suffixes() {
 89            methods.push(generate_predefined_setter(
 90                prefix,
 91                suffix,
 92                &fields,
 93                &width_tokens,
 94                false,
 95                &format!("{prefix_doc_string}\n\n{suffix_doc_string}"),
 96            ));
 97        }
 98    }
 99    methods
100}
101
102fn generate_predefined_setter(
103    name: &'static str,
104    length: &'static str,
105    fields: &Vec<TokenStream2>,
106    length_tokens: &TokenStream2,
107    negate: bool,
108    doc_string: &str,
109) -> TokenStream2 {
110    let (negation_prefix, negation_token) = if negate {
111        ("neg_", quote! { - })
112    } else {
113        ("", quote! {})
114    };
115
116    let method_name = if length.is_empty() {
117        format_ident!("{}{}", negation_prefix, name)
118    } else {
119        format_ident!("{}{}_{}", negation_prefix, name, length)
120    };
121
122    let field_assignments = fields
123        .iter()
124        .map(|field_tokens| {
125            quote! {
126                style.#field_tokens = Some((#negation_token gpui3::#length_tokens).into());
127            }
128        })
129        .collect::<Vec<_>>();
130
131    let method = quote! {
132        #[doc = #doc_string]
133        fn #method_name(mut self) -> Self where Self: std::marker::Sized {
134            let style = self.style();
135            #(#field_assignments)*
136            self
137        }
138    };
139
140    method
141}
142
143fn generate_custom_value_setter(
144    prefix: &'static str,
145    length_type: TokenStream2,
146    fields: &Vec<TokenStream2>,
147    doc_string: &str,
148) -> TokenStream2 {
149    let method_name = format_ident!("{}", prefix);
150
151    let mut iter = fields.into_iter();
152    let last = iter.next_back().unwrap();
153    let field_assignments = iter
154        .map(|field_tokens| {
155            quote! {
156                style.#field_tokens = Some(length.clone().into());
157            }
158        })
159        .chain(std::iter::once(quote! {
160            style.#last = Some(length.into());
161        }))
162        .collect::<Vec<_>>();
163
164    let method = quote! {
165        #[doc = #doc_string]
166        fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui3::#length_type>) -> Self where Self: std::marker::Sized {
167            let style = self.style();
168            #(#field_assignments)*
169            self
170        }
171    };
172
173    method
174}
175
176fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>, &'static str)> {
177    vec![
178        (
179            "w",
180            true,
181            vec![quote! { size.width }],
182            "Sets the width of the element. [Docs](https://tailwindcss.com/docs/width)",
183        ),
184        ("h", true, vec![quote! { size.height }], "Sets the height of the element. [Docs](https://tailwindcss.com/docs/height)"),
185        (
186            "size",
187            true,
188            vec![quote! {size.width}, quote! {size.height}],
189            "Sets the width and height of the element."
190        ),
191        (
192            "min_w",
193            true,
194            vec![quote! { min_size.width }],
195            "Sets the minimum width of the element. [Docs](https://tailwindcss.com/docs/min-width)",
196        ),
197        (
198            "min_h",
199            true,
200            vec![quote! { min_size.height }],
201            "Sets the minimum height of the element. [Docs](https://tailwindcss.com/docs/min-height)",
202        ),
203        (
204            "max_w",
205            true,
206            vec![quote! { max_size.width }],
207            "Sets the maximum width of the element. [Docs](https://tailwindcss.com/docs/max-width)",
208        ),
209        (
210            "max_h",
211            true,
212            vec![quote! { max_size.height }],
213            "Sets the maximum height of the element. [Docs](https://tailwindcss.com/docs/max-height)",
214        ),
215        (
216            "m",
217            true,
218            vec![
219                quote! { margin.top },
220                quote! { margin.bottom },
221                quote! { margin.left },
222                quote! { margin.right },
223            ],
224            "Sets the margin of the element. [Docs](https://tailwindcss.com/docs/margin)"
225        ),
226        ("mt", true, vec![quote! { margin.top }], "Sets the top margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"),
227        (
228            "mb",
229            true,
230            vec![quote! { margin.bottom }],
231            "Sets the bottom margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"
232        ),
233        (
234            "my",
235            true,
236            vec![quote! { margin.top }, quote! { margin.bottom }],
237            "Sets the vertical margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-vertical-margin)"
238        ),
239        (
240            "mx",
241            true,
242            vec![quote! { margin.left }, quote! { margin.right }],
243            "Sets the horizontal margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-horizontal-margin)"
244        ),
245        ("ml", true, vec![quote! { margin.left }], "Sets the left margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"),
246        (
247            "mr",
248            true,
249            vec![quote! { margin.right }],
250            "Sets the right margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"
251        ),
252        (
253            "p",
254            false,
255            vec![
256                quote! { padding.top },
257                quote! { padding.bottom },
258                quote! { padding.left },
259                quote! { padding.right },
260            ],
261            "Sets the padding of the element. [Docs](https://tailwindcss.com/docs/padding)"
262        ),
263        (
264            "pt",
265            false,
266            vec![quote! { padding.top }],
267            "Sets the top padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
268        ),
269        (
270            "pb",
271            false,
272            vec![quote! { padding.bottom }],
273            "Sets the bottom padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
274        ),
275        (
276            "px",
277            false,
278            vec![quote! { padding.left }, quote! { padding.right }],
279            "Sets the horizontal padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-horizontal-padding)"
280        ),
281        (
282            "py",
283            false,
284            vec![quote! { padding.top }, quote! { padding.bottom }],
285            "Sets the vertical padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-vertical-padding)"
286        ),
287        (
288            "pl",
289            false,
290            vec![quote! { padding.left }],
291            "Sets the left padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
292        ),
293        (
294            "pr",
295            false,
296            vec![quote! { padding.right }],
297            "Sets the right padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
298        ),
299        ("top", true, vec![quote! { inset.top }], "Sets the top value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",),
300        (
301            "bottom",
302            true,
303            vec![quote! { inset.bottom }],
304            "Sets the bottom value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
305        ),
306        (
307            "left",
308            true,
309            vec![quote! { inset.left }],
310            "Sets the left value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
311        ),
312        (
313            "right",
314            true,
315            vec![quote! { inset.right }],
316            "Sets the right value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
317        ),
318        (
319            "gap",
320            false,
321            vec![quote! { gap.width }, quote! { gap.height }],
322            "Sets the gap between rows and columns in flex layouts. [Docs](https://tailwindcss.com/docs/gap)"
323        ),
324        (
325            "gap_x",
326            false,
327            vec![quote! { gap.width }],
328            "Sets the gap between columns in flex layouts. [Docs](https://tailwindcss.com/docs/gap#changing-row-and-column-gaps-independently)"
329        ),
330        (
331            "gap_y",
332            false,
333            vec![quote! { gap.height }],
334            "Sets the gap between rows in flex layouts. [Docs](https://tailwindcss.com/docs/gap#changing-row-and-column-gaps-independently)"
335        ),
336    ]
337}
338
339fn box_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
340    vec![
341        ("0", quote! { px(0.) }, "0px"),
342        ("0p5", quote! { rems(0.125) }, "2px (0.125rem)"),
343        ("1", quote! { rems(0.25) }, "4px (0.25rem)"),
344        ("1p5", quote! { rems(0.375) }, "6px (0.375rem)"),
345        ("2", quote! { rems(0.5) }, "8px (0.5rem)"),
346        ("2p5", quote! { rems(0.625) }, "10px (0.625rem)"),
347        ("3", quote! { rems(0.75) }, "12px (0.75rem)"),
348        ("3p5", quote! { rems(0.875) }, "14px (0.875rem)"),
349        ("4", quote! { rems(1.) }, "16px (1rem)"),
350        ("5", quote! { rems(1.25) }, "20px (1.25rem)"),
351        ("6", quote! { rems(1.5) }, "24px (1.5rem)"),
352        ("7", quote! { rems(1.75) }, "28px (1.75rem)"),
353        ("8", quote! { rems(2.0) }, "32px (2rem)"),
354        ("9", quote! { rems(2.25) }, "36px (2.25rem)"),
355        ("10", quote! { rems(2.5) }, "40px (2.5rem)"),
356        ("11", quote! { rems(2.75) }, "44px (2.75rem)"),
357        ("12", quote! { rems(3.) }, "48px (3rem)"),
358        ("16", quote! { rems(4.) }, "64px (4rem)"),
359        ("20", quote! { rems(5.) }, "80px (5rem)"),
360        ("24", quote! { rems(6.) }, "96px (6rem)"),
361        ("32", quote! { rems(8.) }, "128px (8rem)"),
362        ("40", quote! { rems(10.) }, "160px (10rem)"),
363        ("48", quote! { rems(12.) }, "192px (12rem)"),
364        ("56", quote! { rems(14.) }, "224px (14rem)"),
365        ("64", quote! { rems(16.) }, "256px (16rem)"),
366        ("72", quote! { rems(18.) }, "288px (18rem)"),
367        ("80", quote! { rems(20.) }, "320px (20rem)"),
368        ("96", quote! { rems(24.) }, "384px (24rem)"),
369        ("auto", quote! { auto() }, "Auto"),
370        ("px", quote! { px(1.) }, "1px"),
371        ("full", quote! { relative(1.) }, "100%"),
372        ("1_2", quote! { relative(0.5) }, "50% (1/2)"),
373        ("1_3", quote! { relative(1./3.) }, "33% (1/3)"),
374        ("2_3", quote! { relative(2./3.) }, "66% (2/3)"),
375        ("1_4", quote! { relative(0.25) }, "25% (1/4)"),
376        ("2_4", quote! { relative(0.5) }, "50% (2/4)"),
377        ("3_4", quote! { relative(0.75) }, "75% (3/4)"),
378        ("1_5", quote! { relative(0.2) }, "20% (1/5)"),
379        ("2_5", quote! { relative(0.4) }, "40% (2/5)"),
380        ("3_5", quote! { relative(0.6) }, "60% (3/5)"),
381        ("4_5", quote! { relative(0.8) }, "80% (4/5)"),
382        ("1_6", quote! { relative(1./6.) }, "16% (1/6)"),
383        ("5_6", quote! { relative(5./6.) }, "80% (5/6)"),
384        ("1_12", quote! { relative(1./12.) }, "8% (1/12)"),
385    ]
386}
387
388fn corner_prefixes() -> Vec<(&'static str, Vec<TokenStream2>, &'static str)> {
389    vec![
390        (
391            "rounded",
392            vec![
393                quote! { corner_radii.top_left },
394                quote! { corner_radii.top_right },
395                quote! { corner_radii.bottom_right },
396                quote! { corner_radii.bottom_left },
397            ],
398            "Sets the border radius of the element. [Docs](https://tailwindcss.com/docs/border-radius)"
399        ),
400        (
401            "rounded_t",
402            vec![
403                quote! { corner_radii.top_left },
404                quote! { corner_radii.top_right },
405            ],
406            "Sets the border radius of the top side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
407        ),
408        (
409            "rounded_b",
410            vec![
411                quote! { corner_radii.bottom_left },
412                quote! { corner_radii.bottom_right },
413            ],
414            "Sets the border radius of the bottom side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
415        ),
416        (
417            "rounded_r",
418            vec![
419                quote! { corner_radii.top_right },
420                quote! { corner_radii.bottom_right },
421            ],
422            "Sets the border radius of the right side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
423        ),
424        (
425            "rounded_l",
426            vec![
427                quote! { corner_radii.top_left },
428                quote! { corner_radii.bottom_left },
429            ],
430            "Sets the border radius of the left side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
431        ),
432        (
433            "rounded_tl",
434            vec![quote! { corner_radii.top_left }],
435            "Sets the border radius of the top left corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
436        ),
437        (
438            "rounded_tr",
439            vec![quote! { corner_radii.top_right }],
440            "Sets the border radius of the top right corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
441        ),
442        (
443            "rounded_bl",
444            vec![quote! { corner_radii.bottom_left }],
445            "Sets the border radius of the bottom left corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
446        ),
447        (
448            "rounded_br",
449            vec![quote! { corner_radii.bottom_right }],
450            "Sets the border radius of the bottom right corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
451        ),
452    ]
453}
454
455fn corner_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
456    vec![
457        ("none", quote! { px(0.) }, "0px"),
458        ("sm", quote! { rems(0.125) }, "2px (0.125rem)"),
459        ("md", quote! { rems(0.25) }, "4px (0.25rem)"),
460        ("lg", quote! { rems(0.5) }, "8px (0.5rem)"),
461        ("xl", quote! { rems(0.75) }, "12px (0.75rem)"),
462        ("2xl", quote! { rems(1.) }, "16px (1rem)"),
463        ("3xl", quote! { rems(1.5) }, "24px (1.5rem)"),
464        ("full", quote! {  px(9999.) }, "9999px"),
465    ]
466}
467
468fn border_prefixes() -> Vec<(&'static str, Vec<TokenStream2>, &'static str)> {
469    vec![
470        (
471            "border",
472            vec![
473                quote! { border_widths.top },
474                quote! { border_widths.right },
475                quote! { border_widths.bottom },
476                quote! { border_widths.left },
477            ],
478            "Sets the border width of the element. [Docs](https://tailwindcss.com/docs/border-width)"
479        ),
480        (
481            "border_t",
482            vec![quote! { border_widths.top }],
483            "Sets the border width of the top side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
484        ),
485        (
486            "border_b",
487            vec![quote! { border_widths.bottom }],
488            "Sets the border width of the bottom side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
489        ),
490        (
491            "border_r",
492            vec![quote! { border_widths.right }],
493            "Sets the border width of the right side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
494        ),
495        (
496            "border_l",
497            vec![quote! { border_widths.left }],
498            "Sets the border width of the left side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
499        ),
500        (
501            "border_x",
502            vec![
503                quote! { border_widths.left },
504                quote! { border_widths.right },
505            ],
506            "Sets the border width of the vertical sides of the element. [Docs](https://tailwindcss.com/docs/border-width#horizontal-and-vertical-sides)"
507        ),
508        (
509            "border_y",
510            vec![
511                quote! { border_widths.top },
512                quote! { border_widths.bottom },
513            ],
514            "Sets the border width of the horizontal sides of the element. [Docs](https://tailwindcss.com/docs/border-width#horizontal-and-vertical-sides)"
515        ),
516    ]
517}
518
519fn border_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
520    vec![
521        ("", quote! { px(1.)}, "1px"),
522        ("0", quote! { px(0.)}, "0px"),
523        ("1", quote! { px(1.) }, "1px"),
524        ("2", quote! { px(2.) }, "2px"),
525        ("3", quote! { px(3.) }, "3px"),
526        ("4", quote! { px(4.) }, "4px"),
527        ("5", quote! { px(5.) }, "5px"),
528        ("6", quote! { px(6.) }, "6px"),
529        ("7", quote! { px(7.) }, "7px"),
530        ("8", quote! { px(8.) }, "8px"),
531        ("9", quote! { px(9.) }, "9px"),
532        ("10", quote! { px(10.) }, "10px"),
533        ("11", quote! { px(11.) }, "11px"),
534        ("12", quote! { px(12.) }, "12px"),
535        ("16", quote! { px(16.) }, "16px"),
536        ("20", quote! { px(20.) }, "20px"),
537        ("24", quote! { px(24.) }, "24px"),
538        ("32", quote! { px(32.) }, "32px"),
539    ]
540}