language_settings.rs

   1//! Provides `language`-related settings.
   2
   3use crate::{File, Language, LanguageName, LanguageServerName};
   4use anyhow::Result;
   5use collections::{HashMap, HashSet};
   6use core::slice;
   7use ec4rs::{
   8    property::{FinalNewline, IndentSize, IndentStyle, TabWidth, TrimTrailingWs},
   9    Properties as EditorconfigProperties,
  10};
  11use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
  12use gpui::App;
  13use itertools::{Either, Itertools};
  14use schemars::{
  15    schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec},
  16    JsonSchema,
  17};
  18use serde::{
  19    de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor},
  20    Deserialize, Deserializer, Serialize,
  21};
  22use serde_json::Value;
  23use settings::{
  24    add_references_to_properties, Settings, SettingsLocation, SettingsSources, SettingsStore,
  25};
  26use std::{borrow::Cow, num::NonZeroU32, path::Path, sync::Arc};
  27use util::serde::default_true;
  28
  29/// Initializes the language settings.
  30pub fn init(cx: &mut App) {
  31    AllLanguageSettings::register(cx);
  32}
  33
  34/// Returns the settings for the specified language from the provided file.
  35pub fn language_settings<'a>(
  36    language: Option<LanguageName>,
  37    file: Option<&'a Arc<dyn File>>,
  38    cx: &'a App,
  39) -> Cow<'a, LanguageSettings> {
  40    let location = file.map(|f| SettingsLocation {
  41        worktree_id: f.worktree_id(cx),
  42        path: f.path().as_ref(),
  43    });
  44    AllLanguageSettings::get(location, cx).language(location, language.as_ref(), cx)
  45}
  46
  47/// Returns the settings for all languages from the provided file.
  48pub fn all_language_settings<'a>(
  49    file: Option<&'a Arc<dyn File>>,
  50    cx: &'a App,
  51) -> &'a AllLanguageSettings {
  52    let location = file.map(|f| SettingsLocation {
  53        worktree_id: f.worktree_id(cx),
  54        path: f.path().as_ref(),
  55    });
  56    AllLanguageSettings::get(location, cx)
  57}
  58
  59/// The settings for all languages.
  60#[derive(Debug, Clone)]
  61pub struct AllLanguageSettings {
  62    /// The edit prediction settings.
  63    pub edit_predictions: EditPredictionSettings,
  64    defaults: LanguageSettings,
  65    languages: HashMap<LanguageName, LanguageSettings>,
  66    pub(crate) file_types: HashMap<Arc<str>, GlobSet>,
  67}
  68
  69/// The settings for a particular language.
  70#[derive(Debug, Clone, Deserialize)]
  71pub struct LanguageSettings {
  72    /// How many columns a tab should occupy.
  73    pub tab_size: NonZeroU32,
  74    /// Whether to indent lines using tab characters, as opposed to multiple
  75    /// spaces.
  76    pub hard_tabs: bool,
  77    /// How to soft-wrap long lines of text.
  78    pub soft_wrap: SoftWrap,
  79    /// The column at which to soft-wrap lines, for buffers where soft-wrap
  80    /// is enabled.
  81    pub preferred_line_length: u32,
  82    // Whether to show wrap guides (vertical rulers) in the editor.
  83    // Setting this to true will show a guide at the 'preferred_line_length' value
  84    // if softwrap is set to 'preferred_line_length', and will show any
  85    // additional guides as specified by the 'wrap_guides' setting.
  86    pub show_wrap_guides: bool,
  87    /// Character counts at which to show wrap guides (vertical rulers) in the editor.
  88    pub wrap_guides: Vec<usize>,
  89    /// Indent guide related settings.
  90    pub indent_guides: IndentGuideSettings,
  91    /// Whether or not to perform a buffer format before saving.
  92    pub format_on_save: FormatOnSave,
  93    /// Whether or not to remove any trailing whitespace from lines of a buffer
  94    /// before saving it.
  95    pub remove_trailing_whitespace_on_save: bool,
  96    /// Whether or not to ensure there's a single newline at the end of a buffer
  97    /// when saving it.
  98    pub ensure_final_newline_on_save: bool,
  99    /// How to perform a buffer format.
 100    pub formatter: SelectedFormatter,
 101    /// Zed's Prettier integration settings.
 102    pub prettier: PrettierSettings,
 103    /// Whether to use language servers to provide code intelligence.
 104    pub enable_language_server: bool,
 105    /// The list of language servers to use (or disable) for this language.
 106    ///
 107    /// This array should consist of language server IDs, as well as the following
 108    /// special tokens:
 109    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
 110    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
 111    pub language_servers: Vec<String>,
 112    /// Controls whether edit predictions are shown immediately (true)
 113    /// or manually by triggering `editor::ShowEditPrediction` (false).
 114    pub show_edit_predictions: bool,
 115    /// Controls whether edit predictions are shown in the given language
 116    /// scopes.
 117    pub edit_predictions_disabled_in: Vec<String>,
 118    /// Whether to show tabs and spaces in the editor.
 119    pub show_whitespaces: ShowWhitespaceSetting,
 120    /// Whether to start a new line with a comment when a previous line is a comment as well.
 121    pub extend_comment_on_newline: bool,
 122    /// Inlay hint related settings.
 123    pub inlay_hints: InlayHintSettings,
 124    /// Whether to automatically close brackets.
 125    pub use_autoclose: bool,
 126    /// Whether to automatically surround text with brackets.
 127    pub use_auto_surround: bool,
 128    /// Whether to use additional LSP queries to format (and amend) the code after
 129    /// every "trigger" symbol input, defined by LSP server capabilities.
 130    pub use_on_type_format: bool,
 131    /// Whether indentation of pasted content should be adjusted based on the context.
 132    pub auto_indent_on_paste: bool,
 133    // Controls how the editor handles the autoclosed characters.
 134    pub always_treat_brackets_as_autoclosed: bool,
 135    /// Which code actions to run on save
 136    pub code_actions_on_format: HashMap<String, bool>,
 137    /// Whether to perform linked edits
 138    pub linked_edits: bool,
 139    /// Task configuration for this language.
 140    pub tasks: LanguageTaskConfig,
 141    /// Whether to pop the completions menu while typing in an editor without
 142    /// explicitly requesting it.
 143    pub show_completions_on_input: bool,
 144    /// Whether to display inline and alongside documentation for items in the
 145    /// completions menu.
 146    pub show_completion_documentation: bool,
 147}
 148
 149impl LanguageSettings {
 150    /// A token representing the rest of the available language servers.
 151    const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
 152
 153    /// Returns the customized list of language servers from the list of
 154    /// available language servers.
 155    pub fn customized_language_servers(
 156        &self,
 157        available_language_servers: &[LanguageServerName],
 158    ) -> Vec<LanguageServerName> {
 159        Self::resolve_language_servers(&self.language_servers, available_language_servers)
 160    }
 161
 162    pub(crate) fn resolve_language_servers(
 163        configured_language_servers: &[String],
 164        available_language_servers: &[LanguageServerName],
 165    ) -> Vec<LanguageServerName> {
 166        let (disabled_language_servers, enabled_language_servers): (
 167            Vec<LanguageServerName>,
 168            Vec<LanguageServerName>,
 169        ) = configured_language_servers.iter().partition_map(
 170            |language_server| match language_server.strip_prefix('!') {
 171                Some(disabled) => Either::Left(LanguageServerName(disabled.to_string().into())),
 172                None => Either::Right(LanguageServerName(language_server.clone().into())),
 173            },
 174        );
 175
 176        let rest = available_language_servers
 177            .iter()
 178            .filter(|&available_language_server| {
 179                !disabled_language_servers.contains(&available_language_server)
 180                    && !enabled_language_servers.contains(&available_language_server)
 181            })
 182            .cloned()
 183            .collect::<Vec<_>>();
 184
 185        enabled_language_servers
 186            .into_iter()
 187            .flat_map(|language_server| {
 188                if language_server.0.as_ref() == Self::REST_OF_LANGUAGE_SERVERS {
 189                    rest.clone()
 190                } else {
 191                    vec![language_server.clone()]
 192                }
 193            })
 194            .collect::<Vec<_>>()
 195    }
 196}
 197
 198/// The provider that supplies edit predictions.
 199#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
 200#[serde(rename_all = "snake_case")]
 201pub enum EditPredictionProvider {
 202    None,
 203    #[default]
 204    Copilot,
 205    Supermaven,
 206    Zed,
 207}
 208
 209impl EditPredictionProvider {
 210    pub fn is_zed(&self) -> bool {
 211        match self {
 212            EditPredictionProvider::Zed => true,
 213            EditPredictionProvider::None
 214            | EditPredictionProvider::Copilot
 215            | EditPredictionProvider::Supermaven => false,
 216        }
 217    }
 218}
 219
 220/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot)
 221/// or [Supermaven](https://supermaven.com).
 222#[derive(Clone, Debug, Default)]
 223pub struct EditPredictionSettings {
 224    /// The provider that supplies edit predictions.
 225    pub provider: EditPredictionProvider,
 226    /// A list of globs representing files that edit predictions should be disabled for.
 227    /// This list adds to a pre-existing, sensible default set of globs.
 228    /// Any additional ones you add are combined with them.
 229    pub disabled_globs: Vec<GlobMatcher>,
 230    /// Configures how edit predictions are displayed in the buffer.
 231    pub mode: EditPredictionsMode,
 232}
 233
 234/// The mode in which edit predictions should be displayed.
 235#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
 236#[serde(rename_all = "snake_case")]
 237pub enum EditPredictionsMode {
 238    /// If provider supports it, display inline when holding modifier key (e.g., alt).
 239    /// Otherwise, eager preview is used.
 240    Auto,
 241    /// Display inline when there are no language server completions available.
 242    #[default]
 243    EagerPreview,
 244}
 245
 246/// The settings for all languages.
 247#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
 248pub struct AllLanguageSettingsContent {
 249    /// The settings for enabling/disabling features.
 250    #[serde(default)]
 251    pub features: Option<FeaturesContent>,
 252    /// The edit prediction settings.
 253    #[serde(default)]
 254    pub edit_predictions: Option<EditPredictionSettingsContent>,
 255    /// The default language settings.
 256    #[serde(flatten)]
 257    pub defaults: LanguageSettingsContent,
 258    /// The settings for individual languages.
 259    #[serde(default)]
 260    pub languages: HashMap<LanguageName, LanguageSettingsContent>,
 261    /// Settings for associating file extensions and filenames
 262    /// with languages.
 263    #[serde(default)]
 264    pub file_types: HashMap<Arc<str>, Vec<String>>,
 265}
 266
 267/// The settings for a particular language.
 268#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
 269pub struct LanguageSettingsContent {
 270    /// How many columns a tab should occupy.
 271    ///
 272    /// Default: 4
 273    #[serde(default)]
 274    pub tab_size: Option<NonZeroU32>,
 275    /// Whether to indent lines using tab characters, as opposed to multiple
 276    /// spaces.
 277    ///
 278    /// Default: false
 279    #[serde(default)]
 280    pub hard_tabs: Option<bool>,
 281    /// How to soft-wrap long lines of text.
 282    ///
 283    /// Default: none
 284    #[serde(default)]
 285    pub soft_wrap: Option<SoftWrap>,
 286    /// The column at which to soft-wrap lines, for buffers where soft-wrap
 287    /// is enabled.
 288    ///
 289    /// Default: 80
 290    #[serde(default)]
 291    pub preferred_line_length: Option<u32>,
 292    /// Whether to show wrap guides in the editor. Setting this to true will
 293    /// show a guide at the 'preferred_line_length' value if softwrap is set to
 294    /// 'preferred_line_length', and will show any additional guides as specified
 295    /// by the 'wrap_guides' setting.
 296    ///
 297    /// Default: true
 298    #[serde(default)]
 299    pub show_wrap_guides: Option<bool>,
 300    /// Character counts at which to show wrap guides in the editor.
 301    ///
 302    /// Default: []
 303    #[serde(default)]
 304    pub wrap_guides: Option<Vec<usize>>,
 305    /// Indent guide related settings.
 306    #[serde(default)]
 307    pub indent_guides: Option<IndentGuideSettings>,
 308    /// Whether or not to perform a buffer format before saving.
 309    ///
 310    /// Default: on
 311    #[serde(default)]
 312    pub format_on_save: Option<FormatOnSave>,
 313    /// Whether or not to remove any trailing whitespace from lines of a buffer
 314    /// before saving it.
 315    ///
 316    /// Default: true
 317    #[serde(default)]
 318    pub remove_trailing_whitespace_on_save: Option<bool>,
 319    /// Whether or not to ensure there's a single newline at the end of a buffer
 320    /// when saving it.
 321    ///
 322    /// Default: true
 323    #[serde(default)]
 324    pub ensure_final_newline_on_save: Option<bool>,
 325    /// How to perform a buffer format.
 326    ///
 327    /// Default: auto
 328    #[serde(default)]
 329    pub formatter: Option<SelectedFormatter>,
 330    /// Zed's Prettier integration settings.
 331    /// Allows to enable/disable formatting with Prettier
 332    /// and configure default Prettier, used when no project-level Prettier installation is found.
 333    ///
 334    /// Default: off
 335    #[serde(default)]
 336    pub prettier: Option<PrettierSettings>,
 337    /// Whether to use language servers to provide code intelligence.
 338    ///
 339    /// Default: true
 340    #[serde(default)]
 341    pub enable_language_server: Option<bool>,
 342    /// The list of language servers to use (or disable) for this language.
 343    ///
 344    /// This array should consist of language server IDs, as well as the following
 345    /// special tokens:
 346    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
 347    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
 348    ///
 349    /// Default: ["..."]
 350    #[serde(default)]
 351    pub language_servers: Option<Vec<String>>,
 352    /// Controls whether edit predictions are shown immediately (true)
 353    /// or manually by triggering `editor::ShowEditPrediction` (false).
 354    ///
 355    /// Default: true
 356    #[serde(default)]
 357    pub show_edit_predictions: Option<bool>,
 358    /// Controls whether edit predictions are shown in the given language
 359    /// scopes.
 360    ///
 361    /// Example: ["string", "comment"]
 362    ///
 363    /// Default: []
 364    #[serde(default)]
 365    pub edit_predictions_disabled_in: Option<Vec<String>>,
 366    /// Whether to show tabs and spaces in the editor.
 367    #[serde(default)]
 368    pub show_whitespaces: Option<ShowWhitespaceSetting>,
 369    /// Whether to start a new line with a comment when a previous line is a comment as well.
 370    ///
 371    /// Default: true
 372    #[serde(default)]
 373    pub extend_comment_on_newline: Option<bool>,
 374    /// Inlay hint related settings.
 375    #[serde(default)]
 376    pub inlay_hints: Option<InlayHintSettings>,
 377    /// Whether to automatically type closing characters for you. For example,
 378    /// when you type (, Zed will automatically add a closing ) at the correct position.
 379    ///
 380    /// Default: true
 381    pub use_autoclose: Option<bool>,
 382    /// Whether to automatically surround text with characters for you. For example,
 383    /// when you select text and type (, Zed will automatically surround text with ().
 384    ///
 385    /// Default: true
 386    pub use_auto_surround: Option<bool>,
 387    /// Controls how the editor handles the autoclosed characters.
 388    /// When set to `false`(default), skipping over and auto-removing of the closing characters
 389    /// happen only for auto-inserted characters.
 390    /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
 391    /// no matter how they were inserted.
 392    ///
 393    /// Default: false
 394    pub always_treat_brackets_as_autoclosed: Option<bool>,
 395    /// Whether to use additional LSP queries to format (and amend) the code after
 396    /// every "trigger" symbol input, defined by LSP server capabilities.
 397    ///
 398    /// Default: true
 399    pub use_on_type_format: Option<bool>,
 400    /// Which code actions to run on save after the formatter.
 401    /// These are not run if formatting is off.
 402    ///
 403    /// Default: {} (or {"source.organizeImports": true} for Go).
 404    pub code_actions_on_format: Option<HashMap<String, bool>>,
 405    /// Whether to perform linked edits of associated ranges, if the language server supports it.
 406    /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
 407    ///
 408    /// Default: true
 409    pub linked_edits: Option<bool>,
 410    /// Whether indentation of pasted content should be adjusted based on the context.
 411    ///
 412    /// Default: true
 413    pub auto_indent_on_paste: Option<bool>,
 414    /// Task configuration for this language.
 415    ///
 416    /// Default: {}
 417    pub tasks: Option<LanguageTaskConfig>,
 418    /// Whether to pop the completions menu while typing in an editor without
 419    /// explicitly requesting it.
 420    ///
 421    /// Default: true
 422    pub show_completions_on_input: Option<bool>,
 423    /// Whether to display inline and alongside documentation for items in the
 424    /// completions menu.
 425    ///
 426    /// Default: true
 427    pub show_completion_documentation: Option<bool>,
 428}
 429
 430/// The contents of the edit prediction settings.
 431#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
 432pub struct EditPredictionSettingsContent {
 433    /// A list of globs representing files that edit predictions should be disabled for.
 434    /// This list adds to a pre-existing, sensible default set of globs.
 435    /// Any additional ones you add are combined with them.
 436    #[serde(default)]
 437    pub disabled_globs: Option<Vec<String>>,
 438    /// The mode used to display edit predictions in the buffer.
 439    /// Provider support required.
 440    #[serde(default)]
 441    pub mode: EditPredictionsMode,
 442}
 443
 444/// The settings for enabling/disabling features.
 445#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
 446#[serde(rename_all = "snake_case")]
 447pub struct FeaturesContent {
 448    /// Whether the GitHub Copilot feature is enabled.
 449    pub copilot: Option<bool>,
 450    /// Determines which edit prediction provider to use.
 451    pub edit_prediction_provider: Option<EditPredictionProvider>,
 452}
 453
 454/// Controls the soft-wrapping behavior in the editor.
 455#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 456#[serde(rename_all = "snake_case")]
 457pub enum SoftWrap {
 458    /// Prefer a single line generally, unless an overly long line is encountered.
 459    None,
 460    /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
 461    /// Prefer a single line generally, unless an overly long line is encountered.
 462    PreferLine,
 463    /// Soft wrap lines that exceed the editor width.
 464    EditorWidth,
 465    /// Soft wrap lines at the preferred line length.
 466    PreferredLineLength,
 467    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
 468    Bounded,
 469}
 470
 471/// Controls the behavior of formatting files when they are saved.
 472#[derive(Debug, Clone, PartialEq, Eq)]
 473pub enum FormatOnSave {
 474    /// Files should be formatted on save.
 475    On,
 476    /// Files should not be formatted on save.
 477    Off,
 478    List(FormatterList),
 479}
 480
 481impl JsonSchema for FormatOnSave {
 482    fn schema_name() -> String {
 483        "OnSaveFormatter".into()
 484    }
 485
 486    fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> Schema {
 487        let mut schema = SchemaObject::default();
 488        let formatter_schema = Formatter::json_schema(generator);
 489        schema.instance_type = Some(
 490            vec![
 491                InstanceType::Object,
 492                InstanceType::String,
 493                InstanceType::Array,
 494            ]
 495            .into(),
 496        );
 497
 498        let valid_raw_values = SchemaObject {
 499            enum_values: Some(vec![
 500                Value::String("on".into()),
 501                Value::String("off".into()),
 502                Value::String("prettier".into()),
 503                Value::String("language_server".into()),
 504            ]),
 505            ..Default::default()
 506        };
 507        let mut nested_values = SchemaObject::default();
 508
 509        nested_values.array().items = Some(formatter_schema.clone().into());
 510
 511        schema.subschemas().any_of = Some(vec![
 512            nested_values.into(),
 513            valid_raw_values.into(),
 514            formatter_schema,
 515        ]);
 516        schema.into()
 517    }
 518}
 519
 520impl Serialize for FormatOnSave {
 521    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
 522    where
 523        S: serde::Serializer,
 524    {
 525        match self {
 526            Self::On => serializer.serialize_str("on"),
 527            Self::Off => serializer.serialize_str("off"),
 528            Self::List(list) => list.serialize(serializer),
 529        }
 530    }
 531}
 532
 533impl<'de> Deserialize<'de> for FormatOnSave {
 534    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
 535    where
 536        D: Deserializer<'de>,
 537    {
 538        struct FormatDeserializer;
 539
 540        impl<'d> Visitor<'d> for FormatDeserializer {
 541            type Value = FormatOnSave;
 542
 543            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 544                formatter.write_str("a valid on-save formatter kind")
 545            }
 546            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
 547            where
 548                E: serde::de::Error,
 549            {
 550                if v == "on" {
 551                    Ok(Self::Value::On)
 552                } else if v == "off" {
 553                    Ok(Self::Value::Off)
 554                } else if v == "language_server" {
 555                    Ok(Self::Value::List(FormatterList(
 556                        Formatter::LanguageServer { name: None }.into(),
 557                    )))
 558                } else {
 559                    let ret: Result<FormatterList, _> =
 560                        Deserialize::deserialize(v.into_deserializer());
 561                    ret.map(Self::Value::List)
 562                }
 563            }
 564            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
 565            where
 566                A: MapAccess<'d>,
 567            {
 568                let ret: Result<FormatterList, _> =
 569                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
 570                ret.map(Self::Value::List)
 571            }
 572            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
 573            where
 574                A: SeqAccess<'d>,
 575            {
 576                let ret: Result<FormatterList, _> =
 577                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
 578                ret.map(Self::Value::List)
 579            }
 580        }
 581        deserializer.deserialize_any(FormatDeserializer)
 582    }
 583}
 584
 585/// Controls how whitespace should be displayedin the editor.
 586#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 587#[serde(rename_all = "snake_case")]
 588pub enum ShowWhitespaceSetting {
 589    /// Draw whitespace only for the selected text.
 590    Selection,
 591    /// Do not draw any tabs or spaces.
 592    None,
 593    /// Draw all invisible symbols.
 594    All,
 595    /// Draw whitespaces at boundaries only.
 596    ///
 597    /// For a whitespace to be on a boundary, any of the following conditions need to be met:
 598    /// - It is a tab
 599    /// - It is adjacent to an edge (start or end)
 600    /// - It is adjacent to a whitespace (left or right)
 601    Boundary,
 602}
 603
 604/// Controls which formatter should be used when formatting code.
 605#[derive(Clone, Debug, Default, PartialEq, Eq)]
 606pub enum SelectedFormatter {
 607    /// Format files using Zed's Prettier integration (if applicable),
 608    /// or falling back to formatting via language server.
 609    #[default]
 610    Auto,
 611    List(FormatterList),
 612}
 613
 614impl JsonSchema for SelectedFormatter {
 615    fn schema_name() -> String {
 616        "Formatter".into()
 617    }
 618
 619    fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> Schema {
 620        let mut schema = SchemaObject::default();
 621        let formatter_schema = Formatter::json_schema(generator);
 622        schema.instance_type = Some(
 623            vec![
 624                InstanceType::Object,
 625                InstanceType::String,
 626                InstanceType::Array,
 627            ]
 628            .into(),
 629        );
 630
 631        let valid_raw_values = SchemaObject {
 632            enum_values: Some(vec![
 633                Value::String("auto".into()),
 634                Value::String("prettier".into()),
 635                Value::String("language_server".into()),
 636            ]),
 637            ..Default::default()
 638        };
 639
 640        let mut nested_values = SchemaObject::default();
 641
 642        nested_values.array().items = Some(formatter_schema.clone().into());
 643
 644        schema.subschemas().any_of = Some(vec![
 645            nested_values.into(),
 646            valid_raw_values.into(),
 647            formatter_schema,
 648        ]);
 649        schema.into()
 650    }
 651}
 652
 653impl Serialize for SelectedFormatter {
 654    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
 655    where
 656        S: serde::Serializer,
 657    {
 658        match self {
 659            SelectedFormatter::Auto => serializer.serialize_str("auto"),
 660            SelectedFormatter::List(list) => list.serialize(serializer),
 661        }
 662    }
 663}
 664impl<'de> Deserialize<'de> for SelectedFormatter {
 665    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
 666    where
 667        D: Deserializer<'de>,
 668    {
 669        struct FormatDeserializer;
 670
 671        impl<'d> Visitor<'d> for FormatDeserializer {
 672            type Value = SelectedFormatter;
 673
 674            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 675                formatter.write_str("a valid formatter kind")
 676            }
 677            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
 678            where
 679                E: serde::de::Error,
 680            {
 681                if v == "auto" {
 682                    Ok(Self::Value::Auto)
 683                } else if v == "language_server" {
 684                    Ok(Self::Value::List(FormatterList(
 685                        Formatter::LanguageServer { name: None }.into(),
 686                    )))
 687                } else {
 688                    let ret: Result<FormatterList, _> =
 689                        Deserialize::deserialize(v.into_deserializer());
 690                    ret.map(SelectedFormatter::List)
 691                }
 692            }
 693            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
 694            where
 695                A: MapAccess<'d>,
 696            {
 697                let ret: Result<FormatterList, _> =
 698                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
 699                ret.map(SelectedFormatter::List)
 700            }
 701            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
 702            where
 703                A: SeqAccess<'d>,
 704            {
 705                let ret: Result<FormatterList, _> =
 706                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
 707                ret.map(SelectedFormatter::List)
 708            }
 709        }
 710        deserializer.deserialize_any(FormatDeserializer)
 711    }
 712}
 713/// Controls which formatter should be used when formatting code.
 714#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 715#[serde(rename_all = "snake_case", transparent)]
 716pub struct FormatterList(pub SingleOrVec<Formatter>);
 717
 718impl AsRef<[Formatter]> for FormatterList {
 719    fn as_ref(&self) -> &[Formatter] {
 720        match &self.0 {
 721            SingleOrVec::Single(single) => slice::from_ref(single),
 722            SingleOrVec::Vec(v) => v,
 723        }
 724    }
 725}
 726
 727/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
 728#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 729#[serde(rename_all = "snake_case")]
 730pub enum Formatter {
 731    /// Format code using the current language server.
 732    LanguageServer { name: Option<String> },
 733    /// Format code using Zed's Prettier integration.
 734    Prettier,
 735    /// Format code using an external command.
 736    External {
 737        /// The external program to run.
 738        command: Arc<str>,
 739        /// The arguments to pass to the program.
 740        arguments: Option<Arc<[String]>>,
 741    },
 742    /// Files should be formatted using code actions executed by language servers.
 743    CodeActions(HashMap<String, bool>),
 744}
 745
 746/// The settings for indent guides.
 747#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
 748pub struct IndentGuideSettings {
 749    /// Whether to display indent guides in the editor.
 750    ///
 751    /// Default: true
 752    #[serde(default = "default_true")]
 753    pub enabled: bool,
 754    /// The width of the indent guides in pixels, between 1 and 10.
 755    ///
 756    /// Default: 1
 757    #[serde(default = "line_width")]
 758    pub line_width: u32,
 759    /// The width of the active indent guide in pixels, between 1 and 10.
 760    ///
 761    /// Default: 1
 762    #[serde(default = "active_line_width")]
 763    pub active_line_width: u32,
 764    /// Determines how indent guides are colored.
 765    ///
 766    /// Default: Fixed
 767    #[serde(default)]
 768    pub coloring: IndentGuideColoring,
 769    /// Determines how indent guide backgrounds are colored.
 770    ///
 771    /// Default: Disabled
 772    #[serde(default)]
 773    pub background_coloring: IndentGuideBackgroundColoring,
 774}
 775
 776fn line_width() -> u32 {
 777    1
 778}
 779
 780fn active_line_width() -> u32 {
 781    line_width()
 782}
 783
 784/// Determines how indent guides are colored.
 785#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
 786#[serde(rename_all = "snake_case")]
 787pub enum IndentGuideColoring {
 788    /// Do not render any lines for indent guides.
 789    Disabled,
 790    /// Use the same color for all indentation levels.
 791    #[default]
 792    Fixed,
 793    /// Use a different color for each indentation level.
 794    IndentAware,
 795}
 796
 797/// Determines how indent guide backgrounds are colored.
 798#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
 799#[serde(rename_all = "snake_case")]
 800pub enum IndentGuideBackgroundColoring {
 801    /// Do not render any background for indent guides.
 802    #[default]
 803    Disabled,
 804    /// Use a different color for each indentation level.
 805    IndentAware,
 806}
 807
 808/// The settings for inlay hints.
 809#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 810pub struct InlayHintSettings {
 811    /// Global switch to toggle hints on and off.
 812    ///
 813    /// Default: false
 814    #[serde(default)]
 815    pub enabled: bool,
 816    /// Whether type hints should be shown.
 817    ///
 818    /// Default: true
 819    #[serde(default = "default_true")]
 820    pub show_type_hints: bool,
 821    /// Whether parameter hints should be shown.
 822    ///
 823    /// Default: true
 824    #[serde(default = "default_true")]
 825    pub show_parameter_hints: bool,
 826    /// Whether other hints should be shown.
 827    ///
 828    /// Default: true
 829    #[serde(default = "default_true")]
 830    pub show_other_hints: bool,
 831    /// Whether to show a background for inlay hints.
 832    ///
 833    /// If set to `true`, the background will use the `hint.background` color
 834    /// from the current theme.
 835    ///
 836    /// Default: false
 837    #[serde(default)]
 838    pub show_background: bool,
 839    /// Whether or not to debounce inlay hints updates after buffer edits.
 840    ///
 841    /// Set to 0 to disable debouncing.
 842    ///
 843    /// Default: 700
 844    #[serde(default = "edit_debounce_ms")]
 845    pub edit_debounce_ms: u64,
 846    /// Whether or not to debounce inlay hints updates after buffer scrolls.
 847    ///
 848    /// Set to 0 to disable debouncing.
 849    ///
 850    /// Default: 50
 851    #[serde(default = "scroll_debounce_ms")]
 852    pub scroll_debounce_ms: u64,
 853}
 854
 855fn edit_debounce_ms() -> u64 {
 856    700
 857}
 858
 859fn scroll_debounce_ms() -> u64 {
 860    50
 861}
 862
 863/// The task settings for a particular language.
 864#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
 865pub struct LanguageTaskConfig {
 866    /// Extra task variables to set for a particular language.
 867    pub variables: HashMap<String, String>,
 868}
 869
 870impl InlayHintSettings {
 871    /// Returns the kinds of inlay hints that are enabled based on the settings.
 872    pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
 873        let mut kinds = HashSet::default();
 874        if self.show_type_hints {
 875            kinds.insert(Some(InlayHintKind::Type));
 876        }
 877        if self.show_parameter_hints {
 878            kinds.insert(Some(InlayHintKind::Parameter));
 879        }
 880        if self.show_other_hints {
 881            kinds.insert(None);
 882        }
 883        kinds
 884    }
 885}
 886
 887impl AllLanguageSettings {
 888    /// Returns the [`LanguageSettings`] for the language with the specified name.
 889    pub fn language<'a>(
 890        &'a self,
 891        location: Option<SettingsLocation<'a>>,
 892        language_name: Option<&LanguageName>,
 893        cx: &'a App,
 894    ) -> Cow<'a, LanguageSettings> {
 895        let settings = language_name
 896            .and_then(|name| self.languages.get(name))
 897            .unwrap_or(&self.defaults);
 898
 899        let editorconfig_properties = location.and_then(|location| {
 900            cx.global::<SettingsStore>()
 901                .editorconfig_properties(location.worktree_id, location.path)
 902        });
 903        if let Some(editorconfig_properties) = editorconfig_properties {
 904            let mut settings = settings.clone();
 905            merge_with_editorconfig(&mut settings, &editorconfig_properties);
 906            Cow::Owned(settings)
 907        } else {
 908            Cow::Borrowed(settings)
 909        }
 910    }
 911
 912    /// Returns whether edit predictions are enabled for the given path.
 913    pub fn inline_completions_enabled_for_path(&self, path: &Path) -> bool {
 914        !self
 915            .edit_predictions
 916            .disabled_globs
 917            .iter()
 918            .any(|glob| glob.is_match(path))
 919    }
 920
 921    /// Returns whether edit predictions are enabled for the given language and path.
 922    pub fn show_inline_completions(&self, language: Option<&Arc<Language>>, cx: &App) -> bool {
 923        self.language(None, language.map(|l| l.name()).as_ref(), cx)
 924            .show_edit_predictions
 925    }
 926
 927    /// Returns the edit predictions preview mode for the given language and path.
 928    pub fn edit_predictions_mode(&self) -> EditPredictionsMode {
 929        self.edit_predictions.mode
 930    }
 931}
 932
 933fn merge_with_editorconfig(settings: &mut LanguageSettings, cfg: &EditorconfigProperties) {
 934    let tab_size = cfg.get::<IndentSize>().ok().and_then(|v| match v {
 935        IndentSize::Value(u) => NonZeroU32::new(u as u32),
 936        IndentSize::UseTabWidth => cfg.get::<TabWidth>().ok().and_then(|w| match w {
 937            TabWidth::Value(u) => NonZeroU32::new(u as u32),
 938        }),
 939    });
 940    let hard_tabs = cfg
 941        .get::<IndentStyle>()
 942        .map(|v| v.eq(&IndentStyle::Tabs))
 943        .ok();
 944    let ensure_final_newline_on_save = cfg
 945        .get::<FinalNewline>()
 946        .map(|v| match v {
 947            FinalNewline::Value(b) => b,
 948        })
 949        .ok();
 950    let remove_trailing_whitespace_on_save = cfg
 951        .get::<TrimTrailingWs>()
 952        .map(|v| match v {
 953            TrimTrailingWs::Value(b) => b,
 954        })
 955        .ok();
 956    fn merge<T>(target: &mut T, value: Option<T>) {
 957        if let Some(value) = value {
 958            *target = value;
 959        }
 960    }
 961    merge(&mut settings.tab_size, tab_size);
 962    merge(&mut settings.hard_tabs, hard_tabs);
 963    merge(
 964        &mut settings.remove_trailing_whitespace_on_save,
 965        remove_trailing_whitespace_on_save,
 966    );
 967    merge(
 968        &mut settings.ensure_final_newline_on_save,
 969        ensure_final_newline_on_save,
 970    );
 971}
 972
 973/// The kind of an inlay hint.
 974#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 975pub enum InlayHintKind {
 976    /// An inlay hint for a type.
 977    Type,
 978    /// An inlay hint for a parameter.
 979    Parameter,
 980}
 981
 982impl InlayHintKind {
 983    /// Returns the [`InlayHintKind`] from the given name.
 984    ///
 985    /// Returns `None` if `name` does not match any of the expected
 986    /// string representations.
 987    pub fn from_name(name: &str) -> Option<Self> {
 988        match name {
 989            "type" => Some(InlayHintKind::Type),
 990            "parameter" => Some(InlayHintKind::Parameter),
 991            _ => None,
 992        }
 993    }
 994
 995    /// Returns the name of this [`InlayHintKind`].
 996    pub fn name(&self) -> &'static str {
 997        match self {
 998            InlayHintKind::Type => "type",
 999            InlayHintKind::Parameter => "parameter",
1000        }
1001    }
1002}
1003
1004impl settings::Settings for AllLanguageSettings {
1005    const KEY: Option<&'static str> = None;
1006
1007    type FileContent = AllLanguageSettingsContent;
1008
1009    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1010        let default_value = sources.default;
1011
1012        // A default is provided for all settings.
1013        let mut defaults: LanguageSettings =
1014            serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
1015
1016        let mut languages = HashMap::default();
1017        for (language_name, settings) in &default_value.languages {
1018            let mut language_settings = defaults.clone();
1019            merge_settings(&mut language_settings, settings);
1020            languages.insert(language_name.clone(), language_settings);
1021        }
1022
1023        let mut copilot_enabled = default_value.features.as_ref().and_then(|f| f.copilot);
1024        let mut edit_prediction_provider = default_value
1025            .features
1026            .as_ref()
1027            .and_then(|f| f.edit_prediction_provider);
1028        let mut edit_predictions_mode = default_value
1029            .edit_predictions
1030            .as_ref()
1031            .map(|edit_predictions| edit_predictions.mode)
1032            .ok_or_else(Self::missing_default)?;
1033
1034        let mut completion_globs: HashSet<&String> = default_value
1035            .edit_predictions
1036            .as_ref()
1037            .and_then(|c| c.disabled_globs.as_ref())
1038            .map(|globs| globs.iter().collect())
1039            .ok_or_else(Self::missing_default)?;
1040
1041        let mut file_types: HashMap<Arc<str>, GlobSet> = HashMap::default();
1042
1043        for (language, suffixes) in &default_value.file_types {
1044            let mut builder = GlobSetBuilder::new();
1045
1046            for suffix in suffixes {
1047                builder.add(Glob::new(suffix)?);
1048            }
1049
1050            file_types.insert(language.clone(), builder.build()?);
1051        }
1052
1053        for user_settings in sources.customizations() {
1054            if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
1055                copilot_enabled = Some(copilot);
1056            }
1057            if let Some(provider) = user_settings
1058                .features
1059                .as_ref()
1060                .and_then(|f| f.edit_prediction_provider)
1061            {
1062                edit_prediction_provider = Some(provider);
1063            }
1064
1065            if let Some(edit_predictions) = user_settings.edit_predictions.as_ref() {
1066                edit_predictions_mode = edit_predictions.mode;
1067
1068                if let Some(disabled_globs) = edit_predictions.disabled_globs.as_ref() {
1069                    completion_globs.extend(disabled_globs.iter());
1070                }
1071            }
1072
1073            // A user's global settings override the default global settings and
1074            // all default language-specific settings.
1075            merge_settings(&mut defaults, &user_settings.defaults);
1076            for language_settings in languages.values_mut() {
1077                merge_settings(language_settings, &user_settings.defaults);
1078            }
1079
1080            // A user's language-specific settings override default language-specific settings.
1081            for (language_name, user_language_settings) in &user_settings.languages {
1082                merge_settings(
1083                    languages
1084                        .entry(language_name.clone())
1085                        .or_insert_with(|| defaults.clone()),
1086                    user_language_settings,
1087                );
1088            }
1089
1090            for (language, suffixes) in &user_settings.file_types {
1091                let mut builder = GlobSetBuilder::new();
1092
1093                let default_value = default_value.file_types.get(&language.clone());
1094
1095                // Merge the default value with the user's value.
1096                if let Some(suffixes) = default_value {
1097                    for suffix in suffixes {
1098                        builder.add(Glob::new(suffix)?);
1099                    }
1100                }
1101
1102                for suffix in suffixes {
1103                    builder.add(Glob::new(suffix)?);
1104                }
1105
1106                file_types.insert(language.clone(), builder.build()?);
1107            }
1108        }
1109
1110        Ok(Self {
1111            edit_predictions: EditPredictionSettings {
1112                provider: if let Some(provider) = edit_prediction_provider {
1113                    provider
1114                } else if copilot_enabled.unwrap_or(true) {
1115                    EditPredictionProvider::Copilot
1116                } else {
1117                    EditPredictionProvider::None
1118                },
1119                disabled_globs: completion_globs
1120                    .iter()
1121                    .filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
1122                    .collect(),
1123                mode: edit_predictions_mode,
1124            },
1125            defaults,
1126            languages,
1127            file_types,
1128        })
1129    }
1130
1131    fn json_schema(
1132        generator: &mut schemars::gen::SchemaGenerator,
1133        params: &settings::SettingsJsonSchemaParams,
1134        _: &App,
1135    ) -> schemars::schema::RootSchema {
1136        let mut root_schema = generator.root_schema_for::<Self::FileContent>();
1137
1138        // Create a schema for a 'languages overrides' object, associating editor
1139        // settings with specific languages.
1140        assert!(root_schema
1141            .definitions
1142            .contains_key("LanguageSettingsContent"));
1143
1144        let languages_object_schema = SchemaObject {
1145            instance_type: Some(InstanceType::Object.into()),
1146            object: Some(Box::new(ObjectValidation {
1147                properties: params
1148                    .language_names
1149                    .iter()
1150                    .map(|name| {
1151                        (
1152                            name.clone(),
1153                            Schema::new_ref("#/definitions/LanguageSettingsContent".into()),
1154                        )
1155                    })
1156                    .collect(),
1157                ..Default::default()
1158            })),
1159            ..Default::default()
1160        };
1161
1162        root_schema
1163            .definitions
1164            .extend([("Languages".into(), languages_object_schema.into())]);
1165
1166        add_references_to_properties(
1167            &mut root_schema,
1168            &[("languages", "#/definitions/Languages")],
1169        );
1170
1171        root_schema
1172    }
1173}
1174
1175fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
1176    fn merge<T>(target: &mut T, value: Option<T>) {
1177        if let Some(value) = value {
1178            *target = value;
1179        }
1180    }
1181
1182    merge(&mut settings.tab_size, src.tab_size);
1183    settings.tab_size = settings
1184        .tab_size
1185        .clamp(NonZeroU32::new(1).unwrap(), NonZeroU32::new(16).unwrap());
1186
1187    merge(&mut settings.hard_tabs, src.hard_tabs);
1188    merge(&mut settings.soft_wrap, src.soft_wrap);
1189    merge(&mut settings.use_autoclose, src.use_autoclose);
1190    merge(&mut settings.use_auto_surround, src.use_auto_surround);
1191    merge(&mut settings.use_on_type_format, src.use_on_type_format);
1192    merge(&mut settings.auto_indent_on_paste, src.auto_indent_on_paste);
1193    merge(
1194        &mut settings.always_treat_brackets_as_autoclosed,
1195        src.always_treat_brackets_as_autoclosed,
1196    );
1197    merge(&mut settings.show_wrap_guides, src.show_wrap_guides);
1198    merge(&mut settings.wrap_guides, src.wrap_guides.clone());
1199    merge(&mut settings.indent_guides, src.indent_guides);
1200    merge(
1201        &mut settings.code_actions_on_format,
1202        src.code_actions_on_format.clone(),
1203    );
1204    merge(&mut settings.linked_edits, src.linked_edits);
1205    merge(&mut settings.tasks, src.tasks.clone());
1206
1207    merge(
1208        &mut settings.preferred_line_length,
1209        src.preferred_line_length,
1210    );
1211    merge(&mut settings.formatter, src.formatter.clone());
1212    merge(&mut settings.prettier, src.prettier.clone());
1213    merge(&mut settings.format_on_save, src.format_on_save.clone());
1214    merge(
1215        &mut settings.remove_trailing_whitespace_on_save,
1216        src.remove_trailing_whitespace_on_save,
1217    );
1218    merge(
1219        &mut settings.ensure_final_newline_on_save,
1220        src.ensure_final_newline_on_save,
1221    );
1222    merge(
1223        &mut settings.enable_language_server,
1224        src.enable_language_server,
1225    );
1226    merge(&mut settings.language_servers, src.language_servers.clone());
1227    merge(
1228        &mut settings.show_edit_predictions,
1229        src.show_edit_predictions,
1230    );
1231    merge(
1232        &mut settings.edit_predictions_disabled_in,
1233        src.edit_predictions_disabled_in.clone(),
1234    );
1235    merge(&mut settings.show_whitespaces, src.show_whitespaces);
1236    merge(
1237        &mut settings.extend_comment_on_newline,
1238        src.extend_comment_on_newline,
1239    );
1240    merge(&mut settings.inlay_hints, src.inlay_hints);
1241    merge(
1242        &mut settings.show_completions_on_input,
1243        src.show_completions_on_input,
1244    );
1245    merge(
1246        &mut settings.show_completion_documentation,
1247        src.show_completion_documentation,
1248    );
1249}
1250
1251/// Allows to enable/disable formatting with Prettier
1252/// and configure default Prettier, used when no project-level Prettier installation is found.
1253/// Prettier formatting is disabled by default.
1254#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1255pub struct PrettierSettings {
1256    /// Enables or disables formatting with Prettier for a given language.
1257    #[serde(default)]
1258    pub allowed: bool,
1259
1260    /// Forces Prettier integration to use a specific parser name when formatting files with the language.
1261    #[serde(default)]
1262    pub parser: Option<String>,
1263
1264    /// Forces Prettier integration to use specific plugins when formatting files with the language.
1265    /// The default Prettier will be installed with these plugins.
1266    #[serde(default)]
1267    pub plugins: HashSet<String>,
1268
1269    /// Default Prettier options, in the format as in package.json section for Prettier.
1270    /// If project installs Prettier via its package.json, these options will be ignored.
1271    #[serde(flatten)]
1272    pub options: HashMap<String, serde_json::Value>,
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277    use super::*;
1278
1279    #[test]
1280    fn test_formatter_deserialization() {
1281        let raw_auto = "{\"formatter\": \"auto\"}";
1282        let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
1283        assert_eq!(settings.formatter, Some(SelectedFormatter::Auto));
1284        let raw = "{\"formatter\": \"language_server\"}";
1285        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1286        assert_eq!(
1287            settings.formatter,
1288            Some(SelectedFormatter::List(FormatterList(
1289                Formatter::LanguageServer { name: None }.into()
1290            )))
1291        );
1292        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
1293        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1294        assert_eq!(
1295            settings.formatter,
1296            Some(SelectedFormatter::List(FormatterList(
1297                vec![Formatter::LanguageServer { name: None }].into()
1298            )))
1299        );
1300        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"prettier\"]}";
1301        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1302        assert_eq!(
1303            settings.formatter,
1304            Some(SelectedFormatter::List(FormatterList(
1305                vec![
1306                    Formatter::LanguageServer { name: None },
1307                    Formatter::Prettier
1308                ]
1309                .into()
1310            )))
1311        );
1312    }
1313
1314    #[test]
1315    fn test_formatter_deserialization_invalid() {
1316        let raw_auto = "{\"formatter\": {}}";
1317        let result: Result<LanguageSettingsContent, _> = serde_json::from_str(raw_auto);
1318        assert!(result.is_err());
1319    }
1320
1321    #[test]
1322    pub fn test_resolve_language_servers() {
1323        fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
1324            names
1325                .iter()
1326                .copied()
1327                .map(|name| LanguageServerName(name.to_string().into()))
1328                .collect::<Vec<_>>()
1329        }
1330
1331        let available_language_servers = language_server_names(&[
1332            "typescript-language-server",
1333            "biome",
1334            "deno",
1335            "eslint",
1336            "tailwind",
1337        ]);
1338
1339        // A value of just `["..."]` is the same as taking all of the available language servers.
1340        assert_eq!(
1341            LanguageSettings::resolve_language_servers(
1342                &[LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()],
1343                &available_language_servers,
1344            ),
1345            available_language_servers
1346        );
1347
1348        // Referencing one of the available language servers will change its order.
1349        assert_eq!(
1350            LanguageSettings::resolve_language_servers(
1351                &[
1352                    "biome".into(),
1353                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into(),
1354                    "deno".into()
1355                ],
1356                &available_language_servers
1357            ),
1358            language_server_names(&[
1359                "biome",
1360                "typescript-language-server",
1361                "eslint",
1362                "tailwind",
1363                "deno",
1364            ])
1365        );
1366
1367        // Negating an available language server removes it from the list.
1368        assert_eq!(
1369            LanguageSettings::resolve_language_servers(
1370                &[
1371                    "deno".into(),
1372                    "!typescript-language-server".into(),
1373                    "!biome".into(),
1374                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1375                ],
1376                &available_language_servers
1377            ),
1378            language_server_names(&["deno", "eslint", "tailwind"])
1379        );
1380
1381        // Adding a language server not in the list of available language servers adds it to the list.
1382        assert_eq!(
1383            LanguageSettings::resolve_language_servers(
1384                &[
1385                    "my-cool-language-server".into(),
1386                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1387                ],
1388                &available_language_servers
1389            ),
1390            language_server_names(&[
1391                "my-cool-language-server",
1392                "typescript-language-server",
1393                "biome",
1394                "deno",
1395                "eslint",
1396                "tailwind",
1397            ])
1398        );
1399    }
1400}