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