From 742d196f359573cdfeae010539c41c1e6e7f49c6 Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Tue, 16 Sep 2025 12:24:07 -0500 Subject: [PATCH] wip - project --- Cargo.lock | 1 + assets/settings/default.json | 13 + crates/agent_ui/src/slash_command_settings.rs | 24 +- crates/call/src/call_settings.rs | 37 +- crates/context_server/Cargo.toml | 3 +- crates/context_server/src/context_server.rs | 27 +- crates/project/src/project.rs | 36 +- crates/project/src/project_settings.rs | 648 +++++++++--------- crates/settings/src/settings_content.rs | 98 +-- .../settings/src/settings_content/project.rs | 384 +++++++++++ crates/settings/src/vscode_import.rs | 5 + 11 files changed, 829 insertions(+), 447 deletions(-) create mode 100644 crates/settings/src/settings_content/project.rs diff --git a/Cargo.lock b/Cargo.lock index 6e2845aabe589d688343a6216c2b13f7a92d3308..cdc3adb1f170eb23bc02abd511b9cc6c093fcc53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3596,6 +3596,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "settings", "smol", "tempfile", "url", diff --git a/assets/settings/default.json b/assets/settings/default.json index fc53e2c3e8cd07db8eb133c90809fd23f35be307..b544e83a0af27b42b2848d47f289c610b6f4d4f9 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1807,6 +1807,15 @@ "api_url": "https://api.mistral.ai/v1" } }, + "session": { + /// Whether or not to restore unsaved buffers on restart. + /// + /// If this is true, user won't be prompted whether to save/discard + /// dirty files when closing the application. + /// + /// Default: true + "restore_unsaved_buffers": true + }, // Zed's Prettier integration settings. // Allows to enable/disable formatting with Prettier // and configure default Prettier, used when no project-level Prettier installation is found. @@ -1845,6 +1854,10 @@ // } // } }, + // DAP Specific settings. + "dap": { + // Specify the DAP name as a key here. + }, // Common language server settings. "global_lsp_settings": { // Whether to show the LSP servers button in the status bar. diff --git a/crates/agent_ui/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs index 9580ffef0f317fbe726c57041fad4f0fa438e143..939459a6129a85dc6b1d8297ad3659f3a0982067 100644 --- a/crates/agent_ui/src/slash_command_settings.rs +++ b/crates/agent_ui/src/slash_command_settings.rs @@ -5,32 +5,30 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; /// Settings for slash commands. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, SettingsUi, SettingsKey)] -#[settings_key(key = "slash_commands")] +#[derive(Debug, Default, Clone)] pub struct SlashCommandSettings { /// Settings for the `/cargo-workspace` slash command. - #[serde(default)] pub cargo_workspace: CargoWorkspaceCommandSettings, } /// Settings for the `/cargo-workspace` slash command. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +#[derive(Debug, Default, Clone)] pub struct CargoWorkspaceCommandSettings { /// Whether `/cargo-workspace` is enabled. - #[serde(default)] pub enabled: bool, } impl Settings for SlashCommandSettings { - type FileContent = Self; + fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self { + Self { + cargo_workspace: CargoWorkspaceCommandSettings { + enabled: content.project.slash_commands.unwrap(), + }, + } + } - fn load(sources: SettingsSources, _cx: &mut App) -> Result { - SettingsSources::::json_merge_with( - [sources.default] - .into_iter() - .chain(sources.user) - .chain(sources.server), - ) + fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) { + todo!() } fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {} diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index c458c17e9fcd8788aae2661fbaaa0d45b117e10f..6fa10a77da327a2e02f15c2793e9d35b9a1f583e 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -3,6 +3,7 @@ use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; +use util::MergeFrom; #[derive(Deserialize, Debug)] pub struct CallSettings { @@ -10,27 +11,25 @@ pub struct CallSettings { pub share_on_join: bool, } -/// Configuration of voice calls in Zed. -#[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. - /// - /// Default: false - pub mute_on_join: Option, - - /// Whether your current project should be shared when joining an empty channel. - /// - /// Default: false - pub share_on_join: Option, -} - impl Settings for CallSettings { - type FileContent = CallSettingsContent; + fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self { + let call = content.call.unwrap(); + CallSettings { + mute_on_join: call.mute_on_join.unwrap(), + share_on_join: call.share_on_join.unwrap(), + } + } - fn load(sources: SettingsSources, _: &mut App) -> Result { - sources.json_merge() + fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) { + if let Some(call) = content.call.clone() { + self.mute_on_join.merge_from(call.mute_on_join); + self.share_on_join.merge_from(call.share_on_join); + } } - fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {} + fn import_from_vscode( + _vscode: &settings::VsCodeSettings, + _current: &settings::SettingsContent, + ) { + } } diff --git a/crates/context_server/Cargo.toml b/crates/context_server/Cargo.toml index 5e4f8369c45f0edb58efda1618bf8fe0aad55749..1c5745408041ae2f67e91ba0d9365188ab957d4e 100644 --- a/crates/context_server/Cargo.toml +++ b/crates/context_server/Cargo.toml @@ -25,8 +25,9 @@ net.workspace = true parking_lot.workspace = true postage.workspace = true schemars.workspace = true -serde.workspace = true serde_json.workspace = true +serde.workspace = true +settings.workspace = true smol.workspace = true tempfile.workspace = true url = { workspace = true, features = ["serde"] } diff --git a/crates/context_server/src/context_server.rs b/crates/context_server/src/context_server.rs index b126bb393784664692b5de39fee5ed7f66e9948a..92b1b2277239a876f35d9d430beb2276361d8f90 100644 --- a/crates/context_server/src/context_server.rs +++ b/crates/context_server/src/context_server.rs @@ -17,6 +17,7 @@ use gpui::AsyncApp; use parking_lot::RwLock; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +pub use settings::ContextServerCommand; use util::redact::should_redact; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -28,32 +29,6 @@ impl Display for ContextServerId { } } -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)] -pub struct ContextServerCommand { - #[serde(rename = "command")] - pub path: PathBuf, - pub args: Vec, - pub env: Option>, - /// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified. - pub timeout: Option, -} - -impl std::fmt::Debug for ContextServerCommand { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let filtered_env = self.env.as_ref().map(|env| { - env.iter() - .map(|(k, v)| (k, if should_redact(k) { "[REDACTED]" } else { v })) - .collect::>() - }); - - f.debug_struct("ContextServerCommand") - .field("path", &self.path) - .field("args", &self.args) - .field("env", &filtered_env) - .finish() - } -} - enum ContextServerTransport { Stdio(ContextServerCommand, Option), Custom(Arc), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b3b0daa4460df1e3a5a813252a9a159e4daf5003..e880144368160ae445db7c79141a2c016b84dd1a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -979,7 +979,7 @@ pub struct DisableAiSettings { impl settings::Settings for DisableAiSettings { fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self { Self { - disable_ai: content.project.disable_ai.unwrap(), + disable_ai: content.disable_ai.unwrap(), } } @@ -3246,21 +3246,7 @@ impl Project { let first_insertion = self.buffers_needing_diff.len() == 1; let settings = ProjectSettings::get_global(cx); - let delay = if let Some(delay) = settings.git.gutter_debounce { - delay - } else { - if first_insertion { - let this = cx.weak_entity(); - cx.defer(move |cx| { - if let Some(this) = this.upgrade() { - this.update(cx, |this, cx| { - this.recalculate_buffer_diffs(cx).detach(); - }); - } - }); - } - return; - }; + let delay = settings.git.gutter_debounce; const MIN_DELAY: u64 = 50; let delay = delay.max(MIN_DELAY); @@ -5648,11 +5634,11 @@ mod disable_ai_settings_tests { cx.update(|cx| { let mut store = SettingsStore::new(cx, &settings::test_settings()); store.register_setting::(cx); - fn ai_disabled(cx: &App) -> bool { - DisableAiSettings::get_global(cx).disable_ai - } // Test 1: Default is false (AI enabled) - assert!(!ai_disabled(cx), "Default should allow AI"); + assert!( + !DisableAiSettings::get_global(cx).disable_ai, + "Default should allow AI" + ); let disable_true = serde_json::json!({ "disable_ai": true @@ -5665,11 +5651,17 @@ mod disable_ai_settings_tests { store.set_user_settings(&disable_false, cx).unwrap(); store.set_global_settings(&disable_true, cx).unwrap(); - assert!(ai_disabled(cx), "Local false cannot override global true"); + assert!( + DisableAiSettings::get_global(cx).disable_ai, + "Local false cannot override global true" + ); store.set_global_settings(&disable_false, cx).unwrap(); store.set_user_settings(&disable_true, cx).unwrap(); - assert!(ai_disabled(cx), "Local false cannot override global true"); + assert!( + DisableAiSettings::get_global(cx).disable_ai, + "Local false cannot override global true" + ); }); } } diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index f6fc66dcc6fd60c49b77e0dcb8c0097c2a27588f..212d3262e6fff1907cf2bd6ac91af8300cc1c739 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -1,10 +1,14 @@ use anyhow::Context as _; +use clock::Global; use collections::HashMap; use context_server::ContextServerCommand; use dap::adapters::DebugAdapterName; use fs::Fs; use futures::StreamExt as _; -use gpui::{App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task}; +use gpui::{ + App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, SharedString, Subscription, + Task, +}; use lsp::LanguageServerName; use paths::{ EDITORCONFIG_NAME, local_debug_file_relative_path, local_settings_file_relative_path, @@ -13,7 +17,7 @@ use paths::{ }; use rpc::{ AnyProtoClient, TypedEnvelope, - proto::{self, FromProto, REMOTE_SERVER_PROJECT_ID, ToProto}, + proto::{self, FromProto, Message, REMOTE_SERVER_PROJECT_ID, ToProto}, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -28,7 +32,7 @@ use std::{ time::Duration, }; use task::{DebugTaskFile, TaskTemplates, VsCodeDebugTaskFile, VsCodeTaskFile}; -use util::{ResultExt, serde::default_true}; +use util::{MergeFrom as _, ResultExt, serde::default_true}; use worktree::{PathChange, UpdatedEntriesSet, Worktree, WorktreeId}; use crate::{ @@ -36,8 +40,7 @@ use crate::{ worktree_store::{WorktreeStore, WorktreeStoreEvent}, }; -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] -#[settings_key(None)] +#[derive(Debug, Clone)] pub struct ProjectSettings { /// Configuration for language servers. /// @@ -47,50 +50,49 @@ pub struct ProjectSettings { /// To override settings for a language, add an entry for that language server's /// name to the lsp value. /// Default: null - #[serde(default)] - pub lsp: HashMap, + // todo! should these hash map types be Map or Map + pub lsp: HashMap, /// Common language server settings. - #[serde(default)] pub global_lsp_settings: GlobalLspSettings, /// Configuration for Debugger-related features - #[serde(default)] pub dap: HashMap, /// Settings for context servers used for AI-related features. - #[serde(default)] pub context_servers: HashMap, ContextServerSettings>, /// Configuration for Diagnostics-related features. - #[serde(default)] pub diagnostics: DiagnosticsSettings, /// Configuration for Git-related features - #[serde(default)] pub git: GitSettings, /// Configuration for Node-related features - #[serde(default)] pub node: NodeBinarySettings, /// Configuration for how direnv configuration should be loaded - #[serde(default)] pub load_direnv: DirenvSettings, /// Configuration for session-related features - #[serde(default)] pub session: SessionSettings, } -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone, Default, PartialEq)] pub struct DapSettings { - pub binary: Option, - #[serde(default)] + pub binary: String, pub args: Vec, } +/// Common language server settings. +#[derive(Debug, Clone, PartialEq)] +pub struct GlobalLspSettings { + /// Whether to show the LSP servers button in the status bar. + /// + /// Default: `true` + pub button: bool, +} + #[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug)] #[serde(tag = "source", rename_all = "snake_case")] pub enum ContextServerSettings { @@ -114,14 +116,17 @@ pub enum ContextServerSettings { }, } -/// Common language server settings. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct GlobalLspSettings { - /// Whether to show the LSP servers button in the status bar. - /// - /// Default: `true` - #[serde(default = "default_true")] - pub button: bool, +impl From for ContextServerSettings { + fn from(value: settings::ContextServerSettingsContent) -> Self { + match value { + settings::ContextServerSettingsContent::Custom { enabled, command } => { + ContextServerSettings::Custom { enabled, command } + } + settings::ContextServerSettingsContent::Extension { enabled, settings } => { + ContextServerSettings::Extension { enabled, settings } + } + } + } } impl ContextServerSettings { @@ -147,140 +152,6 @@ impl ContextServerSettings { } } -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct NodeBinarySettings { - /// The path to the Node binary. - pub path: Option, - /// The path to the npm binary Zed should use (defaults to `.path/../npm`). - pub npm_path: Option, - /// If enabled, Zed will download its own copy of Node. - #[serde(default)] - pub ignore_system_version: bool, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum DirenvSettings { - /// Load direnv configuration through a shell hook - ShellHook, - /// Load direnv configuration directly using `direnv export json` - #[default] - Direct, -} - -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct DiagnosticsSettings { - /// Whether to show the project diagnostics button in the status bar. - pub button: bool, - - /// Whether or not to include warning diagnostics. - pub include_warnings: bool, - - /// Settings for using LSP pull diagnostics mechanism in Zed. - pub lsp_pull_diagnostics: LspPullDiagnosticsSettings, - - /// Settings for showing inline diagnostics. - pub inline: InlineDiagnosticsSettings, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct LspPullDiagnosticsSettings { - /// Whether to pull for diagnostics or not. - /// - /// Default: true - #[serde(default = "default_true")] - pub enabled: bool, - /// Minimum time to wait before pulling diagnostics from the language server(s). - /// 0 turns the debounce off. - /// - /// Default: 50 - #[serde(default = "default_lsp_diagnostics_pull_debounce_ms")] - pub debounce_ms: u64, -} - -fn default_lsp_diagnostics_pull_debounce_ms() -> u64 { - 50 -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct InlineDiagnosticsSettings { - /// Whether or not to show inline diagnostics - /// - /// Default: false - pub enabled: bool, - /// Whether to only show the inline diagnostics after a delay after the - /// last editor event. - /// - /// Default: 150 - #[serde(default = "default_inline_diagnostics_update_debounce_ms")] - pub update_debounce_ms: u64, - /// The amount of padding between the end of the source line and the start - /// of the inline diagnostic in units of columns. - /// - /// Default: 4 - #[serde(default = "default_inline_diagnostics_padding")] - pub padding: u32, - /// The minimum column to display inline diagnostics. This setting can be - /// used to horizontally align inline diagnostics at some position. Lines - /// longer than this value will still push diagnostics further to the right. - /// - /// Default: 0 - pub min_column: u32, - - pub max_severity: Option, -} - -fn default_inline_diagnostics_update_debounce_ms() -> u64 { - 150 -} - -fn default_inline_diagnostics_padding() -> u32 { - 4 -} - -impl Default for DiagnosticsSettings { - fn default() -> Self { - Self { - button: true, - include_warnings: true, - lsp_pull_diagnostics: LspPullDiagnosticsSettings::default(), - inline: InlineDiagnosticsSettings::default(), - } - } -} - -impl Default for LspPullDiagnosticsSettings { - fn default() -> Self { - Self { - enabled: true, - debounce_ms: default_lsp_diagnostics_pull_debounce_ms(), - } - } -} - -impl Default for InlineDiagnosticsSettings { - fn default() -> Self { - Self { - enabled: false, - update_debounce_ms: default_inline_diagnostics_update_debounce_ms(), - padding: default_inline_diagnostics_padding(), - min_column: 0, - max_severity: None, - } - } -} - -impl Default for GlobalLspSettings { - fn default() -> Self { - Self { - button: default_true(), - } - } -} - #[derive( Clone, Copy, @@ -301,7 +172,6 @@ pub enum DiagnosticSeverity { Error, Warning, Info, - #[serde(alias = "all")] Hint, } @@ -317,6 +187,18 @@ impl DiagnosticSeverity { } } +impl From for DiagnosticSeverity { + fn from(severity: settings::DiagnosticSeverityContent) -> Self { + match severity { + settings::DiagnosticSeverityContent::Off => DiagnosticSeverity::Off, + settings::DiagnosticSeverityContent::Error => DiagnosticSeverity::Error, + settings::DiagnosticSeverityContent::Warning => DiagnosticSeverity::Warning, + settings::DiagnosticSeverityContent::Info => DiagnosticSeverity::Info, + settings::DiagnosticSeverityContent::Hint => DiagnosticSeverity::Hint, + } + } +} + /// Determines the severity of the diagnostic that should be moved to. #[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -390,126 +272,90 @@ impl GoToDiagnosticSeverityFilter { } } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Debug)] pub struct GitSettings { /// Whether or not to show the git gutter. /// /// Default: tracked_files - pub git_gutter: Option, + pub git_gutter: settings::GitGutterSetting, /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. /// /// Default: null - pub gutter_debounce: Option, + pub gutter_debounce: u64, /// Whether or not to show git blame data inline in /// the currently focused line. /// /// Default: on - pub inline_blame: Option, + pub inline_blame: InlineBlameSettings, /// Which information to show in the branch picker. /// /// Default: on - pub branch_picker: Option, + pub branch_picker: BranchPickerSettings, /// How hunks are displayed visually in the editor. /// /// Default: staged_hollow - pub hunk_style: Option, -} - -impl GitSettings { - pub fn inline_blame_enabled(&self) -> bool { - #[allow(unknown_lints, clippy::manual_unwrap_or_default)] - match self.inline_blame { - Some(InlineBlameSettings { enabled, .. }) => enabled, - _ => false, - } - } - - pub fn inline_blame_delay(&self) -> Option { - match self.inline_blame { - Some(InlineBlameSettings { delay_ms, .. }) if delay_ms > 0 => { - Some(Duration::from_millis(delay_ms)) - } - _ => None, - } - } - - pub fn show_inline_commit_summary(&self) -> bool { - match self.inline_blame { - Some(InlineBlameSettings { - show_commit_summary, - .. - }) => show_commit_summary, - _ => false, - } - } -} - -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum GitHunkStyleSetting { - /// Show unstaged hunks with a filled background and staged hunks hollow. - #[default] - StagedHollow, - /// Show unstaged hunks hollow and staged hunks with a filled background. - UnstagedHollow, -} - -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum GitGutterSetting { - /// Show git gutter in tracked files. - #[default] - TrackedFiles, - /// Hide git gutter - Hide, + pub hunk_style: settings::GitHunkStyleSetting, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[derive(Clone, Copy, Debug)] pub struct InlineBlameSettings { /// Whether or not to show git blame data inline in /// the currently focused line. /// /// Default: true - #[serde(default = "default_true")] pub enabled: bool, /// Whether to only show the inline blame information /// after a delay once the cursor stops moving. /// /// Default: 0 - #[serde(default)] - pub delay_ms: u64, + pub delay_ms: std::time::Duration, /// The amount of padding between the end of the source line and the start /// of the inline blame in units of columns. /// /// Default: 7 - #[serde(default = "default_inline_blame_padding")] pub padding: u32, /// The minimum column number to show the inline blame information at /// /// Default: 0 - #[serde(default)] pub min_column: u32, /// Whether to show commit summary as part of the inline blame. /// /// Default: false - #[serde(default)] pub show_commit_summary: bool, } -fn default_inline_blame_padding() -> u32 { - 7 -} +impl GitSettings { + // todo! remove + pub fn inline_blame_enabled(&self) -> bool { + self.inline_blame.enabled + // #[allow(unknown_lints, clippy::manual_unwrap_or_default)] + // match self.inline_blame { + // Some(InlineBlameSettings { enabled, .. }) => enabled, + // _ => false, + // } + } -impl Default for InlineBlameSettings { - fn default() -> Self { - Self { - enabled: true, - delay_ms: 0, - padding: default_inline_blame_padding(), - min_column: 0, - show_commit_summary: false, - } + // todo! remove + pub fn inline_blame_delay(&self) -> Option { + Some(self.inline_blame.delay_ms) + // match self.inline_blame { + // Some(InlineBlameSettings { delay_ms, .. }) if delay_ms > 0 => { + // Some(Duration::from_millis(delay_ms)) + // } + // _ => None, + // } + } + + // todo! remove + pub fn show_inline_commit_summary(&self) -> bool { + self.inline_blame.show_commit_summary + // match self.inline_blame { + // Some(InlineBlameSettings { + // show_commit_summary, + // .. + // }) => show_commit_summary, + // _ => false, + // } } } @@ -531,93 +377,263 @@ impl Default for BranchPickerSettings { } } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] -pub struct BinarySettings { - pub path: Option, - pub arguments: Option>, - pub env: Option>, - pub ignore_system_version: Option, -} +#[derive(Clone, Debug)] +pub struct DiagnosticsSettings { + /// Whether to show the project diagnostics button in the status bar. + pub button: bool, -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] -pub struct FetchSettings { - // Whether to consider pre-releases for fetching - pub pre_release: Option, -} + /// Whether or not to include warning diagnostics. + pub include_warnings: bool, -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] -#[serde(rename_all = "snake_case")] -pub struct LspSettings { - pub binary: Option, - pub initialization_options: Option, - pub settings: Option, - /// If the server supports sending tasks over LSP extensions, - /// this setting can be used to enable or disable them in Zed. - /// Default: true - #[serde(default = "default_true")] - pub enable_lsp_tasks: bool, - pub fetch: Option, -} + /// Settings for using LSP pull diagnostics mechanism in Zed. + pub lsp_pull_diagnostics: LspPullDiagnosticsSettings, -impl Default for LspSettings { - fn default() -> Self { - Self { - binary: None, - initialization_options: None, - settings: None, - enable_lsp_tasks: true, - fetch: None, - } - } + /// Settings for showing inline diagnostics. + pub inline: InlineDiagnosticsSettings, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct SessionSettings { - /// Whether or not to restore unsaved buffers on restart. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct InlineDiagnosticsSettings { + /// Whether or not to show inline diagnostics /// - /// If this is true, user won't be prompted whether to save/discard - /// dirty files when closing the application. + /// Default: false + pub enabled: bool, + /// Whether to only show the inline diagnostics after a delay after the + /// last editor event. + /// + /// Default: 150 + pub update_debounce_ms: u64, + /// The amount of padding between the end of the source line and the start + /// of the inline diagnostic in units of columns. + /// + /// Default: 4 + pub padding: u32, + /// The minimum column to display inline diagnostics. This setting can be + /// used to horizontally align inline diagnostics at some position. Lines + /// longer than this value will still push diagnostics further to the right. + /// + /// Default: 0 + pub min_column: u32, + + pub max_severity: DiagnosticSeverity, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct LspPullDiagnosticsSettings { + /// Whether to pull for diagnostics or not. /// /// Default: true - pub restore_unsaved_buffers: bool, + pub enabled: bool, + /// Minimum time to wait before pulling diagnostics from the language server(s). + /// 0 turns the debounce off. + /// + /// Default: 50 + pub debounce_ms: u64, } -impl Default for SessionSettings { - fn default() -> Self { +impl Settings for ProjectSettings { + fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self { + let project = &content.project.clone(); + let diagnostics = content.diagnostics.as_ref().unwrap(); + let lsp_pull_diagnostics = diagnostics.lsp_pull_diagnostics.as_ref().unwrap(); + let inline_diagnostics = diagnostics.inline.as_ref().unwrap(); + + let git = content.git.as_ref().unwrap(); + let git_settings = GitSettings { + git_gutter: git.git_gutter.unwrap(), + gutter_debounce: git.gutter_debounce.unwrap(), + inline_blame: { + let inline = git.inline_blame.unwrap(); + InlineBlameSettings { + enabled: inline.enabled.unwrap(), + delay_ms: std::time::Duration::from_millis(inline.delay_ms.unwrap()), + padding: inline.padding.unwrap(), + min_column: inline.min_column.unwrap(), + show_commit_summary: inline.show_commit_summary.unwrap(), + } + }, + branch_picker: { + let branch_picker = git.branch_picker.unwrap(); + BranchPickerSettings { + show_author_name: branch_picker.show_author_name.unwrap(), + } + }, + hunk_style: git.hunk_style.unwrap(), + }; Self { - restore_unsaved_buffers: true, + context_servers: project + .context_servers + .clone() + .into_iter() + .map(|(key, value)| (key, value.into())) + .collect(), + lsp: project + .lsp + .clone() + .into_iter() + .map(|(key, value)| (LanguageServerName(key.into()), value.into())) + .collect(), + global_lsp_settings: GlobalLspSettings { + button: content.global_lsp_settings.unwrap().button.unwrap(), + }, + dap: project + .dap + .clone() + .into_iter() + .map(|(key, value)| { + ( + DebugAdapterName(key.into()), + DapSettings { + binary: value.binary.unwrap(), + args: value.args, + }, + ) + }) + .collect(), + diagnostics: DiagnosticsSettings { + button: diagnostics.button.unwrap(), + include_warnings: diagnostics.include_warnings.unwrap(), + lsp_pull_diagnostics: LspPullDiagnosticsSettings { + enabled: lsp_pull_diagnostics.enabled.unwrap(), + debounce_ms: lsp_pull_diagnostics.debounce_ms.unwrap(), + }, + inline: InlineDiagnosticsSettings { + enabled: inline_diagnostics.enabled.unwrap(), + update_debounce_ms: inline_diagnostics.update_debounce_ms.unwrap(), + padding: inline_diagnostics.padding.unwrap(), + min_column: inline_diagnostics.min_column.unwrap(), + max_severity: inline_diagnostics.max_severity.unwrap().into(), + }, + }, + git: git_settings, + node: content.node.clone(), + load_direnv: project.load_direnv.unwrap(), + session: content.session.clone(), } } -} -impl Settings for ProjectSettings { - type FileContent = Self; + fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) { + let project = &content.project; + self.context_servers.extend( + project + .context_servers + .clone() + .into_iter() + .map(|(key, value)| (key, value.into())), + ); + self.dap + .extend(project.dap.clone().into_iter().filter_map(|(key, value)| { + Some(( + DebugAdapterName(key.into()), + DapSettings { + binary: value.binary?, + args: value.args, + }, + )) + })); + if let Some(diagnostics) = content.diagnostics.as_ref() { + if let Some(inline) = &diagnostics.inline { + self.diagnostics.inline.enabled.merge_from(&inline.enabled); + self.diagnostics + .inline + .update_debounce_ms + .merge_from(&inline.update_debounce_ms); + self.diagnostics.inline.padding.merge_from(&inline.padding); + self.diagnostics + .inline + .min_column + .merge_from(&inline.min_column); + self.diagnostics + .inline + .max_severity + .merge_from(&inline.max_severity.map(Into::into)); + } - fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { - sources.json_merge() + self.diagnostics.button.merge_from(&diagnostics.button); + self.diagnostics + .include_warnings + .merge_from(&diagnostics.include_warnings); + if let Some(pull_diagnostics) = &diagnostics.lsp_pull_diagnostics { + self.diagnostics + .lsp_pull_diagnostics + .enabled + .merge_from(&pull_diagnostics.enabled); + self.diagnostics + .lsp_pull_diagnostics + .debounce_ms + .merge_from(&pull_diagnostics.debounce_ms); + } + } + if let Some(git) = content.git.as_ref() { + if let Some(branch_picker) = git.branch_picker.as_ref() { + self.git + .branch_picker + .show_author_name + .merge_from(&branch_picker.show_author_name); + } + if let Some(inline_blame) = git.inline_blame.as_ref() { + self.git + .inline_blame + .enabled + .merge_from(&inline_blame.enabled); + self.git + .inline_blame + .delay_ms + .merge_from(&inline_blame.delay_ms.map(std::time::Duration::from_millis)); + self.git + .inline_blame + .padding + .merge_from(&inline_blame.padding); + self.git + .inline_blame + .min_column + .merge_from(&inline_blame.min_column); + self.git + .inline_blame + .show_commit_summary + .merge_from(&inline_blame.show_commit_summary); + } + self.git.git_gutter.merge_from(&git.git_gutter); + self.git.hunk_style.merge_from(&git.hunk_style); + self.git.gutter_debounce.merge_from(&git.gutter_debounce); + } + self.global_lsp_settings = content.global_lsp_settings.clone(); + self.load_direnv = content.project.load_direnv.clone(); + self.lsp.extend( + content + .project + .lsp + .clone() + .into_iter() + .map(|(key, value)| (key, lsp_settings)), + ); } - fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) { + fn import_from_vscode( + vscode: &settings::VsCodeSettings, + current: &mut settings::SettingsContent, + ) { // this just sets the binary name instead of a full path so it relies on path lookup // resolving to the one you want - vscode.enum_setting( - "npm.packageManager", - &mut current.node.npm_path, - |s| match s { - v @ ("npm" | "yarn" | "bun" | "pnpm") => Some(v.to_owned()), - _ => None, - }, - ); + let npm_path = vscode.read_enum_setting("npm.packageManager", |s| match s { + v @ ("npm" | "yarn" | "bun" | "pnpm") => Some(v.to_owned()), + _ => None, + }); + if npm_path.is_some() { + current.node.get_or_insert_default().npm_path = npm_path; + } if let Some(b) = vscode.read_bool("git.blame.editorDecoration.enabled") { - if let Some(blame) = current.git.inline_blame.as_mut() { - blame.enabled = b - } else { - current.git.inline_blame = Some(InlineBlameSettings { - enabled: b, - ..Default::default() - }) - } + // todo! get_or_insert_default is risky considering defaults probably don't correspond + // to default.json, + // probably need to pass in "defaults" for this type as additional arg, and + // use unwrap on those keys + current + .git + .get_or_insert_default() + .inline_blame + .get_or_insert_default() + .enabled = Some(b); } #[derive(Deserialize)] @@ -627,29 +643,27 @@ impl Settings for ProjectSettings { env: Option>, // note: we don't support envFile and type } - impl From for ContextServerCommand { - fn from(cmd: VsCodeContextServerCommand) -> Self { - Self { - path: cmd.command, - args: cmd.args.unwrap_or_default(), - env: cmd.env, - timeout: None, - } - } - } if let Some(mcp) = vscode.read_value("mcp").and_then(|v| v.as_object()) { current + .project .context_servers .extend(mcp.iter().filter_map(|(k, v)| { Some(( k.clone().into(), - ContextServerSettings::Custom { + settings::ContextServerSettingsContent::Custom { enabled: true, command: serde_json::from_value::( v.clone(), ) - .ok()? - .into(), + .ok() + .map(|cmd| { + settings::ContextServerCommand { + path: cmd.command, + args: cmd.args.unwrap_or_default(), + env: cmd.env, + timeout: None, + } + })?, }, )) })); @@ -740,7 +754,7 @@ impl SettingsObserver { if Some(new_settings) != user_settings.as_ref() { if let Some(new_settings_string) = serde_json::to_string(new_settings).ok() { - user_settings = Some(new_settings.clone()); + user_settings = new_settings.clone(); upstream_client .send(proto::UpdateUserSettings { project_id: REMOTE_SERVER_PROJECT_ID, diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index ca1e20a30aa8373e84cf618ad03d1966e8a7865c..3945db5c9ec391273a464b32ee055599ffcf30c2 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -1,19 +1,21 @@ mod agent; mod language; +mod project; mod terminal; mod theme; pub use agent::*; pub use language::*; +pub use project::*; pub use terminal::*; pub use theme::*; -use std::env; - use collections::HashMap; use gpui::{App, SharedString}; use release_channel::ReleaseChannel; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::env; +pub use util::serde::default_true; use crate::ActiveSettingsProfileName; @@ -37,9 +39,18 @@ pub struct SettingsContent { pub debugger: Option, + /// Configuration for Diagnostics-related features. + pub diagnostics: Option, + + /// Configuration for Git-related features + pub git: Option, + /// The list of custom Git hosting providers. pub git_hosting_providers: Option>, + /// Common language server settings. + pub global_lsp_settings: Option, + /// Whether or not to enable Helix mode. /// /// Default: false @@ -50,11 +61,16 @@ pub struct SettingsContent { /// Example: {"log": {"client": "warn"}} pub log: Option>, + /// Configuration for Node-related features + pub node: Option, + pub proxy: Option, /// The URL of the Zed server to connect to. pub server_url: Option, + /// Configuration for session-related features + pub session: Option, /// Control what info is collected by Zed. pub telemetry: Option, @@ -67,6 +83,9 @@ pub struct SettingsContent { /// /// Default: false pub vim_mode: Option, + + // Settings related to calls in Zed + pub calls: Option, } impl SettingsContent { @@ -147,17 +166,6 @@ pub enum BaseKeymapContent { None, } -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct ProjectSettingsContent { - #[serde(flatten)] - pub all_languages: AllLanguageSettingsContent, - - #[serde(flatten)] - pub worktree: WorktreeSettingsContent, - - pub disable_ai: Option, -} - #[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct TitleBarSettingsContent { /// Controls when the title bar is visible: "always" | "never" | "hide_in_full_screen". @@ -246,42 +254,6 @@ pub enum GitHostingProviderKind { Bitbucket, } -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct WorktreeSettingsContent { - /// The displayed name of this project. If not set, the root directory name - /// will be displayed. - /// - /// Default: none - pub project_name: Option, - - /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides - /// `file_scan_inclusions`. - /// - /// Default: [ - /// "**/.git", - /// "**/.svn", - /// "**/.hg", - /// "**/.jj", - /// "**/CVS", - /// "**/.DS_Store", - /// "**/Thumbs.db", - /// "**/.classpath", - /// "**/.settings" - /// ] - pub file_scan_exclusions: Option>, - - /// Always include files that match these globs when scanning for files, even if they're - /// ignored by git. This setting is overridden by `file_scan_exclusions`. - /// Default: [ - /// ".env*", - /// "docker-compose.*.yml", - /// ] - pub file_scan_inclusions: Option>, - - /// Treat the files matching these globs as `.env` files. - /// Default: [ "**/.env*" ] - pub private_files: Option>, -} /// Control what info is collected by Zed. #[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug)] pub struct TelemetrySettingsContent { @@ -348,3 +320,31 @@ pub enum DockPosition { Bottom, Right, } + +/// Settings for slash commands. +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +pub struct SlashCommandSettings { + /// Settings for the `/cargo-workspace` slash command. + pub cargo_workspace: Option, +} + +/// Settings for the `/cargo-workspace` slash command. +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +pub struct CargoWorkspaceCommandSettings { + /// Whether `/cargo-workspace` is enabled. + pub enabled: Option, +} + +/// Configuration of voice calls in Zed. +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct CallSettingsContent { + /// Whether the microphone should be muted when joining a channel or a call. + /// + /// Default: false + pub mute_on_join: Option, + + /// Whether your current project should be shared when joining an empty channel. + /// + /// Default: false + pub share_on_join: Option, +} diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs new file mode 100644 index 0000000000000000000000000000000000000000..b35c829085135ab48909b763e313ad0919c0f9f5 --- /dev/null +++ b/crates/settings/src/settings_content/project.rs @@ -0,0 +1,384 @@ +use std::{path::PathBuf, sync::Arc}; + +use collections::{BTreeMap, HashMap}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use util::serde::default_true; + +use crate::AllLanguageSettingsContent; + +#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ProjectSettingsContent { + #[serde(flatten)] + pub all_languages: AllLanguageSettingsContent, + + #[serde(flatten)] + pub worktree: WorktreeSettingsContent, + + /// Configuration for language servers. + /// + /// The following settings can be overridden for specific language servers: + /// - initialization_options + /// + /// To override settings for a language, add an entry for that language server's + /// name to the lsp value. + /// Default: null + #[serde(default)] + pub lsp: HashMap, LspSettingsContent>, + + /// Configuration for Debugger-related features + #[serde(default)] + pub dap: HashMap, DapSettingsContent>, + + /// Settings for context servers used for AI-related features. + #[serde(default)] + pub context_servers: HashMap, ContextServerSettingsContent>, + + /// Configuration for how direnv configuration should be loaded + pub load_direnv: Option, + + /// Settings for slash commands. + pub slash_commands: Option, +} + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct WorktreeSettingsContent { + /// The displayed name of this project. If not set, the root directory name + /// will be displayed. + /// + /// Default: none + pub project_name: Option, + + /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides + /// `file_scan_inclusions`. + /// + /// Default: [ + /// "**/.git", + /// "**/.svn", + /// "**/.hg", + /// "**/.jj", + /// "**/CVS", + /// "**/.DS_Store", + /// "**/Thumbs.db", + /// "**/.classpath", + /// "**/.settings" + /// ] + pub file_scan_exclusions: Option>, + + /// Always include files that match these globs when scanning for files, even if they're + /// ignored by git. This setting is overridden by `file_scan_exclusions`. + /// Default: [ + /// ".env*", + /// "docker-compose.*.yml", + /// ] + pub file_scan_inclusions: Option>, + + /// Treat the files matching these globs as `.env` files. + /// Default: [ "**/.env*" ] + pub private_files: Option>, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] +#[serde(rename_all = "snake_case")] +pub struct LspSettingsContent { + pub binary: Option, + pub initialization_options: Option, + pub settings: Option, + /// If the server supports sending tasks over LSP extensions, + /// this setting can be used to enable or disable them in Zed. + /// Default: true + #[serde(default = "default_true")] + pub enable_lsp_tasks: bool, + pub fetch: Option, +} + +impl Default for LspSettingsContent { + fn default() -> Self { + Self { + binary: None, + initialization_options: None, + settings: None, + enable_lsp_tasks: true, + fetch: None, + } + } +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] +pub struct BinarySettings { + pub path: Option, + pub arguments: Option>, + pub env: Option>, + pub ignore_system_version: Option, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] +pub struct FetchSettings { + // Whether to consider pre-releases for fetching + pub pre_release: Option, +} + +/// Common language server settings. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct GlobalLspSettingsContent { + /// Whether to show the LSP servers button in the status bar. + /// + /// Default: `true` + pub button: Option, +} + +// todo! binary is actually just required, shouldn't be an option +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct DapSettingsContent { + pub binary: Option, + #[serde(default)] + pub args: Vec, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct SessionSettings { + /// Whether or not to restore unsaved buffers on restart. + /// + /// If this is true, user won't be prompted whether to save/discard + /// dirty files when closing the application. + /// + /// Default: true + pub restore_unsaved_buffers: bool, +} + +impl Default for SessionSettings { + fn default() -> Self { + Self { + restore_unsaved_buffers: true, + } + } +} + +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug)] +#[serde(tag = "source", rename_all = "snake_case")] +pub enum ContextServerSettingsContent { + Custom { + /// Whether the context server is enabled. + #[serde(default = "default_true")] + enabled: bool, + + #[serde(flatten)] + command: ContextServerCommand, + }, + Extension { + /// Whether the context server is enabled. + #[serde(default = "default_true")] + enabled: bool, + /// The settings for this context server specified by the extension. + /// + /// Consult the documentation for the context server to see what settings + /// are supported. + settings: serde_json::Value, + }, +} + +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)] +pub struct ContextServerCommand { + #[serde(rename = "command")] + pub path: PathBuf, + pub args: Vec, + pub env: Option>, + /// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified. + pub timeout: Option, +} + +impl std::fmt::Debug for ContextServerCommand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let filtered_env = self.env.as_ref().map(|env| { + env.iter() + .map(|(k, v)| { + ( + k, + if util::redact::should_redact(k) { + "[REDACTED]" + } else { + v + }, + ) + }) + .collect::>() + }); + + f.debug_struct("ContextServerCommand") + .field("path", &self.path) + .field("args", &self.args) + .field("env", &filtered_env) + .finish() + } +} + +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct GitSettings { + /// Whether or not to show the git gutter. + /// + /// Default: tracked_files + pub git_gutter: Option, + /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. + /// + /// Default: null + pub gutter_debounce: Option, + /// Whether or not to show git blame data inline in + /// the currently focused line. + /// + /// Default: on + pub inline_blame: Option, + /// Which information to show in the branch picker. + /// + /// Default: on + pub branch_picker: Option, + /// How hunks are displayed visually in the editor. + /// + /// Default: staged_hollow + pub hunk_style: Option, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitGutterSetting { + /// Show git gutter in tracked files. + #[default] + TrackedFiles, + /// Hide git gutter + Hide, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct InlineBlameSettings { + /// Whether or not to show git blame data inline in + /// the currently focused line. + /// + /// Default: true + pub enabled: Option, + /// Whether to only show the inline blame information + /// after a delay once the cursor stops moving. + /// + /// Default: 0 + pub delay_ms: Option, + /// The amount of padding between the end of the source line and the start + /// of the inline blame in units of columns. + /// + /// Default: 7 + pub padding: Option, + /// The minimum column number to show the inline blame information at + /// + /// Default: 0 + pub min_column: Option, + /// Whether to show commit summary as part of the inline blame. + /// + /// Default: false + pub show_commit_summary: Option, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct BranchPickerSettingsContent { + /// Whether to show author name as part of the commit information. + /// + /// Default: false + pub show_author_name: Option, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitHunkStyleSetting { + /// Show unstaged hunks with a filled background and staged hunks hollow. + #[default] + StagedHollow, + /// Show unstaged hunks hollow and staged hunks with a filled background. + UnstagedHollow, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct DiagnosticsSettingsContent { + /// Whether to show the project diagnostics button in the status bar. + pub button: Option, + + /// Whether or not to include warning diagnostics. + pub include_warnings: Option, + + /// Settings for using LSP pull diagnostics mechanism in Zed. + pub lsp_pull_diagnostics: Option, + + /// Settings for showing inline diagnostics. + pub inline: Option, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct LspPullDiagnosticsSettingsContent { + /// Whether to pull for diagnostics or not. + /// + /// Default: true + pub enabled: Option, + /// Minimum time to wait before pulling diagnostics from the language server(s). + /// 0 turns the debounce off. + /// + /// Default: 50 + pub debounce_ms: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema, Eq)] +pub struct InlineDiagnosticsSettingsContent { + /// Whether or not to show inline diagnostics + /// + /// Default: false + pub enabled: Option, + /// Whether to only show the inline diagnostics after a delay after the + /// last editor event. + /// + /// Default: 150 + pub update_debounce_ms: Option, + /// The amount of padding between the end of the source line and the start + /// of the inline diagnostic in units of columns. + /// + /// Default: 4 + pub padding: Option, + /// The minimum column to display inline diagnostics. This setting can be + /// used to horizontally align inline diagnostics at some position. Lines + /// longer than this value will still push diagnostics further to the right. + /// + /// Default: 0 + pub min_column: Option, + + pub max_severity: Option, +} + +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct NodeBinarySettings { + /// The path to the Node binary. + pub path: Option, + /// The path to the npm binary Zed should use (defaults to `.path/../npm`). + pub npm_path: Option, + /// If enabled, Zed will download its own copy of Node. + pub ignore_system_version: Option, +} + +#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DirenvSettings { + /// Load direnv configuration through a shell hook + ShellHook, + /// Load direnv configuration directly using `direnv export json` + #[default] + Direct, +} + +#[derive( + Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub enum DiagnosticSeverityContent { + // No diagnostics are shown. + Off, + Error, + Warning, + Info, + #[serde(alias = "all")] + Hint, +} diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index 5792dcc12bd687faf50c91530f027b1f90d7ff92..0c63b4ad304761d7d088a80baa9dbe281c2d266d 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -135,4 +135,9 @@ impl VsCodeSettings { *setting = Some(s) } } + + // todo! replace enum_setting + pub fn read_enum_setting(&self, key: &str, f: impl FnOnce(&str) -> Option) -> Option { + self.content.get(key).and_then(Value::as_str).and_then(f) + } }