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