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}