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