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