language.rs

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