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