language_settings.rs

   1//! Provides `language`-related settings.
   2
   3use crate::{File, Language, LanguageName, LanguageServerName};
   4use anyhow::Result;
   5use collections::{FxHashMap, HashMap, HashSet};
   6use ec4rs::{
   7    Properties as EditorconfigProperties,
   8    property::{FinalNewline, IndentSize, IndentStyle, MaxLineLen, TabWidth, TrimTrailingWs},
   9};
  10use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
  11use gpui::{App, Modifiers, SharedString};
  12use itertools::{Either, Itertools};
  13use schemars::{JsonSchema, json_schema};
  14use serde::{
  15    Deserialize, Deserializer, Serialize,
  16    de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor},
  17};
  18
  19use settings::{
  20    ParameterizedJsonSchema, Settings, SettingsKey, SettingsLocation, SettingsSources,
  21    SettingsStore, SettingsUi,
  22};
  23use shellexpand;
  24use std::{borrow::Cow, num::NonZeroU32, path::Path, slice, sync::Arc};
  25use util::schemars::replace_subschema;
  26use util::serde::default_true;
  27
  28/// Initializes the language settings.
  29pub fn init(cx: &mut App) {
  30    AllLanguageSettings::register(cx);
  31}
  32
  33/// Returns the settings for the specified language from the provided file.
  34pub fn language_settings<'a>(
  35    language: Option<LanguageName>,
  36    file: Option<&'a Arc<dyn File>>,
  37    cx: &'a App,
  38) -> Cow<'a, LanguageSettings> {
  39    let location = file.map(|f| SettingsLocation {
  40        worktree_id: f.worktree_id(cx),
  41        path: f.path().as_ref(),
  42    });
  43    AllLanguageSettings::get(location, cx).language(location, language.as_ref(), cx)
  44}
  45
  46/// Returns the settings for all languages from the provided file.
  47pub fn all_language_settings<'a>(
  48    file: Option<&'a Arc<dyn File>>,
  49    cx: &'a App,
  50) -> &'a AllLanguageSettings {
  51    let location = file.map(|f| SettingsLocation {
  52        worktree_id: f.worktree_id(cx),
  53        path: f.path().as_ref(),
  54    });
  55    AllLanguageSettings::get(location, cx)
  56}
  57
  58/// The settings for all languages.
  59#[derive(Debug, Clone)]
  60pub struct AllLanguageSettings {
  61    /// The edit prediction settings.
  62    pub edit_predictions: EditPredictionSettings,
  63    pub defaults: LanguageSettings,
  64    languages: HashMap<LanguageName, LanguageSettings>,
  65    pub(crate) file_types: FxHashMap<Arc<str>, GlobSet>,
  66}
  67
  68/// The settings for a particular language.
  69#[derive(Debug, Clone, Deserialize)]
  70pub struct LanguageSettings {
  71    /// How many columns a tab should occupy.
  72    pub tab_size: NonZeroU32,
  73    /// Whether to indent lines using tab characters, as opposed to multiple
  74    /// spaces.
  75    pub hard_tabs: bool,
  76    /// How to soft-wrap long lines of text.
  77    pub soft_wrap: SoftWrap,
  78    /// The column at which to soft-wrap lines, for buffers where soft-wrap
  79    /// is enabled.
  80    pub preferred_line_length: u32,
  81    /// Whether to show wrap guides (vertical rulers) in the editor.
  82    /// Setting this to true will show a guide at the 'preferred_line_length' value
  83    /// if softwrap is set to 'preferred_line_length', and will show any
  84    /// additional guides as specified by the 'wrap_guides' setting.
  85    pub show_wrap_guides: bool,
  86    /// Character counts at which to show wrap guides (vertical rulers) in the editor.
  87    pub wrap_guides: Vec<usize>,
  88    /// Indent guide related settings.
  89    pub indent_guides: IndentGuideSettings,
  90    /// Whether or not to perform a buffer format before saving.
  91    pub format_on_save: FormatOnSave,
  92    /// Whether or not to remove any trailing whitespace from lines of a buffer
  93    /// before saving it.
  94    pub remove_trailing_whitespace_on_save: bool,
  95    /// Whether or not to ensure there's a single newline at the end of a buffer
  96    /// when saving it.
  97    pub ensure_final_newline_on_save: bool,
  98    /// How to perform a buffer format.
  99    pub formatter: SelectedFormatter,
 100    /// Zed's Prettier integration settings.
 101    pub prettier: PrettierSettings,
 102    /// Whether to automatically close JSX tags.
 103    pub jsx_tag_auto_close: JsxTagAutoCloseSettings,
 104    /// Whether to use language servers to provide code intelligence.
 105    pub enable_language_server: bool,
 106    /// The list of language servers to use (or disable) for this language.
 107    ///
 108    /// This array should consist of language server IDs, as well as the following
 109    /// special tokens:
 110    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
 111    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
 112    pub language_servers: Vec<String>,
 113    /// Controls where the `editor::Rewrap` action is allowed for this language.
 114    ///
 115    /// Note: This setting has no effect in Vim mode, as rewrap is already
 116    /// allowed everywhere.
 117    pub allow_rewrap: RewrapBehavior,
 118    /// Controls whether edit predictions are shown immediately (true)
 119    /// or manually by triggering `editor::ShowEditPrediction` (false).
 120    pub show_edit_predictions: bool,
 121    /// Controls whether edit predictions are shown in the given language
 122    /// scopes.
 123    pub edit_predictions_disabled_in: Vec<String>,
 124    /// Whether to show tabs and spaces in the editor.
 125    pub show_whitespaces: ShowWhitespaceSetting,
 126    /// Visible characters used to render whitespace when show_whitespaces is enabled.
 127    pub whitespace_map: WhitespaceMap,
 128    /// Whether to start a new line with a comment when a previous line is a comment as well.
 129    pub extend_comment_on_newline: bool,
 130    /// Inlay hint related settings.
 131    pub inlay_hints: InlayHintSettings,
 132    /// Whether to automatically close brackets.
 133    pub use_autoclose: bool,
 134    /// Whether to automatically surround text with brackets.
 135    pub use_auto_surround: bool,
 136    /// Whether to use additional LSP queries to format (and amend) the code after
 137    /// every "trigger" symbol input, defined by LSP server capabilities.
 138    pub use_on_type_format: bool,
 139    /// Whether indentation should be adjusted based on the context whilst typing.
 140    pub auto_indent: bool,
 141    /// Whether indentation of pasted content should be adjusted based on the context.
 142    pub auto_indent_on_paste: bool,
 143    /// Controls how the editor handles the autoclosed characters.
 144    pub always_treat_brackets_as_autoclosed: bool,
 145    /// Which code actions to run on save
 146    pub code_actions_on_format: HashMap<String, bool>,
 147    /// Whether to perform linked edits
 148    pub linked_edits: bool,
 149    /// Task configuration for this language.
 150    pub tasks: LanguageTaskConfig,
 151    /// Whether to pop the completions menu while typing in an editor without
 152    /// explicitly requesting it.
 153    pub show_completions_on_input: bool,
 154    /// Whether to display inline and alongside documentation for items in the
 155    /// completions menu.
 156    pub show_completion_documentation: bool,
 157    /// Completion settings for this language.
 158    pub completions: CompletionSettings,
 159    /// Preferred debuggers for this language.
 160    pub debuggers: Vec<String>,
 161}
 162
 163impl LanguageSettings {
 164    /// A token representing the rest of the available language servers.
 165    const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
 166
 167    /// Returns the customized list of language servers from the list of
 168    /// available language servers.
 169    pub fn customized_language_servers(
 170        &self,
 171        available_language_servers: &[LanguageServerName],
 172    ) -> Vec<LanguageServerName> {
 173        Self::resolve_language_servers(&self.language_servers, available_language_servers)
 174    }
 175
 176    pub(crate) fn resolve_language_servers(
 177        configured_language_servers: &[String],
 178        available_language_servers: &[LanguageServerName],
 179    ) -> Vec<LanguageServerName> {
 180        let (disabled_language_servers, enabled_language_servers): (
 181            Vec<LanguageServerName>,
 182            Vec<LanguageServerName>,
 183        ) = configured_language_servers.iter().partition_map(
 184            |language_server| match language_server.strip_prefix('!') {
 185                Some(disabled) => Either::Left(LanguageServerName(disabled.to_string().into())),
 186                None => Either::Right(LanguageServerName(language_server.clone().into())),
 187            },
 188        );
 189
 190        let rest = available_language_servers
 191            .iter()
 192            .filter(|&available_language_server| {
 193                !disabled_language_servers.contains(available_language_server)
 194                    && !enabled_language_servers.contains(available_language_server)
 195            })
 196            .cloned()
 197            .collect::<Vec<_>>();
 198
 199        enabled_language_servers
 200            .into_iter()
 201            .flat_map(|language_server| {
 202                if language_server.0.as_ref() == Self::REST_OF_LANGUAGE_SERVERS {
 203                    rest.clone()
 204                } else {
 205                    vec![language_server]
 206                }
 207            })
 208            .collect::<Vec<_>>()
 209    }
 210}
 211
 212/// The provider that supplies edit predictions.
 213#[derive(
 214    Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi,
 215)]
 216#[serde(rename_all = "snake_case")]
 217pub enum EditPredictionProvider {
 218    None,
 219    #[default]
 220    Copilot,
 221    Supermaven,
 222    Zed,
 223    Zed2,
 224}
 225
 226impl EditPredictionProvider {
 227    pub fn is_zed(&self) -> bool {
 228        match self {
 229            EditPredictionProvider::Zed | EditPredictionProvider::Zed2 => true,
 230            EditPredictionProvider::None
 231            | EditPredictionProvider::Copilot
 232            | EditPredictionProvider::Supermaven => false,
 233        }
 234    }
 235}
 236
 237/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot)
 238/// or [Supermaven](https://supermaven.com).
 239#[derive(Clone, Debug, Default, SettingsUi)]
 240pub struct EditPredictionSettings {
 241    /// The provider that supplies edit predictions.
 242    pub provider: EditPredictionProvider,
 243    /// A list of globs representing files that edit predictions should be disabled for.
 244    /// This list adds to a pre-existing, sensible default set of globs.
 245    /// Any additional ones you add are combined with them.
 246    #[settings_ui(skip)]
 247    pub disabled_globs: Vec<DisabledGlob>,
 248    /// Configures how edit predictions are displayed in the buffer.
 249    pub mode: EditPredictionsMode,
 250    /// Settings specific to GitHub Copilot.
 251    pub copilot: CopilotSettings,
 252    /// Whether edit predictions are enabled in the assistant panel.
 253    /// This setting has no effect if globally disabled.
 254    pub enabled_in_text_threads: bool,
 255}
 256
 257impl EditPredictionSettings {
 258    /// Returns whether edit predictions are enabled for the given path.
 259    pub fn enabled_for_file(&self, file: &Arc<dyn File>, cx: &App) -> bool {
 260        !self.disabled_globs.iter().any(|glob| {
 261            if glob.is_absolute {
 262                file.as_local()
 263                    .is_some_and(|local| glob.matcher.is_match(local.abs_path(cx)))
 264            } else {
 265                glob.matcher.is_match(file.path())
 266            }
 267        })
 268    }
 269}
 270
 271#[derive(Clone, Debug)]
 272pub struct DisabledGlob {
 273    matcher: GlobMatcher,
 274    is_absolute: bool,
 275}
 276
 277/// The mode in which edit predictions should be displayed.
 278#[derive(
 279    Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi,
 280)]
 281#[serde(rename_all = "snake_case")]
 282pub enum EditPredictionsMode {
 283    /// If provider supports it, display inline when holding modifier key (e.g., alt).
 284    /// Otherwise, eager preview is used.
 285    #[serde(alias = "auto")]
 286    Subtle,
 287    /// Display inline when there are no language server completions available.
 288    #[default]
 289    #[serde(alias = "eager_preview")]
 290    Eager,
 291}
 292
 293#[derive(Clone, Debug, Default, SettingsUi)]
 294pub struct CopilotSettings {
 295    /// HTTP/HTTPS proxy to use for Copilot.
 296    #[settings_ui(skip)]
 297    pub proxy: Option<String>,
 298    /// Disable certificate verification for proxy (not recommended).
 299    pub proxy_no_verify: Option<bool>,
 300    /// Enterprise URI for Copilot.
 301    #[settings_ui(skip)]
 302    pub enterprise_uri: Option<String>,
 303}
 304
 305/// The settings for all languages.
 306#[derive(
 307    Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey,
 308)]
 309#[settings_key(None)]
 310#[settings_ui(group = "Default Language Settings")]
 311pub struct AllLanguageSettingsContent {
 312    /// The settings for enabling/disabling features.
 313    #[serde(default)]
 314    pub features: Option<FeaturesContent>,
 315    /// The edit prediction settings.
 316    #[serde(default)]
 317    pub edit_predictions: Option<EditPredictionSettingsContent>,
 318    /// The default language settings.
 319    #[serde(flatten)]
 320    pub defaults: LanguageSettingsContent,
 321    /// The settings for individual languages.
 322    #[serde(default)]
 323    pub languages: LanguageToSettingsMap,
 324    /// Settings for associating file extensions and filenames
 325    /// with languages.
 326    #[serde(default)]
 327    #[settings_ui(skip)]
 328    pub file_types: HashMap<Arc<str>, Vec<String>>,
 329}
 330
 331/// Map from language name to settings. Its `ParameterizedJsonSchema` allows only known language
 332/// names in the keys.
 333#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
 334pub struct LanguageToSettingsMap(pub HashMap<LanguageName, LanguageSettingsContent>);
 335
 336impl SettingsUi for LanguageToSettingsMap {
 337    fn settings_ui_item() -> settings::SettingsUiItem {
 338        settings::SettingsUiItem::DynamicMap(settings::SettingsUiItemDynamicMap {
 339            item: LanguageSettingsContent::settings_ui_item,
 340            defaults_path: &[],
 341            determine_items: |settings_value, cx| {
 342                use settings::SettingsUiEntryMetaData;
 343
 344                // todo(settings_ui): We should be using a global LanguageRegistry, but it's not implemented yet
 345                _ = cx;
 346
 347                let Some(settings_language_map) = settings_value.as_object() else {
 348                    return Vec::new();
 349                };
 350                let mut languages = Vec::with_capacity(settings_language_map.len());
 351
 352                for language_name in settings_language_map.keys().map(gpui::SharedString::from) {
 353                    languages.push(SettingsUiEntryMetaData {
 354                        title: language_name.clone(),
 355                        path: language_name,
 356                        // todo(settings_ui): Implement documentation for each language
 357                        // ideally based on the language's official docs from extension or builtin info
 358                        documentation: None,
 359                    });
 360                }
 361                return languages;
 362            },
 363        })
 364    }
 365}
 366
 367inventory::submit! {
 368    ParameterizedJsonSchema {
 369        add_and_get_ref: |generator, params, _cx| {
 370            let language_settings_content_ref = generator
 371                .subschema_for::<LanguageSettingsContent>()
 372                .to_value();
 373            replace_subschema::<LanguageToSettingsMap>(generator, || json_schema!({
 374                "type": "object",
 375                "properties": params
 376                    .language_names
 377                    .iter()
 378                    .map(|name| {
 379                        (
 380                            name.clone(),
 381                            language_settings_content_ref.clone(),
 382                        )
 383                    })
 384                    .collect::<serde_json::Map<_, _>>()
 385            }))
 386        }
 387    }
 388}
 389
 390/// Controls how completions are processed for this language.
 391#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)]
 392#[serde(rename_all = "snake_case")]
 393pub struct CompletionSettings {
 394    /// Controls how words are completed.
 395    /// For large documents, not all words may be fetched for completion.
 396    ///
 397    /// Default: `fallback`
 398    #[serde(default = "default_words_completion_mode")]
 399    pub words: WordsCompletionMode,
 400    /// How many characters has to be in the completions query to automatically show the words-based completions.
 401    /// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
 402    ///
 403    /// Default: 3
 404    #[serde(default = "default_3")]
 405    pub words_min_length: usize,
 406    /// Whether to fetch LSP completions or not.
 407    ///
 408    /// Default: true
 409    #[serde(default = "default_true")]
 410    pub lsp: bool,
 411    /// When fetching LSP completions, determines how long to wait for a response of a particular server.
 412    /// When set to 0, waits indefinitely.
 413    ///
 414    /// Default: 0
 415    #[serde(default)]
 416    pub lsp_fetch_timeout_ms: u64,
 417    /// Controls how LSP completions are inserted.
 418    ///
 419    /// Default: "replace_suffix"
 420    #[serde(default = "default_lsp_insert_mode")]
 421    pub lsp_insert_mode: LspInsertMode,
 422}
 423
 424/// Controls how document's words are completed.
 425#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 426#[serde(rename_all = "snake_case")]
 427pub enum WordsCompletionMode {
 428    /// Always fetch document's words for completions along with LSP completions.
 429    Enabled,
 430    /// Only if LSP response errors or times out,
 431    /// use document's words to show completions.
 432    Fallback,
 433    /// Never fetch or complete document's words for completions.
 434    /// (Word-based completions can still be queried via a separate action)
 435    Disabled,
 436}
 437
 438#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
 439#[serde(rename_all = "snake_case")]
 440pub enum LspInsertMode {
 441    /// Replaces text before the cursor, using the `insert` range described in the LSP specification.
 442    Insert,
 443    /// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
 444    Replace,
 445    /// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
 446    /// and like `"insert"` otherwise.
 447    ReplaceSubsequence,
 448    /// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
 449    /// `"insert"` otherwise.
 450    ReplaceSuffix,
 451}
 452
 453fn default_words_completion_mode() -> WordsCompletionMode {
 454    WordsCompletionMode::Fallback
 455}
 456
 457fn default_lsp_insert_mode() -> LspInsertMode {
 458    LspInsertMode::ReplaceSuffix
 459}
 460
 461fn default_3() -> usize {
 462    3
 463}
 464
 465/// The settings for a particular language.
 466#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, SettingsUi)]
 467#[settings_ui(group = "Default")]
 468pub struct LanguageSettingsContent {
 469    /// How many columns a tab should occupy.
 470    ///
 471    /// Default: 4
 472    #[serde(default)]
 473    #[settings_ui(skip)]
 474    pub tab_size: Option<NonZeroU32>,
 475    /// Whether to indent lines using tab characters, as opposed to multiple
 476    /// spaces.
 477    ///
 478    /// Default: false
 479    #[serde(default)]
 480    pub hard_tabs: Option<bool>,
 481    /// How to soft-wrap long lines of text.
 482    ///
 483    /// Default: none
 484    #[serde(default)]
 485    pub soft_wrap: Option<SoftWrap>,
 486    /// The column at which to soft-wrap lines, for buffers where soft-wrap
 487    /// is enabled.
 488    ///
 489    /// Default: 80
 490    #[serde(default)]
 491    pub preferred_line_length: Option<u32>,
 492    /// Whether to show wrap guides in the editor. Setting this to true will
 493    /// show a guide at the 'preferred_line_length' value if softwrap is set to
 494    /// 'preferred_line_length', and will show any additional guides as specified
 495    /// by the 'wrap_guides' setting.
 496    ///
 497    /// Default: true
 498    #[serde(default)]
 499    pub show_wrap_guides: Option<bool>,
 500    /// Character counts at which to show wrap guides in the editor.
 501    ///
 502    /// Default: []
 503    #[serde(default)]
 504    #[settings_ui(skip)]
 505    pub wrap_guides: Option<Vec<usize>>,
 506    /// Indent guide related settings.
 507    #[serde(default)]
 508    pub indent_guides: Option<IndentGuideSettings>,
 509    /// Whether or not to perform a buffer format before saving.
 510    ///
 511    /// Default: on
 512    #[serde(default)]
 513    pub format_on_save: Option<FormatOnSave>,
 514    /// Whether or not to remove any trailing whitespace from lines of a buffer
 515    /// before saving it.
 516    ///
 517    /// Default: true
 518    #[serde(default)]
 519    pub remove_trailing_whitespace_on_save: Option<bool>,
 520    /// Whether or not to ensure there's a single newline at the end of a buffer
 521    /// when saving it.
 522    ///
 523    /// Default: true
 524    #[serde(default)]
 525    pub ensure_final_newline_on_save: Option<bool>,
 526    /// How to perform a buffer format.
 527    ///
 528    /// Default: auto
 529    #[serde(default)]
 530    #[settings_ui(skip)]
 531    pub formatter: Option<SelectedFormatter>,
 532    /// Zed's Prettier integration settings.
 533    /// Allows to enable/disable formatting with Prettier
 534    /// and configure default Prettier, used when no project-level Prettier installation is found.
 535    ///
 536    /// Default: off
 537    #[serde(default)]
 538    pub prettier: Option<PrettierSettings>,
 539    /// Whether to automatically close JSX tags.
 540    #[serde(default)]
 541    pub jsx_tag_auto_close: Option<JsxTagAutoCloseSettings>,
 542    /// Whether to use language servers to provide code intelligence.
 543    ///
 544    /// Default: true
 545    #[serde(default)]
 546    pub enable_language_server: Option<bool>,
 547    /// The list of language servers to use (or disable) for this language.
 548    ///
 549    /// This array should consist of language server IDs, as well as the following
 550    /// special tokens:
 551    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
 552    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
 553    ///
 554    /// Default: ["..."]
 555    #[serde(default)]
 556    #[settings_ui(skip)]
 557    pub language_servers: Option<Vec<String>>,
 558    /// Controls where the `editor::Rewrap` action is allowed for this language.
 559    ///
 560    /// Note: This setting has no effect in Vim mode, as rewrap is already
 561    /// allowed everywhere.
 562    ///
 563    /// Default: "in_comments"
 564    #[serde(default)]
 565    pub allow_rewrap: Option<RewrapBehavior>,
 566    /// Controls whether edit predictions are shown immediately (true)
 567    /// or manually by triggering `editor::ShowEditPrediction` (false).
 568    ///
 569    /// Default: true
 570    #[serde(default)]
 571    pub show_edit_predictions: Option<bool>,
 572    /// Controls whether edit predictions are shown in the given language
 573    /// scopes.
 574    ///
 575    /// Example: ["string", "comment"]
 576    ///
 577    /// Default: []
 578    #[serde(default)]
 579    #[settings_ui(skip)]
 580    pub edit_predictions_disabled_in: Option<Vec<String>>,
 581    /// Whether to show tabs and spaces in the editor.
 582    #[serde(default)]
 583    pub show_whitespaces: Option<ShowWhitespaceSetting>,
 584    /// Visible characters used to render whitespace when show_whitespaces is enabled.
 585    ///
 586    /// Default: "•" for spaces, "→" for tabs.
 587    #[serde(default)]
 588    pub whitespace_map: Option<WhitespaceMap>,
 589    /// Whether to start a new line with a comment when a previous line is a comment as well.
 590    ///
 591    /// Default: true
 592    #[serde(default)]
 593    pub extend_comment_on_newline: Option<bool>,
 594    /// Inlay hint related settings.
 595    #[serde(default)]
 596    pub inlay_hints: Option<InlayHintSettings>,
 597    /// Whether to automatically type closing characters for you. For example,
 598    /// when you type (, Zed will automatically add a closing ) at the correct position.
 599    ///
 600    /// Default: true
 601    pub use_autoclose: Option<bool>,
 602    /// Whether to automatically surround text with characters for you. For example,
 603    /// when you select text and type (, Zed will automatically surround text with ().
 604    ///
 605    /// Default: true
 606    pub use_auto_surround: Option<bool>,
 607    /// Controls how the editor handles the autoclosed characters.
 608    /// When set to `false`(default), skipping over and auto-removing of the closing characters
 609    /// happen only for auto-inserted characters.
 610    /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
 611    /// no matter how they were inserted.
 612    ///
 613    /// Default: false
 614    pub always_treat_brackets_as_autoclosed: Option<bool>,
 615    /// Whether to use additional LSP queries to format (and amend) the code after
 616    /// every "trigger" symbol input, defined by LSP server capabilities.
 617    ///
 618    /// Default: true
 619    pub use_on_type_format: Option<bool>,
 620    /// Which code actions to run on save after the formatter.
 621    /// These are not run if formatting is off.
 622    ///
 623    /// Default: {} (or {"source.organizeImports": true} for Go).
 624    #[settings_ui(skip)]
 625    pub code_actions_on_format: Option<HashMap<String, bool>>,
 626    /// Whether to perform linked edits of associated ranges, if the language server supports it.
 627    /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
 628    ///
 629    /// Default: true
 630    pub linked_edits: Option<bool>,
 631    /// Whether indentation should be adjusted based on the context whilst typing.
 632    ///
 633    /// Default: true
 634    pub auto_indent: Option<bool>,
 635    /// Whether indentation of pasted content should be adjusted based on the context.
 636    ///
 637    /// Default: true
 638    pub auto_indent_on_paste: Option<bool>,
 639    /// Task configuration for this language.
 640    ///
 641    /// Default: {}
 642    pub tasks: Option<LanguageTaskConfig>,
 643    /// Whether to pop the completions menu while typing in an editor without
 644    /// explicitly requesting it.
 645    ///
 646    /// Default: true
 647    pub show_completions_on_input: Option<bool>,
 648    /// Whether to display inline and alongside documentation for items in the
 649    /// completions menu.
 650    ///
 651    /// Default: true
 652    pub show_completion_documentation: Option<bool>,
 653    /// Controls how completions are processed for this language.
 654    pub completions: Option<CompletionSettings>,
 655    /// Preferred debuggers for this language.
 656    ///
 657    /// Default: []
 658    #[settings_ui(skip)]
 659    pub debuggers: Option<Vec<String>>,
 660}
 661
 662/// The behavior of `editor::Rewrap`.
 663#[derive(
 664    Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, SettingsUi,
 665)]
 666#[serde(rename_all = "snake_case")]
 667pub enum RewrapBehavior {
 668    /// Only rewrap within comments.
 669    #[default]
 670    InComments,
 671    /// Only rewrap within the current selection(s).
 672    InSelections,
 673    /// Allow rewrapping anywhere.
 674    Anywhere,
 675}
 676
 677/// The contents of the edit prediction settings.
 678#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, SettingsUi)]
 679pub struct EditPredictionSettingsContent {
 680    /// A list of globs representing files that edit predictions should be disabled for.
 681    /// This list adds to a pre-existing, sensible default set of globs.
 682    /// Any additional ones you add are combined with them.
 683    #[serde(default)]
 684    #[settings_ui(skip)]
 685    pub disabled_globs: Option<Vec<String>>,
 686    /// The mode used to display edit predictions in the buffer.
 687    /// Provider support required.
 688    #[serde(default)]
 689    pub mode: EditPredictionsMode,
 690    /// Settings specific to GitHub Copilot.
 691    #[serde(default)]
 692    pub copilot: CopilotSettingsContent,
 693    /// Whether edit predictions are enabled in the assistant prompt editor.
 694    /// This has no effect if globally disabled.
 695    #[serde(default = "default_true")]
 696    pub enabled_in_text_threads: bool,
 697}
 698
 699#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, SettingsUi)]
 700pub struct CopilotSettingsContent {
 701    /// HTTP/HTTPS proxy to use for Copilot.
 702    ///
 703    /// Default: none
 704    #[serde(default)]
 705    #[settings_ui(skip)]
 706    pub proxy: Option<String>,
 707    /// Disable certificate verification for the proxy (not recommended).
 708    ///
 709    /// Default: false
 710    #[serde(default)]
 711    pub proxy_no_verify: Option<bool>,
 712    /// Enterprise URI for Copilot.
 713    ///
 714    /// Default: none
 715    #[serde(default)]
 716    #[settings_ui(skip)]
 717    pub enterprise_uri: Option<String>,
 718}
 719
 720/// The settings for enabling/disabling features.
 721#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, SettingsUi)]
 722#[serde(rename_all = "snake_case")]
 723#[settings_ui(group = "Features")]
 724pub struct FeaturesContent {
 725    /// Determines which edit prediction provider to use.
 726    pub edit_prediction_provider: Option<EditPredictionProvider>,
 727}
 728
 729/// Controls the soft-wrapping behavior in the editor.
 730#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)]
 731#[serde(rename_all = "snake_case")]
 732pub enum SoftWrap {
 733    /// Prefer a single line generally, unless an overly long line is encountered.
 734    None,
 735    /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
 736    /// Prefer a single line generally, unless an overly long line is encountered.
 737    PreferLine,
 738    /// Soft wrap lines that exceed the editor width.
 739    EditorWidth,
 740    /// Soft wrap lines at the preferred line length.
 741    PreferredLineLength,
 742    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
 743    Bounded,
 744}
 745
 746/// Controls the behavior of formatting files when they are saved.
 747#[derive(Debug, Clone, PartialEq, Eq, SettingsUi)]
 748pub enum FormatOnSave {
 749    /// Files should be formatted on save.
 750    On,
 751    /// Files should not be formatted on save.
 752    Off,
 753    List(FormatterList),
 754}
 755
 756impl JsonSchema for FormatOnSave {
 757    fn schema_name() -> Cow<'static, str> {
 758        "OnSaveFormatter".into()
 759    }
 760
 761    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
 762        let formatter_schema = Formatter::json_schema(generator);
 763
 764        json_schema!({
 765            "oneOf": [
 766                {
 767                    "type": "array",
 768                    "items": formatter_schema
 769                },
 770                {
 771                    "type": "string",
 772                    "enum": ["on", "off", "language_server"]
 773                },
 774                formatter_schema
 775            ]
 776        })
 777    }
 778}
 779
 780impl Serialize for FormatOnSave {
 781    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
 782    where
 783        S: serde::Serializer,
 784    {
 785        match self {
 786            Self::On => serializer.serialize_str("on"),
 787            Self::Off => serializer.serialize_str("off"),
 788            Self::List(list) => list.serialize(serializer),
 789        }
 790    }
 791}
 792
 793impl<'de> Deserialize<'de> for FormatOnSave {
 794    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
 795    where
 796        D: Deserializer<'de>,
 797    {
 798        struct FormatDeserializer;
 799
 800        impl<'d> Visitor<'d> for FormatDeserializer {
 801            type Value = FormatOnSave;
 802
 803            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 804                formatter.write_str("a valid on-save formatter kind")
 805            }
 806            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
 807            where
 808                E: serde::de::Error,
 809            {
 810                if v == "on" {
 811                    Ok(Self::Value::On)
 812                } else if v == "off" {
 813                    Ok(Self::Value::Off)
 814                } else if v == "language_server" {
 815                    Ok(Self::Value::List(FormatterList::Single(
 816                        Formatter::LanguageServer { name: None },
 817                    )))
 818                } else {
 819                    let ret: Result<FormatterList, _> =
 820                        Deserialize::deserialize(v.into_deserializer());
 821                    ret.map(Self::Value::List)
 822                }
 823            }
 824            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
 825            where
 826                A: MapAccess<'d>,
 827            {
 828                let ret: Result<FormatterList, _> =
 829                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
 830                ret.map(Self::Value::List)
 831            }
 832            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
 833            where
 834                A: SeqAccess<'d>,
 835            {
 836                let ret: Result<FormatterList, _> =
 837                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
 838                ret.map(Self::Value::List)
 839            }
 840        }
 841        deserializer.deserialize_any(FormatDeserializer)
 842    }
 843}
 844
 845/// Controls how whitespace should be displayedin the editor.
 846#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)]
 847#[serde(rename_all = "snake_case")]
 848pub enum ShowWhitespaceSetting {
 849    /// Draw whitespace only for the selected text.
 850    Selection,
 851    /// Do not draw any tabs or spaces.
 852    None,
 853    /// Draw all invisible symbols.
 854    All,
 855    /// Draw whitespaces at boundaries only.
 856    ///
 857    /// For a whitespace to be on a boundary, any of the following conditions need to be met:
 858    /// - It is a tab
 859    /// - It is adjacent to an edge (start or end)
 860    /// - It is adjacent to a whitespace (left or right)
 861    Boundary,
 862    /// Draw whitespaces only after non-whitespace characters.
 863    Trailing,
 864}
 865
 866#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, SettingsUi)]
 867pub struct WhitespaceMap {
 868    #[serde(default)]
 869    pub space: Option<String>,
 870    #[serde(default)]
 871    pub tab: Option<String>,
 872}
 873
 874impl WhitespaceMap {
 875    pub fn space(&self) -> SharedString {
 876        self.space
 877            .as_ref()
 878            .map_or_else(|| SharedString::from(""), |s| SharedString::from(s))
 879    }
 880
 881    pub fn tab(&self) -> SharedString {
 882        self.tab
 883            .as_ref()
 884            .map_or_else(|| SharedString::from(""), |s| SharedString::from(s))
 885    }
 886}
 887
 888/// Controls which formatter should be used when formatting code.
 889#[derive(Clone, Debug, Default, PartialEq, Eq, SettingsUi)]
 890pub enum SelectedFormatter {
 891    /// Format files using Zed's Prettier integration (if applicable),
 892    /// or falling back to formatting via language server.
 893    #[default]
 894    Auto,
 895    List(FormatterList),
 896}
 897
 898impl JsonSchema for SelectedFormatter {
 899    fn schema_name() -> Cow<'static, str> {
 900        "Formatter".into()
 901    }
 902
 903    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
 904        let formatter_schema = Formatter::json_schema(generator);
 905
 906        json_schema!({
 907            "oneOf": [
 908                {
 909                    "type": "array",
 910                    "items": formatter_schema
 911                },
 912                {
 913                    "type": "string",
 914                    "enum": ["auto", "language_server"]
 915                },
 916                formatter_schema
 917            ]
 918        })
 919    }
 920}
 921
 922impl Serialize for SelectedFormatter {
 923    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
 924    where
 925        S: serde::Serializer,
 926    {
 927        match self {
 928            SelectedFormatter::Auto => serializer.serialize_str("auto"),
 929            SelectedFormatter::List(list) => list.serialize(serializer),
 930        }
 931    }
 932}
 933
 934impl<'de> Deserialize<'de> for SelectedFormatter {
 935    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
 936    where
 937        D: Deserializer<'de>,
 938    {
 939        struct FormatDeserializer;
 940
 941        impl<'d> Visitor<'d> for FormatDeserializer {
 942            type Value = SelectedFormatter;
 943
 944            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 945                formatter.write_str("a valid formatter kind")
 946            }
 947            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
 948            where
 949                E: serde::de::Error,
 950            {
 951                if v == "auto" {
 952                    Ok(Self::Value::Auto)
 953                } else if v == "language_server" {
 954                    Ok(Self::Value::List(FormatterList::Single(
 955                        Formatter::LanguageServer { name: None },
 956                    )))
 957                } else {
 958                    let ret: Result<FormatterList, _> =
 959                        Deserialize::deserialize(v.into_deserializer());
 960                    ret.map(SelectedFormatter::List)
 961                }
 962            }
 963            fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
 964            where
 965                A: MapAccess<'d>,
 966            {
 967                let ret: Result<FormatterList, _> =
 968                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
 969                ret.map(SelectedFormatter::List)
 970            }
 971            fn visit_seq<A>(self, map: A) -> Result<Self::Value, A::Error>
 972            where
 973                A: SeqAccess<'d>,
 974            {
 975                let ret: Result<FormatterList, _> =
 976                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
 977                ret.map(SelectedFormatter::List)
 978            }
 979        }
 980        deserializer.deserialize_any(FormatDeserializer)
 981    }
 982}
 983
 984/// Controls which formatters should be used when formatting code.
 985#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)]
 986#[serde(untagged)]
 987pub enum FormatterList {
 988    Single(Formatter),
 989    Vec(#[settings_ui(skip)] Vec<Formatter>),
 990}
 991
 992impl Default for FormatterList {
 993    fn default() -> Self {
 994        Self::Single(Formatter::default())
 995    }
 996}
 997
 998impl AsRef<[Formatter]> for FormatterList {
 999    fn as_ref(&self) -> &[Formatter] {
1000        match &self {
1001            Self::Single(single) => slice::from_ref(single),
1002            Self::Vec(v) => v,
1003        }
1004    }
1005}
1006
1007/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
1008#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)]
1009#[serde(rename_all = "snake_case")]
1010pub enum Formatter {
1011    /// Format code using the current language server.
1012    LanguageServer {
1013        #[settings_ui(skip)]
1014        name: Option<String>,
1015    },
1016    /// Format code using Zed's Prettier integration.
1017    #[default]
1018    Prettier,
1019    /// Format code using an external command.
1020    External {
1021        /// The external program to run.
1022        #[settings_ui(skip)]
1023        command: Arc<str>,
1024        /// The arguments to pass to the program.
1025        #[settings_ui(skip)]
1026        arguments: Option<Arc<[String]>>,
1027    },
1028    /// Files should be formatted using code actions executed by language servers.
1029    CodeActions(#[settings_ui(skip)] HashMap<String, bool>),
1030}
1031
1032/// The settings for indent guides.
1033#[derive(
1034    Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, SettingsUi,
1035)]
1036pub struct IndentGuideSettings {
1037    /// Whether to display indent guides in the editor.
1038    ///
1039    /// Default: true
1040    #[serde(default = "default_true")]
1041    pub enabled: bool,
1042    /// The width of the indent guides in pixels, between 1 and 10.
1043    ///
1044    /// Default: 1
1045    #[serde(default = "line_width")]
1046    pub line_width: u32,
1047    /// The width of the active indent guide in pixels, between 1 and 10.
1048    ///
1049    /// Default: 1
1050    #[serde(default = "active_line_width")]
1051    pub active_line_width: u32,
1052    /// Determines how indent guides are colored.
1053    ///
1054    /// Default: Fixed
1055    #[serde(default)]
1056    pub coloring: IndentGuideColoring,
1057    /// Determines how indent guide backgrounds are colored.
1058    ///
1059    /// Default: Disabled
1060    #[serde(default)]
1061    pub background_coloring: IndentGuideBackgroundColoring,
1062}
1063
1064fn line_width() -> u32 {
1065    1
1066}
1067
1068fn active_line_width() -> u32 {
1069    line_width()
1070}
1071
1072/// Determines how indent guides are colored.
1073#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1074#[serde(rename_all = "snake_case")]
1075pub enum IndentGuideColoring {
1076    /// Do not render any lines for indent guides.
1077    Disabled,
1078    /// Use the same color for all indentation levels.
1079    #[default]
1080    Fixed,
1081    /// Use a different color for each indentation level.
1082    IndentAware,
1083}
1084
1085/// Determines how indent guide backgrounds are colored.
1086#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1087#[serde(rename_all = "snake_case")]
1088pub enum IndentGuideBackgroundColoring {
1089    /// Do not render any background for indent guides.
1090    #[default]
1091    Disabled,
1092    /// Use a different color for each indentation level.
1093    IndentAware,
1094}
1095
1096/// The settings for inlay hints.
1097#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)]
1098pub struct InlayHintSettings {
1099    /// Global switch to toggle hints on and off.
1100    ///
1101    /// Default: false
1102    #[serde(default)]
1103    pub enabled: bool,
1104    /// Global switch to toggle inline values on and off when debugging.
1105    ///
1106    /// Default: true
1107    #[serde(default = "default_true")]
1108    pub show_value_hints: bool,
1109    /// Whether type hints should be shown.
1110    ///
1111    /// Default: true
1112    #[serde(default = "default_true")]
1113    pub show_type_hints: bool,
1114    /// Whether parameter hints should be shown.
1115    ///
1116    /// Default: true
1117    #[serde(default = "default_true")]
1118    pub show_parameter_hints: bool,
1119    /// Whether other hints should be shown.
1120    ///
1121    /// Default: true
1122    #[serde(default = "default_true")]
1123    pub show_other_hints: bool,
1124    /// Whether to show a background for inlay hints.
1125    ///
1126    /// If set to `true`, the background will use the `hint.background` color
1127    /// from the current theme.
1128    ///
1129    /// Default: false
1130    #[serde(default)]
1131    pub show_background: bool,
1132    /// Whether or not to debounce inlay hints updates after buffer edits.
1133    ///
1134    /// Set to 0 to disable debouncing.
1135    ///
1136    /// Default: 700
1137    #[serde(default = "edit_debounce_ms")]
1138    pub edit_debounce_ms: u64,
1139    /// Whether or not to debounce inlay hints updates after buffer scrolls.
1140    ///
1141    /// Set to 0 to disable debouncing.
1142    ///
1143    /// Default: 50
1144    #[serde(default = "scroll_debounce_ms")]
1145    pub scroll_debounce_ms: u64,
1146    /// Toggles inlay hints (hides or shows) when the user presses the modifiers specified.
1147    /// If only a subset of the modifiers specified is pressed, hints are not toggled.
1148    /// If no modifiers are specified, this is equivalent to `None`.
1149    ///
1150    /// Default: None
1151    #[serde(default)]
1152    pub toggle_on_modifiers_press: Option<Modifiers>,
1153}
1154
1155fn edit_debounce_ms() -> u64 {
1156    700
1157}
1158
1159fn scroll_debounce_ms() -> u64 {
1160    50
1161}
1162
1163/// The task settings for a particular language.
1164#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema, SettingsUi)]
1165pub struct LanguageTaskConfig {
1166    /// Extra task variables to set for a particular language.
1167    #[serde(default)]
1168    pub variables: HashMap<String, String>,
1169    #[serde(default = "default_true")]
1170    pub enabled: bool,
1171    /// Use LSP tasks over Zed language extension ones.
1172    /// If no LSP tasks are returned due to error/timeout or regular execution,
1173    /// Zed language extension tasks will be used instead.
1174    ///
1175    /// Other Zed tasks will still be shown:
1176    /// * Zed task from either of the task config file
1177    /// * Zed task from history (e.g. one-off task was spawned before)
1178    #[serde(default = "default_true")]
1179    pub prefer_lsp: bool,
1180}
1181
1182impl InlayHintSettings {
1183    /// Returns the kinds of inlay hints that are enabled based on the settings.
1184    pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
1185        let mut kinds = HashSet::default();
1186        if self.show_type_hints {
1187            kinds.insert(Some(InlayHintKind::Type));
1188        }
1189        if self.show_parameter_hints {
1190            kinds.insert(Some(InlayHintKind::Parameter));
1191        }
1192        if self.show_other_hints {
1193            kinds.insert(None);
1194        }
1195        kinds
1196    }
1197}
1198
1199impl AllLanguageSettings {
1200    /// Returns the [`LanguageSettings`] for the language with the specified name.
1201    pub fn language<'a>(
1202        &'a self,
1203        location: Option<SettingsLocation<'a>>,
1204        language_name: Option<&LanguageName>,
1205        cx: &'a App,
1206    ) -> Cow<'a, LanguageSettings> {
1207        let settings = language_name
1208            .and_then(|name| self.languages.get(name))
1209            .unwrap_or(&self.defaults);
1210
1211        let editorconfig_properties = location.and_then(|location| {
1212            cx.global::<SettingsStore>()
1213                .editorconfig_properties(location.worktree_id, location.path)
1214        });
1215        if let Some(editorconfig_properties) = editorconfig_properties {
1216            let mut settings = settings.clone();
1217            merge_with_editorconfig(&mut settings, &editorconfig_properties);
1218            Cow::Owned(settings)
1219        } else {
1220            Cow::Borrowed(settings)
1221        }
1222    }
1223
1224    /// Returns whether edit predictions are enabled for the given path.
1225    pub fn edit_predictions_enabled_for_file(&self, file: &Arc<dyn File>, cx: &App) -> bool {
1226        self.edit_predictions.enabled_for_file(file, cx)
1227    }
1228
1229    /// Returns whether edit predictions are enabled for the given language and path.
1230    pub fn show_edit_predictions(&self, language: Option<&Arc<Language>>, cx: &App) -> bool {
1231        self.language(None, language.map(|l| l.name()).as_ref(), cx)
1232            .show_edit_predictions
1233    }
1234
1235    /// Returns the edit predictions preview mode for the given language and path.
1236    pub fn edit_predictions_mode(&self) -> EditPredictionsMode {
1237        self.edit_predictions.mode
1238    }
1239}
1240
1241fn merge_with_editorconfig(settings: &mut LanguageSettings, cfg: &EditorconfigProperties) {
1242    let preferred_line_length = cfg.get::<MaxLineLen>().ok().and_then(|v| match v {
1243        MaxLineLen::Value(u) => Some(u as u32),
1244        MaxLineLen::Off => None,
1245    });
1246    let tab_size = cfg.get::<IndentSize>().ok().and_then(|v| match v {
1247        IndentSize::Value(u) => NonZeroU32::new(u as u32),
1248        IndentSize::UseTabWidth => cfg.get::<TabWidth>().ok().and_then(|w| match w {
1249            TabWidth::Value(u) => NonZeroU32::new(u as u32),
1250        }),
1251    });
1252    let hard_tabs = cfg
1253        .get::<IndentStyle>()
1254        .map(|v| v.eq(&IndentStyle::Tabs))
1255        .ok();
1256    let ensure_final_newline_on_save = cfg
1257        .get::<FinalNewline>()
1258        .map(|v| match v {
1259            FinalNewline::Value(b) => b,
1260        })
1261        .ok();
1262    let remove_trailing_whitespace_on_save = cfg
1263        .get::<TrimTrailingWs>()
1264        .map(|v| match v {
1265            TrimTrailingWs::Value(b) => b,
1266        })
1267        .ok();
1268    fn merge<T>(target: &mut T, value: Option<T>) {
1269        if let Some(value) = value {
1270            *target = value;
1271        }
1272    }
1273    merge(&mut settings.preferred_line_length, preferred_line_length);
1274    merge(&mut settings.tab_size, tab_size);
1275    merge(&mut settings.hard_tabs, hard_tabs);
1276    merge(
1277        &mut settings.remove_trailing_whitespace_on_save,
1278        remove_trailing_whitespace_on_save,
1279    );
1280    merge(
1281        &mut settings.ensure_final_newline_on_save,
1282        ensure_final_newline_on_save,
1283    );
1284}
1285
1286/// The kind of an inlay hint.
1287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1288pub enum InlayHintKind {
1289    /// An inlay hint for a type.
1290    Type,
1291    /// An inlay hint for a parameter.
1292    Parameter,
1293}
1294
1295impl InlayHintKind {
1296    /// Returns the [`InlayHintKind`] from the given name.
1297    ///
1298    /// Returns `None` if `name` does not match any of the expected
1299    /// string representations.
1300    pub fn from_name(name: &str) -> Option<Self> {
1301        match name {
1302            "type" => Some(InlayHintKind::Type),
1303            "parameter" => Some(InlayHintKind::Parameter),
1304            _ => None,
1305        }
1306    }
1307
1308    /// Returns the name of this [`InlayHintKind`].
1309    pub fn name(&self) -> &'static str {
1310        match self {
1311            InlayHintKind::Type => "type",
1312            InlayHintKind::Parameter => "parameter",
1313        }
1314    }
1315}
1316
1317impl settings::Settings for AllLanguageSettings {
1318    type FileContent = AllLanguageSettingsContent;
1319
1320    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1321        let default_value = sources.default;
1322
1323        // A default is provided for all settings.
1324        let mut defaults: LanguageSettings =
1325            serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
1326
1327        let mut languages = HashMap::default();
1328        for (language_name, settings) in &default_value.languages.0 {
1329            let mut language_settings = defaults.clone();
1330            merge_settings(&mut language_settings, settings);
1331            languages.insert(language_name.clone(), language_settings);
1332        }
1333
1334        let mut edit_prediction_provider = default_value
1335            .features
1336            .as_ref()
1337            .and_then(|f| f.edit_prediction_provider);
1338        let mut edit_predictions_mode = default_value
1339            .edit_predictions
1340            .as_ref()
1341            .map(|edit_predictions| edit_predictions.mode)
1342            .ok_or_else(Self::missing_default)?;
1343
1344        let mut completion_globs: HashSet<&String> = default_value
1345            .edit_predictions
1346            .as_ref()
1347            .and_then(|c| c.disabled_globs.as_ref())
1348            .map(|globs| globs.iter().collect())
1349            .ok_or_else(Self::missing_default)?;
1350
1351        let mut copilot_settings = default_value
1352            .edit_predictions
1353            .as_ref()
1354            .map(|settings| CopilotSettings {
1355                proxy: settings.copilot.proxy.clone(),
1356                proxy_no_verify: settings.copilot.proxy_no_verify,
1357                enterprise_uri: settings.copilot.enterprise_uri.clone(),
1358            })
1359            .unwrap_or_default();
1360
1361        let mut enabled_in_text_threads = default_value
1362            .edit_predictions
1363            .as_ref()
1364            .map(|settings| settings.enabled_in_text_threads)
1365            .unwrap_or(true);
1366
1367        let mut file_types: FxHashMap<Arc<str>, GlobSet> = FxHashMap::default();
1368
1369        for (language, patterns) in &default_value.file_types {
1370            let mut builder = GlobSetBuilder::new();
1371
1372            for pattern in patterns {
1373                builder.add(Glob::new(pattern)?);
1374            }
1375
1376            file_types.insert(language.clone(), builder.build()?);
1377        }
1378
1379        for user_settings in sources.customizations() {
1380            if let Some(provider) = user_settings
1381                .features
1382                .as_ref()
1383                .and_then(|f| f.edit_prediction_provider)
1384            {
1385                edit_prediction_provider = Some(provider);
1386            }
1387
1388            if let Some(edit_predictions) = user_settings.edit_predictions.as_ref() {
1389                edit_predictions_mode = edit_predictions.mode;
1390                enabled_in_text_threads = edit_predictions.enabled_in_text_threads;
1391
1392                if let Some(disabled_globs) = edit_predictions.disabled_globs.as_ref() {
1393                    completion_globs.extend(disabled_globs.iter());
1394                }
1395            }
1396
1397            if let Some(proxy) = user_settings
1398                .edit_predictions
1399                .as_ref()
1400                .and_then(|settings| settings.copilot.proxy.clone())
1401            {
1402                copilot_settings.proxy = Some(proxy);
1403            }
1404
1405            if let Some(proxy_no_verify) = user_settings
1406                .edit_predictions
1407                .as_ref()
1408                .and_then(|settings| settings.copilot.proxy_no_verify)
1409            {
1410                copilot_settings.proxy_no_verify = Some(proxy_no_verify);
1411            }
1412
1413            if let Some(enterprise_uri) = user_settings
1414                .edit_predictions
1415                .as_ref()
1416                .and_then(|settings| settings.copilot.enterprise_uri.clone())
1417            {
1418                copilot_settings.enterprise_uri = Some(enterprise_uri);
1419            }
1420
1421            // A user's global settings override the default global settings and
1422            // all default language-specific settings.
1423            merge_settings(&mut defaults, &user_settings.defaults);
1424            for language_settings in languages.values_mut() {
1425                merge_settings(language_settings, &user_settings.defaults);
1426            }
1427
1428            // A user's language-specific settings override default language-specific settings.
1429            for (language_name, user_language_settings) in &user_settings.languages.0 {
1430                merge_settings(
1431                    languages
1432                        .entry(language_name.clone())
1433                        .or_insert_with(|| defaults.clone()),
1434                    user_language_settings,
1435                );
1436            }
1437
1438            for (language, patterns) in &user_settings.file_types {
1439                let mut builder = GlobSetBuilder::new();
1440
1441                let default_value = default_value.file_types.get(&language.clone());
1442
1443                // Merge the default value with the user's value.
1444                if let Some(patterns) = default_value {
1445                    for pattern in patterns {
1446                        builder.add(Glob::new(pattern)?);
1447                    }
1448                }
1449
1450                for pattern in patterns {
1451                    builder.add(Glob::new(pattern)?);
1452                }
1453
1454                file_types.insert(language.clone(), builder.build()?);
1455            }
1456        }
1457
1458        Ok(Self {
1459            edit_predictions: EditPredictionSettings {
1460                provider: if let Some(provider) = edit_prediction_provider {
1461                    provider
1462                } else {
1463                    EditPredictionProvider::None
1464                },
1465                disabled_globs: completion_globs
1466                    .iter()
1467                    .filter_map(|g| {
1468                        let expanded_g = shellexpand::tilde(g).into_owned();
1469                        Some(DisabledGlob {
1470                            matcher: globset::Glob::new(&expanded_g).ok()?.compile_matcher(),
1471                            is_absolute: Path::new(&expanded_g).is_absolute(),
1472                        })
1473                    })
1474                    .collect(),
1475                mode: edit_predictions_mode,
1476                copilot: copilot_settings,
1477                enabled_in_text_threads,
1478            },
1479            defaults,
1480            languages,
1481            file_types,
1482        })
1483    }
1484
1485    fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
1486        let d = &mut current.defaults;
1487        if let Some(size) = vscode
1488            .read_value("editor.tabSize")
1489            .and_then(|v| v.as_u64())
1490            .and_then(|n| NonZeroU32::new(n as u32))
1491        {
1492            d.tab_size = Some(size);
1493        }
1494        if let Some(v) = vscode.read_bool("editor.insertSpaces") {
1495            d.hard_tabs = Some(!v);
1496        }
1497
1498        vscode.enum_setting("editor.wordWrap", &mut d.soft_wrap, |s| match s {
1499            "on" => Some(SoftWrap::EditorWidth),
1500            "wordWrapColumn" => Some(SoftWrap::PreferLine),
1501            "bounded" => Some(SoftWrap::Bounded),
1502            "off" => Some(SoftWrap::None),
1503            _ => None,
1504        });
1505        vscode.u32_setting("editor.wordWrapColumn", &mut d.preferred_line_length);
1506
1507        if let Some(arr) = vscode
1508            .read_value("editor.rulers")
1509            .and_then(|v| v.as_array())
1510            .map(|v| v.iter().map(|n| n.as_u64().map(|n| n as usize)).collect())
1511        {
1512            d.wrap_guides = arr;
1513        }
1514        if let Some(b) = vscode.read_bool("editor.guides.indentation") {
1515            if let Some(guide_settings) = d.indent_guides.as_mut() {
1516                guide_settings.enabled = b;
1517            } else {
1518                d.indent_guides = Some(IndentGuideSettings {
1519                    enabled: b,
1520                    ..Default::default()
1521                });
1522            }
1523        }
1524
1525        if let Some(b) = vscode.read_bool("editor.guides.formatOnSave") {
1526            d.format_on_save = Some(if b {
1527                FormatOnSave::On
1528            } else {
1529                FormatOnSave::Off
1530            });
1531        }
1532        vscode.bool_setting(
1533            "editor.trimAutoWhitespace",
1534            &mut d.remove_trailing_whitespace_on_save,
1535        );
1536        vscode.bool_setting(
1537            "files.insertFinalNewline",
1538            &mut d.ensure_final_newline_on_save,
1539        );
1540        vscode.bool_setting("editor.inlineSuggest.enabled", &mut d.show_edit_predictions);
1541        vscode.enum_setting("editor.renderWhitespace", &mut d.show_whitespaces, |s| {
1542            Some(match s {
1543                "boundary" => ShowWhitespaceSetting::Boundary,
1544                "trailing" => ShowWhitespaceSetting::Trailing,
1545                "selection" => ShowWhitespaceSetting::Selection,
1546                "all" => ShowWhitespaceSetting::All,
1547                _ => ShowWhitespaceSetting::None,
1548            })
1549        });
1550        vscode.enum_setting(
1551            "editor.autoSurround",
1552            &mut d.use_auto_surround,
1553            |s| match s {
1554                "languageDefined" | "quotes" | "brackets" => Some(true),
1555                "never" => Some(false),
1556                _ => None,
1557            },
1558        );
1559        vscode.bool_setting("editor.formatOnType", &mut d.use_on_type_format);
1560        vscode.bool_setting("editor.linkedEditing", &mut d.linked_edits);
1561        vscode.bool_setting("editor.formatOnPaste", &mut d.auto_indent_on_paste);
1562        vscode.bool_setting(
1563            "editor.suggestOnTriggerCharacters",
1564            &mut d.show_completions_on_input,
1565        );
1566        if let Some(b) = vscode.read_bool("editor.suggest.showWords") {
1567            let mode = if b {
1568                WordsCompletionMode::Enabled
1569            } else {
1570                WordsCompletionMode::Disabled
1571            };
1572            if let Some(completion_settings) = d.completions.as_mut() {
1573                completion_settings.words = mode;
1574            } else {
1575                d.completions = Some(CompletionSettings {
1576                    words: mode,
1577                    words_min_length: 3,
1578                    lsp: true,
1579                    lsp_fetch_timeout_ms: 0,
1580                    lsp_insert_mode: LspInsertMode::ReplaceSuffix,
1581                });
1582            }
1583        }
1584        // TODO: pull ^ out into helper and reuse for per-language settings
1585
1586        // vscodes file association map is inverted from ours, so we flip the mapping before merging
1587        let mut associations: HashMap<Arc<str>, Vec<String>> = HashMap::default();
1588        if let Some(map) = vscode
1589            .read_value("files.associations")
1590            .and_then(|v| v.as_object())
1591        {
1592            for (k, v) in map {
1593                let Some(v) = v.as_str() else { continue };
1594                associations.entry(v.into()).or_default().push(k.clone());
1595            }
1596        }
1597
1598        // TODO: do we want to merge imported globs per filetype? for now we'll just replace
1599        current.file_types.extend(associations);
1600
1601        // cursor global ignore list applies to cursor-tab, so transfer it to edit_predictions.disabled_globs
1602        if let Some(disabled_globs) = vscode
1603            .read_value("cursor.general.globalCursorIgnoreList")
1604            .and_then(|v| v.as_array())
1605        {
1606            current
1607                .edit_predictions
1608                .get_or_insert_default()
1609                .disabled_globs
1610                .get_or_insert_default()
1611                .extend(
1612                    disabled_globs
1613                        .iter()
1614                        .filter_map(|glob| glob.as_str())
1615                        .map(|s| s.to_string()),
1616                );
1617        }
1618    }
1619}
1620
1621fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
1622    fn merge<T>(target: &mut T, value: Option<T>) {
1623        if let Some(value) = value {
1624            *target = value;
1625        }
1626    }
1627
1628    merge(&mut settings.tab_size, src.tab_size);
1629    settings.tab_size = settings
1630        .tab_size
1631        .clamp(NonZeroU32::new(1).unwrap(), NonZeroU32::new(16).unwrap());
1632
1633    merge(&mut settings.hard_tabs, src.hard_tabs);
1634    merge(&mut settings.soft_wrap, src.soft_wrap);
1635    merge(&mut settings.use_autoclose, src.use_autoclose);
1636    merge(&mut settings.use_auto_surround, src.use_auto_surround);
1637    merge(&mut settings.use_on_type_format, src.use_on_type_format);
1638    merge(&mut settings.auto_indent, src.auto_indent);
1639    merge(&mut settings.auto_indent_on_paste, src.auto_indent_on_paste);
1640    merge(
1641        &mut settings.always_treat_brackets_as_autoclosed,
1642        src.always_treat_brackets_as_autoclosed,
1643    );
1644    merge(&mut settings.show_wrap_guides, src.show_wrap_guides);
1645    merge(&mut settings.wrap_guides, src.wrap_guides.clone());
1646    merge(&mut settings.indent_guides, src.indent_guides);
1647    merge(
1648        &mut settings.code_actions_on_format,
1649        src.code_actions_on_format.clone(),
1650    );
1651    merge(&mut settings.linked_edits, src.linked_edits);
1652    merge(&mut settings.tasks, src.tasks.clone());
1653
1654    merge(
1655        &mut settings.preferred_line_length,
1656        src.preferred_line_length,
1657    );
1658    merge(&mut settings.formatter, src.formatter.clone());
1659    merge(&mut settings.prettier, src.prettier.clone());
1660    merge(
1661        &mut settings.jsx_tag_auto_close,
1662        src.jsx_tag_auto_close.clone(),
1663    );
1664    merge(&mut settings.format_on_save, src.format_on_save.clone());
1665    merge(
1666        &mut settings.remove_trailing_whitespace_on_save,
1667        src.remove_trailing_whitespace_on_save,
1668    );
1669    merge(
1670        &mut settings.ensure_final_newline_on_save,
1671        src.ensure_final_newline_on_save,
1672    );
1673    merge(
1674        &mut settings.enable_language_server,
1675        src.enable_language_server,
1676    );
1677    merge(&mut settings.language_servers, src.language_servers.clone());
1678    merge(&mut settings.allow_rewrap, src.allow_rewrap);
1679    merge(
1680        &mut settings.show_edit_predictions,
1681        src.show_edit_predictions,
1682    );
1683    merge(
1684        &mut settings.edit_predictions_disabled_in,
1685        src.edit_predictions_disabled_in.clone(),
1686    );
1687    merge(&mut settings.show_whitespaces, src.show_whitespaces);
1688    merge(&mut settings.whitespace_map, src.whitespace_map.clone());
1689    merge(
1690        &mut settings.extend_comment_on_newline,
1691        src.extend_comment_on_newline,
1692    );
1693    merge(&mut settings.inlay_hints, src.inlay_hints);
1694    merge(
1695        &mut settings.show_completions_on_input,
1696        src.show_completions_on_input,
1697    );
1698    merge(
1699        &mut settings.show_completion_documentation,
1700        src.show_completion_documentation,
1701    );
1702    merge(&mut settings.completions, src.completions);
1703}
1704
1705/// Allows to enable/disable formatting with Prettier
1706/// and configure default Prettier, used when no project-level Prettier installation is found.
1707/// Prettier formatting is disabled by default.
1708#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, SettingsUi)]
1709pub struct PrettierSettings {
1710    /// Enables or disables formatting with Prettier for a given language.
1711    #[serde(default)]
1712    pub allowed: bool,
1713
1714    /// Forces Prettier integration to use a specific parser name when formatting files with the language.
1715    #[serde(default)]
1716    pub parser: Option<String>,
1717
1718    /// Forces Prettier integration to use specific plugins when formatting files with the language.
1719    /// The default Prettier will be installed with these plugins.
1720    #[serde(default)]
1721    #[settings_ui(skip)]
1722    pub plugins: HashSet<String>,
1723
1724    /// Default Prettier options, in the format as in package.json section for Prettier.
1725    /// If project installs Prettier via its package.json, these options will be ignored.
1726    #[serde(flatten)]
1727    #[settings_ui(skip)]
1728    pub options: HashMap<String, serde_json::Value>,
1729}
1730
1731#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, SettingsUi)]
1732pub struct JsxTagAutoCloseSettings {
1733    /// Enables or disables auto-closing of JSX tags.
1734    #[serde(default)]
1735    pub enabled: bool,
1736}
1737
1738#[cfg(test)]
1739mod tests {
1740    use gpui::TestAppContext;
1741
1742    use super::*;
1743
1744    #[test]
1745    fn test_formatter_deserialization() {
1746        let raw_auto = "{\"formatter\": \"auto\"}";
1747        let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
1748        assert_eq!(settings.formatter, Some(SelectedFormatter::Auto));
1749        let raw = "{\"formatter\": \"language_server\"}";
1750        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1751        assert_eq!(
1752            settings.formatter,
1753            Some(SelectedFormatter::List(FormatterList::Single(
1754                Formatter::LanguageServer { name: None }
1755            )))
1756        );
1757        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
1758        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1759        assert_eq!(
1760            settings.formatter,
1761            Some(SelectedFormatter::List(FormatterList::Vec(vec![
1762                Formatter::LanguageServer { name: None }
1763            ])))
1764        );
1765        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"prettier\"]}";
1766        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1767        assert_eq!(
1768            settings.formatter,
1769            Some(SelectedFormatter::List(FormatterList::Vec(vec![
1770                Formatter::LanguageServer { name: None },
1771                Formatter::Prettier
1772            ])))
1773        );
1774    }
1775
1776    #[test]
1777    fn test_formatter_deserialization_invalid() {
1778        let raw_auto = "{\"formatter\": {}}";
1779        let result: Result<LanguageSettingsContent, _> = serde_json::from_str(raw_auto);
1780        assert!(result.is_err());
1781    }
1782
1783    #[gpui::test]
1784    fn test_edit_predictions_enabled_for_file(cx: &mut TestAppContext) {
1785        use crate::TestFile;
1786        use std::path::PathBuf;
1787
1788        let cx = cx.app.borrow_mut();
1789
1790        let build_settings = |globs: &[&str]| -> EditPredictionSettings {
1791            EditPredictionSettings {
1792                disabled_globs: globs
1793                    .iter()
1794                    .map(|glob_str| {
1795                        #[cfg(windows)]
1796                        let glob_str = {
1797                            let mut g = String::new();
1798
1799                            if glob_str.starts_with('/') {
1800                                g.push_str("C:");
1801                            }
1802
1803                            g.push_str(&glob_str.replace('/', "\\"));
1804                            g
1805                        };
1806                        #[cfg(windows)]
1807                        let glob_str = glob_str.as_str();
1808                        let expanded_glob_str = shellexpand::tilde(glob_str).into_owned();
1809                        DisabledGlob {
1810                            matcher: globset::Glob::new(&expanded_glob_str)
1811                                .unwrap()
1812                                .compile_matcher(),
1813                            is_absolute: Path::new(&expanded_glob_str).is_absolute(),
1814                        }
1815                    })
1816                    .collect(),
1817                ..Default::default()
1818            }
1819        };
1820
1821        const WORKTREE_NAME: &str = "project";
1822        let make_test_file = |segments: &[&str]| -> Arc<dyn File> {
1823            let mut path_buf = PathBuf::new();
1824            path_buf.extend(segments);
1825
1826            Arc::new(TestFile {
1827                path: path_buf.as_path().into(),
1828                root_name: WORKTREE_NAME.to_string(),
1829                local_root: Some(PathBuf::from(if cfg!(windows) {
1830                    "C:\\absolute\\"
1831                } else {
1832                    "/absolute/"
1833                })),
1834            })
1835        };
1836
1837        let test_file = make_test_file(&["src", "test", "file.rs"]);
1838
1839        // Test relative globs
1840        let settings = build_settings(&["*.rs"]);
1841        assert!(!settings.enabled_for_file(&test_file, &cx));
1842        let settings = build_settings(&["*.txt"]);
1843        assert!(settings.enabled_for_file(&test_file, &cx));
1844
1845        // Test absolute globs
1846        let settings = build_settings(&["/absolute/**/*.rs"]);
1847        assert!(!settings.enabled_for_file(&test_file, &cx));
1848        let settings = build_settings(&["/other/**/*.rs"]);
1849        assert!(settings.enabled_for_file(&test_file, &cx));
1850
1851        // Test exact path match relative
1852        let settings = build_settings(&["src/test/file.rs"]);
1853        assert!(!settings.enabled_for_file(&test_file, &cx));
1854        let settings = build_settings(&["src/test/otherfile.rs"]);
1855        assert!(settings.enabled_for_file(&test_file, &cx));
1856
1857        // Test exact path match absolute
1858        let settings = build_settings(&[&format!("/absolute/{}/src/test/file.rs", WORKTREE_NAME)]);
1859        assert!(!settings.enabled_for_file(&test_file, &cx));
1860        let settings = build_settings(&["/other/test/otherfile.rs"]);
1861        assert!(settings.enabled_for_file(&test_file, &cx));
1862
1863        // Test * glob
1864        let settings = build_settings(&["*"]);
1865        assert!(!settings.enabled_for_file(&test_file, &cx));
1866        let settings = build_settings(&["*.txt"]);
1867        assert!(settings.enabled_for_file(&test_file, &cx));
1868
1869        // Test **/* glob
1870        let settings = build_settings(&["**/*"]);
1871        assert!(!settings.enabled_for_file(&test_file, &cx));
1872        let settings = build_settings(&["other/**/*"]);
1873        assert!(settings.enabled_for_file(&test_file, &cx));
1874
1875        // Test directory/** glob
1876        let settings = build_settings(&["src/**"]);
1877        assert!(!settings.enabled_for_file(&test_file, &cx));
1878
1879        let test_file_root: Arc<dyn File> = Arc::new(TestFile {
1880            path: PathBuf::from("file.rs").as_path().into(),
1881            root_name: WORKTREE_NAME.to_string(),
1882            local_root: Some(PathBuf::from("/absolute/")),
1883        });
1884        assert!(settings.enabled_for_file(&test_file_root, &cx));
1885
1886        let settings = build_settings(&["other/**"]);
1887        assert!(settings.enabled_for_file(&test_file, &cx));
1888
1889        // Test **/directory/* glob
1890        let settings = build_settings(&["**/test/*"]);
1891        assert!(!settings.enabled_for_file(&test_file, &cx));
1892        let settings = build_settings(&["**/other/*"]);
1893        assert!(settings.enabled_for_file(&test_file, &cx));
1894
1895        // Test multiple globs
1896        let settings = build_settings(&["*.rs", "*.txt", "src/**"]);
1897        assert!(!settings.enabled_for_file(&test_file, &cx));
1898        let settings = build_settings(&["*.txt", "*.md", "other/**"]);
1899        assert!(settings.enabled_for_file(&test_file, &cx));
1900
1901        // Test dot files
1902        let dot_file = make_test_file(&[".config", "settings.json"]);
1903        let settings = build_settings(&[".*/**"]);
1904        assert!(!settings.enabled_for_file(&dot_file, &cx));
1905
1906        let dot_env_file = make_test_file(&[".env"]);
1907        let settings = build_settings(&[".env"]);
1908        assert!(!settings.enabled_for_file(&dot_env_file, &cx));
1909
1910        // Test tilde expansion
1911        let home = shellexpand::tilde("~").into_owned();
1912        let home_file = make_test_file(&[&home, "test.rs"]);
1913        let settings = build_settings(&["~/test.rs"]);
1914        assert!(!settings.enabled_for_file(&home_file, &cx));
1915    }
1916
1917    #[test]
1918    fn test_resolve_language_servers() {
1919        fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
1920            names
1921                .iter()
1922                .copied()
1923                .map(|name| LanguageServerName(name.to_string().into()))
1924                .collect::<Vec<_>>()
1925        }
1926
1927        let available_language_servers = language_server_names(&[
1928            "typescript-language-server",
1929            "biome",
1930            "deno",
1931            "eslint",
1932            "tailwind",
1933        ]);
1934
1935        // A value of just `["..."]` is the same as taking all of the available language servers.
1936        assert_eq!(
1937            LanguageSettings::resolve_language_servers(
1938                &[LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()],
1939                &available_language_servers,
1940            ),
1941            available_language_servers
1942        );
1943
1944        // Referencing one of the available language servers will change its order.
1945        assert_eq!(
1946            LanguageSettings::resolve_language_servers(
1947                &[
1948                    "biome".into(),
1949                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into(),
1950                    "deno".into()
1951                ],
1952                &available_language_servers
1953            ),
1954            language_server_names(&[
1955                "biome",
1956                "typescript-language-server",
1957                "eslint",
1958                "tailwind",
1959                "deno",
1960            ])
1961        );
1962
1963        // Negating an available language server removes it from the list.
1964        assert_eq!(
1965            LanguageSettings::resolve_language_servers(
1966                &[
1967                    "deno".into(),
1968                    "!typescript-language-server".into(),
1969                    "!biome".into(),
1970                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1971                ],
1972                &available_language_servers
1973            ),
1974            language_server_names(&["deno", "eslint", "tailwind"])
1975        );
1976
1977        // Adding a language server not in the list of available language servers adds it to the list.
1978        assert_eq!(
1979            LanguageSettings::resolve_language_servers(
1980                &[
1981                    "my-cool-language-server".into(),
1982                    LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1983                ],
1984                &available_language_servers
1985            ),
1986            language_server_names(&[
1987                "my-cool-language-server",
1988                "typescript-language-server",
1989                "biome",
1990                "deno",
1991                "eslint",
1992                "tailwind",
1993            ])
1994        );
1995    }
1996}