From 3c0ec5f612610ac64cfea43a147f04a30d11569a Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 25 Aug 2025 15:42:15 -0400 Subject: [PATCH] Start work on creating the inital structure for the settings UI We created a proc macro that derives the settings ui trait on types and added that trait as a marker on Settings trait. Then we added the derive macro on all settings Co-authored-by: Ben Kunkle --- Cargo.lock | 10 ++++++ Cargo.toml | 2 ++ crates/agent_servers/src/settings.rs | 4 +-- crates/agent_settings/src/agent_settings.rs | 4 +-- crates/agent_ui/src/slash_command_settings.rs | 4 +-- crates/audio/src/audio_settings.rs | 4 +-- crates/auto_update/src/auto_update.rs | 3 +- crates/call/src/call_settings.rs | 4 +-- crates/client/src/client.rs | 8 ++--- crates/collab_ui/src/panel_settings.rs | 10 +++--- crates/dap/src/debugger_settings.rs | 4 +-- crates/editor/src/editor_settings.rs | 4 +-- .../extension_host/src/extension_settings.rs | 4 +-- .../file_finder/src/file_finder_settings.rs | 4 +-- crates/git_hosting_providers/src/settings.rs | 4 +-- crates/git_ui/src/git_panel_settings.rs | 4 +-- crates/go_to_line/src/cursor_position.rs | 4 +-- .../image_viewer/src/image_viewer_settings.rs | 4 +-- crates/journal/src/journal.rs | 4 +-- crates/language/src/language_settings.rs | 5 +-- crates/language_models/src/settings.rs | 4 +-- .../src/outline_panel_settings.rs | 4 +-- crates/project/src/project.rs | 2 +- crates/project/src/project_settings.rs | 6 ++-- .../src/project_panel_settings.rs | 4 +-- crates/recent_projects/src/ssh_connections.rs | 4 +-- crates/repl/src/jupyter_settings.rs | 4 +-- crates/settings/Cargo.toml | 1 + crates/settings/src/base_keymap_setting.rs | 4 ++- crates/settings/src/settings.rs | 4 ++- crates/settings/src/settings_store.rs | 19 ++++++---- crates/settings_ui_macros/Cargo.toml | 21 +++++++++++ crates/settings_ui_macros/LICENSE-GPL | 1 + .../src/settings_ui_macros.rs | 36 +++++++++++++++++++ crates/terminal/src/terminal_settings.rs | 4 +-- crates/theme/src/settings.rs | 6 ++-- crates/title_bar/src/title_bar_settings.rs | 4 +-- crates/vim/src/vim.rs | 6 ++-- .../vim_mode_setting/src/vim_mode_setting.rs | 4 ++- crates/workspace/src/item.rs | 6 ++-- crates/workspace/src/workspace_settings.rs | 6 ++-- crates/worktree/src/worktree_settings.rs | 4 +-- crates/zlog_settings/src/zlog_settings.rs | 4 +-- 43 files changed, 170 insertions(+), 82 deletions(-) create mode 100644 crates/settings_ui_macros/Cargo.toml create mode 120000 crates/settings_ui_macros/LICENSE-GPL create mode 100644 crates/settings_ui_macros/src/settings_ui_macros.rs diff --git a/Cargo.lock b/Cargo.lock index c835b503ad..5b3ac97f36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14856,6 +14856,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_json_lenient", + "settings_ui_macros", "smallvec", "tree-sitter", "tree-sitter-json", @@ -14926,6 +14927,15 @@ dependencies = [ "zed_actions", ] +[[package]] +name = "settings_ui_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "sha1" version = "0.10.6" diff --git a/Cargo.toml b/Cargo.toml index 6ec243a9b9..6ecca122a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -146,6 +146,7 @@ members = [ "crates/settings", "crates/settings_profile_selector", "crates/settings_ui", + "crates/settings_ui_macros", "crates/snippet", "crates/snippet_provider", "crates/snippets_ui", @@ -373,6 +374,7 @@ semantic_version = { path = "crates/semantic_version" } session = { path = "crates/session" } settings = { path = "crates/settings" } settings_ui = { path = "crates/settings_ui" } +settings_ui_macros = { path = "crates/settings_ui_macros" } snippet = { path = "crates/snippet" } snippet_provider = { path = "crates/snippet_provider" } snippets_ui = { path = "crates/snippets_ui" } diff --git a/crates/agent_servers/src/settings.rs b/crates/agent_servers/src/settings.rs index 96ac6e3cbe..c89d2b54a6 100644 --- a/crates/agent_servers/src/settings.rs +++ b/crates/agent_servers/src/settings.rs @@ -4,13 +4,13 @@ use collections::HashMap; use gpui::{App, SharedString}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; pub fn init(cx: &mut App) { AllAgentServersSettings::register(cx); } -#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug)] +#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, SettingsUI)] pub struct AllAgentServersSettings { pub gemini: Option, pub claude: Option, diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index ed1ed2b898..dc57d6ebf0 100644 --- a/crates/agent_settings/src/agent_settings.rs +++ b/crates/agent_settings/src/agent_settings.rs @@ -8,7 +8,7 @@ use gpui::{App, Pixels, SharedString}; use language_model::LanguageModel; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use std::borrow::Cow; pub use crate::agent_profile::*; @@ -48,7 +48,7 @@ pub enum NotifyWhenAgentWaiting { Never, } -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, SettingsUI)] pub struct AgentSettings { pub enabled: bool, pub button: bool, diff --git a/crates/agent_ui/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs index 73e5622aa9..a81188b7f2 100644 --- a/crates/agent_ui/src/slash_command_settings.rs +++ b/crates/agent_ui/src/slash_command_settings.rs @@ -2,10 +2,10 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; /// Settings for slash commands. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUI)] pub struct SlashCommandSettings { /// Settings for the `/cargo-workspace` slash command. #[serde(default)] diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs index 807179881c..9eb61d537f 100644 --- a/crates/audio/src/audio_settings.rs +++ b/crates/audio/src/audio_settings.rs @@ -2,9 +2,9 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Deserialize, Debug)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUI)] pub struct AudioSettings { /// Opt into the new audio system. #[serde(rename = "experimental.rodio_audio", default)] diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 2150873cad..b7824f1ae9 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -10,7 +10,7 @@ use paths::remote_servers_dir; use release_channel::{AppCommitSha, ReleaseChannel}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsStore}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources, SettingsStore}; use smol::{fs, io::AsyncReadExt}; use smol::{fs::File, process::Command}; use std::{ @@ -113,6 +113,7 @@ impl Drop for MacOsUnmounter { } } +#[derive(SettingsUI)] struct AutoUpdateSetting(bool); /// Whether or not to automatically check for updates. diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index c8f51e0c1a..4280134c6e 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -2,9 +2,9 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SettingsUI)] pub struct CallSettings { pub mute_on_join: bool, pub share_on_join: bool, diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 2bbe7dd1b5..4f3e7cfd4e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -31,7 +31,7 @@ use release_channel::{AppVersion, ReleaseChannel}; use rpc::proto::{AnyTypedEnvelope, EnvelopedMessage, PeerId, RequestMessage}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use std::{ any::TypeId, convert::TryFrom, @@ -101,7 +101,7 @@ pub struct ClientSettingsContent { server_url: Option, } -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct ClientSettings { pub server_url: String, } @@ -127,7 +127,7 @@ pub struct ProxySettingsContent { proxy: Option, } -#[derive(Deserialize, Default)] +#[derive(Deserialize, Default, SettingsUI)] pub struct ProxySettings { pub proxy: Option, } @@ -504,7 +504,7 @@ impl Drop for PendingEntitySubscription { } } -#[derive(Copy, Clone, Deserialize, Debug)] +#[derive(Copy, Clone, Deserialize, Debug, SettingsUI)] pub struct TelemetrySettings { pub diagnostics: bool, pub metrics: bool, diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index 652d9eb67f..7952e950b3 100644 --- a/crates/collab_ui/src/panel_settings.rs +++ b/crates/collab_ui/src/panel_settings.rs @@ -1,10 +1,10 @@ use gpui::Pixels; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use workspace::dock::DockPosition; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SettingsUI)] pub struct CollaborationPanelSettings { pub button: bool, pub dock: DockPosition, @@ -20,7 +20,7 @@ pub enum ChatPanelButton { WhenInCall, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SettingsUI)] pub struct ChatPanelSettings { pub button: ChatPanelButton, pub dock: DockPosition, @@ -43,7 +43,7 @@ pub struct ChatPanelSettingsContent { pub default_width: Option, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SettingsUI)] pub struct NotificationPanelSettings { pub button: bool, pub dock: DockPosition, @@ -66,7 +66,7 @@ pub struct PanelSettingsContent { pub default_width: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUI)] pub struct MessageEditorSettings { /// Whether to automatically replace emoji shortcodes with emoji characters. /// For example: typing `:wave:` gets replaced with `👋`. diff --git a/crates/dap/src/debugger_settings.rs b/crates/dap/src/debugger_settings.rs index e1176633e5..ee7b7b47f6 100644 --- a/crates/dap/src/debugger_settings.rs +++ b/crates/dap/src/debugger_settings.rs @@ -2,7 +2,7 @@ use dap_types::SteppingGranularity; use gpui::{App, Global}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] @@ -12,7 +12,7 @@ pub enum DebugPanelDockPosition { Right, } -#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy)] +#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy, SettingsUI)] #[serde(default)] pub struct DebuggerSettings { /// Determines the stepping granularity. diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 1d7e04cae0..4641dbe7fa 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -6,12 +6,12 @@ use language::CursorShape; use project::project_settings::DiagnosticSeverity; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, VsCodeSettings}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources, VsCodeSettings}; use util::serde::default_true; /// Imports from the VSCode settings at /// https://code.visualstudio.com/docs/reference/default-settings -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, SettingsUI)] pub struct EditorSettings { pub cursor_blink: bool, pub cursor_shape: Option, diff --git a/crates/extension_host/src/extension_settings.rs b/crates/extension_host/src/extension_settings.rs index cfa67990b0..e6246311d1 100644 --- a/crates/extension_host/src/extension_settings.rs +++ b/crates/extension_host/src/extension_settings.rs @@ -3,10 +3,10 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use std::sync::Arc; -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUI)] pub struct ExtensionSettings { /// The extensions that should be automatically installed by Zed. /// diff --git a/crates/file_finder/src/file_finder_settings.rs b/crates/file_finder/src/file_finder_settings.rs index 350e1de3b3..cb08c04156 100644 --- a/crates/file_finder/src/file_finder_settings.rs +++ b/crates/file_finder/src/file_finder_settings.rs @@ -1,9 +1,9 @@ use anyhow::Result; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, SettingsUI)] pub struct FileFinderSettings { pub file_icons: bool, pub modal_max_width: Option, diff --git a/crates/git_hosting_providers/src/settings.rs b/crates/git_hosting_providers/src/settings.rs index 91179fea39..64265f4d35 100644 --- a/crates/git_hosting_providers/src/settings.rs +++ b/crates/git_hosting_providers/src/settings.rs @@ -5,7 +5,7 @@ use git::GitHostingProviderRegistry; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsStore}; use url::Url; use util::ResultExt as _; @@ -78,7 +78,7 @@ pub struct GitHostingProviderConfig { pub name: String, } -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, SettingsUI)] pub struct GitHostingProviderSettings { /// The list of custom Git hosting providers. #[serde(default)] diff --git a/crates/git_ui/src/git_panel_settings.rs b/crates/git_ui/src/git_panel_settings.rs index b6891c7d25..0d8b852663 100644 --- a/crates/git_ui/src/git_panel_settings.rs +++ b/crates/git_ui/src/git_panel_settings.rs @@ -2,7 +2,7 @@ use editor::ShowScrollbar; use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use workspace::dock::DockPosition; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -77,7 +77,7 @@ pub struct GitPanelSettingsContent { pub collapse_untracked_diff: Option, } -#[derive(Deserialize, Debug, Clone, PartialEq)] +#[derive(Deserialize, Debug, Clone, PartialEq, SettingsUI)] pub struct GitPanelSettings { pub button: bool, pub dock: DockPosition, diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index e60a3651aa..4537dfe7cc 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -2,7 +2,7 @@ use editor::{Editor, EditorSettings, MultiBufferSnapshot}; use gpui::{App, Entity, FocusHandle, Focusable, Subscription, Task, WeakEntity}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use std::{fmt::Write, num::NonZeroU32, time::Duration}; use text::{Point, Selection}; use ui::{ @@ -293,7 +293,7 @@ impl StatusItemView for CursorPosition { } } -#[derive(Clone, Copy, Default, PartialEq, JsonSchema, Deserialize, Serialize)] +#[derive(Clone, Copy, Default, PartialEq, JsonSchema, Deserialize, Serialize, SettingsUI)] #[serde(rename_all = "snake_case")] pub(crate) enum LineIndicatorFormat { Short, diff --git a/crates/image_viewer/src/image_viewer_settings.rs b/crates/image_viewer/src/image_viewer_settings.rs index 1dcf99c0af..1ce9971f9d 100644 --- a/crates/image_viewer/src/image_viewer_settings.rs +++ b/crates/image_viewer/src/image_viewer_settings.rs @@ -1,10 +1,10 @@ use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; /// The settings for the image viewer. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default)] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, SettingsUI)] pub struct ImageViewerSettings { /// The unit to use for displaying image file sizes. /// diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index c09ab6f764..38545c3b7c 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -5,7 +5,7 @@ use editor::{Editor, SelectionEffects}; use gpui::{App, AppContext as _, Context, Window, actions}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use std::{ fs::OpenOptions, path::{Path, PathBuf}, @@ -22,7 +22,7 @@ actions!( ); /// Settings specific to journaling -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, SettingsUI)] pub struct JournalSettings { /// The path of the directory where journal entries are stored. /// diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 0f82d3997f..ab4ee967b8 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -17,7 +17,8 @@ use serde::{ }; use settings::{ - ParameterizedJsonSchema, Settings, SettingsLocation, SettingsSources, SettingsStore, + DeriveSettingsUI as SettingsUI, ParameterizedJsonSchema, Settings, SettingsLocation, + SettingsSources, SettingsStore, }; use shellexpand; use std::{borrow::Cow, num::NonZeroU32, path::Path, slice, sync::Arc}; @@ -55,7 +56,7 @@ pub fn all_language_settings<'a>( } /// The settings for all languages. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, SettingsUI)] pub struct AllLanguageSettings { /// The edit prediction settings. pub edit_predictions: EditPredictionSettings, diff --git a/crates/language_models/src/settings.rs b/crates/language_models/src/settings.rs index b163585aa7..147d4bee5d 100644 --- a/crates/language_models/src/settings.rs +++ b/crates/language_models/src/settings.rs @@ -5,7 +5,7 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use crate::provider::{ self, @@ -29,7 +29,7 @@ pub fn init_settings(cx: &mut App) { AllLanguageModelSettings::register(cx); } -#[derive(Default)] +#[derive(Default, SettingsUI)] pub struct AllLanguageModelSettings { pub anthropic: AnthropicSettings, pub bedrock: AmazonBedrockSettings, diff --git a/crates/outline_panel/src/outline_panel_settings.rs b/crates/outline_panel/src/outline_panel_settings.rs index 133d28b748..b34946e643 100644 --- a/crates/outline_panel/src/outline_panel_settings.rs +++ b/crates/outline_panel/src/outline_panel_settings.rs @@ -2,7 +2,7 @@ use editor::ShowScrollbar; use gpui::Pixels; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] @@ -18,7 +18,7 @@ pub enum ShowIndentGuides { Never, } -#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, SettingsUI)] pub struct OutlinePanelSettings { pub button: bool, pub default_width: Pixels, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 9fd4eed641..9d34feb9e0 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -948,7 +948,7 @@ pub enum PulledDiagnostics { /// Whether to disable all AI features in Zed. /// /// Default: false -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, settings::DeriveSettingsUI)] pub struct DisableAiSettings { pub disable_ai: bool, } diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 4447c25129..beab54cd2d 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -18,8 +18,8 @@ use rpc::{ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{ - InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources, - SettingsStore, parse_json_with_comments, watch_config_file, + DeriveSettingsUI as SettingsUI, InvalidSettingsError, LocalSettingsKind, Settings, + SettingsLocation, SettingsSources, SettingsStore, parse_json_with_comments, watch_config_file, }; use std::{ collections::BTreeMap, @@ -36,7 +36,7 @@ use crate::{ worktree_store::{WorktreeStore, WorktreeStoreEvent}, }; -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUI)] pub struct ProjectSettings { /// Configuration for language servers. /// diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index 8a243589ed..a64fe41191 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -2,7 +2,7 @@ use editor::ShowScrollbar; use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] @@ -28,7 +28,7 @@ pub enum EntrySpacing { Standard, } -#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, SettingsUI)] pub struct ProjectPanelSettings { pub button: bool, pub hide_gitignore: bool, diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index d07ea48c7e..fa82536f57 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -19,7 +19,7 @@ use remote::ssh_session::{ConnectionIdentifier, SshPortForwardOption}; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use theme::ThemeSettings; use ui::{ ActiveTheme, Color, Context, Icon, IconName, IconSize, InteractiveElement, IntoElement, Label, @@ -28,7 +28,7 @@ use ui::{ use util::serde::default_true; use workspace::{AppState, ModalView, Workspace}; -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct SshSettings { pub ssh_connections: Option>, /// Whether to read ~/.ssh/config for ssh connection sources. diff --git a/crates/repl/src/jupyter_settings.rs b/crates/repl/src/jupyter_settings.rs index 8b00e0f757..2d1ee7bd84 100644 --- a/crates/repl/src/jupyter_settings.rs +++ b/crates/repl/src/jupyter_settings.rs @@ -4,9 +4,9 @@ use editor::EditorSettings; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Debug, Default)] +#[derive(Debug, Default, SettingsUI)] pub struct JupyterSettings { pub kernel_selections: HashMap, } diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 892d4dea8b..8768b40736 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -31,6 +31,7 @@ schemars.workspace = true serde.workspace = true serde_derive.workspace = true serde_json.workspace = true +settings_ui_macros.workspace = true serde_json_lenient.workspace = true smallvec.workspace = true tree-sitter-json.workspace = true diff --git a/crates/settings/src/base_keymap_setting.rs b/crates/settings/src/base_keymap_setting.rs index 91dda03d00..272eb2e78e 100644 --- a/crates/settings/src/base_keymap_setting.rs +++ b/crates/settings/src/base_keymap_setting.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter}; -use crate::{Settings, SettingsSources, VsCodeSettings}; +use crate::{Settings, SettingsSources, SettingsUI, VsCodeSettings}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -96,6 +96,8 @@ impl BaseKeymap { } } +impl SettingsUI for BaseKeymap {} + impl Settings for BaseKeymap { const KEY: Option<&'static str> = Some("base_keymap"); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index b73ab9ae95..7df274d23d 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -23,8 +23,10 @@ pub use settings_file::*; pub use settings_json::*; pub use settings_store::{ InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources, - SettingsStore, + SettingsStore, SettingsUI, }; +// Re-export the derive macro +pub use settings_ui_macros::SettingsUI as DeriveSettingsUI; pub use vscode_import::{VsCodeSettings, VsCodeSettingsSource}; #[derive(Clone, Debug, PartialEq)] diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 3deaed8b9d..58da654b0e 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -35,10 +35,12 @@ use crate::{ WorktreeId, parse_json_with_comments, update_value_in_json_text, }; +pub trait SettingsUI {} + /// A value that can be defined as a user setting. /// /// Settings can be loaded from a combination of multiple JSON files. -pub trait Settings: 'static + Send + Sync { +pub trait Settings: SettingsUI + 'static + Send + Sync { /// The name of a key within the JSON file from which this setting should /// be deserialized. If this is `None`, then the setting will be deserialized /// from the root object. @@ -1505,7 +1507,10 @@ mod tests { use crate::VsCodeSettingsSource; use super::*; + // This is so the SettingsUI macro can still work properly + use crate as settings; use serde_derive::Deserialize; + use settings_ui_macros::SettingsUI; use unindent::Unindent; #[gpui::test] @@ -2048,14 +2053,14 @@ mod tests { pretty_assertions::assert_eq!(new, expected); } - #[derive(Debug, PartialEq, Deserialize)] + #[derive(Debug, PartialEq, Deserialize, SettingsUI)] struct UserSettings { name: String, age: u32, staff: bool, } - #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)] + #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUI)] struct UserSettingsContent { name: Option, age: Option, @@ -2075,7 +2080,7 @@ mod tests { } } - #[derive(Debug, Deserialize, PartialEq)] + #[derive(Debug, Deserialize, PartialEq, SettingsUI)] struct TurboSetting(bool); impl Settings for TurboSetting { @@ -2089,7 +2094,7 @@ mod tests { fn import_from_vscode(_vscode: &VsCodeSettings, _current: &mut Self::FileContent) {} } - #[derive(Clone, Debug, PartialEq, Deserialize)] + #[derive(Clone, Debug, PartialEq, Deserialize, SettingsUI)] struct MultiKeySettings { #[serde(default)] key1: String, @@ -2122,7 +2127,7 @@ mod tests { } } - #[derive(Debug, Deserialize)] + #[derive(Debug, Deserialize, SettingsUI)] struct JournalSettings { pub path: String, pub hour_format: HourFormat, @@ -2223,7 +2228,7 @@ mod tests { ); } - #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] + #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUI)] struct LanguageSettings { #[serde(default)] languages: HashMap, diff --git a/crates/settings_ui_macros/Cargo.toml b/crates/settings_ui_macros/Cargo.toml new file mode 100644 index 0000000000..7df5074c6f --- /dev/null +++ b/crates/settings_ui_macros/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "settings_ui_macros" +version = "0.1.0" +edition.workspace = true +publish.workspace = true +license = "GPL-3.0-or-later" + +[lib] +path = "src/settings_ui_macros.rs" +proc-macro = true + +[lints] +workspace = true + +[features] +default = [] + +[dependencies] +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true diff --git a/crates/settings_ui_macros/LICENSE-GPL b/crates/settings_ui_macros/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/crates/settings_ui_macros/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/settings_ui_macros/src/settings_ui_macros.rs b/crates/settings_ui_macros/src/settings_ui_macros.rs new file mode 100644 index 0000000000..099d5246c9 --- /dev/null +++ b/crates/settings_ui_macros/src/settings_ui_macros.rs @@ -0,0 +1,36 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{DeriveInput, parse_macro_input}; + +/// Derive macro for the `SettingsUI` marker trait. +/// +/// This macro automatically implements the `SettingsUI` trait for the annotated type. +/// The `SettingsUI` trait is a marker trait used to indicate that a type can be +/// displayed in the settings UI. +/// +/// # Example +/// +/// ``` +/// use settings::SettingsUI; +/// use settings_ui_macros::SettingsUI; +/// +/// #[derive(SettingsUI)] +/// struct MySettings { +/// enabled: bool, +/// count: usize, +/// } +/// ``` +#[proc_macro_derive(SettingsUI)] +pub fn derive_settings_ui(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + + // Handle generic parameters if present + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let expanded = quote! { + impl #impl_generics settings::SettingsUI for #name #ty_generics #where_clause {} + }; + + TokenStream::from(expanded) +} diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index 635e3e2ca5..b412c84088 100644 --- a/crates/terminal/src/terminal_settings.rs +++ b/crates/terminal/src/terminal_settings.rs @@ -6,7 +6,7 @@ use gpui::{AbsoluteLength, App, FontFallbacks, FontFeatures, FontWeight, Pixels, use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::SettingsSources; +use settings::{DeriveSettingsUI as SettingsUI, SettingsSources}; use std::path::PathBuf; use task::Shell; use theme::FontFamilyName; @@ -24,7 +24,7 @@ pub struct Toolbar { pub breadcrumbs: bool, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, SettingsUI)] pub struct TerminalSettings { pub shell: Shell, pub working_directory: WorkingDirectory, diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index df147cfe92..e54ee893db 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -13,7 +13,9 @@ use gpui::{ use refineable::Refineable; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use settings::{ParameterizedJsonSchema, Settings, SettingsSources}; +use settings::{ + DeriveSettingsUI as SettingsUI, ParameterizedJsonSchema, Settings, SettingsSources, +}; use std::sync::Arc; use util::ResultExt as _; use util::schemars::replace_subschema; @@ -87,7 +89,7 @@ impl From for String { } /// Customizable settings for the UI and theme system. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, SettingsUI)] pub struct ThemeSettings { /// The UI font size. Determines the size of text in the UI, /// as well as the size of a [gpui::Rems] unit. diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index a98e984d80..e393ecdc9d 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -1,9 +1,9 @@ use db::anyhow; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Copy, Clone, Deserialize, Debug)] +#[derive(Copy, Clone, Deserialize, Debug, SettingsUI)] pub struct TitleBarSettings { pub show_branch_icon: bool, pub show_onboarding_banner: bool, diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 9da01e6f44..fb5b5d36ff 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -39,7 +39,9 @@ use object::Object; use schemars::JsonSchema; use serde::Deserialize; use serde_derive::Serialize; -use settings::{Settings, SettingsSources, SettingsStore, update_settings_file}; +use settings::{ + DeriveSettingsUI as SettingsUI, Settings, SettingsSources, SettingsStore, update_settings_file, +}; use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals}; use std::{mem, ops::Range, sync::Arc}; use surrounds::SurroundsType; @@ -1774,7 +1776,7 @@ struct CursorShapeSettings { pub insert: Option, } -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] struct VimSettings { pub default_mode: Mode, pub toggle_relative_line_numbers: bool, diff --git a/crates/vim_mode_setting/src/vim_mode_setting.rs b/crates/vim_mode_setting/src/vim_mode_setting.rs index 6f60d3f21f..3b25a56795 100644 --- a/crates/vim_mode_setting/src/vim_mode_setting.rs +++ b/crates/vim_mode_setting/src/vim_mode_setting.rs @@ -6,7 +6,7 @@ use anyhow::Result; use gpui::App; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; /// Initializes the `vim_mode_setting` crate. pub fn init(cx: &mut App) { @@ -17,6 +17,7 @@ pub fn init(cx: &mut App) { /// Whether or not to enable Vim mode. /// /// Default: false +#[derive(SettingsUI)] pub struct VimModeSetting(pub bool); impl Settings for VimModeSetting { @@ -43,6 +44,7 @@ impl Settings for VimModeSetting { /// Whether or not to enable Helix mode. /// /// Default: false +#[derive(SettingsUI)] pub struct HelixModeSetting(pub bool); impl Settings for HelixModeSetting { diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 3485fcca43..7e52e95e68 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -17,7 +17,7 @@ use gpui::{ use project::{Project, ProjectEntryId, ProjectPath}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsLocation, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsLocation, SettingsSources}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -49,7 +49,7 @@ impl Default for SaveOptions { } } -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct ItemSettings { pub git_status: bool, pub close_position: ClosePosition, @@ -59,7 +59,7 @@ pub struct ItemSettings { pub show_close_button: ShowCloseButton, } -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct PreviewTabsSettings { pub enabled: bool, pub enable_preview_from_file_finder: bool, diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index 5635347514..87d592ac54 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -6,9 +6,9 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct WorkspaceSettings { pub active_pane_modifiers: ActivePanelModifiers, pub bottom_dock_layout: BottomDockLayout, @@ -204,7 +204,7 @@ pub struct WorkspaceSettingsContent { pub close_on_file_delete: Option, } -#[derive(Deserialize)] +#[derive(Deserialize, SettingsUI)] pub struct TabBarSettings { pub show: bool, pub show_nav_history_buttons: bool, diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index b18d3509be..4f9f7af1d4 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -4,10 +4,10 @@ use anyhow::Context as _; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; use util::paths::PathMatcher; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, SettingsUI)] pub struct WorktreeSettings { pub file_scan_inclusions: PathMatcher, pub file_scan_exclusions: PathMatcher, diff --git a/crates/zlog_settings/src/zlog_settings.rs b/crates/zlog_settings/src/zlog_settings.rs index b58cbcc143..5aad945b9b 100644 --- a/crates/zlog_settings/src/zlog_settings.rs +++ b/crates/zlog_settings/src/zlog_settings.rs @@ -3,7 +3,7 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsStore}; pub fn init(cx: &mut App) { ZlogSettings::register(cx); @@ -15,7 +15,7 @@ pub fn init(cx: &mut App) { .detach(); } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUI)] pub struct ZlogSettings { #[serde(default, flatten)] pub scopes: std::collections::HashMap,