1use std::{path::PathBuf, sync::Arc};
  2
  3use collections::{BTreeMap, HashMap};
  4use schemars::JsonSchema;
  5use serde::{Deserialize, Serialize};
  6use serde_with::skip_serializing_none;
  7use settings_macros::MergeFrom;
  8use util::serde::default_true;
  9
 10use crate::{
 11    AllLanguageSettingsContent, DelayMs, ExtendingVec, Maybe, ProjectTerminalSettingsContent,
 12    SlashCommandSettings,
 13};
 14
 15#[skip_serializing_none]
 16#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
 17pub struct ProjectSettingsContent {
 18    #[serde(flatten)]
 19    pub all_languages: AllLanguageSettingsContent,
 20
 21    #[serde(flatten)]
 22    pub worktree: WorktreeSettingsContent,
 23
 24    /// Configuration for language servers.
 25    ///
 26    /// The following settings can be overridden for specific language servers:
 27    /// - initialization_options
 28    ///
 29    /// To override settings for a language, add an entry for that language server's
 30    /// name to the lsp value.
 31    /// Default: null
 32    #[serde(default)]
 33    pub lsp: HashMap<Arc<str>, LspSettings>,
 34
 35    #[serde(default)]
 36    pub terminal: Option<ProjectTerminalSettingsContent>,
 37
 38    /// Configuration for Debugger-related features
 39    #[serde(default)]
 40    pub dap: HashMap<Arc<str>, DapSettingsContent>,
 41
 42    /// Settings for context servers used for AI-related features.
 43    #[serde(default)]
 44    pub context_servers: HashMap<Arc<str>, ContextServerSettingsContent>,
 45
 46    /// Configuration for how direnv configuration should be loaded
 47    pub load_direnv: Option<DirenvSettings>,
 48
 49    /// Settings for slash commands.
 50    pub slash_commands: Option<SlashCommandSettings>,
 51
 52    /// The list of custom Git hosting providers.
 53    pub git_hosting_providers: Option<ExtendingVec<GitHostingProviderConfig>>,
 54}
 55
 56#[skip_serializing_none]
 57#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 58pub struct WorktreeSettingsContent {
 59    /// The displayed name of this project. If not set or null, the root directory name
 60    /// will be displayed.
 61    ///
 62    /// Default: null
 63    #[serde(default)]
 64    #[serde(skip_serializing_if = "Maybe::is_unset")]
 65    pub project_name: Maybe<String>,
 66
 67    /// Whether to prevent this project from being shared in public channels.
 68    ///
 69    /// Default: false
 70    #[serde(default)]
 71    pub prevent_sharing_in_public_channels: bool,
 72
 73    /// Completely ignore files matching globs from `file_scan_exclusions`. Overrides
 74    /// `file_scan_inclusions`.
 75    ///
 76    /// Default: [
 77    ///   "**/.git",
 78    ///   "**/.svn",
 79    ///   "**/.hg",
 80    ///   "**/.jj",
 81    ///   "**/CVS",
 82    ///   "**/.DS_Store",
 83    ///   "**/Thumbs.db",
 84    ///   "**/.classpath",
 85    ///   "**/.settings"
 86    /// ]
 87    pub file_scan_exclusions: Option<Vec<String>>,
 88
 89    /// Always include files that match these globs when scanning for files, even if they're
 90    /// ignored by git. This setting is overridden by `file_scan_exclusions`.
 91    /// Default: [
 92    ///  ".env*",
 93    ///  "docker-compose.*.yml",
 94    /// ]
 95    pub file_scan_inclusions: Option<Vec<String>>,
 96
 97    /// Treat the files matching these globs as `.env` files.
 98    /// Default: ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"]
 99    pub private_files: Option<ExtendingVec<String>>,
