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