1use std::borrow::Cow;
2use std::env;
3use std::num::NonZeroU32;
4use std::sync::Arc;
5
6use anyhow::Result;
7use collections::{HashMap, HashSet};
8use gpui::{App, FontFallbacks, FontFeatures, HighlightStyle, Hsla, Modifiers, SharedString};
9use release_channel::ReleaseChannel;
10use schemars::{JsonSchema, json_schema};
11use serde::de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor};
12use serde::{Deserialize, Deserializer, Serialize};
13use util::schemars::replace_subschema;
14use util::serde::default_true;
15
16use crate::{ActiveSettingsProfileName, ParameterizedJsonSchema, Settings};
17
18#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
19pub struct SettingsContent {
20 #[serde(flatten)]
21 pub project: ProjectSettingsContent,
22
23 pub base_keymap: Option<BaseKeymapContent>,
24
25 pub auto_update: Option<bool>,
26
27 pub title_bar: Option<TitleBarSettingsContent>,
28}
29
30impl SettingsContent {
31 pub fn languages_mut(&mut self) -> &mut HashMap<SharedString, LanguageSettingsContent> {
32 &mut self.project.all_languages.languages.0
33 }
34}
35
36// todo!() what should this be?
37#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
38pub struct ServerSettingsContent {
39 #[serde(flatten)]
40 pub project: ProjectSettingsContent,
41}
42
43#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
44pub(crate) struct UserSettingsContent {
45 #[serde(flatten)]
46 pub(crate) content: SettingsContent,
47
48 pub(crate) dev: Option<SettingsContent>,
49 pub(crate) nightly: Option<SettingsContent>,
50 pub(crate) preview: Option<SettingsContent>,
51 pub(crate) stable: Option<SettingsContent>,
52
53 pub(crate) macos: Option<SettingsContent>,
54 pub(crate) windows: Option<SettingsContent>,
55 pub(crate) linux: Option<SettingsContent>,
56
57 #[serde(default)]
58 pub(crate) profiles: HashMap<String, SettingsContent>,
59}
60
61pub struct ExtensionsSettingsContent {
62 pub(crate) all_languages: AllLanguageSettingsContent,
63}
64
65impl UserSettingsContent {
66 pub(crate) fn for_release_channel(&self) -> Option<&SettingsContent> {
67 match *release_channel::RELEASE_CHANNEL {
68 ReleaseChannel::Dev => self.dev.as_ref(),
69 ReleaseChannel::Nightly => self.nightly.as_ref(),
70 ReleaseChannel::Preview => self.preview.as_ref(),
71 ReleaseChannel::Stable => self.stable.as_ref(),
72 }
73 }
74
75 pub(crate) fn for_os(&self) -> Option<&SettingsContent> {
76 match env::consts::OS {
77 "macos" => self.macos.as_ref(),
78 "linux" => self.linux.as_ref(),
79 "windows" => self.windows.as_ref(),
80 _ => None,
81 }
82 }
83
84 pub(crate) fn for_profile(&self, cx: &App) -> Option<&SettingsContent> {
85 let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>() else {
86 return None;
87 };
88 self.profiles.get(&active_profile.0)
89 }
90}
91
92/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
93///
94/// Default: VSCode
95#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
96pub enum BaseKeymapContent {
97 #[default]
98 VSCode,
99 JetBrains,
100 SublimeText,
101 Atom,
102 TextMate,
103 Emacs,
104 Cursor,
105 None,
106}
107
108#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
109pub struct ProjectSettingsContent {
110 #[serde(flatten)]
111 pub(crate) all_languages: AllLanguageSettingsContent,
112}
113
114#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
115pub struct AllLanguageSettingsContent {
116 /// The settings for enabling/disabling features.
117 #[serde(default)]
118 pub features: Option<FeaturesContent>,
119 /// The edit prediction settings.
120 #[serde(default)]
121 pub edit_predictions: Option<EditPredictionSettingsContent>,
122 /// The default language settings.
123 #[serde(flatten)]
124 pub defaults: LanguageSettingsContent,
125 /// The settings for individual languages.
126 #[serde(default)]
127 pub languages: LanguageToSettingsMap,
128 /// Settings for associating file extensions and filenames
129 /// with languages.
130 #[serde(default)]
131 pub file_types: HashMap<SharedString, Vec<String>>,
132}
133
134/// The settings for enabling/disabling features.
135#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
136#[serde(rename_all = "snake_case")]
137pub struct FeaturesContent {
138 /// Determines which edit prediction provider to use.
139 pub edit_prediction_provider: Option<EditPredictionProviderContent>,
140}
141
142/// The provider that supplies edit predictions.
143#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
144#[serde(rename_all = "snake_case")]
145pub enum EditPredictionProviderContent {
146 None,
147 #[default]
148 Copilot,
149 Supermaven,
150 Zed,
151}
152
153/// The contents of the edit prediction settings.
154#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
155pub struct EditPredictionSettingsContent {
156 /// A list of globs representing files that edit predictions should be disabled for.
157 /// This list adds to a pre-existing, sensible default set of globs.
158 /// Any additional ones you add are combined with them.
159 #[serde(default)]
160 pub disabled_globs: Option<Vec<String>>,
161 /// The mode used to display edit predictions in the buffer.
162 /// Provider support required.
163 #[serde(default)]
164 pub mode: EditPredictionsModeContent,
165 /// Settings specific to GitHub Copilot.
166 #[serde(default)]
167 pub copilot: CopilotSettingsContent,
168 /// Whether edit predictions are enabled in the assistant prompt editor.
169 /// This has no effect if globally disabled.
170 #[serde(default = "default_true")]
171 pub enabled_in_text_threads: bool,
172}
173
174#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
175pub struct CopilotSettingsContent {
176 /// HTTP/HTTPS proxy to use for Copilot.
177 ///
178 /// Default: none
179 #[serde(default)]
180 pub proxy: Option<String>,
181 /// Disable certificate verification for the proxy (not recommended).
182 ///
183 /// Default: false
184 #[serde(default)]
185 pub proxy_no_verify: Option<bool>,
186 /// Enterprise URI for Copilot.
187 ///
188 /// Default: none
189 #[serde(default)]
190 pub enterprise_uri: Option<String>,
191}
192
193/// The mode in which edit predictions should be displayed.
194#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
195#[serde(rename_all = "snake_case")]
196pub enum EditPredictionsModeContent {
197 /// If provider supports it, display inline when holding modifier key (e.g., alt).
198 /// Otherwise, eager preview is used.
199 #[serde(alias = "auto")]
200 Subtle,
201 /// Display inline when there are no language server completions available.
202 #[default]
203 #[serde(alias = "eager_preview")]
204 Eager,
205}
206
207/// Controls the soft-wrapping behavior in the editor.
208#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
209#[serde(rename_all = "snake_case")]
210pub enum SoftWrapContent {
211 /// Prefer a single line generally, unless an overly long line is encountered.
212 None,
213 /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
214 /// Prefer a single line generally, unless an overly long line is encountered.
215 PreferLine,
216 /// Soft wrap lines that exceed the editor width.
217 EditorWidth,
218 /// Soft wrap lines at the preferred line length.
219 PreferredLineLength,
220 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
221 Bounded,
222}
223
224/// The settings for a particular language.
225#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
226pub struct LanguageSettingsContent {
227 /// How many columns a tab should occupy.
228 ///
229 /// Default: 4
230 #[serde(default)]
231 pub tab_size: Option<NonZeroU32>,
232 /// Whether to indent lines using tab characters, as opposed to multiple
233 /// spaces.
234 ///
235 /// Default: false
236 #[serde(default)]
237 pub hard_tabs: Option<bool>,
238 /// How to soft-wrap long lines of text.
239 ///
240 /// Default: none
241 #[serde(default)]
242 pub soft_wrap: Option<SoftWrapContent>,
243 /// The column at which to soft-wrap lines, for buffers where soft-wrap
244 /// is enabled.
245 ///
246 /// Default: 80
247 #[serde(default)]
248 pub preferred_line_length: Option<u32>,
249 /// Whether to show wrap guides in the editor. Setting this to true will
250 /// show a guide at the 'preferred_line_length' value if softwrap is set to
251 /// 'preferred_line_length', and will show any additional guides as specified
252 /// by the 'wrap_guides' setting.
253 ///
254 /// Default: true
255 #[serde(default)]
256 pub show_wrap_guides: Option<bool>,
257 /// Character counts at which to show wrap guides in the editor.
258 ///
259 /// Default: []
260 #[serde(default)]
261 pub wrap_guides: Option<Vec<usize>>,
262 /// Indent guide related settings.
263 #[serde(default)]
264 pub indent_guides: Option<IndentGuideSettingsContent>,
265 /// Whether or not to perform a buffer format before saving.
266 ///
267 /// Default: on
268 #[serde(default)]
269 pub format_on_save: Option<FormatOnSave>,
270 /// Whether or not to remove any trailing whitespace from lines of a buffer
271 /// before saving it.
272 ///
273 /// Default: true
274 #[serde(default)]
275 pub remove_trailing_whitespace_on_save: Option<bool>,
276 /// Whether or not to ensure there's a single newline at the end of a buffer
277 /// when saving it.
278 ///
279 /// Default: true
280 #[serde(default)]
281 pub ensure_final_newline_on_save: Option<bool>,
282 /// How to perform a buffer format.
283 ///
284 /// Default: auto
285 #[serde(default)]
286 pub formatter: Option<SelectedFormatter>,
287 /// Zed's Prettier integration settings.
288 /// Allows to enable/disable formatting with Prettier
289 /// and configure default Prettier, used when no project-level Prettier installation is found.
290 ///
291 /// Default: off
292 #[serde(default)]
293 pub prettier: Option<PrettierSettings>,
294 /// Whether to automatically close JSX tags.
295 #[serde(default)]
296 pub jsx_tag_auto_close: Option<JsxTagAutoCloseSettings>,
297 /// Whether to use language servers to provide code intelligence.
298 ///
299 /// Default: true
300 #[serde(default)]
301 pub enable_language_server: Option<bool>,
302 /// The list of language servers to use (or disable) for this language.
303 ///
304 /// This array should consist of language server IDs, as well as the following
305 /// special tokens:
306 /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
307 /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
308 ///
309 /// Default: ["..."]
310 #[serde(default)]
311 pub language_servers: Option<Vec<String>>,
312 /// Controls where the `editor::Rewrap` action is allowed for this language.
313 ///
314 /// Note: This setting has no effect in Vim mode, as rewrap is already
315 /// allowed everywhere.
316 ///
317 /// Default: "in_comments"
318 #[serde(default)]
319 pub allow_rewrap: Option<RewrapBehavior>,
320 /// Controls whether edit predictions are shown immediately (true)
321 /// or manually by triggering `editor::ShowEditPrediction` (false).
322 ///
323 /// Default: true
324 #[serde(default)]
325 pub show_edit_predictions: Option<bool>,
326 /// Controls whether edit predictions are shown in the given language
327 /// scopes.
328 ///
329 /// Example: ["string", "comment"]
330 ///
331 /// Default: []
332 #[serde(default)]
333 pub edit_predictions_disabled_in: Option<Vec<String>>,
334 /// Whether to show tabs and spaces in the editor.
335 #[serde(default)]
336 pub show_whitespaces: Option<ShowWhitespaceSetting>,
337 /// Visible characters used to render whitespace when show_whitespaces is enabled.
338 ///
339 /// Default: "•" for spaces, "→" for tabs.
340 #[serde(default)]
341 pub whitespace_map: Option<WhitespaceMap>,
342 /// Whether to start a new line with a comment when a previous line is a comment as well.
343 ///
344 /// Default: true
345 #[serde(default)]
346 pub extend_comment_on_newline: Option<bool>,
347 /// Inlay hint related settings.
348 #[serde(default)]
349 pub inlay_hints: Option<InlayHintSettings>,
350 /// Whether to automatically type closing characters for you. For example,
351 /// when you type (, Zed will automatically add a closing ) at the correct position.
352 ///
353 /// Default: true
354 pub use_autoclose: Option<bool>,
355 /// Whether to automatically surround text with characters for you. For example,
356 /// when you select text and type (, Zed will automatically surround text with ().
357 ///
358 /// Default: true
359 pub use_auto_surround: Option<bool>,
360 /// Controls how the editor handles the autoclosed characters.
361 /// When set to `false`(default), skipping over and auto-removing of the closing characters
362 /// happen only for auto-inserted characters.
363 /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
364 /// no matter how they were inserted.
365 ///
366 /// Default: false
367 pub always_treat_brackets_as_autoclosed: Option<bool>,
368 /// Whether to use additional LSP queries to format (and amend) the code after
369 /// every "trigger" symbol input, defined by LSP server capabilities.
370 ///
371 /// Default: true
372 pub use_on_type_format: Option<bool>,
373 /// Which code actions to run on save after the formatter.
374 /// These are not run if formatting is off.
375 ///
376 /// Default: {} (or {"source.organizeImports": true} for Go).
377 pub code_actions_on_format: Option<HashMap<String, bool>>,
378 /// Whether to perform linked edits of associated ranges, if the language server supports it.
379 /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
380 ///
381 /// Default: true
382 pub linked_edits: Option<bool>,
383 /// Whether indentation should be adjusted based on the context whilst typing.
384 ///
385 /// Default: true
386 pub auto_indent: Option<bool>,
387 /// Whether indentation of pasted content should be adjusted based on the context.
388 ///
389 /// Default: true
390 pub auto_indent_on_paste: Option<bool>,
391 /// Task configuration for this language.
392 ///
393 /// Default: {}
394 pub tasks: Option<LanguageTaskConfig>,
395 /// Whether to pop the completions menu while typing in an editor without
396 /// explicitly requesting it.
397 ///
398 /// Default: true
399 pub show_completions_on_input: Option<bool>,
400 /// Whether to display inline and alongside documentation for items in the
401 /// completions menu.
402 ///
403 /// Default: true
404 pub show_completion_documentation: Option<bool>,
405 /// Controls how completions are processed for this language.
406 pub completions: Option<CompletionSettings>,
407 /// Preferred debuggers for this language.
408 ///
409 /// Default: []
410 pub debuggers: Option<Vec<String>>,
411}
412
413/// Controls how whitespace should be displayedin the editor.
414#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
415#[serde(rename_all = "snake_case")]
416pub enum ShowWhitespaceSetting {
417 /// Draw whitespace only for the selected text.
418 Selection,
419 /// Do not draw any tabs or spaces.
420 None,
421 /// Draw all invisible symbols.
422 All,
423 /// Draw whitespaces at boundaries only.
424 ///
425 /// For a whitespace to be on a boundary, any of the following conditions need to be met:
426 /// - It is a tab
427 /// - It is adjacent to an edge (start or end)
428 /// - It is adjacent to a whitespace (left or right)
429 Boundary,
430 /// Draw whitespaces only after non-whitespace characters.
431 Trailing,
432}
433
434#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
435pub struct WhitespaceMap {
436 #[serde(default)]
437 pub space: Option<String>,
438 #[serde(default)]
439 pub tab: Option<String>,
440}
441
442impl WhitespaceMap {
443 pub fn space(&self) -> SharedString {
444 self.space
445 .as_ref()
446 .map_or_else(|| SharedString::from("•"), |s| SharedString::from(s))
447 }
448
449 pub fn tab(&self) -> SharedString {
450 self.tab
451 .as_ref()
452 .map_or_else(|| SharedString::from("→"), |s| SharedString::from(s))
453 }
454}
455
456/// The behavior of `editor::Rewrap`.
457#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
458#[serde(rename_all = "snake_case")]
459pub enum RewrapBehavior {
460 /// Only rewrap within comments.
461 #[default]
462 InComments,
463 /// Only rewrap within the current selection(s).
464 InSelections,
465 /// Allow rewrapping anywhere.
466 Anywhere,
467}
468
469#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
470pub struct JsxTagAutoCloseSettings {
471 /// Enables or disables auto-closing of JSX tags.
472 #[serde(default)]
473 pub enabled: bool,
474}
475
476/// The settings for inlay hints.
477#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
478pub struct InlayHintSettings {
479 /// Global switch to toggle hints on and off.
480 ///
481 /// Default: false
482 #[serde(default)]
483 pub enabled: bool,
484 /// Global switch to toggle inline values on and off when debugging.
485 ///
486 /// Default: true
487 #[serde(default = "default_true")]
488 pub show_value_hints: bool,
489 /// Whether type hints should be shown.
490 ///
491 /// Default: true
492 #[serde(default = "default_true")]
493 pub show_type_hints: bool,
494 /// Whether parameter hints should be shown.
495 ///
496 /// Default: true
497 #[serde(default = "default_true")]
498 pub show_parameter_hints: bool,
499 /// Whether other hints should be shown.
500 ///
501 /// Default: true
502 #[serde(default = "default_true")]
503 pub show_other_hints: bool,
504 /// Whether to show a background for inlay hints.
505 ///
506 /// If set to `true`, the background will use the `hint.background` color
507 /// from the current theme.
508 ///
509 /// Default: false
510 #[serde(default)]
511 pub show_background: bool,
512 /// Whether or not to debounce inlay hints updates after buffer edits.
513 ///
514 /// Set to 0 to disable debouncing.
515 ///
516 /// Default: 700
517 #[serde(default = "edit_debounce_ms")]
518 pub edit_debounce_ms: u64,
519 /// Whether or not to debounce inlay hints updates after buffer scrolls.
520 ///
521 /// Set to 0 to disable debouncing.
522 ///
523 /// Default: 50
524 #[serde(default = "scroll_debounce_ms")]
525 pub scroll_debounce_ms: u64,
526 /// Toggles inlay hints (hides or shows) when the user presses the modifiers specified.
527 /// If only a subset of the modifiers specified is pressed, hints are not toggled.
528 /// If no modifiers are specified, this is equivalent to `None`.
529 ///
530 /// Default: None
531 #[serde(default)]
532 pub toggle_on_modifiers_press: Option<Modifiers>,
533}
534
535fn edit_debounce_ms() -> u64 {
536 700
537}
538
539fn scroll_debounce_ms() -> u64 {
540 50
541}
542
543/// Controls how completions are processed for this language.
544#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
545#[serde(rename_all = "snake_case")]
546pub struct CompletionSettings {
547 /// Controls how words are completed.
548 /// For large documents, not all words may be fetched for completion.
549 ///
550 /// Default: `fallback`
551 #[serde(default = "default_words_completion_mode")]
552 pub words: WordsCompletionMode,
553 /// How many characters has to be in the completions query to automatically show the words-based completions.
554 /// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
555 ///
556 /// Default: 3
557 #[serde(default = "default_3")]
558 pub words_min_length: usize,
559 /// Whether to fetch LSP completions or not.
560 ///
561 /// Default: true
562 #[serde(default = "default_true")]
563 pub lsp: bool,
564 /// When fetching LSP completions, determines how long to wait for a response of a particular server.
565 /// When set to 0, waits indefinitely.
566 ///
567 /// Default: 0
568 #[serde(default)]
569 pub lsp_fetch_timeout_ms: u64,
570 /// Controls how LSP completions are inserted.
571 ///
572 /// Default: "replace_suffix"
573 #[serde(default = "default_lsp_insert_mode")]
574 pub lsp_insert_mode: LspInsertMode,
575}
576
577#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
578#[serde(rename_all = "snake_case")]
579pub enum LspInsertMode {
580 /// Replaces text before the cursor, using the `insert` range described in the LSP specification.
581 Insert,
582 /// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
583 Replace,
584 /// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
585 /// and like `"insert"` otherwise.
586 ReplaceSubsequence,
587 /// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
588 /// `"insert"` otherwise.
589 ReplaceSuffix,
590}
591
592/// Controls how document's words are completed.
593#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
594#[serde(rename_all = "snake_case")]
595pub enum WordsCompletionMode {
596 /// Always fetch document's words for completions along with LSP completions.
597 Enabled,
598 /// Only if LSP response errors or times out,
599 /// use document's words to show completions.
600 Fallback,
601 /// Never fetch or complete document's words for completions.
602 /// (Word-based completions can still be queried via a separate action)
603 Disabled,
604}
605
606fn default_words_completion_mode() -> WordsCompletionMode {
607 WordsCompletionMode::Fallback
608}
609
610fn default_lsp_insert_mode() -> LspInsertMode {
611 LspInsertMode::ReplaceSuffix
612}
613
614fn default_3() -> usize {
615 3
616}
617
618/// Allows to enable/disable formatting with Prettier
619/// and configure default Prettier, used when no project-level Prettier installation is found.
620/// Prettier formatting is disabled by default.
621#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
622pub struct PrettierSettings {
623 /// Enables or disables formatting with Prettier for a given language.
624 #[serde(default)]
625 pub allowed: bool,
626
627 /// Forces Prettier integration to use a specific parser name when formatting files with the language.
628 #[serde(default)]
629 pub parser: Option<String>,
630
631 /// Forces Prettier integration to use specific plugins when formatting files with the language.
632 /// The default Prettier will be installed with these plugins.
633 #[serde(default)]
634 pub plugins: HashSet<String>,
635
636 /// Default Prettier options, in the format as in package.json section for Prettier.
637 /// If project installs Prettier via its package.json, these options will be ignored.
638 #[serde(flatten)]
639 pub options: HashMap<String, serde_json::Value>,
640}
641/// Controls the behavior of formatting files when they are saved.
642#[derive(Debug, Clone, PartialEq, Eq)]
643pub enum FormatOnSave {
644 /// Files should be formatted on save.
645 On,
646 /// Files should not be formatted on save.
647 Off,
648 List(FormatterList),
649}
650
651impl JsonSchema for FormatOnSave {
652 fn schema_name() -> Cow<'static, str> {
653 "OnSaveFormatter".into()
654 }
655
656 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
657 let formatter_schema = Formatter::json_schema(generator);
658
659 json_schema!({
660 "oneOf": [
661 {
662 "type": "array",
663 "items": formatter_schema
664 },
665 {
666 "type": "string",
667 "enum": ["on", "off", "language_server"]
668 },
669 formatter_schema
670 ]
671 })
672 }
673}
674
675impl Serialize for FormatOnSave {
676 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
677 where
678 S: serde::Serializer,
679 {
680 match self {
681 Self::On => serializer.serialize_str("on"),
682 Self::Off => serializer.serialize_str("off"),
683 Self::List(list) => list.serialize(serializer),
684 }
685 }
686}
687
688impl<'de> Deserialize<'de> for FormatOnSave {
689 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
690 where
691 D: Deserializer<'de>,
692 {
693 struct FormatDeserializer;
694
695 impl<'d> Visitor<'d> for FormatDeserializer {
696 type Value = FormatOnSave;
697
698 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
699 formatter.write_str("a valid on-save formatter kind")
700 }
701 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
702 where
703 E: serde::de::Error,
704 {
705 if v == "on" {
706 Ok(Self::Value::On)
707 } else if v == "off" {
708 Ok(Self::Value::Off)
709 } else if v == "language_server" {
710 Ok(Self::Value::List(FormatterList::Single(
711 Formatter::LanguageServer { name: None },
712 )))
713 } else {
714 let ret: Result<FormatterList, _> =
715 Deserialize::deserialize(v.into_deserializer());
716 ret.map(Self::Value::List)
717 }
718 }
719 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
720 where
721 A: MapAccess<'d>,
722 {
723 let ret: Result<FormatterList, _> =
724 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
725 ret.map(Self::Value::List)
726 }
727 fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
728 where
729 A: SeqAccess<'d>,
730 {
731 let ret: Result<FormatterList, _> =
732 Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
733 ret.map(Self::Value::List)
734 }
735 }
736 deserializer.deserialize_any(FormatDeserializer)
737 }
738}
739
740/// Controls which formatter should be used when formatting code.
741#[derive(Clone, Debug, Default, PartialEq, Eq)]
742pub enum SelectedFormatter {
743 /// Format files using Zed's Prettier integration (if applicable),
744 /// or falling back to formatting via language server.
745 #[default]
746 Auto,
747 List(FormatterList),
748}
749
750impl JsonSchema for SelectedFormatter {
751 fn schema_name() -> Cow<'static, str> {
752 "Formatter".into()
753 }
754
755 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
756 let formatter_schema = Formatter::json_schema(generator);
757
758 json_schema!({
759 "oneOf": [
760 {
761 "type": "array",
762 "items": formatter_schema
763 },
764 {
765 "type": "string",
766 "enum": ["auto", "language_server"]
767 },
768 formatter_schema
769 ]
770 })
771 }
772}
773
774impl Serialize for SelectedFormatter {
775 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
776 where
777 S: serde::Serializer,
778 {
779 match self {
780 SelectedFormatter::Auto => serializer.serialize_str("auto"),
781 SelectedFormatter::List(list) => list.serialize(serializer),
782 }
783 }
784}
785
786impl<'de> Deserialize<'de> for SelectedFormatter {
787 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
788 where
789 D: Deserializer<'de>,
790 {
791 struct FormatDeserializer;
792
793 impl<'d> Visitor<'d> for FormatDeserializer {
794 type Value = SelectedFormatter;
795
796 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
797 formatter.write_str("a valid formatter kind")
798 }
799 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
800 where
801 E: serde::de::Error,
802 {
803 if v == "auto" {
804 Ok(Self::Value::Auto)
805 } else if v == "language_server" {
806 Ok(Self::Value::List(FormatterList::Single(
807 Formatter::LanguageServer { name: None },
808 )))
809 } else {
810 let ret: Result<FormatterList, _> =
811 Deserialize::deserialize(v.into_deserializer());
812 ret.map(SelectedFormatter::List)
813 }
814 }
815 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
816 where
817 A: MapAccess<'d>,
818 {
819 let ret: Result<FormatterList, _> =
820 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
821 ret.map(SelectedFormatter::List)
822 }
823 fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
824 where
825 A: SeqAccess<'d>,
826 {
827 let ret: Result<FormatterList, _> =
828 Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
829 ret.map(SelectedFormatter::List)
830 }
831 }
832 deserializer.deserialize_any(FormatDeserializer)
833 }
834}
835
836/// Controls which formatters should be used when formatting code.
837#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
838#[serde(untagged)]
839pub enum FormatterList {
840 Single(Formatter),
841 Vec(Vec<Formatter>),
842}
843
844impl Default for FormatterList {
845 fn default() -> Self {
846 Self::Single(Formatter::default())
847 }
848}
849
850/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
851#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
852#[serde(rename_all = "snake_case")]
853pub enum Formatter {
854 /// Format code using the current language server.
855 LanguageServer { name: Option<String> },
856 /// Format code using Zed's Prettier integration.
857 #[default]
858 Prettier,
859 /// Format code using an external command.
860 External {
861 /// The external program to run.
862 command: Arc<str>,
863 /// The arguments to pass to the program.
864 arguments: Option<Arc<[String]>>,
865 },
866 /// Files should be formatted using code actions executed by language servers.
867 CodeActions(HashMap<String, bool>),
868}
869
870/// The settings for indent guides.
871#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
872pub struct IndentGuideSettingsContent {
873 /// Whether to display indent guides in the editor.
874 ///
875 /// Default: true
876 #[serde(default = "default_true")]
877 pub enabled: bool,
878 /// The width of the indent guides in pixels, between 1 and 10.
879 ///
880 /// Default: 1
881 #[serde(default = "line_width")]
882 pub line_width: u32,
883 /// The width of the active indent guide in pixels, between 1 and 10.
884 ///
885 /// Default: 1
886 #[serde(default = "active_line_width")]
887 pub active_line_width: u32,
888 /// Determines how indent guides are colored.
889 ///
890 /// Default: Fixed
891 #[serde(default)]
892 pub coloring: IndentGuideColoring,
893 /// Determines how indent guide backgrounds are colored.
894 ///
895 /// Default: Disabled
896 #[serde(default)]
897 pub background_coloring: IndentGuideBackgroundColoring,
898}
899
900fn line_width() -> u32 {
901 1
902}
903
904fn active_line_width() -> u32 {
905 line_width()
906}
907
908/// The task settings for a particular language.
909#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
910pub struct LanguageTaskConfig {
911 /// Extra task variables to set for a particular language.
912 #[serde(default)]
913 pub variables: HashMap<String, String>,
914 #[serde(default = "default_true")]
915 pub enabled: bool,
916 /// Use LSP tasks over Zed language extension ones.
917 /// If no LSP tasks are returned due to error/timeout or regular execution,
918 /// Zed language extension tasks will be used instead.
919 ///
920 /// Other Zed tasks will still be shown:
921 /// * Zed task from either of the task config file
922 /// * Zed task from history (e.g. one-off task was spawned before)
923 #[serde(default = "default_true")]
924 pub prefer_lsp: bool,
925}
926
927/// Map from language name to settings. Its `ParameterizedJsonSchema` allows only known language
928/// names in the keys.
929#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
930pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
931
932inventory::submit! {
933 ParameterizedJsonSchema {
934 add_and_get_ref: |generator, params, _cx| {
935 let language_settings_content_ref = generator
936 .subschema_for::<LanguageSettingsContent>()
937 .to_value();
938 replace_subschema::<LanguageToSettingsMap>(generator, || json_schema!({
939 "type": "object",
940 "properties": params
941 .language_names
942 .iter()
943 .map(|name| {
944 (
945 name.clone(),
946 language_settings_content_ref.clone(),
947 )
948 })
949 .collect::<serde_json::Map<_, _>>()
950 }))
951 }
952 }
953}
954
955/// Determines how indent guides are colored.
956#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
957#[serde(rename_all = "snake_case")]
958pub enum IndentGuideColoring {
959 /// Do not render any lines for indent guides.
960 Disabled,
961 /// Use the same color for all indentation levels.
962 #[default]
963 Fixed,
964 /// Use a different color for each indentation level.
965 IndentAware,
966}
967
968/// Determines how indent guide backgrounds are colored.
969#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
970#[serde(rename_all = "snake_case")]
971pub enum IndentGuideBackgroundColoring {
972 /// Do not render any background for indent guides.
973 #[default]
974 Disabled,
975 /// Use a different color for each indentation level.
976 IndentAware,
977}
978
979#[derive(Copy, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
980pub struct TitleBarSettingsContent {
981 /// Controls when the title bar is visible: "always" | "never" | "hide_in_full_screen".
982 ///
983 /// Default: "always"
984 pub show: Option<TitleBarVisibilityContent>,
985 /// Whether to show the branch icon beside branch switcher in the title bar.
986 ///
987 /// Default: false
988 pub show_branch_icon: Option<bool>,
989 /// Whether to show onboarding banners in the title bar.
990 ///
991 /// Default: true
992 pub show_onboarding_banner: Option<bool>,
993 /// Whether to show user avatar in the title bar.
994 ///
995 /// Default: true
996 pub show_user_picture: Option<bool>,
997 /// Whether to show the branch name button in the titlebar.
998 ///
999 /// Default: true
1000 pub show_branch_name: Option<bool>,
1001 /// Whether to show the project host and name in the titlebar.
1002 ///
1003 /// Default: true
1004 pub show_project_items: Option<bool>,
1005 /// Whether to show the sign in button in the title bar.
1006 ///
1007 /// Default: true
1008 pub show_sign_in: Option<bool>,
1009 /// Whether to show the menus in the title bar.
1010 ///
1011 /// Default: false
1012 pub show_menus: Option<bool>,
1013}
1014
1015#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Debug)]
1016#[serde(rename_all = "snake_case")]
1017pub enum TitleBarVisibilityContent {
1018 Always,
1019 Never,
1020 HideInFullScreen,
1021}
1022
1023/// Settings for rendering text in UI and text buffers.
1024#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1025pub struct ThemeSettingsContent {
1026 /// The default font size for text in the UI.
1027 #[serde(default)]
1028 pub ui_font_size: Option<f32>,
1029 /// The name of a font to use for rendering in the UI.
1030 #[serde(default)]
1031 pub ui_font_family: Option<FontFamilyName>,
1032 /// The font fallbacks to use for rendering in the UI.
1033 #[serde(default)]
1034 #[schemars(default = "default_font_fallbacks")]
1035 #[schemars(extend("uniqueItems" = true))]
1036 pub ui_font_fallbacks: Option<Vec<FontFamilyName>>,
1037 /// The OpenType features to enable for text in the UI.
1038 #[serde(default)]
1039 #[schemars(default = "default_font_features")]
1040 pub ui_font_features: Option<FontFeatures>,
1041 /// The weight of the UI font in CSS units from 100 to 900.
1042 #[serde(default)]
1043 pub ui_font_weight: Option<f32>,
1044 /// The name of a font to use for rendering in text buffers.
1045 #[serde(default)]
1046 pub buffer_font_family: Option<FontFamilyName>,
1047 /// The font fallbacks to use for rendering in text buffers.
1048 #[serde(default)]
1049 #[schemars(extend("uniqueItems" = true))]
1050 pub buffer_font_fallbacks: Option<Vec<FontFamilyName>>,
1051 /// The default font size for rendering in text buffers.
1052 #[serde(default)]
1053 pub buffer_font_size: Option<f32>,
1054 /// The weight of the editor font in CSS units from 100 to 900.
1055 #[serde(default)]
1056 pub buffer_font_weight: Option<f32>,
1057 /// The buffer's line height.
1058 #[serde(default)]
1059 pub buffer_line_height: Option<BufferLineHeight>,
1060 /// The OpenType features to enable for rendering in text buffers.
1061 #[serde(default)]
1062 #[schemars(default = "default_font_features")]
1063 pub buffer_font_features: Option<FontFeatures>,
1064 /// The font size for the agent panel. Falls back to the UI font size if unset.
1065 #[serde(default)]
1066 pub agent_font_size: Option<Option<f32>>,
1067 /// The name of the Zed theme to use.
1068 #[serde(default)]
1069 pub theme: Option<ThemeSelection>,
1070 /// The name of the icon theme to use.
1071 #[serde(default)]
1072 pub icon_theme: Option<IconThemeSelection>,
1073
1074 /// UNSTABLE: Expect many elements to be broken.
1075 ///
1076 // Controls the density of the UI.
1077 #[serde(rename = "unstable.ui_density", default)]
1078 pub ui_density: Option<UiDensity>,
1079
1080 /// How much to fade out unused code.
1081 #[serde(default)]
1082 pub unnecessary_code_fade: Option<f32>,
1083
1084 /// EXPERIMENTAL: Overrides for the current theme.
1085 ///
1086 /// These values will override the ones on the current theme specified in `theme`.
1087 #[serde(rename = "experimental.theme_overrides", default)]
1088 pub experimental_theme_overrides: Option<ThemeStyleContent>,
1089
1090 /// Overrides per theme
1091 ///
1092 /// These values will override the ones on the specified theme
1093 #[serde(default)]
1094 pub theme_overrides: HashMap<String, ThemeStyleContent>,
1095}
1096
1097fn default_font_features() -> Option<FontFeatures> {
1098 Some(FontFeatures::default())
1099}
1100
1101fn default_font_fallbacks() -> Option<FontFallbacks> {
1102 Some(FontFallbacks::default())
1103}
1104
1105/// Represents the selection of a theme, which can be either static or dynamic.
1106#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1107#[serde(untagged)]
1108pub enum ThemeSelection {
1109 /// A static theme selection, represented by a single theme name.
1110 Static(ThemeName),
1111 /// A dynamic theme selection, which can change based the [ThemeMode].
1112 Dynamic {
1113 /// The mode used to determine which theme to use.
1114 #[serde(default)]
1115 mode: ThemeMode,
1116 /// The theme to use for light mode.
1117 light: ThemeName,
1118 /// The theme to use for dark mode.
1119 dark: ThemeName,
1120 },
1121}
1122
1123/// Represents the selection of an icon theme, which can be either static or dynamic.
1124#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1125#[serde(untagged)]
1126pub enum IconThemeSelection {
1127 /// A static icon theme selection, represented by a single icon theme name.
1128 Static(IconThemeName),
1129 /// A dynamic icon theme selection, which can change based on the [`ThemeMode`].
1130 Dynamic {
1131 /// The mode used to determine which theme to use.
1132 #[serde(default)]
1133 mode: ThemeMode,
1134 /// The icon theme to use for light mode.
1135 light: IconThemeName,
1136 /// The icon theme to use for dark mode.
1137 dark: IconThemeName,
1138 },
1139}
1140
1141// TODO: Rename ThemeMode -> ThemeAppearanceMode
1142/// The mode use to select a theme.
1143///
1144/// `Light` and `Dark` will select their respective themes.
1145///
1146/// `System` will select the theme based on the system's appearance.
1147#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
1148#[serde(rename_all = "snake_case")]
1149pub enum ThemeMode {
1150 /// Use the specified `light` theme.
1151 Light,
1152
1153 /// Use the specified `dark` theme.
1154 Dark,
1155
1156 /// Use the theme based on the system's appearance.
1157 #[default]
1158 System,
1159}
1160
1161/// Specifies the density of the UI.
1162/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
1163#[derive(
1164 Debug,
1165 Default,
1166 PartialEq,
1167 Eq,
1168 PartialOrd,
1169 Ord,
1170 Hash,
1171 Clone,
1172 Copy,
1173 Serialize,
1174 Deserialize,
1175 JsonSchema,
1176)]
1177#[serde(rename_all = "snake_case")]
1178pub enum UiDensity {
1179 /// A denser UI with tighter spacing and smaller elements.
1180 #[serde(alias = "compact")]
1181 Compact,
1182 #[default]
1183 #[serde(alias = "default")]
1184 /// The default UI density.
1185 Default,
1186 #[serde(alias = "comfortable")]
1187 /// A looser UI with more spacing and larger elements.
1188 Comfortable,
1189}
1190
1191impl UiDensity {
1192 /// The spacing ratio of a given density.
1193 /// TODO: Standardize usage throughout the app or remove
1194 pub fn spacing_ratio(self) -> f32 {
1195 match self {
1196 UiDensity::Compact => 0.75,
1197 UiDensity::Default => 1.0,
1198 UiDensity::Comfortable => 1.25,
1199 }
1200 }
1201}
1202
1203/// Newtype for font family name. Its `ParameterizedJsonSchema` lists the font families known at
1204/// runtime.
1205#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1206#[serde(transparent)]
1207pub struct FontFamilyName(pub Arc<str>);
1208
1209inventory::submit! {
1210 ParameterizedJsonSchema {
1211 add_and_get_ref: |generator, params, _cx| {
1212 replace_subschema::<FontFamilyName>(generator, || {
1213 json_schema!({
1214 "type": "string",
1215 "enum": params.font_names,
1216 })
1217 })
1218 }
1219 }
1220}
1221
1222/// The buffer's line height.
1223#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
1224#[serde(rename_all = "snake_case")]
1225pub enum BufferLineHeight {
1226 /// A less dense line height.
1227 #[default]
1228 Comfortable,
1229 /// The default line height.
1230 Standard,
1231 /// A custom line height, where 1.0 is the font's height. Must be at least 1.0.
1232 Custom(#[serde(deserialize_with = "deserialize_line_height")] f32),
1233}
1234
1235fn deserialize_line_height<'de, D>(deserializer: D) -> Result<f32, D::Error>
1236where
1237 D: serde::Deserializer<'de>,
1238{
1239 let value = f32::deserialize(deserializer)?;
1240 if value < 1.0 {
1241 return Err(serde::de::Error::custom(
1242 "buffer_line_height.custom must be at least 1.0",
1243 ));
1244 }
1245
1246 Ok(value)
1247}
1248
1249/// The content of a serialized theme.
1250#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
1251#[serde(default)]
1252pub struct ThemeStyleContent {
1253 #[serde(default, rename = "background.appearance")]
1254 pub window_background_appearance: Option<WindowBackgroundContent>,
1255
1256 #[serde(default)]
1257 pub accents: Vec<AccentContent>,
1258
1259 #[serde(flatten, default)]
1260 pub colors: ThemeColorsContent,
1261
1262 #[serde(flatten, default)]
1263 pub status: StatusColorsContent,
1264
1265 #[serde(default)]
1266 pub players: Vec<PlayerColorContent>,
1267
1268 /// The styles for syntax nodes.
1269 #[serde(default)]
1270 pub syntax: IndexMap<String, HighlightStyleContent>,
1271}
1272
1273#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1274pub struct AccentContent(pub Option<String>);
1275
1276#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1277pub struct PlayerColorContent {
1278 pub cursor: Option<String>,
1279 pub background: Option<String>,
1280 pub selection: Option<String>,
1281}
1282
1283pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
1284 let rgba = gpui::Rgba::try_from(color)?;
1285 let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
1286 let hsla = palette::Hsla::from_color(rgba);
1287
1288 let hsla = gpui::hsla(
1289 hsla.hue.into_positive_degrees() / 360.,
1290 hsla.saturation,
1291 hsla.lightness,
1292 hsla.alpha,
1293 );
1294
1295 Ok(hsla)
1296}
1297
1298/// Newtype for a theme name. Its `ParameterizedJsonSchema` lists the theme names known at runtime.
1299#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1300#[serde(transparent)]
1301pub struct ThemeName(pub Arc<str>);
1302
1303inventory::submit! {
1304 ParameterizedJsonSchema {
1305 add_and_get_ref: |generator, _params, cx| {
1306 todo!()
1307 // replace_subschema::<ThemeName>(generator, || json_schema!({
1308 // "type": "string",
1309 // "enum": ThemeRegistry::global(cx).list_names(),
1310 // }))
1311 }
1312 }
1313}
1314
1315/// Newtype for a icon theme name. Its `ParameterizedJsonSchema` lists the icon theme names known at
1316/// runtime.
1317#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1318#[serde(transparent)]
1319pub struct IconThemeName(pub Arc<str>);
1320
1321inventory::submit! {
1322 ParameterizedJsonSchema {
1323 add_and_get_ref: |generator, _params, cx| {
1324 todo!()
1325 // replace_subschema::<IconThemeName>(generator, || json_schema!({
1326 // "type": "string",
1327 // "enum": ThemeRegistry::global(cx)
1328 // .list_icon_themes()
1329 // .into_iter()
1330 // .map(|icon_theme| icon_theme.name)
1331 // .collect::<Vec<SharedString>>(),
1332 // }))
1333 }
1334 }
1335}