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