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