project.rs

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