diff --git a/Cargo.lock b/Cargo.lock index 1a15d11e664f808d4ba68d61a3ad7a6c557c2420..a99c59a1890080ac220b669b26864d859d2ad377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17995,6 +17995,8 @@ version = "0.1.0" dependencies = [ "anyhow", "gpui", + "schemars", + "serde", "settings", "workspace-hack", ] diff --git a/crates/agent_servers/src/settings.rs b/crates/agent_servers/src/settings.rs index 693d7d7b7014b3abbecfbe592bac67210b336872..167753296a1a489128ba916f114f4895c15afcf9 100644 --- a/crates/agent_servers/src/settings.rs +++ b/crates/agent_servers/src/settings.rs @@ -6,13 +6,14 @@ use collections::HashMap; use gpui::{App, SharedString}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; pub fn init(cx: &mut App) { AllAgentServersSettings::register(cx); } -#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, SettingsUi)] +#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "agent_servers")] pub struct AllAgentServersSettings { pub gemini: Option, pub claude: Option, @@ -75,8 +76,6 @@ pub struct CustomAgentServerSettings { } impl settings::Settings for AllAgentServersSettings { - const KEY: Option<&'static str> = Some("agent_servers"); - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index 8aebdcd288c8451d9bc391f1fc1598d6098d55af..8c4a190e1c3135b5bbfbc90544bb92db7a6bdd22 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use std::borrow::Cow; pub use crate::agent_profile::*; @@ -223,7 +223,8 @@ impl AgentSettingsContent { } } -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default, SettingsUi)] +#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default, SettingsUi, SettingsKey)] +#[settings_key(key = "agent", fallback_key = "assistant")] pub struct AgentSettingsContent { /// Whether the Agent is enabled. /// @@ -399,10 +400,6 @@ pub struct ContextServerPresetContent { } impl Settings for AgentSettings { - const KEY: Option<&'static str> = Some("agent"); - - const FALLBACK_KEY: Option<&'static str> = Some("assistant"); - const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]); type FileContent = AgentSettingsContent; diff --git a/crates/agent_ui/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs index c54a10ed49a77d395c4968e551b1cd30ad1c6e07..9580ffef0f317fbe726c57041fad4f0fa438e143 100644 --- a/crates/agent_ui/src/slash_command_settings.rs +++ b/crates/agent_ui/src/slash_command_settings.rs @@ -2,10 +2,11 @@ use anyhow::Result; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; /// Settings for slash commands. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "slash_commands")] pub struct SlashCommandSettings { /// Settings for the `/cargo-workspace` slash command. #[serde(default)] @@ -21,8 +22,6 @@ pub struct CargoWorkspaceCommandSettings { } impl Settings for SlashCommandSettings { - const KEY: Option<&'static str> = Some("slash_commands"); - type FileContent = Self; fn load(sources: SettingsSources, _cx: &mut App) -> Result { diff --git a/crates/audio/src/audio_settings.rs b/crates/audio/src/audio_settings.rs index d30d950273f2138f3bd54c573513373574f1ce43..168519030bcbd4a422965580ddbe01121934278d 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] pub struct AudioSettings { /// Opt into the new audio system. #[serde(rename = "experimental.rodio_audio", default)] @@ -12,8 +12,9 @@ pub struct AudioSettings { } /// Configuration of audio in Zed. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] #[serde(default)] +#[settings_key(key = "audio")] pub struct AudioSettingsContent { /// Whether to use the experimental audio system #[serde(rename = "experimental.rodio_audio", default)] @@ -21,8 +22,6 @@ pub struct AudioSettingsContent { } impl Settings for AudioSettings { - const KEY: Option<&'static str> = Some("audio"); - type FileContent = AudioSettingsContent; fn load(sources: SettingsSources, _cx: &mut App) -> Result { diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index f0ae3fdb1cfef667a9f737aa6545a42046a9d322..f5d4533a9ee042e62752f26b989bc75561c534ae 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi}; use smol::{fs, io::AsyncReadExt}; use smol::{fs::File, process::Command}; use std::{ @@ -118,13 +118,12 @@ struct AutoUpdateSetting(bool); /// Whether or not to automatically check for updates. /// /// Default: true -#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi)] +#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi, SettingsKey)] #[serde(transparent)] +#[settings_key(key = "auto_update")] struct AutoUpdateSettingContent(bool); impl Settings for AutoUpdateSetting { - const KEY: Option<&'static str> = Some("auto_update"); - type FileContent = AutoUpdateSettingContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index 7b0838e3a96185c1e4c33b8116fbd6a41b35f3dc..b0677e3c3bcb5112fdd9ad2abc4bf188b225aeac 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Deserialize, Debug)] pub struct CallSettings { @@ -11,7 +11,8 @@ pub struct CallSettings { } /// Configuration of voice calls in Zed. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "calls")] pub struct CallSettingsContent { /// Whether the microphone should be muted when joining a channel or a call. /// @@ -25,8 +26,6 @@ pub struct CallSettingsContent { } impl Settings for CallSettings { - const KEY: Option<&'static str> = Some("calls"); - type FileContent = CallSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 85f6aeade69cc04c5f58b72258ac062157094460..cb8185c7ed326ed7d45726a99077c53903118316 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use std::{ any::TypeId, convert::TryFrom, @@ -96,7 +96,8 @@ actions!( ] ); -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct ClientSettingsContent { server_url: Option, } @@ -107,8 +108,6 @@ pub struct ClientSettings { } impl Settings for ClientSettings { - const KEY: Option<&'static str> = None; - type FileContent = ClientSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -122,7 +121,8 @@ impl Settings for ClientSettings { fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {} } -#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct ProxySettingsContent { proxy: Option, } @@ -133,8 +133,6 @@ pub struct ProxySettings { } impl Settings for ProxySettings { - const KEY: Option<&'static str> = None; - type FileContent = ProxySettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -527,7 +525,8 @@ pub struct TelemetrySettings { } /// Control what info is collected by Zed. -#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "telemetry")] pub struct TelemetrySettingsContent { /// Send debug info like crash reports. /// @@ -540,8 +539,6 @@ pub struct TelemetrySettingsContent { } impl settings::Settings for TelemetrySettings { - const KEY: Option<&'static str> = Some("telemetry"); - type FileContent = TelemetrySettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index 64f0a9366df7cdef1f2c05809752fb1cf912111b..bae118d819c2e38e7b77e5aa841c084e4c45d6e8 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use workspace::dock::DockPosition; #[derive(Deserialize, Debug)] @@ -27,7 +27,8 @@ pub struct ChatPanelSettings { pub default_width: Pixels, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "chat_panel")] pub struct ChatPanelSettingsContent { /// When to show the panel button in the status bar. /// @@ -43,14 +44,16 @@ pub struct ChatPanelSettingsContent { pub default_width: Option, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SettingsKey)] +#[settings_key(key = "notification_panel")] pub struct NotificationPanelSettings { pub button: bool, pub dock: DockPosition, pub default_width: Pixels, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "collaboration_panel")] pub struct PanelSettingsContent { /// Whether to show the panel button in the status bar. /// @@ -66,7 +69,8 @@ pub struct PanelSettingsContent { pub default_width: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "message_editor")] pub struct MessageEditorSettings { /// Whether to automatically replace emoji shortcodes with emoji characters. /// For example: typing `:wave:` gets replaced with `👋`. @@ -76,8 +80,6 @@ pub struct MessageEditorSettings { } impl Settings for CollaborationPanelSettings { - const KEY: Option<&'static str> = Some("collaboration_panel"); - type FileContent = PanelSettingsContent; fn load( @@ -91,8 +93,6 @@ impl Settings for CollaborationPanelSettings { } impl Settings for ChatPanelSettings { - const KEY: Option<&'static str> = Some("chat_panel"); - type FileContent = ChatPanelSettingsContent; fn load( @@ -106,8 +106,6 @@ impl Settings for ChatPanelSettings { } impl Settings for NotificationPanelSettings { - const KEY: Option<&'static str> = Some("notification_panel"); - type FileContent = PanelSettingsContent; fn load( @@ -121,8 +119,6 @@ impl Settings for NotificationPanelSettings { } impl Settings for MessageEditorSettings { - const KEY: Option<&'static str> = Some("message_editor"); - type FileContent = MessageEditorSettings; fn load( diff --git a/crates/dap/src/debugger_settings.rs b/crates/dap/src/debugger_settings.rs index 929bff747e8685ec9a4b36fa9db63d12a769faa2..8d53fdea8649f1c62fa74cc6f0ddd6aec6ecff6d 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)] #[serde(rename_all = "snake_case")] @@ -12,11 +12,12 @@ pub enum DebugPanelDockPosition { Right, } -#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy, SettingsUi)] +#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy, SettingsUi, SettingsKey)] #[serde(default)] // todo(settings_ui) @ben: I'm pretty sure not having the fields be optional here is a bug, // it means the defaults will override previously set values if a single key is missing -#[settings_ui(group = "Debugger", path = "debugger")] +#[settings_ui(group = "Debugger")] +#[settings_key(key = "debugger")] pub struct DebuggerSettings { /// Determines the stepping granularity. /// @@ -64,8 +65,6 @@ impl Default for DebuggerSettings { } impl Settings for DebuggerSettings { - const KEY: Option<&'static str> = Some("debugger"); - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 44cb0749760e9e3af91bc837df0ef0589e251703..d74244131e6635c7b9eda6ace0723ced96b0e041 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::{Settings, SettingsSources, SettingsUi, VsCodeSettings}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi, VsCodeSettings}; use util::serde::default_true; /// Imports from the VSCode settings at @@ -431,8 +431,9 @@ pub enum SnippetSortOrder { None, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] #[settings_ui(group = "Editor")] +#[settings_key(None)] pub struct EditorSettingsContent { /// Whether the cursor blinks in the editor. /// @@ -777,8 +778,6 @@ impl EditorSettings { } impl Settings for EditorSettings { - const KEY: Option<&'static str> = None; - type FileContent = EditorSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/extension_host/src/extension_settings.rs b/crates/extension_host/src/extension_settings.rs index 6bd760795cec6d1c4208770f1355e8ac7a34eb95..fa5a613c55a76a0b5660b114d49acc17fcf79120 100644 --- a/crates/extension_host/src/extension_settings.rs +++ b/crates/extension_host/src/extension_settings.rs @@ -3,10 +3,11 @@ use collections::HashMap; use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use std::sync::Arc; -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct ExtensionSettings { /// The extensions that should be automatically installed by Zed. /// @@ -38,8 +39,6 @@ impl ExtensionSettings { } impl Settings for ExtensionSettings { - const KEY: Option<&'static str> = None; - type FileContent = Self; fn load(sources: SettingsSources, _cx: &mut App) -> Result { diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index fd504764b65826ea74e092ea4c11d5576fa51524..0b925dceb1544d97a77082881626bc1e97f3d1b0 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -1345,7 +1345,7 @@ impl ExtensionsPage { this.update_settings::( selection, cx, - |setting, value| *setting = Some(value), + |setting, value| setting.vim_mode = Some(value), ); }), )), diff --git a/crates/file_finder/src/file_finder_settings.rs b/crates/file_finder/src/file_finder_settings.rs index 007af53b1144ed4caa7985d75cdf4707f13ed13e..6a6b98b8ea3e1c7e7f0e3cc0385fdd7f413b659f 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Deserialize, Debug, Clone, Copy, PartialEq)] pub struct FileFinderSettings { @@ -11,7 +11,8 @@ pub struct FileFinderSettings { pub include_ignored: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "file_finder")] pub struct FileFinderSettingsContent { /// Whether to show file icons in the file finder. /// @@ -42,8 +43,6 @@ pub struct FileFinderSettingsContent { } impl Settings for FileFinderSettings { - const KEY: Option<&'static str> = Some("file_finder"); - type FileContent = FileFinderSettingsContent; fn load(sources: SettingsSources, _: &mut gpui::App) -> Result { diff --git a/crates/git_hosting_providers/src/settings.rs b/crates/git_hosting_providers/src/settings.rs index 34e3805a39ea8a13a6a2f79552a6a917c4597692..3249981db91015479bab728484341519db357683 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsStore, SettingsUi}; use url::Url; use util::ResultExt as _; @@ -78,7 +78,8 @@ pub struct GitHostingProviderConfig { pub name: String, } -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct GitHostingProviderSettings { /// The list of custom Git hosting providers. #[serde(default)] @@ -86,8 +87,6 @@ pub struct GitHostingProviderSettings { } impl Settings for GitHostingProviderSettings { - const KEY: Option<&'static str> = None; - type FileContent = Self; fn load(sources: settings::SettingsSources, _: &mut App) -> Result { diff --git a/crates/git_ui/src/git_panel_settings.rs b/crates/git_ui/src/git_panel_settings.rs index 39d6540db52046845521a23c0290be4e6e595492..be207314acd82446566dffd2eb58339974f177ff 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use workspace::dock::DockPosition; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -36,7 +36,8 @@ pub enum StatusStyle { LabelColor, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "git_panel")] pub struct GitPanelSettingsContent { /// Whether to show the panel button in the status bar. /// @@ -90,8 +91,6 @@ pub struct GitPanelSettings { } impl Settings for GitPanelSettings { - const KEY: Option<&'static str> = Some("git_panel"); - type FileContent = GitPanelSettingsContent; fn load( diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index 5840993ece84b1e8098ee341395e7f77fb791ace..6af8c79fe9cc4ed0be0d7cb466753fa939355eec 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use std::{fmt::Write, num::NonZeroU32, time::Duration}; use text::{Point, Selection}; use ui::{ @@ -301,13 +301,12 @@ pub(crate) enum LineIndicatorFormat { Long, } -#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi)] +#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize, SettingsUi, SettingsKey)] #[serde(transparent)] +#[settings_key(key = "line_indicator_format")] pub(crate) struct LineIndicatorFormatContent(LineIndicatorFormat); impl Settings for LineIndicatorFormat { - const KEY: Option<&'static str> = Some("line_indicator_format"); - type FileContent = LineIndicatorFormatContent; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/image_viewer/src/image_viewer_settings.rs b/crates/image_viewer/src/image_viewer_settings.rs index 4949b266b4e03c7089d4bc25e2a223a0ce64a081..510de69b522fbb07cb8eedba43edfe3a95e4a591 100644 --- a/crates/image_viewer/src/image_viewer_settings.rs +++ b/crates/image_viewer/src/image_viewer_settings.rs @@ -1,10 +1,11 @@ use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; /// The settings for the image viewer. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, SettingsUi)] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, SettingsUi, SettingsKey)] +#[settings_key(key = "image_viewer")] pub struct ImageViewerSettings { /// The unit to use for displaying image file sizes. /// @@ -24,8 +25,6 @@ pub enum ImageFileSizeUnit { } impl Settings for ImageViewerSettings { - const KEY: Option<&'static str> = Some("image_viewer"); - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index ffa24571c88a0f0e06252565261b1a6d285d098c..5cdfa6c1df034deaf06e1c99ea99415757b84c29 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use std::{ fs::OpenOptions, path::{Path, PathBuf}, @@ -22,7 +22,8 @@ actions!( ); /// Settings specific to journaling -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "journal")] pub struct JournalSettings { /// The path of the directory where journal entries are stored. /// @@ -52,8 +53,6 @@ pub enum HourFormat { } impl settings::Settings for JournalSettings { - const KEY: Option<&'static str> = Some("journal"); - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index f04b83bc7336143672647a07107fa27bc55f5823..3443ccf592a4138edb61959f0dd82bdb8cc8d418 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, SettingsUi, + ParameterizedJsonSchema, Settings, SettingsKey, SettingsLocation, SettingsSources, + SettingsStore, SettingsUi, }; use shellexpand; use std::{borrow::Cow, num::NonZeroU32, path::Path, slice, sync::Arc}; @@ -292,7 +293,10 @@ pub struct CopilotSettings { } /// The settings for all languages. -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive( + Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey, +)] +#[settings_key(None)] pub struct AllLanguageSettingsContent { /// The settings for enabling/disabling features. #[serde(default)] @@ -1213,8 +1217,6 @@ impl InlayHintKind { } impl settings::Settings for AllLanguageSettings { - const KEY: Option<&'static str> = None; - type FileContent = AllLanguageSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/language_models/src/settings.rs b/crates/language_models/src/settings.rs index 8b7ab5fc2547bd0b014238739f1b940dad831f66..cfe66c91a36d4da562cba84363f79bd1d5b4e1ce 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use crate::provider::{ self, @@ -46,7 +46,10 @@ pub struct AllLanguageModelSettings { pub zed_dot_dev: ZedDotDevSettings, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, SettingsUi)] +#[derive( + Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, SettingsUi, SettingsKey, +)] +#[settings_key(key = "language_models")] pub struct AllLanguageModelSettingsContent { pub anthropic: Option, pub bedrock: Option, @@ -145,8 +148,6 @@ pub struct OpenRouterSettingsContent { } impl settings::Settings for AllLanguageModelSettings { - const KEY: Option<&'static str> = Some("language_models"); - const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]); type FileContent = AllLanguageModelSettingsContent; diff --git a/crates/onboarding/src/ai_setup_page.rs b/crates/onboarding/src/ai_setup_page.rs index 54c49bc72a49309002421c4f8ac3544c86e4dc69..3631ad00dfb8662d5d4142a4cbd11186c1b1b137 100644 --- a/crates/onboarding/src/ai_setup_page.rs +++ b/crates/onboarding/src/ai_setup_page.rs @@ -264,13 +264,9 @@ pub(crate) fn render_ai_setup_page( ); let fs = ::global(cx); - update_settings_file::( - fs, - cx, - move |ai_settings: &mut Option, _| { - *ai_settings = Some(enabled); - }, - ); + update_settings_file::(fs, cx, move |ai_settings, _| { + ai_settings.disable_ai = Some(enabled); + }); }, ) .tab_index({ diff --git a/crates/onboarding/src/basics_page.rs b/crates/onboarding/src/basics_page.rs index 59ec437dcf8d11209e9c73020f1b51e40aa56cce..aef9dcca86ce49a70f1a508c0a43614737a653c7 100644 --- a/crates/onboarding/src/basics_page.rs +++ b/crates/onboarding/src/basics_page.rs @@ -388,7 +388,7 @@ fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoEleme } }; update_settings_file::(fs.clone(), cx, move |setting, _| { - *setting = Some(vim_mode); + setting.vim_mode = Some(vim_mode); }); telemetry::event!( diff --git a/crates/outline_panel/src/outline_panel_settings.rs b/crates/outline_panel/src/outline_panel_settings.rs index 48c6621e3509c1eda69a6a5e92602ba2ab12a484..dc123f2ba5fb38dd80b72aee8fc6ad6a000be23d 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] @@ -61,7 +61,8 @@ pub struct IndentGuidesSettingsContent { pub show: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "outline_panel")] pub struct OutlinePanelSettingsContent { /// Whether to show the outline panel button in the status bar. /// @@ -116,8 +117,6 @@ pub struct OutlinePanelSettingsContent { } impl Settings for OutlinePanelSettings { - const KEY: Option<&'static str> = Some("outline_panel"); - type FileContent = OutlinePanelSettingsContent; fn load( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4adebabc5a03636ca81fbc3b04a277c2d6d03a66..1e2e52c120f95a7c7540cd6f916d2d401f411af2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,6 +28,7 @@ use context_server_store::ContextServerStore; pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent}; use git::repository::get_git_committer; use git_store::{Repository, RepositoryId}; +use schemars::JsonSchema; pub mod search_history; mod yarn; @@ -94,7 +95,10 @@ use rpc::{ }; use search::{SearchInputKind, SearchQuery, SearchResult}; use search_history::SearchHistory; -use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsSources, SettingsStore}; +use settings::{ + InvalidSettingsError, Settings, SettingsKey, SettingsLocation, SettingsSources, SettingsStore, + SettingsUi, +}; use smol::channel::Receiver; use snippet::Snippet; use snippet_provider::SnippetProvider; @@ -968,10 +972,26 @@ pub struct DisableAiSettings { pub disable_ai: bool, } -impl settings::Settings for DisableAiSettings { - const KEY: Option<&'static str> = Some("disable_ai"); +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Default, + serde::Serialize, + serde::Deserialize, + SettingsUi, + SettingsKey, + JsonSchema, +)] +#[settings_key(None)] +pub struct DisableAiSettingContent { + pub disable_ai: Option, +} - type FileContent = Option; +impl settings::Settings for DisableAiSettings { + type FileContent = DisableAiSettingContent; fn load(sources: SettingsSources, _: &mut App) -> Result { // For security reasons, settings can only make AI restrictions MORE strict, not less. @@ -984,7 +1004,7 @@ impl settings::Settings for DisableAiSettings { .iter() .chain(sources.user.iter()) .chain(sources.server.iter()) - .any(|disabled| **disabled == Some(true)); + .any(|disabled| disabled.disable_ai == Some(true)); Ok(Self { disable_ai }) } @@ -5550,10 +5570,15 @@ mod disable_ai_settings_tests { #[gpui::test] async fn test_disable_ai_settings_security(cx: &mut TestAppContext) { + fn disable_setting(value: Option) -> DisableAiSettingContent { + DisableAiSettingContent { disable_ai: value } + } cx.update(|cx| { // Test 1: Default is false (AI enabled) let sources = SettingsSources { - default: &Some(false), + default: &DisableAiSettingContent { + disable_ai: Some(false), + }, global: None, extensions: None, user: None, @@ -5567,10 +5592,10 @@ mod disable_ai_settings_tests { assert!(!settings.disable_ai, "Default should allow AI"); // Test 2: Global true, local false -> still disabled (local cannot re-enable) - let global_true = Some(true); - let local_false = Some(false); + let global_true = disable_setting(Some(true)); + let local_false = disable_setting(Some(false)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&global_true), @@ -5587,10 +5612,10 @@ mod disable_ai_settings_tests { ); // Test 3: Global false, local true -> disabled (local can make more restrictive) - let global_false = Some(false); - let local_true = Some(true); + let global_false = disable_setting(Some(false)); + let local_true = disable_setting(Some(true)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&global_false), @@ -5604,10 +5629,10 @@ mod disable_ai_settings_tests { assert!(settings.disable_ai, "Local true can override global false"); // Test 4: Server can only make more restrictive (set to true) - let user_false = Some(false); - let server_true = Some(true); + let user_false = disable_setting(Some(false)); + let server_true = disable_setting(Some(true)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&user_false), @@ -5624,10 +5649,10 @@ mod disable_ai_settings_tests { ); // Test 5: Server false cannot override user true - let user_true = Some(true); - let server_false = Some(false); + let user_true = disable_setting(Some(true)); + let server_false = disable_setting(Some(false)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&user_true), @@ -5644,12 +5669,12 @@ mod disable_ai_settings_tests { ); // Test 6: Multiple local settings, any true disables AI - let global_false = Some(false); - let local_false3 = Some(false); - let local_true2 = Some(true); - let local_false4 = Some(false); + let global_false = disable_setting(Some(false)); + let local_false3 = disable_setting(Some(false)); + let local_true2 = disable_setting(Some(true)); + let local_false4 = disable_setting(Some(false)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&global_false), @@ -5663,11 +5688,11 @@ mod disable_ai_settings_tests { assert!(settings.disable_ai, "Any local true should disable AI"); // Test 7: All three sources can independently disable AI - let user_false2 = Some(false); - let server_false2 = Some(false); - let local_true3 = Some(true); + let user_false2 = disable_setting(Some(false)); + let server_false2 = disable_setting(Some(false)); + let local_true3 = disable_setting(Some(true)); let sources = SettingsSources { - default: &Some(false), + default: &disable_setting(Some(false)), global: None, extensions: None, user: Some(&user_false2), diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 57969ec9938602b477293aa3033a31bc8b3deae1..694e244e63e2b2861d640ec32ce0a1f5c50be52f 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, SettingsUi, parse_json_with_comments, watch_config_file, + InvalidSettingsError, LocalSettingsKind, Settings, SettingsKey, SettingsLocation, + SettingsSources, SettingsStore, SettingsUi, parse_json_with_comments, watch_config_file, }; use std::{ collections::BTreeMap, @@ -36,7 +36,8 @@ use crate::{ worktree_store::{WorktreeStore, WorktreeStoreEvent}, }; -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct ProjectSettings { /// Configuration for language servers. /// @@ -568,8 +569,6 @@ impl Default for SessionSettings { } impl Settings for ProjectSettings { - const KEY: Option<&'static str> = None; - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index db9b2b85d545e85a0cff3ec13a8f75e28dac88fa..6c812c294663d1d6fe7915d201f9e8925fa943ab 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] @@ -92,7 +92,8 @@ pub enum ShowDiagnostics { All, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "project_panel")] pub struct ProjectPanelSettingsContent { /// Whether to show the project panel button in the status bar. /// @@ -168,8 +169,6 @@ pub struct ProjectPanelSettingsContent { } impl Settings for ProjectPanelSettings { - const KEY: Option<&'static str> = Some("project_panel"); - type FileContent = ProjectPanelSettingsContent; fn load( diff --git a/crates/recent_projects/src/remote_connections.rs b/crates/recent_projects/src/remote_connections.rs index a7f915301f42850b03be951f596a8542842a6877..3e6810239c80c72d74624bcc243157290fcd93fa 100644 --- a/crates/recent_projects/src/remote_connections.rs +++ b/crates/recent_projects/src/remote_connections.rs @@ -21,7 +21,7 @@ use remote::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use theme::ThemeSettings; use ui::{ ActiveTheme, Color, CommonAnimationExt, Context, Icon, IconName, IconSize, InteractiveElement, @@ -121,15 +121,14 @@ pub struct SshProject { pub paths: Vec, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct RemoteSettingsContent { pub ssh_connections: Option>, pub read_ssh_config: Option, } impl Settings for SshSettings { - const KEY: Option<&'static str> = None; - type FileContent = RemoteSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/repl/src/jupyter_settings.rs b/crates/repl/src/jupyter_settings.rs index 6f3d6b1db631267e9b41ae7598d6e573387f2ac6..c89736a03dc6d77dd89bb33c4990b25149189a41 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Debug, Default)] pub struct JupyterSettings { @@ -20,7 +20,8 @@ impl JupyterSettings { } } -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] +#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey)] +#[settings_key(key = "jupyter")] pub struct JupyterSettingsContent { /// Default kernels to select for each language. /// @@ -37,8 +38,6 @@ impl Default for JupyterSettingsContent { } impl Settings for JupyterSettings { - const KEY: Option<&'static str> = Some("jupyter"); - type FileContent = JupyterSettingsContent; fn load( diff --git a/crates/settings/src/base_keymap_setting.rs b/crates/settings/src/base_keymap_setting.rs index fb5b445b49a1fdbfac34ce8bc1a3d17d8241e009..a6bfeecbc3c01eb5309221443d1b9905b99dcd5b 100644 --- a/crates/settings/src/base_keymap_setting.rs +++ b/crates/settings/src/base_keymap_setting.rs @@ -1,10 +1,10 @@ use std::fmt::{Display, Formatter}; -use crate as settings; +use crate::{self as settings}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources, VsCodeSettings}; -use settings_ui_macros::SettingsUi; +use settings_ui_macros::{SettingsKey, SettingsUi}; /// Base key bindings scheme. Base keymaps can be overridden with user keymaps. /// @@ -101,16 +101,25 @@ impl BaseKeymap { } #[derive( - Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default, SettingsUi, + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + PartialEq, + Eq, + Default, + SettingsUi, + SettingsKey, )] // extracted so that it can be an option, and still work with derive(SettingsUi) +#[settings_key(None)] pub struct BaseKeymapSetting { pub base_keymap: Option, } impl Settings for BaseKeymap { - const KEY: Option<&'static str> = None; - type FileContent = BaseKeymapSetting; fn load( diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 7e567cc085101713b0f6b100d0b47f6bf4c3531f..8a50b1afe5d0c68365efe0652421937f6dad2783 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -21,12 +21,12 @@ pub use keymap_file::{ pub use settings_file::*; pub use settings_json::*; pub use settings_store::{ - InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources, - SettingsStore, + InvalidSettingsError, LocalSettingsKind, Settings, SettingsKey, SettingsLocation, + SettingsSources, SettingsStore, }; pub use settings_ui_core::*; // Re-export the derive macro -pub use settings_ui_macros::SettingsUi; +pub use settings_ui_macros::{SettingsKey, 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 72df08d14fb61536d147b4d1fb8b9a2466f5f0aa..cc0ebf10cd004ce660572d7ea3a44ec945a47432 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -36,17 +36,19 @@ use crate::{ settings_ui_core::SettingsUi, update_value_in_json_text, }; -/// 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 SettingsKey: '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. const KEY: Option<&'static str>; const FALLBACK_KEY: Option<&'static str> = None; +} +/// 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 { /// The name of the keys in the [`FileContent`](Self::FileContent) that should /// always be written to a settings file, even if their value matches the default /// value. @@ -57,8 +59,19 @@ pub trait Settings: 'static + Send + Sync { const PRESERVED_KEYS: Option<&'static [&'static str]> = None; /// The type that is stored in an individual JSON file. - type FileContent: Clone + Default + Serialize + DeserializeOwned + JsonSchema + SettingsUi; - + type FileContent: Clone + + Default + + Serialize + + DeserializeOwned + + JsonSchema + + SettingsUi + + SettingsKey; + + /* + * let path = Settings + * + * + */ /// The logic for combining together values from one or more JSON files into the /// final value for this setting. /// @@ -71,7 +84,7 @@ pub trait Settings: 'static + Send + Sync { Self: Sized; fn missing_default() -> anyhow::Error { - anyhow::anyhow!("missing default") + anyhow::anyhow!("missing default for: {}", std::any::type_name::()) } /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known @@ -1393,7 +1406,7 @@ impl Debug for SettingsStore { impl AnySettingValue for SettingValue { fn key(&self) -> Option<&'static str> { - T::KEY + T::FileContent::KEY } fn setting_type_name(&self) -> &'static str { @@ -1445,16 +1458,21 @@ impl AnySettingValue for SettingValue { mut json: &Value, ) -> (Option<&'static str>, Result) { let mut key = None; - if let Some(k) = T::KEY { + if let Some(k) = T::FileContent::KEY { if let Some(value) = json.get(k) { json = value; key = Some(k); - } else if let Some((k, value)) = T::FALLBACK_KEY.and_then(|k| Some((k, json.get(k)?))) { + } else if let Some((k, value)) = + T::FileContent::FALLBACK_KEY.and_then(|k| Some((k, json.get(k)?))) + { json = value; key = Some(k); } else { let value = T::FileContent::default(); - return (T::KEY, Ok(DeserializedSetting(Box::new(value)))); + return ( + T::FileContent::KEY, + Ok(DeserializedSetting(Box::new(value))), + ); } } let value = serde_path_to_error::deserialize::<_, T::FileContent>(json) @@ -1498,6 +1516,7 @@ impl AnySettingValue for SettingValue { } } } + self.global_value .as_ref() .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name())) @@ -1570,7 +1589,7 @@ mod tests { // This is so the SettingsUi macro can still work properly use crate as settings; use serde_derive::Deserialize; - use settings_ui_macros::SettingsUi; + use settings_ui_macros::{SettingsKey, SettingsUi}; use unindent::Unindent; #[gpui::test] @@ -2120,7 +2139,8 @@ mod tests { staff: bool, } - #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi)] + #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] + #[settings_key(key = "user")] struct UserSettingsContent { name: Option, age: Option, @@ -2128,7 +2148,6 @@ mod tests { } impl Settings for UserSettings { - const KEY: Option<&'static str> = Some("user"); type FileContent = UserSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -2143,12 +2162,37 @@ mod tests { #[derive(Debug, Deserialize, PartialEq)] struct TurboSetting(bool); + #[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Default, + serde::Serialize, + serde::Deserialize, + SettingsUi, + SettingsKey, + JsonSchema, + )] + #[serde(default)] + #[settings_key(None)] + pub struct TurboSettingContent { + turbo: Option, + } + impl Settings for TurboSetting { - const KEY: Option<&'static str> = Some("turbo"); - type FileContent = bool; + type FileContent = TurboSettingContent; fn load(sources: SettingsSources, _: &mut App) -> Result { - sources.json_merge() + Ok(Self( + sources + .user + .or(sources.server) + .unwrap_or(sources.default) + .turbo + .unwrap_or_default(), + )) } fn import_from_vscode(_vscode: &VsCodeSettings, _current: &mut Self::FileContent) {} @@ -2162,15 +2206,14 @@ mod tests { key2: String, } - #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] + #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] + #[settings_key(None)] struct MultiKeySettingsJson { key1: Option, key2: Option, } impl Settings for MultiKeySettings { - const KEY: Option<&'static str> = None; - type FileContent = MultiKeySettingsJson; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -2200,15 +2243,16 @@ mod tests { Hour24, } - #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, SettingsUi)] + #[derive( + Clone, Default, Debug, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey, + )] + #[settings_key(key = "journal")] struct JournalSettingsJson { pub path: Option, pub hour_format: Option, } impl Settings for JournalSettings { - const KEY: Option<&'static str> = Some("journal"); - type FileContent = JournalSettingsJson; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -2288,7 +2332,10 @@ mod tests { ); } - #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] + #[derive( + Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey, + )] + #[settings_key(None)] struct LanguageSettings { #[serde(default)] languages: HashMap, @@ -2301,8 +2348,6 @@ mod tests { } impl Settings for LanguageSettings { - const KEY: Option<&'static str> = None; - type FileContent = Self; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/settings_ui_macros/src/settings_ui_macros.rs b/crates/settings_ui_macros/src/settings_ui_macros.rs index c98705d5f8d4de3f42b4756a32353123f5779fbc..1895083508a6a606f4dd9889529719aa12ea0b10 100644 --- a/crates/settings_ui_macros/src/settings_ui_macros.rs +++ b/crates/settings_ui_macros/src/settings_ui_macros.rs @@ -43,10 +43,9 @@ pub fn derive_settings_ui(input: proc_macro::TokenStream) -> proc_macro::TokenSt let lit: LitStr = meta.input.parse()?; group_name = Some(lit.value()); } else if meta.path.is_ident("path") { - // todo(settings_ui) try get KEY from Settings if possible, and once we do, - // if can get key from settings, throw error if path also passed + // todo(settings_ui) rely entirely on settings_key, remove path attribute if path_name.is_some() { - return Err(meta.error("Only one 'path' can be specified")); + return Err(meta.error("Only one 'path' can be specified, either with `path` in `settings_ui` or with `settings_key`")); } meta.input.parse::()?; let lit: LitStr = meta.input.parse()?; @@ -55,6 +54,12 @@ pub fn derive_settings_ui(input: proc_macro::TokenStream) -> proc_macro::TokenSt Ok(()) }) .unwrap_or_else(|e| panic!("in #[settings_ui] attribute: {}", e)); + } else if let Some(settings_key) = parse_setting_key_attr(attr) { + // todo(settings_ui) either remove fallback key or handle it here + if path_name.is_some() && settings_key.key.is_some() { + panic!("Both 'path' and 'settings_key' are specified. Must specify only one"); + } + path_name = settings_key.key; } } @@ -212,3 +217,126 @@ fn generate_ui_item_body( }, } } + +struct SettingsKey { + key: Option, + fallback_key: Option, +} + +fn parse_setting_key_attr(attr: &syn::Attribute) -> Option { + if !attr.path().is_ident("settings_key") { + return None; + } + + let mut settings_key = SettingsKey { + key: None, + fallback_key: None, + }; + + let mut found_none = false; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("None") { + found_none = true; + } else if meta.path.is_ident("key") { + if settings_key.key.is_some() { + return Err(meta.error("Only one 'group' path can be specified")); + } + meta.input.parse::()?; + let lit: LitStr = meta.input.parse()?; + settings_key.key = Some(lit.value()); + } else if meta.path.is_ident("fallback_key") { + if found_none { + return Err(meta.error("Cannot specify 'fallback_key' and 'None'")); + } + + if settings_key.fallback_key.is_some() { + return Err(meta.error("Only one 'fallback_key' can be specified")); + } + + meta.input.parse::()?; + let lit: LitStr = meta.input.parse()?; + settings_key.fallback_key = Some(lit.value()); + } + Ok(()) + }) + .unwrap_or_else(|e| panic!("in #[settings_key] attribute: {}", e)); + + if found_none && settings_key.fallback_key.is_some() { + panic!("in #[settings_key] attribute: Cannot specify 'None' and 'fallback_key'"); + } + if found_none && settings_key.key.is_some() { + panic!("in #[settings_key] attribute: Cannot specify 'None' and 'key'"); + } + if !found_none && settings_key.key.is_none() { + panic!("in #[settings_key] attribute: 'key' must be specified"); + } + + return Some(settings_key); +} + +#[proc_macro_derive(SettingsKey, attributes(settings_key))] +pub fn derive_settings_key(input: proc_macro::TokenStream) -> proc_macro::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 mut settings_key = Option::::None; + + for attr in &input.attrs { + let parsed_settings_key = parse_setting_key_attr(attr); + if parsed_settings_key.is_some() && settings_key.is_some() { + panic!("Duplicate #[settings_key] attribute"); + } + settings_key = parsed_settings_key; + } + + let Some(SettingsKey { key, fallback_key }) = settings_key else { + panic!("Missing #[settings_key] attribute"); + }; + + let key = key.map_or_else(|| quote! {None}, |key| quote! {Some(#key)}); + let fallback_key = fallback_key.map_or_else( + || quote! {None}, + |fallback_key| quote! {Some(#fallback_key)}, + ); + + let expanded = quote! { + impl #impl_generics settings::SettingsKey for #name #ty_generics #where_clause { + const KEY: Option<&'static str> = #key; + + const FALLBACK_KEY: Option<&'static str> = #fallback_key; + }; + }; + + proc_macro::TokenStream::from(expanded) +} + +#[cfg(test)] +mod tests { + use syn::{Attribute, parse_quote}; + + use super::*; + + #[test] + fn test_extract_key() { + let input: Attribute = parse_quote!( + #[settings_key(key = "my_key")] + ); + let settings_key = parse_setting_key_attr(&input).unwrap(); + assert_eq!(settings_key.key, Some("my_key".to_string())); + assert_eq!(settings_key.fallback_key, None); + } + + #[test] + fn test_empty_key() { + let input: Attribute = parse_quote!( + #[settings_key(None)] + ); + let settings_key = parse_setting_key_attr(&input).unwrap(); + assert_eq!(settings_key.key, None); + assert_eq!(settings_key.fallback_key, None); + } +} diff --git a/crates/terminal/src/terminal_settings.rs b/crates/terminal/src/terminal_settings.rs index c3051e089c68e3df0733c9e6cf7c8a42f56e742d..0ab92a0f26d35710da7fd0a2e88542a98c7affed 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, SettingsUi}; +use settings::{SettingsKey, SettingsSources, SettingsUi}; use std::path::PathBuf; use task::Shell; use theme::FontFamilyName; @@ -135,7 +135,8 @@ pub enum ActivateScript { Pyenv, } -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "terminal")] pub struct TerminalSettingsContent { /// What shell to use when opening a terminal. /// @@ -253,8 +254,6 @@ pub struct TerminalSettingsContent { } impl settings::Settings for TerminalSettings { - const KEY: Option<&'static str> = Some("terminal"); - type FileContent = TerminalSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 825176a2a0b5e35c60606d0922cef37fe91caea7..8409c60b22b03b8d917b84ae20229dc2db63fe4a 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -13,7 +13,7 @@ use gpui::{ use refineable::Refineable; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use settings::{ParameterizedJsonSchema, Settings, SettingsSources, SettingsUi}; +use settings::{ParameterizedJsonSchema, Settings, SettingsKey, SettingsSources, SettingsUi}; use std::sync::Arc; use util::ResultExt as _; use util::schemars::replace_subschema; @@ -366,7 +366,8 @@ impl IconThemeSelection { } /// Settings for rendering text in UI and text buffers. -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct ThemeSettingsContent { /// The default font size for text in the UI. #[serde(default)] @@ -818,8 +819,6 @@ fn clamp_font_weight(weight: f32) -> FontWeight { } impl settings::Settings for ThemeSettings { - const KEY: Option<&'static str> = None; - type FileContent = ThemeSettingsContent; fn load(sources: SettingsSources, cx: &mut App) -> Result { diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index 0dc301f7eef6789bf1c0a2ad51cb63dff77d0337..38e529098bd3e97a11ecefac684c1734302f4261 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Copy, Clone, Deserialize, Debug)] pub struct TitleBarSettings { @@ -14,8 +14,11 @@ pub struct TitleBarSettings { pub show_menus: bool, } -#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] -#[settings_ui(group = "Title Bar", path = "title_bar")] +#[derive( + Copy, Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey, +)] +#[settings_ui(group = "Title Bar")] +#[settings_key(key = "title_bar")] pub struct TitleBarSettingsContent { /// Whether to show the branch icon beside branch switcher in the title bar. /// @@ -48,8 +51,6 @@ pub struct TitleBarSettingsContent { } impl Settings for TitleBarSettings { - const KEY: Option<&'static str> = Some("title_bar"); - type FileContent = TitleBarSettingsContent; fn load(sources: SettingsSources, _: &mut gpui::App) -> anyhow::Result diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index ef9588acae181bad2b079d7c89458458bb851a64..4f1173a188b6d3113234c79f02a55d2c34cf12d9 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -68,7 +68,7 @@ impl VimTestContext { pub fn init_keybindings(enabled: bool, cx: &mut App) { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(enabled)); + store.update_user_settings::(cx, |s| s.vim_mode = Some(enabled)); }); let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure( "keymaps/default-macos.json", @@ -134,7 +134,7 @@ impl VimTestContext { pub fn enable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(true)); + store.update_user_settings::(cx, |s| s.vim_mode = Some(true)); }); }) } @@ -142,7 +142,7 @@ impl VimTestContext { pub fn disable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(false)); + store.update_user_settings::(cx, |s| s.vim_mode = Some(false)); }); }) } @@ -151,7 +151,7 @@ impl VimTestContext { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { store.update_user_settings::(cx, |s| { - *s = Some(true) + s.helix_mode = Some(true) }); }); }) diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 5a4ac425183e1843db7075c0f5054a16f82948f9..f4f8de2e7800732bb0a278bbc37928c58002ec7d 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, SettingsUi, update_settings_file}; +use settings::{ + Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi, update_settings_file, +}; use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals}; use std::{mem, ops::Range, sync::Arc}; use surrounds::SurroundsType; @@ -247,7 +249,7 @@ pub fn init(cx: &mut App) { let fs = workspace.app_state().fs.clone(); let currently_enabled = Vim::enabled(cx); update_settings_file::(fs, cx, move |setting, _| { - *setting = Some(!currently_enabled) + setting.vim_mode = Some(!currently_enabled) }) }); @@ -1785,7 +1787,8 @@ struct VimSettings { pub cursor_shape: CursorShapeSettings, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "vim")] struct VimSettingsContent { pub default_mode: Option, pub toggle_relative_line_numbers: Option, @@ -1824,8 +1827,6 @@ impl From for Mode { } impl Settings for VimSettings { - const KEY: Option<&'static str> = Some("vim"); - type FileContent = VimSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/vim_mode_setting/Cargo.toml b/crates/vim_mode_setting/Cargo.toml index fbb7f30b4c2a03aca48ad5db26283c33aedb885b..61d265b958b10fac700bd78577ac5fefb19b7d09 100644 --- a/crates/vim_mode_setting/Cargo.toml +++ b/crates/vim_mode_setting/Cargo.toml @@ -14,5 +14,7 @@ path = "src/vim_mode_setting.rs" [dependencies] anyhow.workspace = true gpui.workspace = true +schemars.workspace = true +serde.workspace = true settings.workspace = true workspace-hack.workspace = true diff --git a/crates/vim_mode_setting/src/vim_mode_setting.rs b/crates/vim_mode_setting/src/vim_mode_setting.rs index 7fb39ef4f6f10370f1a0fb2cf83dcb3a88b80d81..660520a307dbef1e73174aa5449417d766c04235 100644 --- a/crates/vim_mode_setting/src/vim_mode_setting.rs +++ b/crates/vim_mode_setting/src/vim_mode_setting.rs @@ -6,7 +6,8 @@ use anyhow::Result; use gpui::App; -use settings::{Settings, SettingsSources, SettingsUi}; +use schemars::JsonSchema; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; /// Initializes the `vim_mode_setting` crate. pub fn init(cx: &mut App) { @@ -14,25 +15,40 @@ pub fn init(cx: &mut App) { HelixModeSetting::register(cx); } -/// Whether or not to enable Vim mode. -/// -/// Default: false -#[derive(SettingsUi)] pub struct VimModeSetting(pub bool); -impl Settings for VimModeSetting { - const KEY: Option<&'static str> = Some("vim_mode"); +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Default, + serde::Serialize, + serde::Deserialize, + SettingsUi, + SettingsKey, + JsonSchema, +)] +#[settings_key(None)] +pub struct VimModeSettingContent { + /// Whether or not to enable Vim mode. + /// + /// Default: false + pub vim_mode: Option, +} - type FileContent = Option; +impl Settings for VimModeSetting { + type FileContent = VimModeSettingContent; fn load(sources: SettingsSources, _: &mut App) -> Result { Ok(Self( sources .user - .or(sources.server) - .copied() - .flatten() - .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), + .and_then(|mode| mode.vim_mode) + .or(sources.server.and_then(|mode| mode.vim_mode)) + .or(sources.default.vim_mode) + .ok_or_else(Self::missing_default)?, )) } @@ -41,25 +57,41 @@ impl Settings for VimModeSetting { } } -/// Whether or not to enable Helix mode. -/// -/// Default: false -#[derive(SettingsUi)] +#[derive(Debug)] pub struct HelixModeSetting(pub bool); -impl Settings for HelixModeSetting { - const KEY: Option<&'static str> = Some("helix_mode"); +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Debug, + Default, + serde::Serialize, + serde::Deserialize, + SettingsUi, + SettingsKey, + JsonSchema, +)] +#[settings_key(None)] +pub struct HelixModeSettingContent { + /// Whether or not to enable Helix mode. + /// + /// Default: false + pub helix_mode: Option, +} - type FileContent = Option; +impl Settings for HelixModeSetting { + type FileContent = HelixModeSettingContent; fn load(sources: SettingsSources, _: &mut App) -> Result { Ok(Self( sources .user - .or(sources.server) - .copied() - .flatten() - .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), + .and_then(|mode| mode.helix_mode) + .or(sources.server.and_then(|mode| mode.helix_mode)) + .or(sources.default.helix_mode) + .ok_or_else(Self::missing_default)?, )) } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index f37be0f154f736b021b0fcf5f29cf26074e3299f..23fbec470c4d2e305bf7b51679bbe56f6dfeaa95 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsLocation, SettingsSources, SettingsUi}; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -101,7 +101,8 @@ pub enum ActivateOnClose { LeftNeighbour, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "tabs")] pub struct ItemSettingsContent { /// Whether to show the Git file status on a tab item. /// @@ -130,7 +131,8 @@ pub struct ItemSettingsContent { show_close_button: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "preview_tabs")] pub struct PreviewTabsSettingsContent { /// Whether to show opened editors as preview tabs. /// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic. @@ -148,8 +150,6 @@ pub struct PreviewTabsSettingsContent { } impl Settings for ItemSettings { - const KEY: Option<&'static str> = Some("tabs"); - type FileContent = ItemSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -187,8 +187,6 @@ impl Settings for ItemSettings { } impl Settings for PreviewTabsSettings { - const KEY: Option<&'static str> = Some("preview_tabs"); - type FileContent = PreviewTabsSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index 1a7e548e4eda1f41e36c6ad0883cdd57be8828d7..8868f3190575ac4b861e0619732890f477d83b69 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; #[derive(Deserialize)] pub struct WorkspaceSettings { @@ -118,7 +118,8 @@ pub enum RestoreOnStartupBehavior { LastSession, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct WorkspaceSettingsContent { /// Active pane styling settings. pub active_pane_modifiers: Option, @@ -223,7 +224,8 @@ pub struct TabBarSettings { pub show_tab_bar_buttons: bool, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(key = "tab_bar")] pub struct TabBarSettingsContent { /// Whether or not to show the tab bar in the editor. /// @@ -282,8 +284,6 @@ pub struct CenteredLayoutSettings { } impl Settings for WorkspaceSettings { - const KEY: Option<&'static str> = None; - type FileContent = WorkspaceSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { @@ -373,8 +373,6 @@ impl Settings for WorkspaceSettings { } impl Settings for TabBarSettings { - const KEY: Option<&'static str> = Some("tab_bar"); - type FileContent = TabBarSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 6a8e2b5d89b0201b81f45817adb439fe85e24d91..41eb3ab6f6aa971d44009c1cbb00567a4f3448ea 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::{Settings, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; use util::paths::PathMatcher; #[derive(Clone, PartialEq, Eq)] @@ -31,7 +31,8 @@ impl WorktreeSettings { } } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] +#[settings_key(None)] pub struct WorktreeSettingsContent { /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides /// `file_scan_inclusions`. @@ -65,8 +66,6 @@ pub struct WorktreeSettingsContent { } impl Settings for WorktreeSettings { - const KEY: Option<&'static str> = None; - type FileContent = WorktreeSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { diff --git a/crates/zlog_settings/src/zlog_settings.rs b/crates/zlog_settings/src/zlog_settings.rs index 0cdc784489b47d89388edc9ed20aed6f3c2f9959..dd74fc574ff23dc78beca1feafeb34d874a68c22 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, SettingsUi}; +use settings::{Settings, SettingsKey, SettingsStore, SettingsUi}; pub fn init(cx: &mut App) { ZlogSettings::register(cx); @@ -15,15 +15,25 @@ pub fn init(cx: &mut App) { .detach(); } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)] +#[derive( + Clone, + Debug, + Default, + Serialize, + Deserialize, + PartialEq, + Eq, + JsonSchema, + SettingsUi, + SettingsKey, +)] +#[settings_key(key = "log")] pub struct ZlogSettings { #[serde(default, flatten)] pub scopes: std::collections::HashMap, } impl Settings for ZlogSettings { - const KEY: Option<&'static str> = Some("log"); - type FileContent = Self; fn load(sources: settings::SettingsSources, _: &mut App) -> Result