1use std::num::NonZeroU32;
2
3use collections::{HashMap, HashSet};
4use gpui::{Modifiers, SharedString};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize, de::Error as _};
7use settings_macros::{MergeFrom, with_fallible_options};
8use std::sync::Arc;
9
10use crate::{ExtendingVec, merge_from};
11
12#[with_fallible_options]
13#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
14pub struct AllLanguageSettingsContent {
15 /// The settings for enabling/disabling features.
16 pub features: Option<FeaturesContent>,
17 /// The edit prediction settings.
18 pub edit_predictions: Option<EditPredictionSettingsContent>,
19 /// The default language settings.
20 #[serde(flatten)]
21 pub defaults: LanguageSettingsContent,
22 /// The settings for individual languages.
23 #[serde(default)]
24 pub languages: LanguageToSettingsMap,
25 /// Settings for associating file extensions and filenames
26 /// with languages.
27 pub file_types: Option<HashMap<Arc<str>, ExtendingVec<String>>>,
28}
29
30impl merge_from::MergeFrom for AllLanguageSettingsContent {
31 fn merge_from(&mut self, other: &Self) {
32 self.file_types.merge_from(&other.file_types);
33 self.features.merge_from(&other.features);
34 self.edit_predictions.merge_from(&other.edit_predictions);
35
36 // A user's global settings override the default global settings and
37 // all default language-specific settings.
38 //
39 self.defaults.merge_from(&other.defaults);
40 for language_settings in self.languages.0.values_mut() {
41 language_settings.merge_from(&other.defaults);
42 }
43
44 // A user's language-specific settings override default language-specific settings.
45 for (language_name, user_language_settings) in &other.languages.0 {
46 if let Some(existing) = self.languages.0.get_mut(language_name) {
47 existing.merge_from(&user_language_settings);
48 } else {
49 let mut new_settings = self.defaults.clone();
50 new_settings.merge_from(&user_language_settings);
51
52 self.languages.0.insert(language_name.clone(), new_settings);
53 }
54 }
55 }
56}
57
58/// The settings for enabling/disabling features.
59#[with_fallible_options]
60#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
61#[serde(rename_all = "snake_case")]
62pub struct FeaturesContent {
63 /// Determines which edit prediction provider to use.
64 pub edit_prediction_provider: Option<EditPredictionProvider>,
65 /// Enables the experimental edit prediction context retrieval system.
66 pub experimental_edit_prediction_context_retrieval: Option<bool>,
67}
68
69/// The provider that supplies edit predictions.
70#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, JsonSchema, MergeFrom)]
71#[serde(rename_all = "snake_case")]
72pub enum EditPredictionProvider {
73 None,
74 #[default]
75 Copilot,
76 Supermaven,
77 Zed,
78 Codestral,
79 Experimental(&'static str),
80}
81
82pub const EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME: &str = "sweep";
83pub const EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME: &str = "zeta2";
84pub const EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME: &str = "mercury";
85
86impl<'de> Deserialize<'de> for EditPredictionProvider {
87 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88 where
89 D: serde::Deserializer<'de>,
90 {
91 #[derive(Deserialize)]
92 #[serde(rename_all = "snake_case")]
93 pub enum Content {
94 None,
95 Copilot,
96 Supermaven,
97 Zed,
98 Codestral,
99 Experimental(String),
100 }
101
102 Ok(match Content::deserialize(deserializer)? {
103 Content::None => EditPredictionProvider::None,
104 Content::Copilot => EditPredictionProvider::Copilot,
105 Content::Supermaven => EditPredictionProvider::Supermaven,
106 Content::Zed => EditPredictionProvider::Zed,
107 Content::Codestral => EditPredictionProvider::Codestral,
108 Content::Experimental(name)
109 if name == EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME =>
110 {
111 EditPredictionProvider::Experimental(
112 EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME,
113 )
114 }
115 Content::Experimental(name)
116 if name == EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME =>
117 {
118 EditPredictionProvider::Experimental(
119 EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME,
120 )
121 }
122 Content::Experimental(name)
123 if name == EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME =>
124 {
125 EditPredictionProvider::Experimental(
126 EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME,
127 )
128 }
129 Content::Experimental(name) => {
130 return Err(D::Error::custom(format!(
131 "Unknown experimental edit prediction provider: {}",
132 name
133 )));
134 }
135 })
136 }
137}
138
139impl EditPredictionProvider {
140 pub fn is_zed(&self) -> bool {
141 match self {
142 EditPredictionProvider::Zed => true,
143 EditPredictionProvider::None
144 | EditPredictionProvider::Copilot
145 | EditPredictionProvider::Supermaven
146 | EditPredictionProvider::Codestral
147 | EditPredictionProvider::Experimental(_) => false,
148 }
149 }
150}
151
152/// The contents of the edit prediction settings.
153#[with_fallible_options]
154#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, 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 pub disabled_globs: Option<Vec<String>>,
160 /// The mode used to display edit predictions in the buffer.
161 /// Provider support required.
162 pub mode: Option<EditPredictionsMode>,
163 /// Settings specific to GitHub Copilot.
164 pub copilot: Option<CopilotSettingsContent>,
165 /// Settings specific to Codestral.
166 pub codestral: Option<CodestralSettingsContent>,
167 /// Whether edit predictions are enabled in the assistant prompt editor.
168 /// This has no effect if globally disabled.
169 pub enabled_in_text_threads: Option<bool>,
170}
171
172#[with_fallible_options]
173#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
174pub struct CopilotSettingsContent {
175 /// HTTP/HTTPS proxy to use for Copilot.
176 ///
177 /// Default: none
178 pub proxy: Option<String>,
179 /// Disable certificate verification for the proxy (not recommended).
180 ///
181 /// Default: false
182 pub proxy_no_verify: Option<bool>,
183 /// Enterprise URI for Copilot.
184 ///
185 /// Default: none
186 pub enterprise_uri: Option<String>,
187}
188
189#[with_fallible_options]
190#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
191pub struct CodestralSettingsContent {
192 /// Model to use for completions.
193 ///
194 /// Default: "codestral-latest"
195 pub model: Option<String>,
196 /// Maximum tokens to generate.
197 ///
198 /// Default: 150
199 pub max_tokens: Option<u32>,
200 /// Api URL to use for completions.
201 ///
202 /// Default: "https://codestral.mistral.ai"
203 pub api_url: Option<String>,
204}
205
206/// The mode in which edit predictions should be displayed.
207#[derive(
208 Copy,
209 Clone,
210 Debug,
211 Default,
212 Eq,
213 PartialEq,
214 Serialize,
215 Deserialize,
216 JsonSchema,
217 MergeFrom,
218 strum::VariantArray,
219 strum::VariantNames,
220)]
221#[serde(rename_all = "snake_case")]
222pub enum EditPredictionsMode {
223 /// If provider supports it, display inline when holding modifier key (e.g., alt).
224 /// Otherwise, eager preview is used.
225 #[serde(alias = "auto")]
226 Subtle,
227 /// Display inline when there are no language server completions available.
228 #[default]
229 #[serde(alias = "eager_preview")]
230 Eager,
231}
232
233/// Controls the soft-wrapping behavior in the editor.
234#[derive(
235 Copy,
236 Clone,
237 Debug,
238 Serialize,
239 Deserialize,
240 PartialEq,
241 Eq,
242 JsonSchema,
243 MergeFrom,
244 strum::VariantArray,
245 strum::VariantNames,
246)]
247#[serde(rename_all = "snake_case")]
248pub enum SoftWrap {
249 /// Prefer a single line generally, unless an overly long line is encountered.
250 None,
251 /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
252 /// Prefer a single line generally, unless an overly long line is encountered.
253 PreferLine,
254 /// Soft wrap lines that exceed the editor width.
255 EditorWidth,
256 /// Soft wrap lines at the preferred line length.
257 PreferredLineLength,
258 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
259 Bounded,
260}
261
262/// The settings for a particular language.
263#[with_fallible_options]
264#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
265pub struct LanguageSettingsContent {
266 /// How many columns a tab should occupy.
267 ///
268 /// Default: 4
269 #[schemars(range(min = 1, max = 128))]
270 pub tab_size: Option<NonZeroU32>,
271 /// Whether to indent lines using tab characters, as opposed to multiple
272 /// spaces.
273 ///
274 /// Default: false
275 pub hard_tabs: Option<bool>,
276 /// How to soft-wrap long lines of text.
277 ///
278 /// Default: none
279 pub soft_wrap: Option<SoftWrap>,
280 /// The column at which to soft-wrap lines, for buffers where soft-wrap
281 /// is enabled.
282 ///
283 /// Default: 80
284 pub preferred_line_length: Option<u32>,
285 /// Whether to show wrap guides in the editor. Setting this to true will
286 /// show a guide at the 'preferred_line_length' value if softwrap is set to
287 /// 'preferred_line_length', and will show any additional guides as specified
288 /// by the 'wrap_guides' setting.
289 ///
290 /// Default: true
291 pub show_wrap_guides: Option<bool>,
292 /// Character counts at which to show wrap guides in the editor.
293 ///
294 /// Default: []
295 pub wrap_guides: Option<Vec<usize>>,
296 /// Indent guide related settings.
297 pub indent_guides: Option<IndentGuideSettingsContent>,
298 /// Whether or not to perform a buffer format before saving.
299 ///
300 /// Default: on
301 pub format_on_save: Option<FormatOnSave>,
302 /// Whether or not to remove any trailing whitespace from lines of a buffer
303 /// before saving it.
304 ///
305 /// Default: true
306 pub remove_trailing_whitespace_on_save: Option<bool>,
307 /// Whether or not to ensure there's a single newline at the end of a buffer
308 /// when saving it.
309 ///
310 /// Default: true
311 pub ensure_final_newline_on_save: Option<bool>,
312 /// How to perform a buffer format.
313 ///
314 /// Default: auto
315 pub formatter: Option<FormatterList>,
316 /// Zed's Prettier integration settings.
317 /// Allows to enable/disable formatting with Prettier
318 /// and configure default Prettier, used when no project-level Prettier installation is found.
319 ///
320 /// Default: off
321 pub prettier: Option<PrettierSettingsContent>,
322 /// Whether to automatically close JSX tags.
323 pub jsx_tag_auto_close: Option<JsxTagAutoCloseSettingsContent>,
324 /// Whether to use language servers to provide code intelligence.
325 ///
326 /// Default: true
327 pub enable_language_server: Option<bool>,
328 /// The list of language servers to use (or disable) for this language.
329 ///
330 /// This array should consist of language server IDs, as well as the following
331 /// special tokens:
332 /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
333 /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
334 ///
335 /// Default: ["..."]
336 pub language_servers: Option<Vec<String>>,
337 /// Controls where the `editor::Rewrap` action is allowed for this language.
338 ///
339 /// Note: This setting has no effect in Vim mode, as rewrap is already
340 /// allowed everywhere.
341 ///
342 /// Default: "in_comments"
343 pub allow_rewrap: Option<RewrapBehavior>,
344 /// Controls whether edit predictions are shown immediately (true)
345 /// or manually by triggering `editor::ShowEditPrediction` (false).
346 ///
347 /// Default: true
348 pub show_edit_predictions: Option<bool>,
349 /// Controls whether edit predictions are shown in the given language
350 /// scopes.
351 ///
352 /// Example: ["string", "comment"]
353 ///
354 /// Default: []
355 pub edit_predictions_disabled_in: Option<Vec<String>>,
356 /// Whether to show tabs and spaces in the editor.
357 pub show_whitespaces: Option<ShowWhitespaceSetting>,
358 /// Visible characters used to render whitespace when show_whitespaces is enabled.
359 ///
360 /// Default: "•" for spaces, "→" for tabs.
361 pub whitespace_map: Option<WhitespaceMapContent>,
362 /// Whether to start a new line with a comment when a previous line is a comment as well.
363 ///
364 /// Default: true
365 pub extend_comment_on_newline: Option<bool>,
366 /// Whether to continue markdown lists when pressing enter.
367 ///
368 /// Default: true
369 pub extend_list_on_newline: Option<bool>,
370 /// Whether to indent list items when pressing tab after a list marker.
371 ///
372 /// Default: true
373 pub indent_list_on_tab: Option<bool>,
374 /// Inlay hint related settings.
375 pub inlay_hints: Option<InlayHintSettingsContent>,
376 /// Whether to automatically type closing characters for you. For example,
377 /// when you type '(', Zed will automatically add a closing ')' at the correct position.
378 ///
379 /// Default: true
380 pub use_autoclose: Option<bool>,
381 /// Whether to automatically surround text with characters for you. For example,
382 /// when you select text and type '(', Zed will automatically surround text with ().
383 ///
384 /// Default: true
385 pub use_auto_surround: Option<bool>,
386 /// Controls how the editor handles the autoclosed characters.
387 /// When set to `false`(default), skipping over and auto-removing of the closing characters
388 /// happen only for auto-inserted characters.
389 /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
390 /// no matter how they were inserted.
391 ///
392 /// Default: false
393 pub always_treat_brackets_as_autoclosed: Option<bool>,
394 /// Whether to use additional LSP queries to format (and amend) the code after
395 /// every "trigger" symbol input, defined by LSP server capabilities.
396 ///
397 /// Default: true
398 pub use_on_type_format: Option<bool>,
399 /// Which code actions to run on save before the formatter.
400 /// These are not run if formatting is off.
401 ///
402 /// Default: {} (or {"source.organizeImports": true} for Go).
403 pub code_actions_on_format: Option<HashMap<String, bool>>,
404 /// Whether to perform linked edits of associated ranges, if the language server supports it.
405 /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
406 ///
407 /// Default: true
408 pub linked_edits: Option<bool>,
409 /// Whether indentation should be adjusted based on the context whilst typing.
410 ///
411 /// Default: true
412 pub auto_indent: Option<bool>,
413 /// Whether indentation of pasted content should be adjusted based on the context.
414 ///
415 /// Default: true
416 pub auto_indent_on_paste: Option<bool>,
417 /// Task configuration for this language.
418 ///
419 /// Default: {}
420 pub tasks: Option<LanguageTaskSettingsContent>,
421 /// Whether to pop the completions menu while typing in an editor without
422 /// explicitly requesting it.
423 ///
424 /// Default: true
425 pub show_completions_on_input: Option<bool>,
426 /// Whether to display inline and alongside documentation for items in the
427 /// completions menu.
428 ///
429 /// Default: true
430 pub show_completion_documentation: Option<bool>,
431 /// Controls how completions are processed for this language.
432 pub completions: Option<CompletionSettingsContent>,
433 /// Preferred debuggers for this language.
434 ///
435 /// Default: []
436 pub debuggers: Option<Vec<String>>,
437 /// Whether to enable word diff highlighting in the editor.
438 ///
439 /// When enabled, changed words within modified lines are highlighted
440 /// to show exactly what changed.
441 ///
442 /// Default: true
443 pub word_diff_enabled: Option<bool>,
444 /// Whether to use tree-sitter bracket queries to detect and colorize the brackets in the editor.
445 ///
446 /// Default: false
447 pub colorize_brackets: Option<bool>,
448}
449
450/// Controls how whitespace should be displayedin the editor.
451#[derive(
452 Copy,
453 Clone,
454 Debug,
455 Serialize,
456 Deserialize,
457 PartialEq,
458 Eq,
459 JsonSchema,
460 MergeFrom,
461 strum::VariantArray,
462 strum::VariantNames,
463)]
464#[serde(rename_all = "snake_case")]
465pub enum ShowWhitespaceSetting {
466 /// Draw whitespace only for the selected text.
467 Selection,
468 /// Do not draw any tabs or spaces.
469 None,
470 /// Draw all invisible symbols.
471 All,
472 /// Draw whitespaces at boundaries only.
473 ///
474 /// For a whitespace to be on a boundary, any of the following conditions need to be met:
475 /// - It is a tab
476 /// - It is adjacent to an edge (start or end)
477 /// - It is adjacent to a whitespace (left or right)
478 Boundary,
479 /// Draw whitespaces only after non-whitespace characters.
480 Trailing,
481}
482
483#[with_fallible_options]
484#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
485pub struct WhitespaceMapContent {
486 pub space: Option<char>,
487 pub tab: Option<char>,
488}
489
490/// The behavior of `editor::Rewrap`.
491#[derive(
492 Debug,
493 PartialEq,
494 Clone,
495 Copy,
496 Default,
497 Serialize,
498 Deserialize,
499 JsonSchema,
500 MergeFrom,
501 strum::VariantArray,
502 strum::VariantNames,
503)]
504#[serde(rename_all = "snake_case")]
505pub enum RewrapBehavior {
506 /// Only rewrap within comments.
507 #[default]
508 InComments,
509 /// Only rewrap within the current selection(s).
510 InSelections,
511 /// Allow rewrapping anywhere.
512 Anywhere,
513}
514
515#[with_fallible_options]
516#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
517pub struct JsxTagAutoCloseSettingsContent {
518 /// Enables or disables auto-closing of JSX tags.
519 pub enabled: Option<bool>,
520}
521
522/// The settings for inlay hints.
523#[with_fallible_options]
524#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
525pub struct InlayHintSettingsContent {
526 /// Global switch to toggle hints on and off.
527 ///
528 /// Default: false
529 pub enabled: Option<bool>,
530 /// Global switch to toggle inline values on and off when debugging.
531 ///
532 /// Default: true
533 pub show_value_hints: Option<bool>,
534 /// Whether type hints should be shown.
535 ///
536 /// Default: true
537 pub show_type_hints: Option<bool>,
538 /// Whether parameter hints should be shown.
539 ///
540 /// Default: true
541 pub show_parameter_hints: Option<bool>,
542 /// Whether other hints should be shown.
543 ///
544 /// Default: true
545 pub show_other_hints: Option<bool>,
546 /// Whether to show a background for inlay hints.
547 ///
548 /// If set to `true`, the background will use the `hint.background` color
549 /// from the current theme.
550 ///
551 /// Default: false
552 pub show_background: Option<bool>,
553 /// Whether or not to debounce inlay hints updates after buffer edits.
554 ///
555 /// Set to 0 to disable debouncing.
556 ///
557 /// Default: 700
558 pub edit_debounce_ms: Option<u64>,
559 /// Whether or not to debounce inlay hints updates after buffer scrolls.
560 ///
561 /// Set to 0 to disable debouncing.
562 ///
563 /// Default: 50
564 pub scroll_debounce_ms: Option<u64>,
565 /// Toggles inlay hints (hides or shows) when the user presses the modifiers specified.
566 /// If only a subset of the modifiers specified is pressed, hints are not toggled.
567 /// If no modifiers are specified, this is equivalent to `null`.
568 ///
569 /// Default: null
570 pub toggle_on_modifiers_press: Option<Modifiers>,
571}
572
573/// The kind of an inlay hint.
574#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
575pub enum InlayHintKind {
576 /// An inlay hint for a type.
577 Type,
578 /// An inlay hint for a parameter.
579 Parameter,
580}
581
582impl InlayHintKind {
583 /// Returns the [`InlayHintKind`]fromthe given name.
584 ///
585 /// Returns `None` if `name` does not match any of the expected
586 /// string representations.
587 pub fn from_name(name: &str) -> Option<Self> {
588 match name {
589 "type" => Some(InlayHintKind::Type),
590 "parameter" => Some(InlayHintKind::Parameter),
591 _ => None,
592 }
593 }
594
595 /// Returns the name of this [`InlayHintKind`].
596 pub fn name(&self) -> &'static str {
597 match self {
598 InlayHintKind::Type => "type",
599 InlayHintKind::Parameter => "parameter",
600 }
601 }
602}
603
604/// Controls how completions are processed for this language.
605#[with_fallible_options]
606#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
607#[serde(rename_all = "snake_case")]
608pub struct CompletionSettingsContent {
609 /// Controls how words are completed.
610 /// For large documents, not all words may be fetched for completion.
611 ///
612 /// Default: `fallback`
613 pub words: Option<WordsCompletionMode>,
614 /// How many characters has to be in the completions query to automatically show the words-based completions.
615 /// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
616 ///
617 /// Default: 3
618 pub words_min_length: Option<u32>,
619 /// Whether to fetch LSP completions or not.
620 ///
621 /// Default: true
622 pub lsp: Option<bool>,
623 /// When fetching LSP completions, determines how long to wait for a response of a particular server.
624 /// When set to 0, waits indefinitely.
625 ///
626 /// Default: 0
627 pub lsp_fetch_timeout_ms: Option<u64>,
628 /// Controls how LSP completions are inserted.
629 ///
630 /// Default: "replace_suffix"
631 pub lsp_insert_mode: Option<LspInsertMode>,
632}
633
634#[derive(
635 Copy,
636 Clone,
637 Debug,
638 Serialize,
639 Deserialize,
640 PartialEq,
641 Eq,
642 JsonSchema,
643 MergeFrom,
644 strum::VariantArray,
645 strum::VariantNames,
646)]
647#[serde(rename_all = "snake_case")]
648pub enum LspInsertMode {
649 /// Replaces text before the cursor, using the `insert` range described in the LSP specification.
650 Insert,
651 /// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
652 Replace,
653 /// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
654 /// and like `"insert"` otherwise.
655 ReplaceSubsequence,
656 /// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
657 /// `"insert"` otherwise.
658 ReplaceSuffix,
659}
660
661/// Controls how document's words are completed.
662#[derive(
663 Copy,
664 Clone,
665 Debug,
666 Serialize,
667 Deserialize,
668 PartialEq,
669 Eq,
670 JsonSchema,
671 MergeFrom,
672 strum::VariantArray,
673 strum::VariantNames,
674)]
675#[serde(rename_all = "snake_case")]
676pub enum WordsCompletionMode {
677 /// Always fetch document's words for completions along with LSP completions.
678 Enabled,
679 /// Only if LSP response errors or times out,
680 /// use document's words to show completions.
681 Fallback,
682 /// Never fetch or complete document's words for completions.
683 /// (Word-based completions can still be queried via a separate action)
684 Disabled,
685}
686
687/// Allows to enable/disable formatting with Prettier
688/// and configure default Prettier, used when no project-level Prettier installation is found.
689/// Prettier formatting is disabled by default.
690#[with_fallible_options]
691#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
692pub struct PrettierSettingsContent {
693 /// Enables or disables formatting with Prettier for a given language.
694 pub allowed: Option<bool>,
695
696 /// Forces Prettier integration to use a specific parser name when formatting files with the language.
697 pub parser: Option<String>,
698
699 /// Forces Prettier integration to use specific plugins when formatting files with the language.
700 /// The default Prettier will be installed with these plugins.
701 pub plugins: Option<HashSet<String>>,
702
703 /// Default Prettier options, in the format as in package.json section for Prettier.
704 /// If project installs Prettier via its package.json, these options will be ignored.
705 #[serde(flatten)]
706 pub options: Option<HashMap<String, serde_json::Value>>,
707}
708
709/// TODO: this should just be a bool
710/// Controls the behavior of formatting files when they are saved.
711#[derive(
712 Debug,
713 Clone,
714 Copy,
715 PartialEq,
716 Eq,
717 Serialize,
718 Deserialize,
719 JsonSchema,
720 MergeFrom,
721 strum::VariantArray,
722 strum::VariantNames,
723)]
724#[serde(rename_all = "lowercase")]
725pub enum FormatOnSave {
726 /// Files should be formatted on save.
727 On,
728 /// Files should not be formatted on save.
729 Off,
730}
731
732/// Controls which formatters should be used when formatting code.
733#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
734#[serde(untagged)]
735pub enum FormatterList {
736 Single(Formatter),
737 Vec(Vec<Formatter>),
738}
739
740impl Default for FormatterList {
741 fn default() -> Self {
742 Self::Single(Formatter::default())
743 }
744}
745
746impl AsRef<[Formatter]> for FormatterList {
747 fn as_ref(&self) -> &[Formatter] {
748 match &self {
749 Self::Single(single) => std::slice::from_ref(single),
750 Self::Vec(v) => v,
751 }
752 }
753}
754
755/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
756#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
757#[serde(rename_all = "snake_case")]
758pub enum Formatter {
759 /// Format files using Zed's Prettier integration (if applicable),
760 /// or falling back to formatting via language server.
761 #[default]
762 Auto,
763 /// Format code using Zed's Prettier integration.
764 Prettier,
765 /// Format code using an external command.
766 External {
767 /// The external program to run.
768 command: Arc<str>,
769 /// The arguments to pass to the program.
770 arguments: Option<Arc<[String]>>,
771 },
772 /// Files should be formatted using a code action executed by language servers.
773 CodeAction(String),
774 /// Format code using a language server.
775 #[serde(untagged)]
776 LanguageServer(LanguageServerFormatterSpecifier),
777}
778
779#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
780#[serde(
781 rename_all = "snake_case",
782 // allow specifying language servers as "language_server" or {"language_server": {"name": ...}}
783 from = "LanguageServerVariantContent",
784 into = "LanguageServerVariantContent"
785)]
786pub enum LanguageServerFormatterSpecifier {
787 Specific {
788 name: String,
789 },
790 #[default]
791 Current,
792}
793
794impl From<LanguageServerVariantContent> for LanguageServerFormatterSpecifier {
795 fn from(value: LanguageServerVariantContent) -> Self {
796 match value {
797 LanguageServerVariantContent::Specific {
798 language_server: LanguageServerSpecifierContent { name: Some(name) },
799 } => Self::Specific { name },
800 _ => Self::Current,
801 }
802 }
803}
804
805impl From<LanguageServerFormatterSpecifier> for LanguageServerVariantContent {
806 fn from(value: LanguageServerFormatterSpecifier) -> Self {
807 match value {
808 LanguageServerFormatterSpecifier::Specific { name } => Self::Specific {
809 language_server: LanguageServerSpecifierContent { name: Some(name) },
810 },
811 LanguageServerFormatterSpecifier::Current => {
812 Self::Current(CurrentLanguageServerContent::LanguageServer)
813 }
814 }
815 }
816}
817
818#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
819#[serde(rename_all = "snake_case", untagged)]
820enum LanguageServerVariantContent {
821 /// Format code using a specific language server.
822 Specific {
823 language_server: LanguageServerSpecifierContent,
824 },
825 /// Format code using the current language server.
826 Current(CurrentLanguageServerContent),
827}
828
829#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
830#[serde(rename_all = "snake_case")]
831enum CurrentLanguageServerContent {
832 #[default]
833 LanguageServer,
834}
835
836#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
837#[serde(rename_all = "snake_case")]
838struct LanguageServerSpecifierContent {
839 /// The name of the language server to format with
840 name: Option<String>,
841}
842
843/// The settings for indent guides.
844#[with_fallible_options]
845#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
846pub struct IndentGuideSettingsContent {
847 /// Whether to display indent guides in the editor.
848 ///
849 /// Default: true
850 pub enabled: Option<bool>,
851 /// The width of the indent guides in pixels, between 1 and 10.
852 ///
853 /// Default: 1
854 pub line_width: Option<u32>,
855 /// The width of the active indent guide in pixels, between 1 and 10.
856 ///
857 /// Default: 1
858 pub active_line_width: Option<u32>,
859 /// Determines how indent guides are colored.
860 ///
861 /// Default: Fixed
862 pub coloring: Option<IndentGuideColoring>,
863 /// Determines how indent guide backgrounds are colored.
864 ///
865 /// Default: Disabled
866 pub background_coloring: Option<IndentGuideBackgroundColoring>,
867}
868
869/// The task settings for a particular language.
870#[with_fallible_options]
871#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize, JsonSchema, MergeFrom)]
872pub struct LanguageTaskSettingsContent {
873 /// Extra task variables to set for a particular language.
874 pub variables: Option<HashMap<String, String>>,
875 pub enabled: Option<bool>,
876 /// Use LSP tasks over Zed language extension ones.
877 /// If no LSP tasks are returned due to error/timeout or regular execution,
878 /// Zed language extension tasks will be used instead.
879 ///
880 /// Other Zed tasks will still be shown:
881 /// * Zed task from either of the task config file
882 /// * Zed task from history (e.g. one-off task was spawned before)
883 pub prefer_lsp: Option<bool>,
884}
885
886/// Map from language name to settings.
887#[with_fallible_options]
888#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
889pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
890
891/// Determines how indent guides are colored.
892#[derive(
893 Default,
894 Debug,
895 Copy,
896 Clone,
897 PartialEq,
898 Eq,
899 Serialize,
900 Deserialize,
901 JsonSchema,
902 MergeFrom,
903 strum::VariantArray,
904 strum::VariantNames,
905)]
906#[serde(rename_all = "snake_case")]
907pub enum IndentGuideColoring {
908 /// Do not render any lines for indent guides.
909 Disabled,
910 /// Use the same color for all indentation levels.
911 #[default]
912 Fixed,
913 /// Use a different color for each indentation level.
914 IndentAware,
915}
916
917/// Determines how indent guide backgrounds are colored.
918#[derive(
919 Default,
920 Debug,
921 Copy,
922 Clone,
923 PartialEq,
924 Eq,
925 Serialize,
926 Deserialize,
927 JsonSchema,
928 MergeFrom,
929 strum::VariantArray,
930 strum::VariantNames,
931)]
932#[serde(rename_all = "snake_case")]
933pub enum IndentGuideBackgroundColoring {
934 /// Do not render any background for indent guides.
935 #[default]
936 Disabled,
937 /// Use a different color for each indentation level.
938 IndentAware,
939}
940
941#[cfg(test)]
942mod test {
943
944 use crate::{ParseStatus, fallible_options};
945
946 use super::*;
947
948 #[test]
949 fn test_formatter_deserialization() {
950 let raw_auto = "{\"formatter\": \"auto\"}";
951 let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
952 assert_eq!(
953 settings.formatter,
954 Some(FormatterList::Single(Formatter::Auto))
955 );
956 let raw = "{\"formatter\": \"language_server\"}";
957 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
958 assert_eq!(
959 settings.formatter,
960 Some(FormatterList::Single(Formatter::LanguageServer(
961 LanguageServerFormatterSpecifier::Current
962 )))
963 );
964
965 let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
966 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
967 assert_eq!(
968 settings.formatter,
969 Some(FormatterList::Vec(vec![Formatter::LanguageServer(
970 LanguageServerFormatterSpecifier::Current
971 )]))
972 );
973 let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"language_server\", \"prettier\"]}";
974 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
975 assert_eq!(
976 settings.formatter,
977 Some(FormatterList::Vec(vec![
978 Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
979 Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
980 Formatter::Prettier
981 ]))
982 );
983
984 let raw = "{\"formatter\": [{\"language_server\": {\"name\": \"ruff\"}}, \"prettier\"]}";
985 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
986 assert_eq!(
987 settings.formatter,
988 Some(FormatterList::Vec(vec![
989 Formatter::LanguageServer(LanguageServerFormatterSpecifier::Specific {
990 name: "ruff".to_string()
991 }),
992 Formatter::Prettier
993 ]))
994 );
995
996 assert_eq!(
997 serde_json::to_string(&LanguageServerFormatterSpecifier::Current).unwrap(),
998 "\"language_server\"",
999 );
1000 }
1001
1002 #[test]
1003 fn test_formatter_deserialization_invalid() {
1004 let raw_auto = "{\"formatter\": {}}";
1005 let (_, result) = fallible_options::parse_json::<LanguageSettingsContent>(raw_auto);
1006 assert!(matches!(result, ParseStatus::Failed { .. }));
1007 }
1008
1009 #[test]
1010 fn test_prettier_options() {
1011 let raw_prettier = r#"{"allowed": false, "tabWidth": 4, "semi": false}"#;
1012 let result = serde_json::from_str::<PrettierSettingsContent>(raw_prettier)
1013 .expect("Failed to parse prettier options");
1014 assert!(
1015 result
1016 .options
1017 .as_ref()
1018 .expect("options were flattened")
1019 .contains_key("semi")
1020 );
1021 assert!(
1022 result
1023 .options
1024 .as_ref()
1025 .expect("options were flattened")
1026 .contains_key("tabWidth")
1027 );
1028 }
1029}