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: &[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 gpui::#length_tokens).into());
127            }
128        })
129        .collect::<Vec<_>>();
130
131    let method = quote! {
132        #[doc = #doc_string]
133        fn #method_name(mut self) -> Self {
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: &[TokenStream2],
147    doc_string: &str,
148) -> TokenStream2 {
149    let method_name = format_ident!("{}", prefix);
150
151    let mut iter = fields.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<gpui::#length_type>) -> Self {
167            let style = self.style();
168            #(#field_assignments)*
169            self
170        }
171    };
172
173    method
174}
175
176/// Returns a vec of (Property name, has 'auto' suffix, tokens for accessing the property, documentation)
177fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>, &'static str)> {
178    vec![
179        (
180            "w",
181            true,
182            vec![quote! { size.width }],
183            "Sets the width of the element. [Docs](https://tailwindcss.com/docs/width)",
184        ),
185        ("h", true, vec![quote! { size.height }], "Sets the height of the element. [Docs](https://tailwindcss.com/docs/height)"),
186        (
187            "size",
188            true,
189            vec![quote! {size.width}, quote! {size.height}],
190            "Sets the width and height of the element."
191        ),
192        // TODO: These don't use the same size ramp as the others
193        // see https://tailwindcss.com/docs/max-width
194        (
195            "min_w",
196            true,
197            vec![quote! { min_size.width }],
198            "Sets the minimum width of the element. [Docs](https://tailwindcss.com/docs/min-width)",
199        ),
200        // TODO: These don't use the same size ramp as the others
201        // see https://tailwindcss.com/docs/max-width
202        (
203            "min_h",
204            true,
205            vec![quote! { min_size.height }],
206            "Sets the minimum height of the element. [Docs](https://tailwindcss.com/docs/min-height)",
207        ),
208        // TODO: These don't use the same size ramp as the others
209        // see https://tailwindcss.com/docs/max-width
210        (
211            "max_w",
212            true,
213            vec![quote! { max_size.width }],
214            "Sets the maximum width of the element. [Docs](https://tailwindcss.com/docs/max-width)",
215        ),
216        // TODO: These don't use the same size ramp as the others
217        // see https://tailwindcss.com/docs/max-width
218        (
219            "max_h",
220            true,
221            vec![quote! { max_size.height }],
222            "Sets the maximum height of the element. [Docs](https://tailwindcss.com/docs/max-height)",
223        ),
224        (
225            "m",
226            true,
227            vec![
228                quote! { margin.top },
229                quote! { margin.bottom },
230                quote! { margin.left },
231                quote! { margin.right },
232            ],
233            "Sets the margin of the element. [Docs](https://tailwindcss.com/docs/margin)"
234        ),
235        ("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)"),
236        (
237            "mb",
238            true,
239            vec![quote! { margin.bottom }],
240            "Sets the bottom margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"
241        ),
242        (
243            "my",
244            true,
245            vec![quote! { margin.top }, quote! { margin.bottom }],
246            "Sets the vertical margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-vertical-margin)"
247        ),
248        (
249            "mx",
250            true,
251            vec![quote! { margin.left }, quote! { margin.right }],
252            "Sets the horizontal margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-horizontal-margin)"
253        ),
254        ("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)"),
255        (
256            "mr",
257            true,
258            vec![quote! { margin.right }],
259            "Sets the right margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)"
260        ),
261        (
262            "p",
263            false,
264            vec![
265                quote! { padding.top },
266                quote! { padding.bottom },
267                quote! { padding.left },
268                quote! { padding.right },
269            ],
270            "Sets the padding of the element. [Docs](https://tailwindcss.com/docs/padding)"
271        ),
272        (
273            "pt",
274            false,
275            vec![quote! { padding.top }],
276            "Sets the top padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
277        ),
278        (
279            "pb",
280            false,
281            vec![quote! { padding.bottom }],
282            "Sets the bottom padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
283        ),
284        (
285            "px",
286            false,
287            vec![quote! { padding.left }, quote! { padding.right }],
288            "Sets the horizontal padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-horizontal-padding)"
289        ),
290        (
291            "py",
292            false,
293            vec![quote! { padding.top }, quote! { padding.bottom }],
294            "Sets the vertical padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-vertical-padding)"
295        ),
296        (
297            "pl",
298            false,
299            vec![quote! { padding.left }],
300            "Sets the left padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
301        ),
302        (
303            "pr",
304            false,
305            vec![quote! { padding.right }],
306            "Sets the right padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)"
307        ),
308        (
309            "inset",
310            true,
311            vec![quote! { inset.top }, quote! { inset.right }, quote! { inset.bottom }, quote! { inset.left }],
312            "Sets the top, right, bottom, and left values of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
313        ),
314        (
315            "top",
316            true,
317            vec![quote! { inset.top }],
318            "Sets the top value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
319        ),
320        (
321            "bottom",
322            true,
323            vec![quote! { inset.bottom }],
324            "Sets the bottom value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
325        ),
326        (
327            "left",
328            true,
329            vec![quote! { inset.left }],
330            "Sets the left value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
331        ),
332        (
333            "right",
334            true,
335            vec![quote! { inset.right }],
336            "Sets the right value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)",
337        ),
338        (
339            "gap",
340            false,
341            vec![quote! { gap.width }, quote! { gap.height }],
342            "Sets the gap between rows and columns in flex layouts. [Docs](https://tailwindcss.com/docs/gap)"
343        ),
344        (
345            "gap_x",
346            false,
347            vec![quote! { gap.width }],
348            "Sets the gap between columns in flex layouts. [Docs](https://tailwindcss.com/docs/gap#changing-row-and-column-gaps-independently)"
349        ),
350        (
351            "gap_y",
352            false,
353            vec![quote! { gap.height }],
354            "Sets the gap between rows in flex layouts. [Docs](https://tailwindcss.com/docs/gap#changing-row-and-column-gaps-independently)"
355        ),
356    ]
357}
358
359/// Returns a vec of (Suffix size, tokens that correspond to this size, documentation)
360fn box_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
361    vec![
362        ("0", quote! { px(0.) }, "0px"),
363        ("0p5", quote! { rems(0.125) }, "2px (0.125rem)"),
364        ("1", quote! { rems(0.25) }, "4px (0.25rem)"),
365        ("1p5", quote! { rems(0.375) }, "6px (0.375rem)"),
366        ("2", quote! { rems(0.5) }, "8px (0.5rem)"),
367        ("2p5", quote! { rems(0.625) }, "10px (0.625rem)"),
368        ("3", quote! { rems(0.75) }, "12px (0.75rem)"),
369        ("3p5", quote! { rems(0.875) }, "14px (0.875rem)"),
370        ("4", quote! { rems(1.) }, "16px (1rem)"),
371        ("5", quote! { rems(1.25) }, "20px (1.25rem)"),
372        ("6", quote! { rems(1.5) }, "24px (1.5rem)"),
373        ("7", quote! { rems(1.75) }, "28px (1.75rem)"),
374        ("8", quote! { rems(2.0) }, "32px (2rem)"),
375        ("9", quote! { rems(2.25) }, "36px (2.25rem)"),
376        ("10", quote! { rems(2.5) }, "40px (2.5rem)"),
377        ("11", quote! { rems(2.75) }, "44px (2.75rem)"),
378        ("12", quote! { rems(3.) }, "48px (3rem)"),
379        ("16", quote! { rems(4.) }, "64px (4rem)"),
380        ("20", quote! { rems(5.) }, "80px (5rem)"),
381        ("24", quote! { rems(6.) }, "96px (6rem)"),
382        ("32", quote! { rems(8.) }, "128px (8rem)"),
383        ("40", quote! { rems(10.) }, "160px (10rem)"),
384        ("48", quote! { rems(12.) }, "192px (12rem)"),
385        ("56", quote! { rems(14.) }, "224px (14rem)"),
386        ("64", quote! { rems(16.) }, "256px (16rem)"),
387        ("72", quote! { rems(18.) }, "288px (18rem)"),
388        ("80", quote! { rems(20.) }, "320px (20rem)"),
389        ("96", quote! { rems(24.) }, "384px (24rem)"),
390        ("auto", quote! { auto() }, "Auto"),
391        ("px", quote! { px(1.) }, "1px"),
392        ("full", quote! { relative(1.) }, "100%"),
393        ("1_2", quote! { relative(0.5) }, "50% (1/2)"),
394        ("1_3", quote! { relative(1./3.) }, "33% (1/3)"),
395        ("2_3", quote! { relative(2./3.) }, "66% (2/3)"),
396        ("1_4", quote! { relative(0.25) }, "25% (1/4)"),
397        ("2_4", quote! { relative(0.5) }, "50% (2/4)"),
398        ("3_4", quote! { relative(0.75) }, "75% (3/4)"),
399        ("1_5", quote! { relative(0.2) }, "20% (1/5)"),
400        ("2_5", quote! { relative(0.4) }, "40% (2/5)"),
401        ("3_5", quote! { relative(0.6) }, "60% (3/5)"),
402        ("4_5", quote! { relative(0.8) }, "80% (4/5)"),
403        ("1_6", quote! { relative(1./6.) }, "16% (1/6)"),
404        ("5_6", quote! { relative(5./6.) }, "80% (5/6)"),
405        ("1_12", quote! { relative(1./12.) }, "8% (1/12)"),
406    ]
407}
408
409fn corner_prefixes() -> Vec<(&'static str, Vec<TokenStream2>, &'static str)> {
410    vec![
411        (
412            "rounded",
413            vec![
414                quote! { corner_radii.top_left },
415                quote! { corner_radii.top_right },
416                quote! { corner_radii.bottom_right },
417                quote! { corner_radii.bottom_left },
418            ],
419            "Sets the border radius of the element. [Docs](https://tailwindcss.com/docs/border-radius)"
420        ),
421        (
422            "rounded_t",
423            vec![
424                quote! { corner_radii.top_left },
425                quote! { corner_radii.top_right },
426            ],
427            "Sets the border radius of the top side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
428        ),
429        (
430            "rounded_b",
431            vec![
432                quote! { corner_radii.bottom_left },
433                quote! { corner_radii.bottom_right },
434            ],
435            "Sets the border radius of the bottom side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
436        ),
437        (
438            "rounded_r",
439            vec![
440                quote! { corner_radii.top_right },
441                quote! { corner_radii.bottom_right },
442            ],
443            "Sets the border radius of the right side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
444        ),
445        (
446            "rounded_l",
447            vec![
448                quote! { corner_radii.top_left },
449                quote! { corner_radii.bottom_left },
450            ],
451            "Sets the border radius of the left side of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-sides-separately)"
452        ),
453        (
454            "rounded_tl",
455            vec![quote! { corner_radii.top_left }],
456            "Sets the border radius of the top left corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
457        ),
458        (
459            "rounded_tr",
460            vec![quote! { corner_radii.top_right }],
461            "Sets the border radius of the top right corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
462        ),
463        (
464            "rounded_bl",
465            vec![quote! { corner_radii.bottom_left }],
466            "Sets the border radius of the bottom left corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
467        ),
468        (
469            "rounded_br",
470            vec![quote! { corner_radii.bottom_right }],
471            "Sets the border radius of the bottom right corner of the element. [Docs](https://tailwindcss.com/docs/border-radius#rounding-corners-separately)"
472        ),
473    ]
474}
475
476fn corner_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
477    vec![
478        ("none", quote! { px(0.) }, "0px"),
479        ("sm", quote! { rems(0.125) }, "2px (0.125rem)"),
480        ("md", quote! { rems(0.25) }, "4px (0.25rem)"),
481        ("lg", quote! { rems(0.5) }, "8px (0.5rem)"),
482        ("xl", quote! { rems(0.75) }, "12px (0.75rem)"),
483        ("2xl", quote! { rems(1.) }, "16px (1rem)"),
484        ("3xl", quote! { rems(1.5) }, "24px (1.5rem)"),
485        ("full", quote! {  px(9999.) }, "9999px"),
486    ]
487}
488
489fn border_prefixes() -> Vec<(&'static str, Vec<TokenStream2>, &'static str)> {
490    vec![
491        (
492            "border",
493            vec![
494                quote! { border_widths.top },
495                quote! { border_widths.right },
496                quote! { border_widths.bottom },
497                quote! { border_widths.left },
498            ],
499            "Sets the border width of the element. [Docs](https://tailwindcss.com/docs/border-width)"
500        ),
501        (
502            "border_t",
503            vec![quote! { border_widths.top }],
504            "Sets the border width of the top side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
505        ),
506        (
507            "border_b",
508            vec![quote! { border_widths.bottom }],
509            "Sets the border width of the bottom side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
510        ),
511        (
512            "border_r",
513            vec![quote! { border_widths.right }],
514            "Sets the border width of the right side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
515        ),
516        (
517            "border_l",
518            vec![quote! { border_widths.left }],
519            "Sets the border width of the left side of the element. [Docs](https://tailwindcss.com/docs/border-width#individual-sides)"
520        ),
521        (
522            "border_x",
523            vec![
524                quote! { border_widths.left },
525                quote! { border_widths.right },
526            ],
527            "Sets the border width of the vertical sides of the element. [Docs](https://tailwindcss.com/docs/border-width#horizontal-and-vertical-sides)"
528        ),
529        (
530            "border_y",
531            vec![
532                quote! { border_widths.top },
533                quote! { border_widths.bottom },
534            ],
535            "Sets the border width of the horizontal sides of the element. [Docs](https://tailwindcss.com/docs/border-width#horizontal-and-vertical-sides)"
536        ),
537    ]
538}
539
540fn border_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
541    vec![
542        ("", quote! { px(1.)}, "1px"),
543        ("0", quote! { px(0.)}, "0px"),
544        ("1", quote! { px(1.) }, "1px"),
545        ("2", quote! { px(2.) }, "2px"),
546        ("3", quote! { px(3.) }, "3px"),
547        ("4", quote! { px(4.) }, "4px"),
548        ("5", quote! { px(5.) }, "5px"),
549        ("6", quote! { px(6.) }, "6px"),
550        ("7", quote! { px(7.) }, "7px"),
551        ("8", quote! { px(8.) }, "8px"),
552        ("9", quote! { px(9.) }, "9px"),
553        ("10", quote! { px(10.) }, "10px"),
554        ("11", quote! { px(11.) }, "11px"),
555        ("12", quote! { px(12.) }, "12px"),
556        ("16", quote! { px(16.) }, "16px"),
557        ("20", quote! { px(20.) }, "20px"),
558        ("24", quote! { px(24.) }, "24px"),
559        ("32", quote! { px(32.) }, "32px"),
560    ]
561}