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    /// Treat the files matching these globs as read-only. These files can be opened and viewed,
102    /// but cannot be edited. This is useful for generated files, build outputs, or files from
103    /// external dependencies that should not be modified directly.
104    /// Default: []
105    pub read_only_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    },
225    Extension {
226        /// Whether the context server is enabled.
227        #[serde(default = "default_true")]
228        enabled: bool,
229        /// The settings for this context server specified by the extension.
230        ///
231        /// Consult the documentation for the context server to see what settings
232        /// are supported.
233        settings: serde_json::Value,
234    },
235}
236
237impl ContextServerSettingsContent {
238    pub fn set_enabled(&mut self, enabled: bool) {
239        match self {
240            ContextServerSettingsContent::Stdio {
241                enabled: custom_enabled,
242                ..
243            } => {
244                *custom_enabled = enabled;
245            }
246            ContextServerSettingsContent::Extension {
247                enabled: ext_enabled,
248                ..
249            } => *ext_enabled = enabled,
250            ContextServerSettingsContent::Http {
251                enabled: remote_enabled,
252                ..
253            } => *remote_enabled = enabled,
254        }
255    }
256}
257
258#[with_fallible_options]
259#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom)]
260pub struct ContextServerCommand {
261    #[serde(rename = "command")]
262    pub path: PathBuf,
263    pub args: Vec<String>,
264    pub env: Option<HashMap<String, String>>,
265    /// Timeout for tool calls in milliseconds. Defaults to 60000 (60 seconds) if not specified.
266    pub timeout: Option<u64>,
267}
268
269impl std::fmt::Debug for ContextServerCommand {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        let filtered_env = self.env.as_ref().map(|env| {
272            env.iter()
273                .map(|(k, v)| {
274                    (
275                        k,
276                        if util::redact::should_redact(k) {
277                            "[REDACTED]"
278                        } else {
279                            v
280                        },
281                    )
282                })
283                .collect::<Vec<_>>()
284        });
285
286        f.debug_struct("ContextServerCommand")
287            .field("path", &self.path)
288            .field("args", &self.args)
289            .field("env", &filtered_env)
290            .finish()
291    }
292}
293
294#[with_fallible_options]
295#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
296pub struct GitSettings {
297    /// Whether or not to enable git integration.
298    ///
299    /// Default: true
300    #[serde(flatten)]
301    pub enabled: Option<GitEnabledSettings>,
302    /// Whether or not to show the git gutter.
303    ///
304    /// Default: tracked_files
305    pub git_gutter: Option<GitGutterSetting>,
306    /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter.
307    ///
308    /// Default: 0
309    pub gutter_debounce: Option<u64>,
310    /// Whether or not to show git blame data inline in
311    /// the currently focused line.
312    ///
313    /// Default: on
314    pub inline_blame: Option<InlineBlameSettings>,
315    /// Git blame settings.
316    pub blame: Option<BlameSettings>,
317    /// Which information to show in the branch picker.
318    ///
319    /// Default: on
320    pub branch_picker: Option<BranchPickerSettingsContent>,
321    /// How hunks are displayed visually in the editor.
322    ///
323    /// Default: staged_hollow
324    pub hunk_style: Option<GitHunkStyleSetting>,
325    /// How file paths are displayed in the git gutter.
326    ///
327    /// Default: file_name_first
328    pub path_style: Option<GitPathStyle>,
329}
330
331#[with_fallible_options]
332#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
333#[serde(rename_all = "snake_case")]
334pub struct GitEnabledSettings {
335    pub disable_git: Option<bool>,
336    pub enable_status: Option<bool>,
337    pub enable_diff: Option<bool>,
338}
339
340impl GitEnabledSettings {
341    pub fn is_git_status_enabled(&self) -> bool {
342        !self.disable_git.unwrap_or(false) && self.enable_status.unwrap_or(true)
343    }
344
345    pub fn is_git_diff_enabled(&self) -> bool {
346        !self.disable_git.unwrap_or(false) && self.enable_diff.unwrap_or(true)
347    }
348}
349
350#[derive(
351    Clone,
352    Copy,
353    Debug,
354    PartialEq,
355    Default,
356    Serialize,
357    Deserialize,
358    JsonSchema,
359    MergeFrom,
360    strum::VariantArray,
361    strum::VariantNames,
362)]
363#[serde(rename_all = "snake_case")]
364pub enum GitGutterSetting {
365    /// Show git gutter in tracked files.
366    #[default]
367    TrackedFiles,
368    /// Hide git gutter
369    Hide,
370}
371
372#[with_fallible_options]
373#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
374#[serde(rename_all = "snake_case")]
375pub struct InlineBlameSettings {
376    /// Whether or not to show git blame data inline in
377    /// the currently focused line.
378    ///
379    /// Default: true
380    pub enabled: Option<bool>,
381    /// Whether to only show the inline blame information
382    /// after a delay once the cursor stops moving.
383    ///
384    /// Default: 0
385    pub delay_ms: Option<DelayMs>,
386    /// The amount of padding between the end of the source line and the start
387    /// of the inline blame in units of columns.
388    ///
389    /// Default: 7
390    pub padding: Option<u32>,
391    /// The minimum column number to show the inline blame information at
392    ///
393    /// Default: 0
394    pub min_column: Option<u32>,
395    /// Whether to show commit summary as part of the inline blame.
396    ///
397    /// Default: false
398    pub show_commit_summary: Option<bool>,
399}
400
401#[with_fallible_options]
402#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
403#[serde(rename_all = "snake_case")]
404pub struct BlameSettings {
405    /// Whether to show the avatar of the author of the commit.
406    ///
407    /// Default: true
408    pub show_avatar: Option<bool>,
409}
410
411#[with_fallible_options]
412#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
413#[serde(rename_all = "snake_case")]
414pub struct BranchPickerSettingsContent {
415    /// Whether to show author name as part of the commit information.
416    ///
417    /// Default: false
418    pub show_author_name: Option<bool>,
419}
420
421#[derive(
422    Clone,
423    Copy,
424    PartialEq,
425    Debug,
426    Default,
427    Serialize,
428    Deserialize,
429    JsonSchema,
430    MergeFrom,
431    strum::VariantArray,
432    strum::VariantNames,
433)]
434#[serde(rename_all = "snake_case")]
435pub enum GitHunkStyleSetting {
436    /// Show unstaged hunks with a filled background and staged hunks hollow.
437    #[default]
438    StagedHollow,
439    /// Show unstaged hunks hollow and staged hunks with a filled background.
440    UnstagedHollow,
441}
442
443#[with_fallible_options]
444#[derive(
445    Copy,
446    Clone,
447    Debug,
448    PartialEq,
449    Default,
450    Serialize,
451    Deserialize,
452    JsonSchema,
453    MergeFrom,
454    strum::VariantArray,
455    strum::VariantNames,
456)]
457#[serde(rename_all = "snake_case")]
458pub enum GitPathStyle {
459    /// Show file name first, then path
460    #[default]
461    FileNameFirst,
462    /// Show full path first
463    FilePathFirst,
464}
465
466#[with_fallible_options]
467#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
468pub struct DiagnosticsSettingsContent {
469    /// Whether to show the project diagnostics button in the status bar.
470    pub button: Option<bool>,
471
472    /// Whether or not to include warning diagnostics.
473    pub include_warnings: Option<bool>,
474
475    /// Settings for using LSP pull diagnostics mechanism in Zed.
476    pub lsp_pull_diagnostics: Option<LspPullDiagnosticsSettingsContent>,
477
478    /// Settings for showing inline diagnostics.
479    pub inline: Option<InlineDiagnosticsSettingsContent>,
480}
481
482#[with_fallible_options]
483#[derive(
484    Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
485)]
486pub struct LspPullDiagnosticsSettingsContent {
487    /// Whether to pull for diagnostics or not.
488    ///
489    /// Default: true
490    pub enabled: Option<bool>,
491    /// Minimum time to wait before pulling diagnostics from the language server(s).
492    /// 0 turns the debounce off.
493    ///
494    /// Default: 50
495    pub debounce_ms: Option<DelayMs>,
496}
497
498#[with_fallible_options]
499#[derive(
500    Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Eq,
501)]
502pub struct InlineDiagnosticsSettingsContent {
503    /// Whether or not to show inline diagnostics
504    ///
505    /// Default: false
506    pub enabled: Option<bool>,
507    /// Whether to only show the inline diagnostics after a delay after the
508    /// last editor event.
509    ///
510    /// Default: 150
511    pub update_debounce_ms: Option<DelayMs>,
512    /// The amount of padding between the end of the source line and the start
513    /// of the inline diagnostic in units of columns.
514    ///
515    /// Default: 4
516    pub padding: Option<u32>,
517    /// The minimum column to display inline diagnostics. This setting can be
518    /// used to horizontally align inline diagnostics at some position. Lines
519    /// longer than this value will still push diagnostics further to the right.
520    ///
521    /// Default: 0
522    pub min_column: Option<u32>,
523
524    pub max_severity: Option<DiagnosticSeverityContent>,
525}
526
527#[with_fallible_options]
528#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
529pub struct NodeBinarySettings {
530    /// The path to the Node binary.
531    pub path: Option<String>,
532    /// The path to the npm binary Zed should use (defaults to `.path/../npm`).
533    pub npm_path: Option<String>,
534    /// If enabled, Zed will download its own copy of Node.
535    pub ignore_system_version: Option<bool>,
536}
537
538#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
539#[serde(rename_all = "snake_case")]
540pub enum DirenvSettings {
541    /// Load direnv configuration through a shell hook
542    ShellHook,
543    /// Load direnv configuration directly using `direnv export json`
544    #[default]
545    Direct,
546    /// Do not load direnv configuration
547    Disabled,
548}
549
550#[derive(
551    Clone,
552    Copy,
553    Debug,
554    Eq,
555    PartialEq,
556    Ord,
557    PartialOrd,
558    Serialize,
559    Deserialize,
560    JsonSchema,
561    MergeFrom,
562    strum::VariantArray,
563    strum::VariantNames,
564)]
565#[serde(rename_all = "snake_case")]
566pub enum DiagnosticSeverityContent {
567    // No diagnostics are shown.
568    Off,
569    Error,
570    Warning,
571    Info,
572    Hint,
573    All,
574}
575
576/// A custom Git hosting provider.
577#[with_fallible_options]
578#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
579pub struct GitHostingProviderConfig {
580    /// The type of the provider.
581    ///
582    /// Must be one of `github`, `gitlab`, `bitbucket`, `gitea`, `forgejo`, or `source_hut`.
583    pub provider: GitHostingProviderKind,
584
585    /// The base URL for the provider (e.g., "https://code.corp.big.com").
586    pub base_url: String,
587
588    /// The display name for the provider (e.g., "BigCorp GitHub").
589    pub name: String,
590}
591
592#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
593#[serde(rename_all = "snake_case")]
594pub enum GitHostingProviderKind {
595    Github,
596    Gitlab,
597    Bitbucket,
598    Gitea,
599    Forgejo,
600    SourceHut,
601}