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