From 3c0ec5f612610ac64cfea43a147f04a30d11569a Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 25 Aug 2025 15:42:15 -0400 Subject: [PATCH 1/7] 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, From 15f634f8cc3d4a9960eb14184f6d0325181920d4 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 12:00:10 -0400 Subject: [PATCH 2/7] Move keymap editor into it's own crate and create settings ui crate We also change the structure of the settings ui macro. The trait is still a requirement on the Settings trait implementation, but it returns a SettingUIItemVariant now, which the settings ui crate will take adventage of to generate UI This allows us to get around circular dependency errors and still get the type system to ensure all settings fulfill the settings UI crate Co-authored-by: Ben Kunkle --- Cargo.lock | 66 ++++++++++------- Cargo.toml | 8 ++- crates/agent_servers/src/settings.rs | 2 +- crates/agent_settings/src/agent_settings.rs | 2 +- crates/agent_ui/src/slash_command_settings.rs | 2 +- crates/audio/src/audio_settings.rs | 2 +- crates/auto_update/src/auto_update.rs | 2 +- crates/call/src/call_settings.rs | 2 +- crates/client/src/client.rs | 2 +- crates/collab_ui/src/panel_settings.rs | 2 +- crates/dap/src/debugger_settings.rs | 2 +- crates/editor/src/editor_settings.rs | 2 +- .../extension_host/src/extension_settings.rs | 2 +- .../file_finder/src/file_finder_settings.rs | 2 +- crates/git_hosting_providers/src/settings.rs | 2 +- crates/git_ui/src/git_panel_settings.rs | 2 +- crates/go_to_line/src/cursor_position.rs | 2 +- .../image_viewer/src/image_viewer_settings.rs | 2 +- crates/journal/src/journal.rs | 2 +- crates/keymap_editor/Cargo.toml | 55 ++++++++++++++ crates/keymap_editor/LICENSE-GPL | 1 + .../src/keymap_editor.rs} | 4 +- .../src/ui_components/keystroke_input.rs | 0 .../src/ui_components/mod.rs | 0 .../src/ui_components/table.rs | 0 crates/language/src/language_settings.rs | 3 +- crates/language_models/src/settings.rs | 2 +- .../src/outline_panel_settings.rs | 2 +- crates/project/src/project.rs | 2 +- crates/project/src/project_settings.rs | 2 +- .../src/project_panel_settings.rs | 2 +- crates/recent_projects/src/ssh_connections.rs | 2 +- crates/repl/src/jupyter_settings.rs | 2 +- crates/settings/src/settings.rs | 6 +- crates/settings/src/settings_store.rs | 4 +- crates/settings/src/settings_ui.rs | 72 +++++++++++++++++++ crates/settings_ui/Cargo.toml | 39 +++------- crates/settings_ui/src/settings_ui.rs | 4 -- crates/terminal/src/terminal_settings.rs | 2 +- crates/theme/src/settings.rs | 4 +- crates/title_bar/Cargo.toml | 2 +- crates/title_bar/src/title_bar.rs | 6 +- crates/title_bar/src/title_bar_settings.rs | 2 +- crates/vim/src/vim.rs | 2 +- .../vim_mode_setting/src/vim_mode_setting.rs | 2 +- crates/workspace/src/item.rs | 2 +- crates/workspace/src/workspace_settings.rs | 2 +- crates/worktree/src/worktree_settings.rs | 2 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 1 + crates/zed/src/zed.rs | 2 +- crates/zed/src/zed/app_menus.rs | 3 +- crates/zlog_settings/src/zlog_settings.rs | 2 +- 53 files changed, 234 insertions(+), 111 deletions(-) create mode 100644 crates/keymap_editor/Cargo.toml create mode 120000 crates/keymap_editor/LICENSE-GPL rename crates/{settings_ui/src/keybindings.rs => keymap_editor/src/keymap_editor.rs} (99%) rename crates/{settings_ui => keymap_editor}/src/ui_components/keystroke_input.rs (100%) rename crates/{settings_ui => keymap_editor}/src/ui_components/mod.rs (100%) rename crates/{settings_ui => keymap_editor}/src/ui_components/table.rs (100%) create mode 100644 crates/settings/src/settings_ui.rs diff --git a/Cargo.lock b/Cargo.lock index 5b3ac97f36..ffbc1d7ee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8953,6 +8953,46 @@ dependencies = [ "uuid", ] +[[package]] +name = "keymap_editor" +version = "0.1.0" +dependencies = [ + "anyhow", + "collections", + "command_palette", + "command_palette_hooks", + "component", + "db", + "editor", + "feature_flags", + "fs", + "fuzzy", + "gpui", + "itertools 0.14.0", + "language", + "log", + "menu", + "notifications", + "paths", + "project", + "search", + "serde", + "serde_json", + "settings", + "telemetry", + "tempfile", + "theme", + "tree-sitter-json", + "tree-sitter-rust", + "ui", + "ui_input", + "util", + "vim", + "workspace", + "workspace-hack", + "zed_actions", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -14892,39 +14932,16 @@ name = "settings_ui" version = "0.1.0" dependencies = [ "anyhow", - "collections", "command_palette", "command_palette_hooks", - "component", - "db", "editor", "feature_flags", - "fs", - "fuzzy", "gpui", - "itertools 0.14.0", - "language", - "log", - "menu", - "notifications", - "paths", - "project", - "search", - "serde", - "serde_json", "settings", - "telemetry", - "tempfile", "theme", - "tree-sitter-json", - "tree-sitter-rust", "ui", - "ui_input", "util", - "vim", "workspace", - "workspace-hack", - "zed_actions", ] [[package]] @@ -16749,6 +16766,7 @@ dependencies = [ "db", "gpui", "http_client", + "keymap_editor", "notifications", "pretty_assertions", "project", @@ -16757,7 +16775,6 @@ dependencies = [ "schemars", "serde", "settings", - "settings_ui", "smallvec", "story", "telemetry", @@ -20469,6 +20486,7 @@ dependencies = [ "itertools 0.14.0", "jj_ui", "journal", + "keymap_editor", "language", "language_extension", "language_model", diff --git a/Cargo.toml b/Cargo.toml index 6ecca122a9..7be525cd6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ members = [ "crates/deepseek", "crates/diagnostics", "crates/docs_preprocessor", + "crates/edit_prediction", + "crates/edit_prediction_button", "crates/editor", "crates/eval", "crates/explorer_command_injector", @@ -82,13 +84,12 @@ members = [ "crates/http_client_tls", "crates/icons", "crates/image_viewer", - "crates/edit_prediction", - "crates/edit_prediction_button", "crates/inspector_ui", "crates/install_cli", "crates/jj", "crates/jj_ui", "crates/journal", + "crates/keymap_editor", "crates/language", "crates/language_extension", "crates/language_model", @@ -157,9 +158,9 @@ members = [ "crates/streaming_diff", "crates/sum_tree", "crates/supermaven", - "crates/system_specs", "crates/supermaven_api", "crates/svg_preview", + "crates/system_specs", "crates/tab_switcher", "crates/task", "crates/tasks_ui", @@ -315,6 +316,7 @@ install_cli = { path = "crates/install_cli" } jj = { path = "crates/jj" } jj_ui = { path = "crates/jj_ui" } journal = { path = "crates/journal" } +keymap_editor = { path = "crates/keymap_editor" } language = { path = "crates/language" } language_extension = { path = "crates/language_extension" } language_model = { path = "crates/language_model" } diff --git a/crates/agent_servers/src/settings.rs b/crates/agent_servers/src/settings.rs index c89d2b54a6..916ff72baf 100644 --- a/crates/agent_servers/src/settings.rs +++ b/crates/agent_servers/src/settings.rs @@ -4,7 +4,7 @@ use collections::HashMap; use gpui::{App, SharedString}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; pub fn init(cx: &mut App) { AllAgentServersSettings::register(cx); diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index dc57d6ebf0..82be85be07 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use std::borrow::Cow; pub use crate::agent_profile::*; diff --git a/crates/agent_ui/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs index a81188b7f2..dbc4c9c27c 100644 --- a/crates/agent_ui/src/slash_command_settings.rs +++ b/crates/agent_ui/src/slash_command_settings.rs @@ -2,7 +2,7 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; /// Settings for slash commands. #[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUI)] diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs index 9eb61d537f..bb5f975c3c 100644 --- a/crates/audio/src/audio_settings.rs +++ b/crates/audio/src/audio_settings.rs @@ -2,7 +2,7 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUI)] pub struct AudioSettings { diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index b7824f1ae9..58522d3941 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources, SettingsStore}; +use settings::{SettingsUI, Settings, SettingsSources, SettingsStore}; use smol::{fs, io::AsyncReadExt}; use smol::{fs::File, process::Command}; use std::{ diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index 4280134c6e..d3c867b199 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -2,7 +2,7 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Deserialize, Debug, SettingsUI)] pub struct CallSettings { diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 4f3e7cfd4e..dc106382a0 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use std::{ any::TypeId, convert::TryFrom, diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index 7952e950b3..92815dcf9f 100644 --- a/crates/collab_ui/src/panel_settings.rs +++ b/crates/collab_ui/src/panel_settings.rs @@ -1,7 +1,7 @@ use gpui::Pixels; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use workspace::dock::DockPosition; #[derive(Deserialize, Debug, SettingsUI)] diff --git a/crates/dap/src/debugger_settings.rs b/crates/dap/src/debugger_settings.rs index ee7b7b47f6..bef7157eba 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 4641dbe7fa..5e1675d23e 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -6,7 +6,7 @@ use language::CursorShape; use project::project_settings::DiagnosticSeverity; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources, VsCodeSettings}; +use settings::{SettingsUI, Settings, SettingsSources, VsCodeSettings}; use util::serde::default_true; /// Imports from the VSCode settings at diff --git a/crates/extension_host/src/extension_settings.rs b/crates/extension_host/src/extension_settings.rs index e6246311d1..51ac489ef7 100644 --- a/crates/extension_host/src/extension_settings.rs +++ b/crates/extension_host/src/extension_settings.rs @@ -3,7 +3,7 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use std::sync::Arc; #[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUI)] diff --git a/crates/file_finder/src/file_finder_settings.rs b/crates/file_finder/src/file_finder_settings.rs index cb08c04156..6066397e52 100644 --- a/crates/file_finder/src/file_finder_settings.rs +++ b/crates/file_finder/src/file_finder_settings.rs @@ -1,7 +1,7 @@ use anyhow::Result; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Deserialize, Debug, Clone, Copy, PartialEq, SettingsUI)] pub struct FileFinderSettings { diff --git a/crates/git_hosting_providers/src/settings.rs b/crates/git_hosting_providers/src/settings.rs index 64265f4d35..76fd386d5b 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsStore}; +use settings::{SettingsUI, Settings, SettingsStore}; use url::Url; use util::ResultExt as _; diff --git a/crates/git_ui/src/git_panel_settings.rs b/crates/git_ui/src/git_panel_settings.rs index 0d8b852663..21f40f4a34 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use workspace::dock::DockPosition; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index 4537dfe7cc..4913b4901f 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use std::{fmt::Write, num::NonZeroU32, time::Duration}; use text::{Point, Selection}; use ui::{ diff --git a/crates/image_viewer/src/image_viewer_settings.rs b/crates/image_viewer/src/image_viewer_settings.rs index 1ce9971f9d..2adeb5d616 100644 --- a/crates/image_viewer/src/image_viewer_settings.rs +++ b/crates/image_viewer/src/image_viewer_settings.rs @@ -1,7 +1,7 @@ use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; /// The settings for the image viewer. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, SettingsUI)] diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 38545c3b7c..6d18b304b2 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use std::{ fs::OpenOptions, path::{Path, PathBuf}, diff --git a/crates/keymap_editor/Cargo.toml b/crates/keymap_editor/Cargo.toml new file mode 100644 index 0000000000..03098d5506 --- /dev/null +++ b/crates/keymap_editor/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "keymap_editor" +version = "0.1.0" +edition.workspace = true +publish.workspace = true +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +path = "src/keymap_editor.rs" + +[dependencies] +anyhow.workspace = true +collections.workspace = true +command_palette.workspace = true +command_palette_hooks.workspace = true +component.workspace = true +db.workspace = true +editor.workspace = true +feature_flags.workspace = true +fs.workspace = true +fuzzy.workspace = true +gpui.workspace = true +itertools.workspace = true +language.workspace = true +log.workspace = true +menu.workspace = true +notifications.workspace = true +paths.workspace = true +project.workspace = true +search.workspace = true +serde.workspace = true +serde_json.workspace = true +settings.workspace = true +telemetry.workspace = true +tempfile.workspace = true +theme.workspace = true +tree-sitter-json.workspace = true +tree-sitter-rust.workspace = true +ui.workspace = true +ui_input.workspace = true +util.workspace = true +vim.workspace = true +workspace-hack.workspace = true +workspace.workspace = true +zed_actions.workspace = true + +[dev-dependencies] +db = {"workspace"= true, "features" = ["test-support"]} +fs = { workspace = true, features = ["test-support"] } +gpui = { workspace = true, features = ["test-support"] } +project = { workspace = true, features = ["test-support"] } +workspace = { workspace = true, features = ["test-support"] } diff --git a/crates/keymap_editor/LICENSE-GPL b/crates/keymap_editor/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/crates/keymap_editor/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/settings_ui/src/keybindings.rs b/crates/keymap_editor/src/keymap_editor.rs similarity index 99% rename from crates/settings_ui/src/keybindings.rs rename to crates/keymap_editor/src/keymap_editor.rs index 9c76725972..3c67610aeb 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -1,3 +1,5 @@ +mod ui_components; + use std::{ cmp::{self}, ops::{Not as _, Range}, @@ -35,7 +37,7 @@ use workspace::{ }; use crate::{ - keybindings::persistence::KEYBINDING_EDITORS, + persistence::KEYBINDING_EDITORS, ui_components::{ keystroke_input::{ClearKeystrokes, KeystrokeInput, StartRecording, StopRecording}, table::{ColumnWidths, ResizeBehavior, Table, TableInteractionState}, diff --git a/crates/settings_ui/src/ui_components/keystroke_input.rs b/crates/keymap_editor/src/ui_components/keystroke_input.rs similarity index 100% rename from crates/settings_ui/src/ui_components/keystroke_input.rs rename to crates/keymap_editor/src/ui_components/keystroke_input.rs diff --git a/crates/settings_ui/src/ui_components/mod.rs b/crates/keymap_editor/src/ui_components/mod.rs similarity index 100% rename from crates/settings_ui/src/ui_components/mod.rs rename to crates/keymap_editor/src/ui_components/mod.rs diff --git a/crates/settings_ui/src/ui_components/table.rs b/crates/keymap_editor/src/ui_components/table.rs similarity index 100% rename from crates/settings_ui/src/ui_components/table.rs rename to crates/keymap_editor/src/ui_components/table.rs diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index ab4ee967b8..6e08ca49e1 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -17,8 +17,7 @@ use serde::{ }; use settings::{ - DeriveSettingsUI as SettingsUI, ParameterizedJsonSchema, Settings, SettingsLocation, - SettingsSources, SettingsStore, + ParameterizedJsonSchema, Settings, SettingsLocation, SettingsSources, SettingsStore, SettingsUI, }; use shellexpand; use std::{borrow::Cow, num::NonZeroU32, path::Path, slice, sync::Arc}; diff --git a/crates/language_models/src/settings.rs b/crates/language_models/src/settings.rs index 147d4bee5d..172fcf6446 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use crate::provider::{ self, diff --git a/crates/outline_panel/src/outline_panel_settings.rs b/crates/outline_panel/src/outline_panel_settings.rs index b34946e643..9d46fac0c4 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 9d34feb9e0..48e75c8095 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, settings::DeriveSettingsUI)] +#[derive(Copy, Clone, Debug, settings::SettingsUI)] pub struct DisableAiSettings { pub disable_ai: bool, } diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index beab54cd2d..132c3cc5ab 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -18,7 +18,7 @@ use rpc::{ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{ - DeriveSettingsUI as SettingsUI, InvalidSettingsError, LocalSettingsKind, Settings, + SettingsUI, InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources, SettingsStore, parse_json_with_comments, watch_config_file, }; use std::{ diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index a64fe41191..531136e699 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index fa82536f57..9544d1aae1 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use theme::ThemeSettings; use ui::{ ActiveTheme, Color, Context, Icon, IconName, IconSize, InteractiveElement, IntoElement, Label, diff --git a/crates/repl/src/jupyter_settings.rs b/crates/repl/src/jupyter_settings.rs index 2d1ee7bd84..4dcb3bb603 100644 --- a/crates/repl/src/jupyter_settings.rs +++ b/crates/repl/src/jupyter_settings.rs @@ -4,7 +4,7 @@ use editor::EditorSettings; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Debug, Default, SettingsUI)] pub struct JupyterSettings { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 7df274d23d..aa2af3ac99 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -5,6 +5,7 @@ mod keymap_file; mod settings_file; mod settings_json; mod settings_store; +mod settings_ui; mod vscode_import; use gpui::{App, Global}; @@ -23,10 +24,11 @@ pub use settings_file::*; pub use settings_json::*; pub use settings_store::{ InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources, - SettingsStore, SettingsUI, + SettingsStore, }; +pub use settings_ui::*; // Re-export the derive macro -pub use settings_ui_macros::SettingsUI as DeriveSettingsUI; +pub use settings_ui_macros::SettingsUI; 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 58da654b0e..fe921112b8 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -32,11 +32,9 @@ pub type EditorconfigProperties = ec4rs::Properties; use crate::{ ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, VsCodeSettings, - WorktreeId, parse_json_with_comments, update_value_in_json_text, + WorktreeId, parse_json_with_comments, settings_ui::SettingsUI, 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. diff --git a/crates/settings/src/settings_ui.rs b/crates/settings/src/settings_ui.rs new file mode 100644 index 0000000000..5150e41cf0 --- /dev/null +++ b/crates/settings/src/settings_ui.rs @@ -0,0 +1,72 @@ +use std::any::Any; + +use gpui::{AnyElement, App, Window}; + +pub trait SettingsUI { + fn ui_item() -> SettingsUIItem { + SettingsUIItem { + item: SettingsUIItemVariant::None, + } + } +} + +pub struct SettingsUIItem { + // TODO: + // path: SmallVec<[&'static str; 8]>, + pub item: SettingsUIItemVariant, +} + +pub enum SettingsUIItemVariant { + Group(SettingsUIItemGroup), + Item(SettingsUIItemSingle), + // TODO: remove + None, +} + +pub struct SettingsUIItemGroup { + pub items: Vec, +} + +pub enum SettingsUIItemSingle { + // TODO: default/builtin variants + Custom(Box AnyElement>), +} + +/* NOTES: + +# Root Group +some_setting: { + # First Item + # this shouldn't be a group + # it should just be item with path "some_bool.enabled" and title "Some Bool" + "some_bool": { + # this should + enabled: true | false + } + # Second Item + "some_other_thing": "foo" | "bar" | "baz" +} + +Structure: +Group { + path: "some_item", + items: [ + Item( + path: ["some_bool", "enabled"], + ), + Item( + path: ["some_other_thing"], + ) + ] +} + +is the following better than "foo.enabled"? +- for objects with single key "enabled", should just be a bool, with no "enabled" +for objects with enabled and other settings, enabled should be implicit, +so +"vim": false, # disabled +"vim": true, # enabled with default settings +"vim": { + "default_mode": "HelixNormal" +} # enabled with custom settings +*/ diff --git a/crates/settings_ui/Cargo.toml b/crates/settings_ui/Cargo.toml index 8a151359ec..9c3866e228 100644 --- a/crates/settings_ui/Cargo.toml +++ b/crates/settings_ui/Cargo.toml @@ -11,45 +11,24 @@ workspace = true [lib] path = "src/settings_ui.rs" +[features] +default = [] + [dependencies] anyhow.workspace = true -collections.workspace = true command_palette.workspace = true command_palette_hooks.workspace = true -component.workspace = true -db.workspace = true editor.workspace = true feature_flags.workspace = true -fs.workspace = true -fuzzy.workspace = true gpui.workspace = true -itertools.workspace = true -language.workspace = true -log.workspace = true -menu.workspace = true -notifications.workspace = true -paths.workspace = true -project.workspace = true -search.workspace = true -serde.workspace = true -serde_json.workspace = true -settings.workspace = true -telemetry.workspace = true -tempfile.workspace = true theme.workspace = true -tree-sitter-json.workspace = true -tree-sitter-rust.workspace = true +settings.workspace = true ui.workspace = true -ui_input.workspace = true util.workspace = true -vim.workspace = true -workspace-hack.workspace = true workspace.workspace = true -zed_actions.workspace = true -[dev-dependencies] -db = {"workspace"= true, "features" = ["test-support"]} -fs = { workspace = true, features = ["test-support"] } -gpui = { workspace = true, features = ["test-support"] } -project = { workspace = true, features = ["test-support"] } -workspace = { workspace = true, features = ["test-support"] } +# Uncomment other workspace dependencies as needed +# assistant.workspace = true +# client.workspace = true +# project.workspace = true +# settings.workspace = true diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 3022cc7142..546f948d16 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -12,9 +12,6 @@ use workspace::{Workspace, with_active_or_new_workspace}; use crate::appearance_settings_controls::AppearanceSettingsControls; -pub mod keybindings; -pub mod ui_components; - pub struct SettingsUiFeatureFlag; impl FeatureFlag for SettingsUiFeatureFlag { @@ -76,7 +73,6 @@ pub fn init(cx: &mut App) { }) .detach(); - keybindings::init(cx); } pub struct SettingsPage { diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index b412c84088..7993fdef49 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::{DeriveSettingsUI as SettingsUI, SettingsSources}; +use settings::{SettingsSources, SettingsUI}; use std::path::PathBuf; use task::Shell; use theme::FontFamilyName; diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index e54ee893db..339589f99e 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -13,9 +13,7 @@ use gpui::{ use refineable::Refineable; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use settings::{ - DeriveSettingsUI as SettingsUI, ParameterizedJsonSchema, Settings, SettingsSources, -}; +use settings::{ParameterizedJsonSchema, Settings, SettingsSources, SettingsUI}; use std::sync::Arc; use util::ResultExt as _; use util::schemars::replace_subschema; diff --git a/crates/title_bar/Cargo.toml b/crates/title_bar/Cargo.toml index cf178e2850..f60ac7c301 100644 --- a/crates/title_bar/Cargo.toml +++ b/crates/title_bar/Cargo.toml @@ -42,7 +42,7 @@ rpc.workspace = true schemars.workspace = true serde.workspace = true settings.workspace = true -settings_ui.workspace = true +keymap_editor.workspace = true smallvec.workspace = true story = { workspace = true, optional = true } telemetry.workspace = true diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index b84a2800b6..35e5feabfb 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -27,10 +27,10 @@ use gpui::{ IntoElement, MouseButton, ParentElement, Render, StatefulInteractiveElement, Styled, Subscription, WeakEntity, Window, actions, div, }; +use keymap_editor; use onboarding_banner::OnboardingBanner; use project::Project; use settings::Settings as _; -use settings_ui::keybindings; use std::sync::Arc; use theme::ActiveTheme; use title_bar_settings::TitleBarSettings; @@ -681,7 +681,7 @@ impl TitleBar { "Settings Profiles", zed_actions::settings_profile_selector::Toggle.boxed_clone(), ) - .action("Key Bindings", Box::new(keybindings::OpenKeymapEditor)) + .action("Key Bindings", Box::new(keymap_editor::OpenKeymapEditor)) .action( "Themes…", zed_actions::theme_selector::Toggle::default().boxed_clone(), @@ -729,7 +729,7 @@ impl TitleBar { "Settings Profiles", zed_actions::settings_profile_selector::Toggle.boxed_clone(), ) - .action("Key Bindings", Box::new(keybindings::OpenKeymapEditor)) + .action("Key Bindings", Box::new(keymap_editor::OpenKeymapEditor)) .action( "Themes…", zed_actions::theme_selector::Toggle::default().boxed_clone(), diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index e393ecdc9d..781900cea6 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -1,7 +1,7 @@ use db::anyhow; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Copy, Clone, Deserialize, Debug, SettingsUI)] pub struct TitleBarSettings { diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index fb5b5d36ff..e3ec2d414c 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -40,7 +40,7 @@ use schemars::JsonSchema; use serde::Deserialize; use serde_derive::Serialize; use settings::{ - DeriveSettingsUI as SettingsUI, Settings, SettingsSources, SettingsStore, update_settings_file, + SettingsUI, Settings, SettingsSources, SettingsStore, update_settings_file, }; use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals}; use std::{mem, ops::Range, sync::Arc}; diff --git a/crates/vim_mode_setting/src/vim_mode_setting.rs b/crates/vim_mode_setting/src/vim_mode_setting.rs index 3b25a56795..dd9f122161 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{Settings, SettingsSources, SettingsUI}; /// Initializes the `vim_mode_setting` crate. pub fn init(cx: &mut App) { diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 7e52e95e68..18af720a74 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsLocation, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsLocation, SettingsSources}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index 87d592ac54..b564ac23ad 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -6,7 +6,7 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; #[derive(Deserialize, SettingsUI)] pub struct WorkspaceSettings { diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 4f9f7af1d4..6110b13dc1 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -4,7 +4,7 @@ use anyhow::Context as _; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{DeriveSettingsUI as SettingsUI, Settings, SettingsSources}; +use settings::{SettingsUI, Settings, SettingsSources}; use util::paths::PathMatcher; #[derive(Clone, PartialEq, Eq, SettingsUI)] diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6f4ead9ebb..62910089f2 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -131,6 +131,7 @@ serde_json.workspace = true session.workspace = true settings.workspace = true settings_ui.workspace = true +keymap_editor.workspace = true shellexpand.workspace = true smol.workspace = true snippet_provider.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index e99c8b564b..6f40b975b6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -632,6 +632,7 @@ pub fn main() { svg_preview::init(cx); onboarding::init(cx); settings_ui::init(cx); + keymap_editor::init(cx); extensions_ui::init(cx); zeta::init(cx); inspector_ui::init(app_state.clone(), cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1b9657dcc6..f237a17d97 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1482,7 +1482,7 @@ fn reload_keymaps(cx: &mut App, mut user_key_bindings: Vec) { workspace::NewWindow, )]); // todo: nicer api here? - settings_ui::keybindings::KeymapEventChannel::trigger_keymap_changed(cx); + keymap_editor::KeymapEventChannel::trigger_keymap_changed(cx); } pub fn load_default_keymap(cx: &mut App) { diff --git a/crates/zed/src/zed/app_menus.rs b/crates/zed/src/zed/app_menus.rs index 6c7ab0b374..342fd26cb7 100644 --- a/crates/zed/src/zed/app_menus.rs +++ b/crates/zed/src/zed/app_menus.rs @@ -1,6 +1,5 @@ use collab_ui::collab_panel; use gpui::{Menu, MenuItem, OsAction}; -use settings_ui::keybindings; use terminal_view::terminal_panel; pub fn app_menus() -> Vec { @@ -17,7 +16,7 @@ pub fn app_menus() -> Vec { name: "Settings".into(), items: vec![ MenuItem::action("Open Settings", super::OpenSettings), - MenuItem::action("Open Key Bindings", keybindings::OpenKeymapEditor), + MenuItem::action("Open Key Bindings", keymap_editor::OpenKeymapEditor), MenuItem::action("Open Default Settings", super::OpenDefaultSettings), MenuItem::action( "Open Default Key Bindings", diff --git a/crates/zlog_settings/src/zlog_settings.rs b/crates/zlog_settings/src/zlog_settings.rs index 5aad945b9b..b75ab29d8b 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::{DeriveSettingsUI as SettingsUI, Settings, SettingsStore}; +use settings::{SettingsUI, Settings, SettingsStore}; pub fn init(cx: &mut App) { ZlogSettings::register(cx); From ba9d1092897745e65c34a7f64c11c2c6a2f8ff69 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 12:33:38 -0400 Subject: [PATCH 3/7] Add settings_ui(group) attribute to macro Co-authored-by: Ben Kunkle --- crates/git_hosting_providers/src/settings.rs | 2 +- crates/gpui_macros/src/derive_action.rs | 7 ++++ crates/settings/src/settings_ui.rs | 14 +++++-- .../src/settings_ui_macros.rs | 40 +++++++++++++++++-- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/crates/git_hosting_providers/src/settings.rs b/crates/git_hosting_providers/src/settings.rs index 76fd386d5b..07831ee390 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::{SettingsUI, Settings, SettingsStore}; +use settings::{Settings, SettingsStore, SettingsUI}; use url::Url; use util::ResultExt as _; diff --git a/crates/gpui_macros/src/derive_action.rs b/crates/gpui_macros/src/derive_action.rs index 9c7f97371d..4e6c6277e4 100644 --- a/crates/gpui_macros/src/derive_action.rs +++ b/crates/gpui_macros/src/derive_action.rs @@ -16,6 +16,13 @@ pub(crate) fn derive_action(input: TokenStream) -> TokenStream { let mut deprecated = None; let mut doc_str: Option = 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| { diff --git a/crates/settings/src/settings_ui.rs b/crates/settings/src/settings_ui.rs index 5150e41cf0..e405c5bd8f 100644 --- a/crates/settings/src/settings_ui.rs +++ b/crates/settings/src/settings_ui.rs @@ -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, } diff --git a/crates/settings_ui_macros/src/settings_ui_macros.rs b/crates/settings_ui_macros/src/settings_ui_macros.rs index 099d5246c9..1f6f8fdd4f 100644 --- a/crates/settings_ui_macros/src/settings_ui_macros.rs +++ b/crates/settings_ui_macros/src/settings_ui_macros.rs @@ -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::::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::()?; + 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) From c1631b6e8c836367f256aa9f209e91de7dcf834d Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 13:55:25 -0400 Subject: [PATCH 4/7] Add group setting titles Co-authored-by: Ben Kunkle --- crates/settings/src/settings_store.rs | 16 ++++++++++++++-- crates/settings_ui/src/settings_ui.rs | 15 +++++++++++++-- crates/title_bar/src/title_bar_settings.rs | 3 ++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index fe921112b8..238df0dda1 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -31,8 +31,9 @@ use util::{ pub type EditorconfigProperties = ec4rs::Properties; use crate::{ - ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, VsCodeSettings, - WorktreeId, parse_json_with_comments, settings_ui::SettingsUI, update_value_in_json_text, + ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUIItem, + VsCodeSettings, WorktreeId, parse_json_with_comments, settings_ui::SettingsUI, + update_value_in_json_text, }; /// A value that can be defined as a user setting. @@ -272,6 +273,7 @@ trait AnySettingValue: 'static + Send + Sync { text: &mut String, edits: &mut Vec<(Range, String)>, ); + fn settings_ui_item(&self) -> SettingsUIItem; } struct DeserializedSetting(Box); @@ -604,6 +606,12 @@ impl SettingsStore { rx } + + pub fn settings_ui_items(&self) -> impl IntoIterator { + self.setting_values + .values() + .map(|item| item.settings_ui_item()) + } } impl SettingsStore { @@ -1498,6 +1506,10 @@ impl AnySettingValue for SettingValue { edits, ); } + + fn settings_ui_item(&self) -> SettingsUIItem { + T::ui_item() + } } #[cfg(test)] diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 546f948d16..83c9514792 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -5,7 +5,8 @@ use std::any::TypeId; use command_palette_hooks::CommandPaletteFilter; use editor::EditorSettingsControls; use feature_flags::{FeatureFlag, FeatureFlagViewExt}; -use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, actions}; +use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, ReadGlobal, actions}; +use settings::SettingsStore; use ui::prelude::*; use workspace::item::{Item, ItemEvent}; use workspace::{Workspace, with_active_or_new_workspace}; @@ -72,7 +73,6 @@ pub fn init(cx: &mut App) { .detach(); }) .detach(); - } pub struct SettingsPage { @@ -121,6 +121,17 @@ impl Render for SettingsPage { .p_4() .size_full() .gap_4() + .children( + SettingsStore::global(cx) + .settings_ui_items() + .into_iter() + .flat_map(|item| match item.item { + settings::SettingsUIItemVariant::Group { path, group } => Some(path), + 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( v_flex().gap_1().child(Label::new("Appearance")).child( diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index 781900cea6..d674899370 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -1,9 +1,10 @@ use db::anyhow; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{SettingsUI, Settings, SettingsSources}; +use settings::{Settings, SettingsSources, SettingsUI}; #[derive(Copy, Clone, Deserialize, Debug, SettingsUI)] +#[settings_ui(group = "Title Bar")] pub struct TitleBarSettings { pub show_branch_icon: bool, pub show_onboarding_banner: bool, From 1fd0f7a46b9aeb5f50ac0b7b6f3e56270e4c462b Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 15:36:53 -0400 Subject: [PATCH 5/7] Improve macro to show items Co-authored-by: Ben Kunkle --- crates/settings/src/base_keymap_setting.rs | 10 ++- crates/settings/src/settings_store.rs | 6 +- crates/settings/src/settings_ui.rs | 48 ++++++++++- crates/settings_ui/src/settings_ui.rs | 28 ++++++- .../src/settings_ui_macros.rs | 79 ++++++++++++++++--- 5 files changed, 148 insertions(+), 23 deletions(-) diff --git a/crates/settings/src/base_keymap_setting.rs b/crates/settings/src/base_keymap_setting.rs index 272eb2e78e..4f4ba6e4a4 100644 --- a/crates/settings/src/base_keymap_setting.rs +++ b/crates/settings/src/base_keymap_setting.rs @@ -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"); diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 238df0dda1..cc06e990d3 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -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 AnySettingValue for SettingValue { } fn settings_ui_item(&self) -> SettingsUIItem { - T::ui_item() + ::settings_ui_item() } } diff --git a/crates/settings/src/settings_ui.rs b/crates/settings/src/settings_ui.rs index e405c5bd8f..de6823f3f5 100644 --- a/crates/settings/src/settings_ui.rs +++ b/crates/settings/src/settings_ui.rs @@ -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 AnyElement>), } +pub enum SettingsUIRender { + Group { + title: &'static str, + items: Vec, + }, + 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 diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 83c9514792..678b8afb56 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -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( diff --git a/crates/settings_ui_macros/src/settings_ui_macros.rs b/crates/settings_ui_macros/src/settings_ui_macros.rs index 1f6f8fdd4f..d83056629d 100644 --- a/crates/settings_ui_macros/src/settings_ui_macros.rs +++ b/crates/settings_ui_macros/src/settings_ui_macros.rs @@ -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) } From 8ff656c999fdc10bf8defdbdc2648aa1f71b97cc Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 15:43:20 -0400 Subject: [PATCH 6/7] Update boolean in settings ui to show up as switch fields Co-authored-by: Ben Kunkle --- crates/settings_ui/src/settings_ui.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 678b8afb56..e97b405dbc 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -6,8 +6,8 @@ use command_palette_hooks::CommandPaletteFilter; use editor::EditorSettingsControls; use feature_flags::{FeatureFlag, FeatureFlagViewExt}; use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, ReadGlobal, actions}; -use settings::SettingsStore; -use ui::prelude::*; +use settings::{SettingsStore, SettingsUIItemSingle, SettingsUIItemVariant}; +use ui::{SwitchField, prelude::*}; use workspace::item::{Item, ItemEvent}; use workspace::{Workspace, with_active_or_new_workspace}; @@ -139,9 +139,22 @@ impl Render for SettingsPage { .child(format!("Subgroup: {}", title)) .into_any_element(), settings::SettingsUIItemVariant::Item { path, item } => { - div() - .child(format!("Item: {}", path)) - .into_any_element() + match item { + SettingsUIItemSingle::Custom(_) => div() + .child(format!("Item: {}", path)) + .into_any_element(), + SettingsUIItemSingle::SwitchField => div() + .child(SwitchField::new( + ElementId::Name(SharedString::new_static( + path, + )), + SharedString::new_static(path), + None, + ToggleState::Unselected, + |_, _, _| {}, + )) + .into_any_element(), + } } settings::SettingsUIItemVariant::None => { div().child("None").into_any_element() From e42d5a8264e203a14000d24cabe9294e52b5fa3a Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 26 Aug 2025 15:53:46 -0400 Subject: [PATCH 7/7] Add more notes Co-authored-by: Ben Kunkle --- crates/settings/src/settings_ui.rs | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/settings/src/settings_ui.rs b/crates/settings/src/settings_ui.rs index de6823f3f5..3a1f3c2216 100644 --- a/crates/settings/src/settings_ui.rs +++ b/crates/settings/src/settings_ui.rs @@ -61,6 +61,40 @@ impl SettingsUI for bool { } } +/* +FOR DOC COMMENTS ON "Contents" TYPES: +define trait: SettingsUIDocProvider with derive +derive creates: +impl SettingsUIDocProvider for Foo { + fn settings_ui_doc() -> Hashmap<&'static str, &'static str> { + Hashmap::from(Foo.fields.map(|field| (field.name, field.doc_comment))) + } +} + +on derive settings_ui, have attr +#[settings_ui(doc_from = "Foo")] + +and have derive(SettingsUI) do + +if doc_from { +quote! { + doc_comments = doc_from.type::settings_ui_doc(); + for fields { + field.doc_comment = doc_comments.get(field.name).unwrap() + } + } +} else { + doc_comments = FileContent::settings_ui +} + +FOR PATH: +if derive attr also contains "Settings", then we can use ::KEY, +otherwise we need a #[settings_ui(path = ...)]. + +FOR BOTH OF ABOVE, we can check if derive() attr contains Settings, otherwise assert that both doc_from and path are present +like so: #[settings_ui(doc_from = "Foo", path = "foo")] + */ + /* #[derive(SettingsUI)] #[settings_ui(group = "Foo")]