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