Add settings_ui(group) attribute to macro

Co-authored-by: Ben Kunkle <ben@zed.dev>
This commit is contained in:
Anthony 2025-08-26 12:33:38 -04:00
parent 15f634f8cc
commit ba9d109289
4 changed files with 55 additions and 8 deletions

View file

@ -5,7 +5,7 @@ use git::GitHostingProviderRegistry;
use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{SettingsUI, Settings, SettingsStore};
use settings::{Settings, SettingsStore, SettingsUI};
use url::Url;
use util::ResultExt as _;

View file

@ -16,6 +16,13 @@ pub(crate) fn derive_action(input: TokenStream) -> TokenStream {
let mut deprecated = None;
let mut doc_str: Option<String> = None;
/*
*
* #[action()]
* Struct Foo {
* bar: bool // is bar considered an attribute
}
*/
for attr in &input.attrs {
if attr.path().is_ident("action") {
attr.parse_nested_meta(|meta| {

View file

@ -11,14 +11,20 @@ pub trait SettingsUI {
}
pub struct SettingsUIItem {
// TODO:
// path: SmallVec<[&'static str; 8]>,
// TODO: move this back here once there isn't a None variant
// pub path: &'static str,
pub item: SettingsUIItemVariant,
}
pub enum SettingsUIItemVariant {
Group(SettingsUIItemGroup),
Item(SettingsUIItemSingle),
Group {
path: &'static str,
group: SettingsUIItemGroup,
},
Item {
path: &'static str,
item: SettingsUIItemSingle,
},
// TODO: remove
None,
}

View file

@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, parse_macro_input};
use syn::{DeriveInput, LitStr, Token, parse_macro_input};
/// Derive macro for the `SettingsUI` marker trait.
///
@ -15,12 +15,13 @@ use syn::{DeriveInput, parse_macro_input};
/// use settings_ui_macros::SettingsUI;
///
/// #[derive(SettingsUI)]
/// #[settings_ui(group = "Standard")]
/// struct MySettings {
/// enabled: bool,
/// count: usize,
/// }
/// ```
#[proc_macro_derive(SettingsUI)]
#[proc_macro_derive(SettingsUI, attributes(settings_ui))]
pub fn derive_settings_ui(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
@ -28,8 +29,41 @@ pub fn derive_settings_ui(input: TokenStream) -> TokenStream {
// Handle generic parameters if present
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut group_name = Option::<String>::None;
for attr in &input.attrs {
if attr.path().is_ident("settings_ui") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("group") {
if group_name.is_some() {
return Err(meta.error("Only one 'group' path can be specified"));
}
meta.input.parse::<Token![=]>()?;
let lit: LitStr = meta.input.parse()?;
group_name = Some(lit.value());
}
Ok(())
})
.unwrap_or_else(|e| panic!("in #[settings_ui] attribute: {}", e));
}
}
let ui_item_fn_body = if let Some(group_name) = group_name {
quote! {
settings::SettingsUIItem { item: settings::SettingsUIItemVariant::Group{ path: #group_name, group: settings::SettingsUIItemGroup{ items: Default::default() } } }
}
} else {
quote! {
settings::SettingsUIItem { item: settings::SettingsUIItemVariant::None }
}
};
let expanded = quote! {
impl #impl_generics settings::SettingsUI for #name #ty_generics #where_clause {}
impl #impl_generics settings::SettingsUI for #name #ty_generics #where_clause {
fn ui_item() -> settings::SettingsUIItem {
#ui_item_fn_body
}
}
};
TokenStream::from(expanded)