language_settings.rs

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