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