settings ui: Create settings key trait (#37489)
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>
This commit is contained in:
co-authored by
Ben Kunkle
parent
223fda2fe2
commit
5f03202b5c
Generated
+2
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user