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