project.rs

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