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