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}