100}
101
102#[skip_serializing_none]
103#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash)]
104#[serde(rename_all = "snake_case")]
105pub struct LspSettings {
106    pub binary: Option<BinarySettings>,
107    pub initialization_options: Option<serde_json::Value>,
108    pub settings: Option<serde_json::Value>,
109    /// If the server supports sending tasks over LSP extensions,
110    /// this setting can be used to enable or disable them in Zed.
111    /// Default: true
112    #[serde(default = "default_true")]
113    pub enable_lsp_tasks: bool,
114    pub fetch: Option<FetchSettings>,
115}
116
117impl Default for LspSettings {
118    fn default() -> Self {
119        Self {
120            binary: None,
121            initialization_options: None,
122            settings: None,
123            enable_lsp_tasks: true,
124            fetch: None,
125        }
126    }
127}
128
129#[skip_serializing_none]
130#[derive(
131    Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
132)]
133pub struct BinarySettings {
134    pub path: Option<String>,
135    pub arguments: Option<Vec<String>>,
136    pub env: Option<BTreeMap<String, String>>,
137    pub ignore_system_version: Option<bool>,
138}
139
140#[skip_serializing_none]
141#[derive(
142    Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
143)]
144pub struct FetchSettings {
145    // Whether to consider pre-releases for fetching
146    pub pre_release: Option<bool>,
147}
148
149/// Common language server settings.
150#[skip_serializing_none]
151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
152pub struct GlobalLspSettingsContent {
153    /// Whether to show the LSP servers button in the status bar.
154    ///
155    /// Default: `true`
156    pub button: Option<bool>,
157}
158
159#[skip_serializing_none]
160#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
161#[serde(rename_all = "snake_case")]
162pub struct DapSettingsContent {
163    pub binary: Option<String>,
164    #[serde(default)]
165    pub args: Option<Vec<String>>,
166    #[serde(default)]
167    pub env: Option<HashMap<String, String>>,
168}
169
170#[skip_serializing_none]
171#[derive(
172    Default, Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema, MergeFrom,
173)]
174pub struct SessionSettingsContent {
175    /// Whether or not to restore unsaved buffers on restart.
176    ///
177    /// If this is true, user won't be prompted whether to save/discard
178    /// dirty files when closing the application.
179    ///
180    /// Default: true
181    pub restore_unsaved_buffers: Option<bool>,
182}
183
184#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom, Debug)]
185#[serde(tag = "source", rename_all = "snake_case")]
186pub enum ContextServerSettingsContent {
187    Custom {
188        /// Whether the context server is enabled.
189        #[serde(default = "default_true")]
190        enabled: bool,
191
192        #[serde(flatten)]
193        command: ContextServerCommand,
194    },
195    Extension {
196        /// Whether the context server is enabled.
197        #[serde(default = "default_true")]
198        enabled: bool,
199        /// The settings for this context server specified by the extension.
200        ///
201        /// Consult the documentation for the context server to see what settings
202        /// are supported.
203        settings: serde_json::Value,
204    },
205}
206impl ContextServerSettingsContent {
207    pub fn set_enabled(&mut self, enabled: bool) {
208        match self {
209            ContextServerSettingsContent::Custom {
210                enabled: custom_enabled,
211                command: _,
212            } => {
213                *custom_enabled = enabled;
214            }
215            ContextServerSettingsContent::Extension {
216                enabled: ext_enabled,
217                settings: _,
218            } => *ext_enabled = enabled,
219        }
220    }
221}
222
223#[skip_serializing_none]
224#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom)]
225pub struct ContextServerCommand {
226    #[serde(rename = "command")]
227    pub path: PathBuf,
228    pub args: Vec<String>,
229    pub env: Option<HashMap<String, String>>,
230    /// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified.
231    pub timeout: Option<u64>,
232}
233
234impl std::fmt::Debug for ContextServerCommand {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        let filtered_env = self.env.as_ref().map(|env| {
237            env.iter()
238                .map(|(k, v)| {
239                    (
240                        k,
241                        if util::redact::should_redact(k) {
242                            "[REDACTED]"
243                        } else {
244                            v
245                        },
246                    )
247                })
248                .collect::<Vec<_>>()
249        });
250
251        f.debug_struct("ContextServerCommand")
252            .field("path", &self.path)
253            .field("args", &self.args)
254            .field("env", &filtered_env)
255            .finish()
256    }
257}
258
259#[skip_serializing_none]
260#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
261pub struct GitSettings {
262    /// Whether or not to show the git gutter.
263    ///
264    /// Default: tracked_files
265    pub git_gutter: Option<GitGutterSetting>,
266    /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter.
267    ///
268    /// Default: 0
269    pub gutter_debounce: Option<u64>,
270    /// Whether or not to show git blame data inline in
271    /// the currently focused line.
272    ///
273    /// Default: on
274    pub inline_blame: Option<InlineBlameSettings>,
275    /// Git blame settings.
276    pub blame: Option<BlameSettings>,
277    /// Which information to show in the branch picker.
278    ///
279    /// Default: on
280    pub branch_picker: Option<BranchPickerSettingsContent>,
281    /// How hunks are displayed visually in the editor.
282    ///
283    /// Default: staged_hollow
284    pub hunk_style: Option<GitHunkStyleSetting>,
285}
286
287#[derive(
288    Clone,
289    Copy,
290    Debug,
291    PartialEq,
292    Default,
293    Serialize,
294    Deserialize,
295    JsonSchema,
296    MergeFrom,
297    strum::VariantArray,
298    strum::VariantNames,
299)]
300#[serde(rename_all = "snake_case")]
301pub enum GitGutterSetting {
302    /// Show git gutter in tracked files.
303    #[default]
304    TrackedFiles,
305    /// Hide git gutter
306    Hide,
307}
308
309#[skip_serializing_none]
310#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
311#[serde(rename_all = "snake_case")]
312pub struct InlineBlameSettings {
313    /// Whether or not to show git blame data inline in
314    /// the currently focused line.
315    ///
316    /// Default: true
317    pub enabled: Option<bool>,
318    /// Whether to only show the inline blame information
319    /// after a delay once the cursor stops moving.
320    ///
321    /// Default: 0
322    pub delay_ms: Option<DelayMs>,
323    /// The amount of padding between the end of the source line and the start
324    /// of the inline blame in units of columns.
325    ///
326    /// Default: 7
327    pub padding: Option<u32>,
328    /// The minimum column number to show the inline blame information at
329    ///
330    /// Default: 0
331    pub min_column: Option<u32>,
332    /// Whether to show commit summary as part of the inline blame.
333    ///
334    /// Default: false
335    pub show_commit_summary: Option<bool>,
336}
337
338#[skip_serializing_none]
339#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
340#[serde(rename_all = "snake_case")]
341pub struct BlameSettings {
342    /// Whether to show the avatar of the author of the commit.
343    ///
344    /// Default: true
345    pub show_avatar: Option<bool>,
346}
347
348#[skip_serializing_none]
349#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
350#[serde(rename_all = "snake_case")]
351pub struct BranchPickerSettingsContent {
352    /// Whether to show author name as part of the commit information.
353    ///
354    /// Default: false
355    pub show_author_name: Option<bool>,
356}
357
358#[derive(
359    Clone,
360    Copy,
361    PartialEq,
362    Debug,
363    Default,
364    Serialize,
365    Deserialize,
366    JsonSchema,
367    MergeFrom,
368    strum::VariantArray,
369    strum::VariantNames,
370)]
371#[serde(rename_all = "snake_case")]
372pub enum GitHunkStyleSetting {
373    /// Show unstaged hunks with a filled background and staged hunks hollow.
374    #[default]
375    StagedHollow,
376    /// Show unstaged hunks hollow and staged hunks with a filled background.
377    UnstagedHollow,
378}
379
380#[skip_serializing_none]
381#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
382pub struct DiagnosticsSettingsContent {
383    /// Whether to show the project diagnostics button in the status bar.
384    pub button: Option<bool>,
385
386    /// Whether or not to include warning diagnostics.
387    pub include_warnings: Option<bool>,
388
389    /// Settings for using LSP pull diagnostics mechanism in Zed.
390    pub lsp_pull_diagnostics: Option<LspPullDiagnosticsSettingsContent>,
391
392    /// Settings for showing inline diagnostics.
393    pub inline: Option<InlineDiagnosticsSettingsContent>,
394}
395
396#[skip_serializing_none]
397#[derive(
398    Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
399)]
400pub struct LspPullDiagnosticsSettingsContent {
401    /// Whether to pull for diagnostics or not.
402    ///
403    /// Default: true
404    pub enabled: Option<bool>,
405    /// Minimum time to wait before pulling diagnostics from the language server(s).
406    /// 0 turns the debounce off.
407    ///
408    /// Default: 50
409    pub debounce_ms: Option<DelayMs>,
410}
411
412#[skip_serializing_none]
413#[derive(
414    Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Eq,
415)]
416pub struct InlineDiagnosticsSettingsContent {
417    /// Whether or not to show inline diagnostics
418    ///
419    /// Default: false
420    pub enabled: Option<bool>,
421    /// Whether to only show the inline diagnostics after a delay after the
422    /// last editor event.
423    ///
424    /// Default: 150
425    pub update_debounce_ms: Option<DelayMs>,
426    /// The amount of padding between the end of the source line and the start
427    /// of the inline diagnostic in units of columns.
428    ///
429    /// Default: 4
430    pub padding: Option<u32>,
431    /// The minimum column to display inline diagnostics. This setting can be
432    /// used to horizontally align inline diagnostics at some position. Lines
433    /// longer than this value will still push diagnostics further to the right.
434    ///
435    /// Default: 0
436    pub min_column: Option<u32>,
437
438    pub max_severity: Option<DiagnosticSeverityContent>,
439}
440
441#[skip_serializing_none]
442#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
443pub struct NodeBinarySettings {
444    /// The path to the Node binary.
445    pub path: Option<String>,
446    /// The path to the npm binary Zed should use (defaults to `.path/../npm`).
447    pub npm_path: Option<String>,
448    /// If enabled, Zed will download its own copy of Node.
449    pub ignore_system_version: Option<bool>,
450}
451
452#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
453#[serde(rename_all = "snake_case")]
454pub enum DirenvSettings {
455    /// Load direnv configuration through a shell hook
456    ShellHook,
457    /// Load direnv configuration directly using `direnv export json`
458    #[default]
459    Direct,
460}
461
462#[derive(
463    Clone,
464    Copy,
465    Debug,
466    Eq,
467    PartialEq,
468    Ord,
469    PartialOrd,
470    Serialize,
471    Deserialize,
472    JsonSchema,
473    MergeFrom,
474    strum::VariantArray,
475    strum::VariantNames,
476)]
477#[serde(rename_all = "snake_case")]
478pub enum DiagnosticSeverityContent {
479    // No diagnostics are shown.
480    Off,
481    Error,
482    Warning,
483    Info,
484    Hint,
485    All,
486}
487
488/// A custom Git hosting provider.
489#[skip_serializing_none]
490#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
491pub struct GitHostingProviderConfig {
492    /// The type of the provider.
493    ///
494    /// Must be one of `github`, `gitlab`, or `bitbucket`.
495    pub provider: GitHostingProviderKind,
496
497    /// The base URL for the provider (e.g., "https://code.corp.big.com").
498    pub base_url: String,
499
500    /// The display name for the provider (e.g., "BigCorp GitHub").
501    pub name: String,
502}
503
504#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
505#[serde(rename_all = "snake_case")]
506pub enum GitHostingProviderKind {
507    Github,
508    Gitlab,
509    Bitbucket,
510}