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