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