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