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