Improve macro to show items
Co-authored-by: Ben Kunkle <ben@zed.dev>
This commit is contained in:
parent
c1631b6e8c
commit
1fd0f7a46b
5 changed files with 148 additions and 23 deletions
|
@ -1,13 +1,17 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::{Settings, SettingsSources, SettingsUI, VsCodeSettings};
|
||||
use crate as settings;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsSources, VsCodeSettings};
|
||||
use settings_ui_macros::SettingsUI;
|
||||
|
||||
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
|
||||
///
|
||||
/// Default: VSCode
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default, SettingsUI,
|
||||
)]
|
||||
pub enum BaseKeymap {
|
||||
#[default]
|
||||
VSCode,
|
||||
|
@ -96,8 +100,6 @@ impl BaseKeymap {
|
|||
}
|
||||
}
|
||||
|
||||
impl SettingsUI for BaseKeymap {}
|
||||
|
||||
impl Settings for BaseKeymap {
|
||||
const KEY: Option<&'static str> = Some("base_keymap");
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ pub type EditorconfigProperties = ec4rs::Properties;
|
|||
|
||||
use crate::{
|
||||
ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUIItem,
|
||||
VsCodeSettings, WorktreeId, parse_json_with_comments, settings_ui::SettingsUI,
|
||||
update_value_in_json_text,
|
||||
SettingsUIRender, VsCodeSettings, WorktreeId, parse_json_with_comments,
|
||||
settings_ui::SettingsUI, update_value_in_json_text,
|
||||
};
|
||||
|
||||
/// A value that can be defined as a user setting.
|
||||
|
@ -1508,7 +1508,7 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
|
|||
}
|
||||
|
||||
fn settings_ui_item(&self) -> SettingsUIItem {
|
||||
T::ui_item()
|
||||
<T as SettingsUI>::settings_ui_item()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,22 +3,23 @@ use std::any::Any;
|
|||
use gpui::{AnyElement, App, Window};
|
||||
|
||||
pub trait SettingsUI {
|
||||
fn ui_item() -> SettingsUIItem {
|
||||
SettingsUIItem {
|
||||
item: SettingsUIItemVariant::None,
|
||||
}
|
||||
fn settings_ui_render() -> SettingsUIRender {
|
||||
SettingsUIRender::None
|
||||
}
|
||||
fn settings_ui_item() -> SettingsUIItem;
|
||||
}
|
||||
|
||||
pub struct SettingsUIItem {
|
||||
// TODO: move this back here once there isn't a None variant
|
||||
// pub path: &'static str,
|
||||
// pub title: &'static str,
|
||||
pub item: SettingsUIItemVariant,
|
||||
}
|
||||
|
||||
pub enum SettingsUIItemVariant {
|
||||
Group {
|
||||
path: &'static str,
|
||||
title: &'static str,
|
||||
group: SettingsUIItemGroup,
|
||||
},
|
||||
Item {
|
||||
|
@ -35,9 +36,48 @@ pub struct SettingsUIItemGroup {
|
|||
|
||||
pub enum SettingsUIItemSingle {
|
||||
// TODO: default/builtin variants
|
||||
SwitchField,
|
||||
Custom(Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> AnyElement>),
|
||||
}
|
||||
|
||||
pub enum SettingsUIRender {
|
||||
Group {
|
||||
title: &'static str,
|
||||
items: Vec<SettingsUIItem>,
|
||||
},
|
||||
Item(SettingsUIItemSingle),
|
||||
None,
|
||||
}
|
||||
|
||||
impl SettingsUI for bool {
|
||||
fn settings_ui_render() -> SettingsUIRender {
|
||||
SettingsUIRender::Item(SettingsUIItemSingle::SwitchField)
|
||||
}
|
||||
|
||||
fn settings_ui_item() -> SettingsUIItem {
|
||||
SettingsUIItem {
|
||||
item: SettingsUIItemVariant::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(SettingsUI)]
|
||||
#[settings_ui(group = "Foo")]
|
||||
struct Foo {
|
||||
// #[settings_ui(render = "my_render_function")]
|
||||
pub toggle: bool,
|
||||
pub font_size: u32,
|
||||
|
||||
Group(vec![Item {path: "toggle", item: SwitchField}])
|
||||
}
|
||||
|
||||
macro code:
|
||||
settings_ui_item() {
|
||||
group.items = struct.fields.map((field_name, field_type) => quote! { SettingsUIItem::Item {path: #field_type::settings_ui_path().unwrap_or_else(|| #field_name), item: if field.attrs.render { #render } else field::settings_ui_render()}})
|
||||
}
|
||||
*/
|
||||
|
||||
/* NOTES:
|
||||
|
||||
# Root Group
|
||||
|
|
|
@ -126,11 +126,33 @@ impl Render for SettingsPage {
|
|||
.settings_ui_items()
|
||||
.into_iter()
|
||||
.flat_map(|item| match item.item {
|
||||
settings::SettingsUIItemVariant::Group { path, group } => Some(path),
|
||||
settings::SettingsUIItemVariant::Group { title, path, group } => Some(
|
||||
div()
|
||||
.child(Label::new(title).size(LabelSize::Large))
|
||||
.children(group.items.iter().map(|item| {
|
||||
match &item.item {
|
||||
settings::SettingsUIItemVariant::Group {
|
||||
path,
|
||||
title,
|
||||
group,
|
||||
} => div()
|
||||
.child(format!("Subgroup: {}", title))
|
||||
.into_any_element(),
|
||||
settings::SettingsUIItemVariant::Item { path, item } => {
|
||||
div()
|
||||
.child(format!("Item: {}", path))
|
||||
.into_any_element()
|
||||
}
|
||||
settings::SettingsUIItemVariant::None => {
|
||||
div().child("None").into_any_element()
|
||||
}
|
||||
}
|
||||
})),
|
||||
),
|
||||
|
||||
settings::SettingsUIItemVariant::Item { path, item } => todo!(),
|
||||
settings::SettingsUIItemVariant::None => None,
|
||||
})
|
||||
.map(|group_name| Label::new(group_name).size(LabelSize::Large)),
|
||||
}),
|
||||
)
|
||||
.child(Label::new("Settings").size(LabelSize::Large))
|
||||
.child(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{DeriveInput, LitStr, Token, parse_macro_input};
|
||||
|
||||
/// Derive macro for the `SettingsUI` marker trait.
|
||||
|
@ -22,7 +22,7 @@ use syn::{DeriveInput, LitStr, Token, parse_macro_input};
|
|||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(SettingsUI, attributes(settings_ui))]
|
||||
pub fn derive_settings_ui(input: TokenStream) -> TokenStream {
|
||||
pub fn derive_settings_ui(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
|
||||
|
@ -48,23 +48,84 @@ pub fn derive_settings_ui(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
let ui_item_fn_body = if let Some(group_name) = group_name {
|
||||
// let mut root_item = vec![];
|
||||
// for field in struct {
|
||||
//
|
||||
// match field::settings_ui_render()
|
||||
// Group(items) => {
|
||||
// let item = items.map(|item| something);
|
||||
// item
|
||||
// items.push(item::settings_ui_render());
|
||||
// root_item.push(Group(items));
|
||||
// },
|
||||
// Leaf(item) => {
|
||||
// root_item.push(item);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// group.items = struct.fields.map((field_name, field_type) => quote! { SettingsUIItem::Item {path: #field_type::settings_ui_path().unwrap_or_else(|| #field_name), item: if field.attrs.render { #render } else field::settings_ui_render()}})
|
||||
// }
|
||||
|
||||
fn map_ui_item_to_render(path: &str, ty: TokenStream) -> TokenStream {
|
||||
quote! {
|
||||
settings::SettingsUIItem { item: settings::SettingsUIItemVariant::Group{ path: #group_name, group: settings::SettingsUIItemGroup{ items: Default::default() } } }
|
||||
settings::SettingsUIItem {
|
||||
item: match #ty::settings_ui_render() {
|
||||
settings::SettingsUIRender::Group{title, items} => settings::SettingsUIItemVariant::Group {
|
||||
title,
|
||||
path: #path,
|
||||
group: settings::SettingsUIItemGroup { items },
|
||||
},
|
||||
settings::SettingsUIRender::Item(item) => settings::SettingsUIItemVariant::Item {
|
||||
path: #path,
|
||||
item,
|
||||
},
|
||||
settings::SettingsUIRender::None => settings::SettingsUIItemVariant::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ui_render_fn_body = if let Some(group_name) = group_name {
|
||||
let fields = match input.data {
|
||||
syn::Data::Struct(data_struct) => data_struct
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
(
|
||||
field.ident.clone().expect("tuple fields").to_string(),
|
||||
field.ty.to_token_stream(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
syn::Data::Enum(data_enum) => vec![], // todo! enums
|
||||
syn::Data::Union(data_union) => unimplemented!("Derive SettingsUI for unions"),
|
||||
};
|
||||
let items = fields
|
||||
.into_iter()
|
||||
.map(|(name, ty)| map_ui_item_to_render(&name, ty));
|
||||
quote! {
|
||||
settings::SettingsUIRender::Group{ title: #group_name, items: vec![#(#items),*] }
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
settings::SettingsUIItem { item: settings::SettingsUIItemVariant::None }
|
||||
settings::SettingsUIRender::None
|
||||
}
|
||||
};
|
||||
|
||||
let settings_ui_item_fn_body = map_ui_item_to_render("todo! define path", quote! { Self });
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics settings::SettingsUI for #name #ty_generics #where_clause {
|
||||
fn ui_item() -> settings::SettingsUIItem {
|
||||
#ui_item_fn_body
|
||||
fn settings_ui_render() -> settings::SettingsUIRender {
|
||||
#ui_render_fn_body
|
||||
}
|
||||
|
||||
fn settings_ui_item() -> settings::SettingsUIItem {
|
||||
#settings_ui_item_fn_body
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue