language.rs

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