From 3cdd46522629a051638f32c21c9a5d6c78015081 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 9 Jul 2024 13:52:52 -0400 Subject: [PATCH] gpui: Make style macros more composable (#14007) This PR begins the process of breaking up the `style_helpers!` macro into smaller macros that can be used to generate methods for a related subset of styles. The style method macros also now accept an optional `visibility` parameter to control the visibility of the generated methods. This allows for adding these methods to a struct instead of a just a trait. For example, to expose just the padding styles on a `Facepile` we can do this: ```rs impl Facepile { fn style(&mut self) -> &mut StyleRefinement { self.base.style() } gpui::padding_style_methods!({ visibility: pub }); } ``` Release Notes: - N/A --- crates/gpui/src/styled.rs | 20 +- crates/gpui_macros/src/gpui_macros.rs | 23 +- crates/gpui_macros/src/style_helpers.rs | 312 ++++++++++++++++-------- crates/ui/src/components/facepile.rs | 31 ++- 4 files changed, 259 insertions(+), 127 deletions(-) diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index c5ddd1f8f8499953ac1c2eebac21edf9d54813e0..fa4a5d994336c31a07c1fd546fd78f8c5a653117 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -1,9 +1,10 @@ use crate::{ self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle, DefiniteLength, Fill, FlexDirection, FlexWrap, Font, FontStyle, FontWeight, Hsla, - JustifyContent, Length, Position, SharedString, StyleRefinement, Visibility, WhiteSpace, + JustifyContent, Length, SharedString, StyleRefinement, Visibility, WhiteSpace, }; use crate::{BoxShadow, TextStyleRefinement}; +pub use gpui_macros::{margin_style_methods, padding_style_methods, position_style_methods}; use smallvec::{smallvec, SmallVec}; use taffy::style::{AlignContent, Display, Overflow}; @@ -14,20 +15,9 @@ pub trait Styled: Sized { fn style(&mut self) -> &mut StyleRefinement; gpui_macros::style_helpers!(); - - /// Sets the position of the element to `relative`. - /// [Docs](https://tailwindcss.com/docs/position) - fn relative(mut self) -> Self { - self.style().position = Some(Position::Relative); - self - } - - /// Sets the position of the element to `absolute`. - /// [Docs](https://tailwindcss.com/docs/position) - fn absolute(mut self) -> Self { - self.style().position = Some(Position::Absolute); - self - } + gpui_macros::margin_style_methods!(); + gpui_macros::padding_style_methods!(); + gpui_macros::position_style_methods!(); /// Sets the display type of the element to `block`. /// [Docs](https://tailwindcss.com/docs/display) diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index a454d53be5a85f37e04bc24fc934bd759e87c770..1890bf4a2514343e7c5c91336247865193beee47 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -27,13 +27,34 @@ pub fn derive_render(input: TokenStream) -> TokenStream { derive_render::derive_render(input) } -/// Used by gpui to generate the style helpers. +/// Used by GPUI to generate the style helpers. #[proc_macro] #[doc(hidden)] pub fn style_helpers(input: TokenStream) -> TokenStream { style_helpers::style_helpers(input) } +/// Generates methods for margin styles. +#[proc_macro] +#[doc(hidden)] +pub fn margin_style_methods(input: TokenStream) -> TokenStream { + style_helpers::margin_style_methods(input) +} + +/// Generates methods for padding styles. +#[proc_macro] +#[doc(hidden)] +pub fn padding_style_methods(input: TokenStream) -> TokenStream { + style_helpers::padding_style_methods(input) +} + +/// Generates methods for position styles. +#[proc_macro] +#[doc(hidden)] +pub fn position_style_methods(input: TokenStream) -> TokenStream { + style_helpers::position_style_methods(input) +} + /// #[gpui::test] can be used to annotate test functions that run with GPUI support. /// it supports both synchronous and asynchronous tests, and can provide you with /// as many `TestAppContext` instances as you need. diff --git a/crates/gpui_macros/src/style_helpers.rs b/crates/gpui_macros/src/style_helpers.rs index d5116d810ef6f3433a4dbbcff3f099c6c31f31a3..f184752210c6eb1ff489a8d3fb72e324b78df9ae 100644 --- a/crates/gpui_macros/src/style_helpers.rs +++ b/crates/gpui_macros/src/style_helpers.rs @@ -2,15 +2,38 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; use syn::{ + braced, parse::{Parse, ParseStream, Result}, - parse_macro_input, + parse_macro_input, Token, Visibility, }; -struct StyleableMacroInput; +#[derive(Debug)] +struct StyleableMacroInput { + method_visibility: Visibility, +} impl Parse for StyleableMacroInput { - fn parse(_input: ParseStream) -> Result { - Ok(StyleableMacroInput) + fn parse(input: ParseStream) -> Result { + if !input.peek(syn::token::Brace) { + return Ok(Self { + method_visibility: Visibility::Inherited, + }); + } + + let content; + braced!(content in input); + + let mut method_visibility = None; + + let ident: syn::Ident = content.parse()?; + if ident == "visibility" { + let _colon: Token![:] = content.parse()?; + method_visibility = Some(content.parse()?); + } + + Ok(Self { + method_visibility: method_visibility.unwrap_or(Visibility::Inherited), + }) } } @@ -24,11 +47,110 @@ pub fn style_helpers(input: TokenStream) -> TokenStream { output.into() } -fn generate_methods() -> Vec { +pub fn margin_style_methods(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as StyleableMacroInput); + let methods = generate_box_style_methods( + margin_box_style_prefixes(), + box_style_suffixes(), + input.method_visibility, + ); + let output = quote! { + #(#methods)* + }; + + output.into() +} + +pub fn padding_style_methods(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as StyleableMacroInput); + let methods = generate_box_style_methods( + padding_box_style_prefixes(), + box_style_suffixes(), + input.method_visibility, + ); + let output = quote! { + #(#methods)* + }; + + output.into() +} + +pub fn position_style_methods(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as StyleableMacroInput); + let visibility = input.method_visibility; + let methods = generate_box_style_methods( + position_box_style_prefixes(), + box_style_suffixes(), + visibility.clone(), + ); + let output = quote! { + /// Sets the position of the element to `relative`. + /// [Docs](https://tailwindcss.com/docs/position) + #visibility fn relative(mut self) -> Self { + self.style().position = Some(gpui::Position::Relative); + self + } + + /// Sets the position of the element to `absolute`. + /// [Docs](https://tailwindcss.com/docs/position) + #visibility fn absolute(mut self) -> Self { + self.style().position = Some(gpui::Position::Absolute); + self + } + + #(#methods)* + }; + + output.into() +} + +struct BoxStylePrefix { + prefix: &'static str, + auto_allowed: bool, + fields: Vec, + doc_string_prefix: &'static str, +} + +struct BoxStyleSuffix { + suffix: &'static str, + length_tokens: TokenStream2, + doc_string_suffix: &'static str, +} + +struct CornerStylePrefix { + prefix: &'static str, + fields: Vec, + doc_string_prefix: &'static str, +} + +struct CornerStyleSuffix { + suffix: &'static str, + radius_tokens: TokenStream2, + doc_string_suffix: &'static str, +} + +struct BorderStylePrefix { + prefix: &'static str, + fields: Vec, + doc_string_prefix: &'static str, +} + +struct BorderStyleSuffix { + suffix: &'static str, + width_tokens: TokenStream2, + doc_string_suffix: &'static str, +} + +fn generate_box_style_methods( + prefixes: Vec, + suffixes: Vec, + visibility: Visibility, +) -> Vec { let mut methods = Vec::new(); - for box_style_prefix in box_prefixes() { + for box_style_prefix in prefixes { methods.push(generate_custom_value_setter( + visibility.clone(), box_style_prefix.prefix, if box_style_prefix.auto_allowed { quote! { Length } @@ -39,9 +161,10 @@ fn generate_methods() -> Vec { &box_style_prefix.doc_string_prefix, )); - for box_style_suffix in box_suffixes() { + for box_style_suffix in &suffixes { if box_style_suffix.suffix != "auto" || box_style_prefix.auto_allowed { methods.push(generate_predefined_setter( + visibility.clone(), box_style_prefix.prefix, box_style_suffix.suffix, &box_style_prefix.fields, @@ -57,6 +180,7 @@ fn generate_methods() -> Vec { if box_style_suffix.suffix != "auto" { methods.push(generate_predefined_setter( + visibility.clone(), box_style_prefix.prefix, box_style_suffix.suffix, &box_style_prefix.fields, @@ -72,8 +196,17 @@ fn generate_methods() -> Vec { } } + methods +} + +fn generate_methods() -> Vec { + let visibility = Visibility::Inherited; + let mut methods = + generate_box_style_methods(box_prefixes(), box_style_suffixes(), visibility.clone()); + for corner_style_prefix in corner_prefixes() { methods.push(generate_custom_value_setter( + visibility.clone(), corner_style_prefix.prefix, quote! { AbsoluteLength }, &corner_style_prefix.fields, @@ -82,6 +215,7 @@ fn generate_methods() -> Vec { for corner_style_suffix in corner_suffixes() { methods.push(generate_predefined_setter( + visibility.clone(), corner_style_prefix.prefix, corner_style_suffix.suffix, &corner_style_prefix.fields, @@ -98,6 +232,7 @@ fn generate_methods() -> Vec { for border_style_prefix in border_prefixes() { methods.push(generate_custom_value_setter( + visibility.clone(), border_style_prefix.prefix, quote! { AbsoluteLength }, &border_style_prefix.fields, @@ -106,6 +241,7 @@ fn generate_methods() -> Vec { for border_style_suffix in border_suffixes() { methods.push(generate_predefined_setter( + visibility.clone(), border_style_prefix.prefix, border_style_suffix.suffix, &border_style_prefix.fields, @@ -123,6 +259,7 @@ fn generate_methods() -> Vec { } fn generate_predefined_setter( + visibility: Visibility, name: &'static str, length: &'static str, fields: &[TokenStream2], @@ -153,7 +290,7 @@ fn generate_predefined_setter( let method = quote! { #[doc = #doc_string] - fn #method_name(mut self) -> Self { + #visibility fn #method_name(mut self) -> Self { let style = self.style(); #(#field_assignments)* self @@ -164,6 +301,7 @@ fn generate_predefined_setter( } fn generate_custom_value_setter( + visibility: Visibility, prefix: &str, length_type: TokenStream2, fields: &[TokenStream2], @@ -186,7 +324,7 @@ fn generate_custom_value_setter( let method = quote! { #[doc = #doc_string] - fn #method_name(mut self, length: impl std::clone::Clone + Into) -> Self { + #visibility fn #method_name(mut self, length: impl std::clone::Clone + Into) -> Self { let style = self.style(); #(#field_assignments)* self @@ -196,65 +334,8 @@ fn generate_custom_value_setter( method } -struct BoxStylePrefix { - prefix: &'static str, - auto_allowed: bool, - fields: Vec, - doc_string_prefix: &'static str, -} - -fn box_prefixes() -> Vec { +fn margin_box_style_prefixes() -> Vec { vec![ - BoxStylePrefix { - prefix: "w", - auto_allowed: true, - fields: vec![quote! { size.width }], - doc_string_prefix: "Sets the width of the element. [Docs](https://tailwindcss.com/docs/width)", - }, - BoxStylePrefix { - prefix: "h", - auto_allowed: true, - fields: vec![quote! { size.height }], - doc_string_prefix: "Sets the height of the element. [Docs](https://tailwindcss.com/docs/height)", - }, - BoxStylePrefix { - prefix: "size", - auto_allowed: true, - fields: vec![quote! {size.width}, quote! {size.height}], - doc_string_prefix: "Sets the width and height of the element.", - }, - // TODO: These don't use the same size ramp as the others - // see https://tailwindcss.com/docs/max-width - BoxStylePrefix { - prefix: "min_w", - auto_allowed: true, - fields: vec![quote! { min_size.width }], - doc_string_prefix: "Sets the minimum width of the element. [Docs](https://tailwindcss.com/docs/min-width)", - }, - // TODO: These don't use the same size ramp as the others - // see https://tailwindcss.com/docs/max-width - BoxStylePrefix { - prefix: "min_h", - auto_allowed: true, - fields: vec![quote! { min_size.height }], - doc_string_prefix: "Sets the minimum height of the element. [Docs](https://tailwindcss.com/docs/min-height)", - }, - // TODO: These don't use the same size ramp as the others - // see https://tailwindcss.com/docs/max-width - BoxStylePrefix { - prefix: "max_w", - auto_allowed: true, - fields: vec![quote! { max_size.width }], - doc_string_prefix: "Sets the maximum width of the element. [Docs](https://tailwindcss.com/docs/max-width)", - }, - // TODO: These don't use the same size ramp as the others - // see https://tailwindcss.com/docs/max-width - BoxStylePrefix { - prefix: "max_h", - auto_allowed: true, - fields: vec![quote! { max_size.height }], - doc_string_prefix: "Sets the maximum height of the element. [Docs](https://tailwindcss.com/docs/max-height)", - }, BoxStylePrefix { prefix: "m", auto_allowed: true, @@ -302,6 +383,11 @@ fn box_prefixes() -> Vec { fields: vec![quote! { margin.right }], doc_string_prefix: "Sets the right margin of the element. [Docs](https://tailwindcss.com/docs/margin#add-margin-to-a-single-side)", }, + ] +} + +fn padding_box_style_prefixes() -> Vec { + vec![ BoxStylePrefix { prefix: "p", auto_allowed: false, @@ -349,6 +435,11 @@ fn box_prefixes() -> Vec { fields: vec![quote! { padding.right }], doc_string_prefix: "Sets the right padding of the element. [Docs](https://tailwindcss.com/docs/padding#add-padding-to-a-single-side)", }, + ] +} + +fn position_box_style_prefixes() -> Vec { + vec![ BoxStylePrefix { prefix: "inset", auto_allowed: true, @@ -384,6 +475,61 @@ fn box_prefixes() -> Vec { fields: vec![quote! { inset.right }], doc_string_prefix: "Sets the right value of a positioned element. [Docs](https://tailwindcss.com/docs/top-right-bottom-left)", }, + ] +} + +fn box_prefixes() -> Vec { + vec![ + BoxStylePrefix { + prefix: "w", + auto_allowed: true, + fields: vec![quote! { size.width }], + doc_string_prefix: "Sets the width of the element. [Docs](https://tailwindcss.com/docs/width)", + }, + BoxStylePrefix { + prefix: "h", + auto_allowed: true, + fields: vec![quote! { size.height }], + doc_string_prefix: "Sets the height of the element. [Docs](https://tailwindcss.com/docs/height)", + }, + BoxStylePrefix { + prefix: "size", + auto_allowed: true, + fields: vec![quote! {size.width}, quote! {size.height}], + doc_string_prefix: "Sets the width and height of the element.", + }, + // TODO: These don't use the same size ramp as the others + // see https://tailwindcss.com/docs/max-width + BoxStylePrefix { + prefix: "min_w", + auto_allowed: true, + fields: vec![quote! { min_size.width }], + doc_string_prefix: "Sets the minimum width of the element. [Docs](https://tailwindcss.com/docs/min-width)", + }, + // TODO: These don't use the same size ramp as the others + // see https://tailwindcss.com/docs/max-width + BoxStylePrefix { + prefix: "min_h", + auto_allowed: true, + fields: vec![quote! { min_size.height }], + doc_string_prefix: "Sets the minimum height of the element. [Docs](https://tailwindcss.com/docs/min-height)", + }, + // TODO: These don't use the same size ramp as the others + // see https://tailwindcss.com/docs/max-width + BoxStylePrefix { + prefix: "max_w", + auto_allowed: true, + fields: vec![quote! { max_size.width }], + doc_string_prefix: "Sets the maximum width of the element. [Docs](https://tailwindcss.com/docs/max-width)", + }, + // TODO: These don't use the same size ramp as the others + // see https://tailwindcss.com/docs/max-width + BoxStylePrefix { + prefix: "max_h", + auto_allowed: true, + fields: vec![quote! { max_size.height }], + doc_string_prefix: "Sets the maximum height of the element. [Docs](https://tailwindcss.com/docs/max-height)", + }, BoxStylePrefix { prefix: "gap", auto_allowed: false, @@ -405,13 +551,7 @@ fn box_prefixes() -> Vec { ] } -struct BoxStyleSuffix { - suffix: &'static str, - length_tokens: TokenStream2, - doc_string_suffix: &'static str, -} - -fn box_suffixes() -> Vec { +fn box_style_suffixes() -> Vec { vec![ BoxStyleSuffix { suffix: "0", @@ -646,12 +786,6 @@ fn box_suffixes() -> Vec { ] } -struct CornerStylePrefix { - prefix: &'static str, - fields: Vec, - doc_string_prefix: &'static str, -} - fn corner_prefixes() -> Vec { vec![ CornerStylePrefix { @@ -719,12 +853,6 @@ fn corner_prefixes() -> Vec { ] } -struct CornerStyleSuffix { - suffix: &'static str, - radius_tokens: TokenStream2, - doc_string_suffix: &'static str, -} - fn corner_suffixes() -> Vec { vec![ CornerStyleSuffix { @@ -770,12 +898,6 @@ fn corner_suffixes() -> Vec { ] } -struct BorderStylePrefix { - prefix: &'static str, - fields: Vec, - doc_string_prefix: &'static str, -} - fn border_prefixes() -> Vec { vec![ BorderStylePrefix { @@ -827,12 +949,6 @@ fn border_prefixes() -> Vec { ] } -struct BorderStyleSuffix { - suffix: &'static str, - width_tokens: TokenStream2, - doc_string_suffix: &'static str, -} - fn border_suffixes() -> Vec { vec![ BorderStyleSuffix { diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs index 6aa8a46aa407464fa848157cb3495acdbbf8aae2..f391fc60bb0caab819d6877f8d6c6a00513b0a3a 100644 --- a/crates/ui/src/components/facepile.rs +++ b/crates/ui/src/components/facepile.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use gpui::AnyElement; +use gpui::{AnyElement, StyleRefinement}; use smallvec::SmallVec; /// A facepile is a collection of faces stacked horizontally– @@ -23,6 +23,23 @@ impl Facepile { } } +impl ParentElement for Facepile { + fn extend(&mut self, elements: impl IntoIterator) { + self.faces.extend(elements); + } +} + +// Style methods. +impl Facepile { + fn style(&mut self) -> &mut StyleRefinement { + self.base.style() + } + + gpui::padding_style_methods!({ + visibility: pub + }); +} + impl RenderOnce for Facepile { fn render(self, _cx: &mut WindowContext) -> impl IntoElement { // Lay the faces out in reverse so they overlap in the desired order (left to right, front to back) @@ -40,15 +57,3 @@ impl RenderOnce for Facepile { ) } } - -impl ParentElement for Facepile { - fn extend(&mut self, elements: impl IntoIterator) { - self.faces.extend(elements); - } -} - -impl Styled for Facepile { - fn style(&mut self) -> &mut gpui::StyleRefinement { - self.base.style() - } -}