project.rs

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