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        for (suffix, length_tokens, doc_string) in box_suffixes() {
 32            if auto_allowed || suffix != "auto" {
 33                let method = generate_method(prefix, suffix, &fields, length_tokens, doc_string);
 34                methods.push(method);
 35            }
 36        }
 37    }
 38
 39    for (prefix, fields) in corner_prefixes() {
 40        for (suffix, radius_tokens, doc_string) in corner_suffixes() {
 41            let method = generate_method(prefix, suffix, &fields, radius_tokens, doc_string);
 42            methods.push(method);
 43        }
 44    }
 45
 46    for (prefix, fields) in border_prefixes() {
 47        for (suffix, width_tokens, doc_string) in border_suffixes() {
 48            let method = generate_method(prefix, suffix, &fields, width_tokens, doc_string);
 49            methods.push(method);
 50        }
 51    }
 52
 53    methods
 54}
 55
 56fn generate_method(
 57    prefix: &'static str,
 58    suffix: &'static str,
 59    fields: &Vec<TokenStream2>,
 60    length_tokens: TokenStream2,
 61    doc_string: &'static str,
 62) -> TokenStream2 {
 63    let method_name = if suffix.is_empty() {
 64        format_ident!("{}", prefix)
 65    } else {
 66        format_ident!("{}_{}", prefix, suffix)
 67    };
 68
 69    let field_assignments = fields
 70        .iter()
 71        .map(|field_tokens| {
 72            quote! {
 73                style.#field_tokens = Some(gpui::geometry::#length_tokens);
 74            }
 75        })
 76        .collect::<Vec<_>>();
 77
 78    let method = quote! {
 79        #[doc = #doc_string]
 80        fn #method_name(mut self) -> Self where Self: std::marker::Sized {
 81            let mut style = self.declared_style();
 82            #(#field_assignments)*
 83            self
 84        }
 85    };
 86
 87    method
 88}
 89
 90fn box_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
 91    vec![
 92        ("w", true, vec![quote! { size.width }]),
 93        ("h", true, vec![quote! { size.height }]),
 94        (
 95            "size",
 96            true,
 97            vec![quote! {size.width}, quote! {size.height}],
 98        ),
 99        ("min_w", false, vec![quote! { min_size.width }]),
100        ("min_h", false, vec![quote! { min_size.height }]),
101        ("max_w", false, vec![quote! { max_size.width }]),
102        ("max_h", false, vec![quote! { max_size.height }]),
103        (
104            "m",
105            true,
106            vec![
107                quote! { margin.top },
108                quote! { margin.bottom },
109                quote! { margin.left },
110                quote! { margin.right },
111            ],
112        ),
113        ("mt", true, vec![quote! { margin.top }]),
114        ("mb", true, vec![quote! { margin.bottom }]),
115        (
116            "my",
117            true,
118            vec![quote! { margin.top }, quote! { margin.bottom }],
119        ),
120        (
121            "mx",
122            true,
123            vec![quote! { margin.left }, quote! { margin.right }],
124        ),
125        ("ml", true, vec![quote! { margin.left }]),
126        ("mr", true, vec![quote! { margin.right }]),
127        (
128            "p",
129            false,
130            vec![
131                quote! { padding.top },
132                quote! { padding.bottom },
133                quote! { padding.left },
134                quote! { padding.right },
135            ],
136        ),
137        ("pt", false, vec![quote! { padding.top }]),
138        ("pb", false, vec![quote! { padding.bottom }]),
139        (
140            "px",
141            false,
142            vec![quote! { padding.left }, quote! { padding.right }],
143        ),
144        (
145            "py",
146            false,
147            vec![quote! { padding.top }, quote! { padding.bottom }],
148        ),
149        ("pl", false, vec![quote! { padding.left }]),
150        ("pr", false, vec![quote! { padding.right }]),
151        ("top", true, vec![quote! { inset.top }]),
152        ("bottom", true, vec![quote! { inset.bottom }]),
153        ("left", true, vec![quote! { inset.left }]),
154        ("right", true, vec![quote! { inset.right }]),
155        (
156            "gap",
157            false,
158            vec![quote! { gap.width }, quote! { gap.height }],
159        ),
160        ("gap_x", false, vec![quote! { gap.width }]),
161        ("gap_y", false, vec![quote! { gap.height }]),
162    ]
163}
164
165fn box_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
166    vec![
167        ("0", quote! { pixels(0.) }, "0px"),
168        ("0p5", quote! { rems(0.125) }, "2px (0.125rem)"),
169        ("1", quote! { rems(0.25) }, "4px (0.25rem)"),
170        ("1p5", quote! { rems(0.375) }, "6px (0.375rem)"),
171        ("2", quote! { rems(0.5) }, "8px (0.5rem)"),
172        ("2p5", quote! { rems(0.625) }, "10px (0.625rem)"),
173        ("3", quote! { rems(0.75) }, "12px (0.75rem)"),
174        ("3p5", quote! { rems(0.875) }, "14px (0.875rem)"),
175        ("4", quote! { rems(1.) }, "16px (1rem)"),
176        ("5", quote! { rems(1.25) }, "20px (1.25rem)"),
177        ("6", quote! { rems(1.5) }, "24px (1.5rem)"),
178        ("7", quote! { rems(1.75) }, "28px (1.75rem)"),
179        ("8", quote! { rems(2.0) }, "32px (2rem)"),
180        ("9", quote! { rems(2.25) }, "36px (2.25rem)"),
181        ("10", quote! { rems(2.5) }, "40px (2.5rem)"),
182        ("11", quote! { rems(2.75) }, "44px (2.75rem)"),
183        ("12", quote! { rems(3.) }, "48px (3rem)"),
184        ("16", quote! { rems(4.) }, "64px (4rem)"),
185        ("20", quote! { rems(5.) }, "80px (5rem)"),
186        ("24", quote! { rems(6.) }, "96px (6rem)"),
187        ("32", quote! { rems(8.) }, "128px (8rem)"),
188        ("40", quote! { rems(10.) }, "160px (10rem)"),
189        ("48", quote! { rems(12.) }, "192px (12rem)"),
190        ("56", quote! { rems(14.) }, "224px (14rem)"),
191        ("64", quote! { rems(16.) }, "256px (16rem)"),
192        ("72", quote! { rems(18.) }, "288px (18rem)"),
193        ("80", quote! { rems(20.) }, "320px (20rem)"),
194        ("96", quote! { rems(24.) }, "384px (24rem)"),
195        ("auto", quote! { auto() }, "Auto"),
196        ("px", quote! { pixels(1.) }, "1px"),
197        ("full", quote! { relative(1.) }, "100%"),
198        ("1_2", quote! { relative(0.5) }, "50% (1/2)"),
199        ("1_3", quote! { relative(1./3.) }, "33% (1/3)"),
200        ("2_3", quote! { relative(2./3.) }, "66% (2/3)"),
201        ("1_4", quote! { relative(0.25) }, "25% (1/4)"),
202        ("2_4", quote! { relative(0.5) }, "50% (2/4)"),
203        ("3_4", quote! { relative(0.75) }, "75% (3/4)"),
204        ("1_5", quote! { relative(0.2) }, "20% (1/5)"),
205        ("2_5", quote! { relative(0.4) }, "40% (2/5)"),
206        ("3_5", quote! { relative(0.6) }, "60% (3/5)"),
207        ("4_5", quote! { relative(0.8) }, "80% (4/5)"),
208        ("1_6", quote! { relative(1./6.) }, "16% (1/6)"),
209        ("5_6", quote! { relative(5./6.) }, "80% (5/6)"),
210        ("1_12", quote! { relative(1./12.) }, "8% (1/12)"),
211    ]
212}
213
214fn corner_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
215    vec![
216        (
217            "rounded",
218            vec![
219                quote! { corner_radii.top_left },
220                quote! { corner_radii.top_right },
221                quote! { corner_radii.bottom_right },
222                quote! { corner_radii.bottom_left },
223            ],
224        ),
225        (
226            "rounded_t",
227            vec![
228                quote! { corner_radii.top_left },
229                quote! { corner_radii.top_right },
230            ],
231        ),
232        (
233            "rounded_b",
234            vec![
235                quote! { corner_radii.bottom_left },
236                quote! { corner_radii.bottom_right },
237            ],
238        ),
239        (
240            "rounded_r",
241            vec![
242                quote! { corner_radii.top_right },
243                quote! { corner_radii.bottom_right },
244            ],
245        ),
246        (
247            "rounded_l",
248            vec![
249                quote! { corner_radii.top_left },
250                quote! { corner_radii.bottom_left },
251            ],
252        ),
253        ("rounded_tl", vec![quote! { corner_radii.top_left }]),
254        ("rounded_tr", vec![quote! { corner_radii.top_right }]),
255        ("rounded_bl", vec![quote! { corner_radii.bottom_left }]),
256        ("rounded_br", vec![quote! { corner_radii.bottom_right }]),
257    ]
258}
259
260fn corner_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
261    vec![
262        ("none", quote! { pixels(0.) }, "0px"),
263        ("sm", quote! { rems(0.125) }, "2px (0.125rem)"),
264        ("md", quote! { rems(0.25) }, "4px (0.25rem)"),
265        ("lg", quote! { rems(0.5) }, "8px (0.5rem)"),
266        ("xl", quote! { rems(0.75) }, "12px (0.75rem)"),
267        ("2xl", quote! { rems(1.) }, "16px (1rem)"),
268        ("3xl", quote! { rems(1.5) }, "24px (1.5rem)"),
269        ("full", quote! {  pixels(9999.) }, "9999px"),
270    ]
271}
272
273fn border_prefixes() -> Vec<(&'static str, Vec<TokenStream2>)> {
274    vec![
275        (
276            "border",
277            vec![
278                quote! { border_widths.top },
279                quote! { border_widths.right },
280                quote! { border_widths.bottom },
281                quote! { border_widths.left },
282            ],
283        ),
284        ("border_t", vec![quote! { border_widths.top }]),
285        ("border_b", vec![quote! { border_widths.bottom }]),
286        ("border_r", vec![quote! { border_widths.right }]),
287        ("border_l", vec![quote! { border_widths.left }]),
288        (
289            "border_x",
290            vec![
291                quote! { border_widths.left },
292                quote! { border_widths.right },
293            ],
294        ),
295        (
296            "border_y",
297            vec![
298                quote! { border_widths.top },
299                quote! { border_widths.bottom },
300            ],
301        ),
302    ]
303}
304
305fn border_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
306    vec![
307        ("", quote! { pixels(1.)}, "1px"),
308        ("0", quote! { pixels(0.)}, "0px"),
309        ("1", quote! { pixels(1.) }, "1px"),
310        ("2", quote! { pixels(2.) }, "2px"),
311        ("3", quote! { pixels(3.) }, "3px"),
312        ("4", quote! { pixels(4.) }, "4px"),
313        ("5", quote! { pixels(5.) }, "5px"),
314        ("6", quote! { pixels(6.) }, "6px"),
315        ("7", quote! { pixels(7.) }, "7px"),
316        ("8", quote! { pixels(8.) }, "8px"),
317        ("9", quote! { pixels(9.) }, "9px"),
318        ("10", quote! { pixels(10.) }, "10px"),
319        ("11", quote! { pixels(11.) }, "11px"),
320        ("12", quote! { pixels(12.) }, "12px"),
321        ("16", quote! { pixels(16.) }, "16px"),
322        ("20", quote! { pixels(20.) }, "20px"),
323        ("24", quote! { pixels(24.) }, "24px"),
324        ("32", quote! { pixels(32.) }, "32px"),
325    ]
326}