Cargo.lock 🔗
@@ -17995,6 +17995,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
+ "schemars",
+ "serde",
"settings",
"workspace-hack",
]
Anthony Eid and Ben Kunkle created
This PR separates out the associated constant `KEY` from the `Settings`
trait into a new trait `SettingsKey`. This allows for the key trait to
be derived using attributes to specify the path so that the new
`SettingsUi` derive macro can use the same attributes to determine top
level settings paths thereby removing the need to duplicate the path in
both `Settings::KEY` and `#[settings_ui(path = "...")]`
Co-authored-by: Ben Kunkle <ben@zed.dev>
Release Notes:
- N/A
---------
Co-authored-by: Ben Kunkle <ben@zed.dev>
Cargo.lock | 2
crates/agent_servers/src/settings.rs | 7
crates/agent_settings/src/agent_settings.rs | 9
crates/agent_ui/src/slash_command_settings.rs | 7
crates/audio/src/audio_settings.rs | 9
crates/auto_update/src/auto_update.rs | 7
crates/call/src/call_settings.rs | 7
crates/client/src/client.rs | 17 -
crates/collab_ui/src/panel_settings.rs | 22 +-
crates/dap/src/debugger_settings.rs | 9
crates/editor/src/editor_settings.rs | 7
crates/extension_host/src/extension_settings.rs | 7
crates/extensions_ui/src/extensions_ui.rs | 2
crates/file_finder/src/file_finder_settings.rs | 7
crates/git_hosting_providers/src/settings.rs | 7
crates/git_ui/src/git_panel_settings.rs | 7
crates/go_to_line/src/cursor_position.rs | 7
crates/image_viewer/src/image_viewer_settings.rs | 7
crates/journal/src/journal.rs | 7
crates/language/src/language_settings.rs | 10
crates/language_models/src/settings.rs | 9
crates/onboarding/src/ai_setup_page.rs | 10
crates/onboarding/src/basics_page.rs | 2
crates/outline_panel/src/outline_panel_settings.rs | 7
crates/project/src/project.rs | 79 +++++---
crates/project/src/project_settings.rs | 9
crates/project_panel/src/project_panel_settings.rs | 7
crates/recent_projects/src/remote_connections.rs | 7
crates/repl/src/jupyter_settings.rs | 7
crates/settings/src/base_keymap_setting.rs | 19 +
crates/settings/src/settings.rs | 6
crates/settings/src/settings_store.rs | 97 +++++++--
crates/settings_ui_macros/src/settings_ui_macros.rs | 134 ++++++++++++++
crates/terminal/src/terminal_settings.rs | 7
crates/theme/src/settings.rs | 7
crates/title_bar/src/title_bar_settings.rs | 11
crates/vim/src/test/vim_test_context.rs | 8
crates/vim/src/vim.rs | 11
crates/vim_mode_setting/Cargo.toml | 2
crates/vim_mode_setting/src/vim_mode_setting.rs | 78 ++++++--
crates/workspace/src/item.rs | 12
crates/workspace/src/workspace_settings.rs | 12
crates/worktree/src/worktree_settings.rs | 7
crates/zlog_settings/src/zlog_settings.rs | 18 +
44 files changed, 474 insertions(+), 256 deletions(-)
@@ -17995,6 +17995,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
+ "schemars",
+ "serde",
"settings",
"workspace-hack",
]
@@ -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<BuiltinAgentServerSettings>,
pub claude: Option<CustomAgentServerSettings>,
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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;
@@ -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<Self::FileContent>, _cx: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _cx: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<String>,
}
@@ -107,8 +108,6 @@ pub struct ClientSettings {
}
impl Settings for ClientSettings {
- const KEY: Option<&'static str> = None;
-
type FileContent = ClientSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<String>,
}
@@ -133,8 +133,6 @@ pub struct ProxySettings {
}
impl Settings for ProxySettings {
- const KEY: Option<&'static str> = None;
-
type FileContent = ProxySettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<f32>,
}
-#[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<f32>,
}
-#[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(
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<Self::FileContent>, _cx: &mut App) -> Result<Self> {
@@ -1345,7 +1345,7 @@ impl ExtensionsPage {
this.update_settings::<VimModeSetting>(
selection,
cx,
- |setting, value| *setting = Some(value),
+ |setting, value| setting.vim_mode = Some(value),
);
}),
)),
@@ -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<bool>,
}
-#[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<Self::FileContent>, _: &mut gpui::App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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(
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<AnthropicSettingsContent>,
pub bedrock: Option<AmazonBedrockSettingsContent>,
@@ -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;
@@ -264,13 +264,9 @@ pub(crate) fn render_ai_setup_page(
);
let fs = <dyn Fs>::global(cx);
- update_settings_file::<DisableAiSettings>(
- fs,
- cx,
- move |ai_settings: &mut Option<bool>, _| {
- *ai_settings = Some(enabled);
- },
- );
+ update_settings_file::<DisableAiSettings>(fs, cx, move |ai_settings, _| {
+ ai_settings.disable_ai = Some(enabled);
+ });
},
)
.tab_index({
@@ -388,7 +388,7 @@ fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoEleme
}
};
update_settings_file::<VimModeSetting>(fs.clone(), cx, move |setting, _| {
- *setting = Some(vim_mode);
+ setting.vim_mode = Some(vim_mode);
});
telemetry::event!(
@@ -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<ShowIndentGuides>,
}
-#[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(
@@ -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<bool>,
+}
- type FileContent = Option<bool>;
+impl settings::Settings for DisableAiSettings {
+ type FileContent = DisableAiSettingContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
// 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<bool>) -> 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),
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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(
@@ -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<String>,
}
-#[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<Vec<SshConnection>>,
pub read_ssh_config: Option<bool>,
}
impl Settings for SshSettings {
- const KEY: Option<&'static str> = None;
-
type FileContent = RemoteSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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(
@@ -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<BaseKeymap>,
}
impl Settings for BaseKeymap {
- const KEY: Option<&'static str> = None;
-
type FileContent = BaseKeymapSetting;
fn load(
@@ -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)]
@@ -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::<Self>())
}
/// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known
@@ -1393,7 +1406,7 @@ impl Debug for SettingsStore {
impl<T: Settings> AnySettingValue for SettingValue<T> {
fn key(&self) -> Option<&'static str> {
- T::KEY
+ T::FileContent::KEY
}
fn setting_type_name(&self) -> &'static str {
@@ -1445,16 +1458,21 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
mut json: &Value,
) -> (Option<&'static str>, Result<DeserializedSetting>) {
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<T: Settings> AnySettingValue for SettingValue<T> {
}
}
}
+
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<String>,
age: Option<u32>,
@@ -2128,7 +2148,6 @@ mod tests {
}
impl Settings for UserSettings {
- const KEY: Option<&'static str> = Some("user");
type FileContent = UserSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<bool>,
+ }
+
impl Settings for TurboSetting {
- const KEY: Option<&'static str> = Some("turbo");
- type FileContent = bool;
+ type FileContent = TurboSettingContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
- 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<String>,
key2: Option<String>,
}
impl Settings for MultiKeySettings {
- const KEY: Option<&'static str> = None;
-
type FileContent = MultiKeySettingsJson;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<String>,
pub hour_format: Option<HourFormat>,
}
impl Settings for JournalSettings {
- const KEY: Option<&'static str> = Some("journal");
-
type FileContent = JournalSettingsJson;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<String, LanguageSettingEntry>,
@@ -2301,8 +2348,6 @@ mod tests {
}
impl Settings for LanguageSettings {
- const KEY: Option<&'static str> = None;
-
type FileContent = Self;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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::<Token![=]>()?;
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<String>,
+ fallback_key: Option<String>,
+}
+
+fn parse_setting_key_attr(attr: &syn::Attribute) -> Option<SettingsKey> {
+ 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::<Token![=]>()?;
+ 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::<Token![=]>()?;
+ 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::<SettingsKey>::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);
+ }
+}
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<Self::FileContent>, cx: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut gpui::App) -> anyhow::Result<Self>
@@ -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::<VimModeSetting>(cx, |s| *s = Some(enabled));
+ store.update_user_settings::<VimModeSetting>(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::<VimModeSetting>(cx, |s| *s = Some(true));
+ store.update_user_settings::<VimModeSetting>(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::<VimModeSetting>(cx, |s| *s = Some(false));
+ store.update_user_settings::<VimModeSetting>(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::<vim_mode_setting::HelixModeSetting>(cx, |s| {
- *s = Some(true)
+ s.helix_mode = Some(true)
});
});
})
@@ -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::<VimModeSetting>(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<ModeContent>,
pub toggle_relative_line_numbers: Option<bool>,
@@ -1824,8 +1827,6 @@ impl From<ModeContent> for Mode {
}
impl Settings for VimSettings {
- const KEY: Option<&'static str> = Some("vim");
-
type FileContent = VimSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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
@@ -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<bool>,
+}
- type FileContent = Option<bool>;
+impl Settings for VimModeSetting {
+ type FileContent = VimModeSettingContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
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<bool>,
+}
- type FileContent = Option<bool>;
+impl Settings for HelixModeSetting {
+ type FileContent = HelixModeSettingContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
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)?,
))
}
@@ -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<ShowCloseButton>,
}
-#[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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<ActivePanelModifiers>,
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> Result<Self> {
@@ -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<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
@@ -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<String, String>,
}
impl Settings for ZlogSettings {
- const KEY: Option<&'static str> = Some("log");
-
type FileContent = Self;
fn load(sources: settings::SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self>