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