language.rs

   1use std::{num::NonZeroU32, path::Path};
   2
   3use collections::{HashMap, HashSet};
   4use gpui::{Modifiers, SharedString};
   5use schemars::JsonSchema;
   6use serde::{Deserialize, Serialize, de::Error as _};
   7use settings_macros::{MergeFrom, with_fallible_options};
   8use std::sync::Arc;
   9
  10use crate::{ExtendingVec, merge_from};
  11
  12#[with_fallible_options]
  13#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
  14pub struct AllLanguageSettingsContent {
  15    /// The settings for enabling/disabling features.
  16    pub features: Option<FeaturesContent>,
  17    /// The edit prediction settings.
  18    pub edit_predictions: Option<EditPredictionSettingsContent>,
  19    /// The default language settings.
  20    #[serde(flatten)]
  21    pub defaults: LanguageSettingsContent,
  22    /// The settings for individual languages.
  23    #[serde(default)]
  24    pub languages: LanguageToSettingsMap,
  25    /// Settings for associating file extensions and filenames
  26    /// with languages.
  27    pub file_types: Option<HashMap<Arc<str>, ExtendingVec<String>>>,
  28}
  29
  30impl merge_from::MergeFrom for AllLanguageSettingsContent {
  31    fn merge_from(&mut self, other: &Self) {
  32        self.file_types.merge_from(&other.file_types);
  33        self.features.merge_from(&other.features);
  34        self.edit_predictions.merge_from(&other.edit_predictions);
  35
  36        // A user's global settings override the default global settings and
  37        // all default language-specific settings.
  38        //
  39        self.defaults.merge_from(&other.defaults);
  40        for language_settings in self.languages.0.values_mut() {
  41            language_settings.merge_from(&other.defaults);
  42        }
  43
  44        // A user's language-specific settings override default language-specific settings.
  45        for (language_name, user_language_settings) in &other.languages.0 {
  46            if let Some(existing) = self.languages.0.get_mut(language_name) {
  47                existing.merge_from(&user_language_settings);
  48            } else {
  49                let mut new_settings = self.defaults.clone();
  50                new_settings.merge_from(&user_language_settings);
  51
  52                self.languages.0.insert(language_name.clone(), new_settings);
  53            }
  54        }
  55    }
  56}
  57
  58/// The settings for enabling/disabling features.
  59#[with_fallible_options]
  60#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
  61#[serde(rename_all = "snake_case")]
  62pub struct FeaturesContent {
  63    /// Determines which edit prediction provider to use.
  64    pub edit_prediction_provider: Option<EditPredictionProvider>,
  65}
  66
  67/// The provider that supplies edit predictions.
  68#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, JsonSchema, MergeFrom)]
  69#[serde(rename_all = "snake_case")]
  70pub enum EditPredictionProvider {
  71    None,
  72    #[default]
  73    Copilot,
  74    Supermaven,
  75    Zed,
  76    Codestral,
  77    Experimental(&'static str),
  78}
  79
  80pub const EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME: &str = "sweep";
  81pub const EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME: &str = "zeta2";
  82pub const EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME: &str = "mercury";
  83
  84impl<'de> Deserialize<'de> for EditPredictionProvider {
  85    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  86    where
  87        D: serde::Deserializer<'de>,
  88    {
  89        #[derive(Deserialize)]
  90        #[serde(rename_all = "snake_case")]
  91        pub enum Content {
  92            None,
  93            Copilot,
  94            Supermaven,
  95            Zed,
  96            Codestral,
  97            Experimental(String),
  98        }
  99
 100        Ok(match Content::deserialize(deserializer)? {
 101            Content::None => EditPredictionProvider::None,
 102            Content::Copilot => EditPredictionProvider::Copilot,
 103            Content::Supermaven => EditPredictionProvider::Supermaven,
 104            Content::Zed => EditPredictionProvider::Zed,
 105            Content::Codestral => EditPredictionProvider::Codestral,
 106            Content::Experimental(name)
 107                if name == EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME =>
 108            {
 109                EditPredictionProvider::Experimental(
 110                    EXPERIMENTAL_SWEEP_EDIT_PREDICTION_PROVIDER_NAME,
 111                )
 112            }
 113            Content::Experimental(name)
 114                if name == EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME =>
 115            {
 116                EditPredictionProvider::Experimental(
 117                    EXPERIMENTAL_MERCURY_EDIT_PREDICTION_PROVIDER_NAME,
 118                )
 119            }
 120            Content::Experimental(name)
 121                if name == EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME =>
 122            {
 123                EditPredictionProvider::Experimental(
 124                    EXPERIMENTAL_ZETA2_EDIT_PREDICTION_PROVIDER_NAME,
 125                )
 126            }
 127            Content::Experimental(name) => {
 128                return Err(D::Error::custom(format!(
 129                    "Unknown experimental edit prediction provider: {}",
 130                    name
 131                )));
 132            }
 133        })
 134    }
 135}
 136
 137impl EditPredictionProvider {
 138    pub fn is_zed(&self) -> bool {
 139        match self {
 140            EditPredictionProvider::Zed => true,
 141            EditPredictionProvider::None
 142            | EditPredictionProvider::Copilot
 143            | EditPredictionProvider::Supermaven
 144            | EditPredictionProvider::Codestral
 145            | EditPredictionProvider::Experimental(_) => false,
 146        }
 147    }
 148}
 149
 150/// The contents of the edit prediction settings.
 151#[with_fallible_options]
 152#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
 153pub struct EditPredictionSettingsContent {
 154    /// A list of globs representing files that edit predictions should be disabled for.
 155    /// This list adds to a pre-existing, sensible default set of globs.
 156    /// Any additional ones you add are combined with them.
 157    pub disabled_globs: Option<Vec<String>>,
 158    /// The mode used to display edit predictions in the buffer.
 159    /// Provider support required.
 160    pub mode: Option<EditPredictionsMode>,
 161    /// Settings specific to GitHub Copilot.
 162    pub copilot: Option<CopilotSettingsContent>,
 163    /// Settings specific to Codestral.
 164    pub codestral: Option<CodestralSettingsContent>,
 165    /// Whether edit predictions are enabled in the assistant prompt editor.
 166    /// This has no effect if globally disabled.
 167    pub enabled_in_text_threads: Option<bool>,
 168    /// The directory where manually captured edit prediction examples are stored.
 169    pub examples_dir: Option<Arc<Path>>,
 170    /// The number of edit prediction examples captured per ten thousand predictions.
 171    pub example_capture_rate: Option<u16>,
 172}
 173
 174#[with_fallible_options]
 175#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
 176pub struct CopilotSettingsContent {
 177    /// HTTP/HTTPS proxy to use for Copilot.
 178    ///
 179    /// Default: none
 180    pub proxy: Option<String>,
 181    /// Disable certificate verification for the proxy (not recommended).
 182    ///
 183    /// Default: false
 184    pub proxy_no_verify: Option<bool>,
 185    /// Enterprise URI for Copilot.
 186    ///
 187    /// Default: none
 188    pub enterprise_uri: Option<String>,
 189    /// Whether the Copilot Next Edit Suggestions feature is enabled.
 190    ///
 191    /// Default: true
 192    pub enable_next_edit_suggestions: Option<bool>,
 193}
 194
 195#[with_fallible_options]
 196#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
 197pub struct CodestralSettingsContent {
 198    /// Model to use for completions.
 199    ///
 200    /// Default: "codestral-latest"
 201    pub model: Option<String>,
 202    /// Maximum tokens to generate.
 203    ///
 204    /// Default: 150
 205    pub max_tokens: Option<u32>,
 206    /// Api URL to use for completions.
 207    ///
 208    /// Default: "https://codestral.mistral.ai"
 209    pub api_url: Option<String>,
 210}
 211
 212/// The mode in which edit predictions should be displayed.
 213#[derive(
 214    Copy,
 215    Clone,
 216    Debug,
 217    Default,
 218    Eq,
 219    PartialEq,
 220    Serialize,
 221    Deserialize,
 222    JsonSchema,
 223    MergeFrom,
 224    strum::VariantArray,
 225    strum::VariantNames,
 226)]
 227#[serde(rename_all = "snake_case")]
 228pub enum EditPredictionsMode {
 229    /// If provider supports it, display inline when holding modifier key (e.g., alt).
 230    /// Otherwise, eager preview is used.
 231    #[serde(alias = "auto")]
 232    Subtle,
 233    /// Display inline when there are no language server completions available.
 234    #[default]
 235    #[serde(alias = "eager_preview")]
 236    Eager,
 237}
 238
 239/// Controls the soft-wrapping behavior in the editor.
 240#[derive(
 241    Copy,
 242    Clone,
 243    Debug,
 244    Serialize,
 245    Deserialize,
 246    PartialEq,
 247    Eq,
 248    JsonSchema,
 249    MergeFrom,
 250    strum::VariantArray,
 251    strum::VariantNames,
 252)]
 253#[serde(rename_all = "snake_case")]
 254pub enum SoftWrap {
 255    /// Prefer a single line generally, unless an overly long line is encountered.
 256    None,
 257    /// Deprecated: use None instead. Left to avoid breaking existing users' configs.
 258    /// Prefer a single line generally, unless an overly long line is encountered.
 259    PreferLine,
 260    /// Soft wrap lines that exceed the editor width.
 261    EditorWidth,
 262    /// Soft wrap lines at the preferred line length.
 263    PreferredLineLength,
 264    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
 265    Bounded,
 266}
 267
 268/// The settings for a particular language.
 269#[with_fallible_options]
 270#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 271pub struct LanguageSettingsContent {
 272    /// How many columns a tab should occupy.
 273    ///
 274    /// Default: 4
 275    #[schemars(range(min = 1, max = 128))]
 276    pub tab_size: Option<NonZeroU32>,
 277    /// Whether to indent lines using tab characters, as opposed to multiple
 278    /// spaces.
 279    ///
 280    /// Default: false
 281    pub hard_tabs: Option<bool>,
 282    /// How to soft-wrap long lines of text.
 283    ///
 284    /// Default: none
 285    pub soft_wrap: Option<SoftWrap>,
 286    /// The column at which to soft-wrap lines, for buffers where soft-wrap
 287    /// is enabled.
 288    ///
 289    /// Default: 80
 290    pub preferred_line_length: Option<u32>,
 291    /// Whether to show wrap guides in the editor. Setting this to true will
 292    /// show a guide at the 'preferred_line_length' value if softwrap is set to
 293    /// 'preferred_line_length', and will show any additional guides as specified
 294    /// by the 'wrap_guides' setting.
 295    ///
 296    /// Default: true
 297    pub show_wrap_guides: Option<bool>,
 298    /// Character counts at which to show wrap guides in the editor.
 299    ///
 300    /// Default: []
 301    pub wrap_guides: Option<Vec<usize>>,
 302    /// Indent guide related settings.
 303    pub indent_guides: Option<IndentGuideSettingsContent>,
 304    /// Whether or not to perform a buffer format before saving.
 305    ///
 306    /// Default: on
 307    pub format_on_save: Option<FormatOnSave>,
 308    /// Whether or not to remove any trailing whitespace from lines of a buffer
 309    /// before saving it.
 310    ///
 311    /// Default: true
 312    pub remove_trailing_whitespace_on_save: Option<bool>,
 313    /// Whether or not to ensure there's a single newline at the end of a buffer
 314    /// when saving it.
 315    ///
 316    /// Default: true
 317    pub ensure_final_newline_on_save: Option<bool>,
 318    /// How to perform a buffer format.
 319    ///
 320    /// Default: auto
 321    pub formatter: Option<FormatterList>,
 322    /// Zed's Prettier integration settings.
 323    /// Allows to enable/disable formatting with Prettier
 324    /// and configure default Prettier, used when no project-level Prettier installation is found.
 325    ///
 326    /// Default: off
 327    pub prettier: Option<PrettierSettingsContent>,
 328    /// Whether to automatically close JSX tags.
 329    pub jsx_tag_auto_close: Option<JsxTagAutoCloseSettingsContent>,
 330    /// Whether to use language servers to provide code intelligence.
 331    ///
 332    /// Default: true
 333    pub enable_language_server: Option<bool>,
 334    /// The list of language servers to use (or disable) for this language.
 335    ///
 336    /// This array should consist of language server IDs, as well as the following
 337    /// special tokens:
 338    /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
 339    /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
 340    ///
 341    /// Default: ["..."]
 342    pub language_servers: Option<Vec<String>>,
 343    /// Controls where the `editor::Rewrap` action is allowed for this language.
 344    ///
 345    /// Note: This setting has no effect in Vim mode, as rewrap is already
 346    /// allowed everywhere.
 347    ///
 348    /// Default: "in_comments"
 349    pub allow_rewrap: Option<RewrapBehavior>,
 350    /// Controls whether edit predictions are shown immediately (true)
 351    /// or manually by triggering `editor::ShowEditPrediction` (false).
 352    ///
 353    /// Default: true
 354    pub show_edit_predictions: Option<bool>,
 355    /// Controls whether edit predictions are shown in the given language
 356    /// scopes.
 357    ///
 358    /// Example: ["string", "comment"]
 359    ///
 360    /// Default: []
 361    pub edit_predictions_disabled_in: Option<Vec<String>>,
 362    /// Whether to show tabs and spaces in the editor.
 363    pub show_whitespaces: Option<ShowWhitespaceSetting>,
 364    /// Visible characters used to render whitespace when show_whitespaces is enabled.
 365    ///
 366    /// Default: "•" for spaces, "→" for tabs.
 367    pub whitespace_map: Option<WhitespaceMapContent>,
 368    /// Whether to start a new line with a comment when a previous line is a comment as well.
 369    ///
 370    /// Default: true
 371    pub extend_comment_on_newline: Option<bool>,
 372    /// Whether to continue markdown lists when pressing enter.
 373    ///
 374    /// Default: true
 375    pub extend_list_on_newline: Option<bool>,
 376    /// Whether to indent list items when pressing tab after a list marker.
 377    ///
 378    /// Default: true
 379    pub indent_list_on_tab: Option<bool>,
 380    /// Inlay hint related settings.
 381    pub inlay_hints: Option<InlayHintSettingsContent>,
 382    /// Whether to automatically type closing characters for you. For example,
 383    /// when you type '(', Zed will automatically add a closing ')' at the correct position.
 384    ///
 385    /// Default: true
 386    pub use_autoclose: Option<bool>,
 387    /// Whether to automatically surround text with characters for you. For example,
 388    /// when you select text and type '(', Zed will automatically surround text with ().
 389    ///
 390    /// Default: true
 391    pub use_auto_surround: Option<bool>,
 392    /// Controls how the editor handles the autoclosed characters.
 393    /// When set to `false`(default), skipping over and auto-removing of the closing characters
 394    /// happen only for auto-inserted characters.
 395    /// Otherwise(when `true`), the closing characters are always skipped over and auto-removed
 396    /// no matter how they were inserted.
 397    ///
 398    /// Default: false
 399    pub always_treat_brackets_as_autoclosed: Option<bool>,
 400    /// Whether to use additional LSP queries to format (and amend) the code after
 401    /// every "trigger" symbol input, defined by LSP server capabilities.
 402    ///
 403    /// Default: true
 404    pub use_on_type_format: Option<bool>,
 405    /// Which code actions to run on save before the formatter.
 406    /// These are not run if formatting is off.
 407    ///
 408    /// Default: {} (or {"source.organizeImports": true} for Go).
 409    pub code_actions_on_format: Option<HashMap<String, bool>>,
 410    /// Whether to perform linked edits of associated ranges, if the language server supports it.
 411    /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
 412    ///
 413    /// Default: true
 414    pub linked_edits: Option<bool>,
 415    /// Whether indentation should be adjusted based on the context whilst typing.
 416    ///
 417    /// Default: true
 418    pub auto_indent: Option<bool>,
 419    /// Whether indentation of pasted content should be adjusted based on the context.
 420    ///
 421    /// Default: true
 422    pub auto_indent_on_paste: Option<bool>,
 423    /// Task configuration for this language.
 424    ///
 425    /// Default: {}
 426    pub tasks: Option<LanguageTaskSettingsContent>,
 427    /// Whether to pop the completions menu while typing in an editor without
 428    /// explicitly requesting it.
 429    ///
 430    /// Default: true
 431    pub show_completions_on_input: Option<bool>,
 432    /// Whether to display inline and alongside documentation for items in the
 433    /// completions menu.
 434    ///
 435    /// Default: true
 436    pub show_completion_documentation: Option<bool>,
 437    /// Controls how completions are processed for this language.
 438    pub completions: Option<CompletionSettingsContent>,
 439    /// Preferred debuggers for this language.
 440    ///
 441    /// Default: []
 442    pub debuggers: Option<Vec<String>>,
 443    /// Whether to enable word diff highlighting in the editor.
 444    ///
 445    /// When enabled, changed words within modified lines are highlighted
 446    /// to show exactly what changed.
 447    ///
 448    /// Default: true
 449    pub word_diff_enabled: Option<bool>,
 450    /// Whether to use tree-sitter bracket queries to detect and colorize the brackets in the editor.
 451    ///
 452    /// Default: false
 453    pub colorize_brackets: Option<bool>,
 454}
 455
 456/// Controls how whitespace should be displayedin the editor.
 457#[derive(
 458    Copy,
 459    Clone,
 460    Debug,
 461    Serialize,
 462    Deserialize,
 463    PartialEq,
 464    Eq,
 465    JsonSchema,
 466    MergeFrom,
 467    strum::VariantArray,
 468    strum::VariantNames,
 469)]
 470#[serde(rename_all = "snake_case")]
 471pub enum ShowWhitespaceSetting {
 472    /// Draw whitespace only for the selected text.
 473    Selection,
 474    /// Do not draw any tabs or spaces.
 475    None,
 476    /// Draw all invisible symbols.
 477    All,
 478    /// Draw whitespaces at boundaries only.
 479    ///
 480    /// For a whitespace to be on a boundary, any of the following conditions need to be met:
 481    /// - It is a tab
 482    /// - It is adjacent to an edge (start or end)
 483    /// - It is adjacent to a whitespace (left or right)
 484    Boundary,
 485    /// Draw whitespaces only after non-whitespace characters.
 486    Trailing,
 487}
 488
 489#[with_fallible_options]
 490#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
 491pub struct WhitespaceMapContent {
 492    pub space: Option<char>,
 493    pub tab: Option<char>,
 494}
 495
 496/// The behavior of `editor::Rewrap`.
 497#[derive(
 498    Debug,
 499    PartialEq,
 500    Clone,
 501    Copy,
 502    Default,
 503    Serialize,
 504    Deserialize,
 505    JsonSchema,
 506    MergeFrom,
 507    strum::VariantArray,
 508    strum::VariantNames,
 509)]
 510#[serde(rename_all = "snake_case")]
 511pub enum RewrapBehavior {
 512    /// Only rewrap within comments.
 513    #[default]
 514    InComments,
 515    /// Only rewrap within the current selection(s).
 516    InSelections,
 517    /// Allow rewrapping anywhere.
 518    Anywhere,
 519}
 520
 521#[with_fallible_options]
 522#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 523pub struct JsxTagAutoCloseSettingsContent {
 524    /// Enables or disables auto-closing of JSX tags.
 525    pub enabled: Option<bool>,
 526}
 527
 528/// The settings for inlay hints.
 529#[with_fallible_options]
 530#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
 531pub struct InlayHintSettingsContent {
 532    /// Global switch to toggle hints on and off.
 533    ///
 534    /// Default: false
 535    pub enabled: Option<bool>,
 536    /// Global switch to toggle inline values on and off when debugging.
 537    ///
 538    /// Default: true
 539    pub show_value_hints: Option<bool>,
 540    /// Whether type hints should be shown.
 541    ///
 542    /// Default: true
 543    pub show_type_hints: Option<bool>,
 544    /// Whether parameter hints should be shown.
 545    ///
 546    /// Default: true
 547    pub show_parameter_hints: Option<bool>,
 548    /// Whether other hints should be shown.
 549    ///
 550    /// Default: true
 551    pub show_other_hints: Option<bool>,
 552    /// Whether to show a background for inlay hints.
 553    ///
 554    /// If set to `true`, the background will use the `hint.background` color
 555    /// from the current theme.
 556    ///
 557    /// Default: false
 558    pub show_background: Option<bool>,
 559    /// Whether or not to debounce inlay hints updates after buffer edits.
 560    ///
 561    /// Set to 0 to disable debouncing.
 562    ///
 563    /// Default: 700
 564    pub edit_debounce_ms: Option<u64>,
 565    /// Whether or not to debounce inlay hints updates after buffer scrolls.
 566    ///
 567    /// Set to 0 to disable debouncing.
 568    ///
 569    /// Default: 50
 570    pub scroll_debounce_ms: Option<u64>,
 571    /// Toggles inlay hints (hides or shows) when the user presses the modifiers specified.
 572    /// If only a subset of the modifiers specified is pressed, hints are not toggled.
 573    /// If no modifiers are specified, this is equivalent to `null`.
 574    ///
 575    /// Default: null
 576    pub toggle_on_modifiers_press: Option<Modifiers>,
 577}
 578
 579/// The kind of an inlay hint.
 580#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 581pub enum InlayHintKind {
 582    /// An inlay hint for a type.
 583    Type,
 584    /// An inlay hint for a parameter.
 585    Parameter,
 586}
 587
 588impl InlayHintKind {
 589    /// Returns the [`InlayHintKind`]fromthe given name.
 590    ///
 591    /// Returns `None` if `name` does not match any of the expected
 592    /// string representations.
 593    pub fn from_name(name: &str) -> Option<Self> {
 594        match name {
 595            "type" => Some(InlayHintKind::Type),
 596            "parameter" => Some(InlayHintKind::Parameter),
 597            _ => None,
 598        }
 599    }
 600
 601    /// Returns the name of this [`InlayHintKind`].
 602    pub fn name(&self) -> &'static str {
 603        match self {
 604            InlayHintKind::Type => "type",
 605            InlayHintKind::Parameter => "parameter",
 606        }
 607    }
 608}
 609
 610/// Controls how completions are processed for this language.
 611#[with_fallible_options]
 612#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
 613#[serde(rename_all = "snake_case")]
 614pub struct CompletionSettingsContent {
 615    /// Controls how words are completed.
 616    /// For large documents, not all words may be fetched for completion.
 617    ///
 618    /// Default: `fallback`
 619    pub words: Option<WordsCompletionMode>,
 620    /// How many characters has to be in the completions query to automatically show the words-based completions.
 621    /// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
 622    ///
 623    /// Default: 3
 624    pub words_min_length: Option<u32>,
 625    /// Whether to fetch LSP completions or not.
 626    ///
 627    /// Default: true
 628    pub lsp: Option<bool>,
 629    /// When fetching LSP completions, determines how long to wait for a response of a particular server.
 630    /// When set to 0, waits indefinitely.
 631    ///
 632    /// Default: 0
 633    pub lsp_fetch_timeout_ms: Option<u64>,
 634    /// Controls how LSP completions are inserted.
 635    ///
 636    /// Default: "replace_suffix"
 637    pub lsp_insert_mode: Option<LspInsertMode>,
 638}
 639
 640#[derive(
 641    Copy,
 642    Clone,
 643    Debug,
 644    Serialize,
 645    Deserialize,
 646    PartialEq,
 647    Eq,
 648    JsonSchema,
 649    MergeFrom,
 650    strum::VariantArray,
 651    strum::VariantNames,
 652)]
 653#[serde(rename_all = "snake_case")]
 654pub enum LspInsertMode {
 655    /// Replaces text before the cursor, using the `insert` range described in the LSP specification.
 656    Insert,
 657    /// Replaces text before and after the cursor, using the `replace` range described in the LSP specification.
 658    Replace,
 659    /// Behaves like `"replace"` if the text that would be replaced is a subsequence of the completion text,
 660    /// and like `"insert"` otherwise.
 661    ReplaceSubsequence,
 662    /// Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like
 663    /// `"insert"` otherwise.
 664    ReplaceSuffix,
 665}
 666
 667/// Controls how document's words are completed.
 668#[derive(
 669    Copy,
 670    Clone,
 671    Debug,
 672    Serialize,
 673    Deserialize,
 674    PartialEq,
 675    Eq,
 676    JsonSchema,
 677    MergeFrom,
 678    strum::VariantArray,
 679    strum::VariantNames,
 680)]
 681#[serde(rename_all = "snake_case")]
 682pub enum WordsCompletionMode {
 683    /// Always fetch document's words for completions along with LSP completions.
 684    Enabled,
 685    /// Only if LSP response errors or times out,
 686    /// use document's words to show completions.
 687    Fallback,
 688    /// Never fetch or complete document's words for completions.
 689    /// (Word-based completions can still be queried via a separate action)
 690    Disabled,
 691}
 692
 693/// Allows to enable/disable formatting with Prettier
 694/// and configure default Prettier, used when no project-level Prettier installation is found.
 695/// Prettier formatting is disabled by default.
 696#[with_fallible_options]
 697#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 698pub struct PrettierSettingsContent {
 699    /// Enables or disables formatting with Prettier for a given language.
 700    pub allowed: Option<bool>,
 701
 702    /// Forces Prettier integration to use a specific parser name when formatting files with the language.
 703    pub parser: Option<String>,
 704
 705    /// Forces Prettier integration to use specific plugins when formatting files with the language.
 706    /// The default Prettier will be installed with these plugins.
 707    pub plugins: Option<HashSet<String>>,
 708
 709    /// Default Prettier options, in the format as in package.json section for Prettier.
 710    /// If project installs Prettier via its package.json, these options will be ignored.
 711    #[serde(flatten)]
 712    pub options: Option<HashMap<String, serde_json::Value>>,
 713}
 714
 715/// TODO: this should just be a bool
 716/// Controls the behavior of formatting files when they are saved.
 717#[derive(
 718    Debug,
 719    Clone,
 720    Copy,
 721    PartialEq,
 722    Eq,
 723    Serialize,
 724    Deserialize,
 725    JsonSchema,
 726    MergeFrom,
 727    strum::VariantArray,
 728    strum::VariantNames,
 729)]
 730#[serde(rename_all = "lowercase")]
 731pub enum FormatOnSave {
 732    /// Files should be formatted on save.
 733    On,
 734    /// Files should not be formatted on save.
 735    Off,
 736}
 737
 738/// Controls which formatters should be used when formatting code.
 739#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 740#[serde(untagged)]
 741pub enum FormatterList {
 742    Single(Formatter),
 743    Vec(Vec<Formatter>),
 744}
 745
 746impl Default for FormatterList {
 747    fn default() -> Self {
 748        Self::Single(Formatter::default())
 749    }
 750}
 751
 752impl AsRef<[Formatter]> for FormatterList {
 753    fn as_ref(&self) -> &[Formatter] {
 754        match &self {
 755            Self::Single(single) => std::slice::from_ref(single),
 756            Self::Vec(v) => v,
 757        }
 758    }
 759}
 760
 761/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
 762#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 763#[serde(rename_all = "snake_case")]
 764pub enum Formatter {
 765    /// Format files using Zed's Prettier integration (if applicable),
 766    /// or falling back to formatting via language server.
 767    #[default]
 768    Auto,
 769    /// Format code using Zed's Prettier integration.
 770    Prettier,
 771    /// Format code using an external command.
 772    External {
 773        /// The external program to run.
 774        command: Arc<str>,
 775        /// The arguments to pass to the program.
 776        arguments: Option<Arc<[String]>>,
 777    },
 778    /// Files should be formatted using a code action executed by language servers.
 779    CodeAction(String),
 780    /// Format code using a language server.
 781    #[serde(untagged)]
 782    LanguageServer(LanguageServerFormatterSpecifier),
 783}
 784
 785#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 786#[serde(
 787    rename_all = "snake_case",
 788    // allow specifying language servers as "language_server" or {"language_server": {"name": ...}}
 789    from = "LanguageServerVariantContent",
 790    into = "LanguageServerVariantContent"
 791)]
 792pub enum LanguageServerFormatterSpecifier {
 793    Specific {
 794        name: String,
 795    },
 796    #[default]
 797    Current,
 798}
 799
 800impl From<LanguageServerVariantContent> for LanguageServerFormatterSpecifier {
 801    fn from(value: LanguageServerVariantContent) -> Self {
 802        match value {
 803            LanguageServerVariantContent::Specific {
 804                language_server: LanguageServerSpecifierContent { name: Some(name) },
 805            } => Self::Specific { name },
 806            _ => Self::Current,
 807        }
 808    }
 809}
 810
 811impl From<LanguageServerFormatterSpecifier> for LanguageServerVariantContent {
 812    fn from(value: LanguageServerFormatterSpecifier) -> Self {
 813        match value {
 814            LanguageServerFormatterSpecifier::Specific { name } => Self::Specific {
 815                language_server: LanguageServerSpecifierContent { name: Some(name) },
 816            },
 817            LanguageServerFormatterSpecifier::Current => {
 818                Self::Current(CurrentLanguageServerContent::LanguageServer)
 819            }
 820        }
 821    }
 822}
 823
 824#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 825#[serde(rename_all = "snake_case", untagged)]
 826enum LanguageServerVariantContent {
 827    /// Format code using a specific language server.
 828    Specific {
 829        language_server: LanguageServerSpecifierContent,
 830    },
 831    /// Format code using the current language server.
 832    Current(CurrentLanguageServerContent),
 833}
 834
 835#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 836#[serde(rename_all = "snake_case")]
 837enum CurrentLanguageServerContent {
 838    #[default]
 839    LanguageServer,
 840}
 841
 842#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
 843#[serde(rename_all = "snake_case")]
 844struct LanguageServerSpecifierContent {
 845    /// The name of the language server to format with
 846    name: Option<String>,
 847}
 848
 849/// The settings for indent guides.
 850#[with_fallible_options]
 851#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 852pub struct IndentGuideSettingsContent {
 853    /// Whether to display indent guides in the editor.
 854    ///
 855    /// Default: true
 856    pub enabled: Option<bool>,
 857    /// The width of the indent guides in pixels, between 1 and 10.
 858    ///
 859    /// Default: 1
 860    pub line_width: Option<u32>,
 861    /// The width of the active indent guide in pixels, between 1 and 10.
 862    ///
 863    /// Default: 1
 864    pub active_line_width: Option<u32>,
 865    /// Determines how indent guides are colored.
 866    ///
 867    /// Default: Fixed
 868    pub coloring: Option<IndentGuideColoring>,
 869    /// Determines how indent guide backgrounds are colored.
 870    ///
 871    /// Default: Disabled
 872    pub background_coloring: Option<IndentGuideBackgroundColoring>,
 873}
 874
 875/// The task settings for a particular language.
 876#[with_fallible_options]
 877#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize, JsonSchema, MergeFrom)]
 878pub struct LanguageTaskSettingsContent {
 879    /// Extra task variables to set for a particular language.
 880    pub variables: Option<HashMap<String, String>>,
 881    pub enabled: Option<bool>,
 882    /// Use LSP tasks over Zed language extension ones.
 883    /// If no LSP tasks are returned due to error/timeout or regular execution,
 884    /// Zed language extension tasks will be used instead.
 885    ///
 886    /// Other Zed tasks will still be shown:
 887    /// * Zed task from either of the task config file
 888    /// * Zed task from history (e.g. one-off task was spawned before)
 889    pub prefer_lsp: Option<bool>,
 890}
 891
 892/// Map from language name to settings.
 893#[with_fallible_options]
 894#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
 895pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
 896
 897/// Determines how indent guides are colored.
 898#[derive(
 899    Default,
 900    Debug,
 901    Copy,
 902    Clone,
 903    PartialEq,
 904    Eq,
 905    Serialize,
 906    Deserialize,
 907    JsonSchema,
 908    MergeFrom,
 909    strum::VariantArray,
 910    strum::VariantNames,
 911)]
 912#[serde(rename_all = "snake_case")]
 913pub enum IndentGuideColoring {
 914    /// Do not render any lines for indent guides.
 915    Disabled,
 916    /// Use the same color for all indentation levels.
 917    #[default]
 918    Fixed,
 919    /// Use a different color for each indentation level.
 920    IndentAware,
 921}
 922
 923/// Determines how indent guide backgrounds are colored.
 924#[derive(
 925    Default,
 926    Debug,
 927    Copy,
 928    Clone,
 929    PartialEq,
 930    Eq,
 931    Serialize,
 932    Deserialize,
 933    JsonSchema,
 934    MergeFrom,
 935    strum::VariantArray,
 936    strum::VariantNames,
 937)]
 938#[serde(rename_all = "snake_case")]
 939pub enum IndentGuideBackgroundColoring {
 940    /// Do not render any background for indent guides.
 941    #[default]
 942    Disabled,
 943    /// Use a different color for each indentation level.
 944    IndentAware,
 945}
 946
 947#[cfg(test)]
 948mod test {
 949
 950    use crate::{ParseStatus, fallible_options};
 951
 952    use super::*;
 953
 954    #[test]
 955    fn test_formatter_deserialization() {
 956        let raw_auto = "{\"formatter\": \"auto\"}";
 957        let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
 958        assert_eq!(
 959            settings.formatter,
 960            Some(FormatterList::Single(Formatter::Auto))
 961        );
 962        let raw = "{\"formatter\": \"language_server\"}";
 963        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
 964        assert_eq!(
 965            settings.formatter,
 966            Some(FormatterList::Single(Formatter::LanguageServer(
 967                LanguageServerFormatterSpecifier::Current
 968            )))
 969        );
 970
 971        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
 972        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
 973        assert_eq!(
 974            settings.formatter,
 975            Some(FormatterList::Vec(vec![Formatter::LanguageServer(
 976                LanguageServerFormatterSpecifier::Current
 977            )]))
 978        );
 979        let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"language_server\", \"prettier\"]}";
 980        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
 981        assert_eq!(
 982            settings.formatter,
 983            Some(FormatterList::Vec(vec![
 984                Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
 985                Formatter::LanguageServer(LanguageServerFormatterSpecifier::Current),
 986                Formatter::Prettier
 987            ]))
 988        );
 989
 990        let raw = "{\"formatter\": [{\"language_server\": {\"name\": \"ruff\"}}, \"prettier\"]}";
 991        let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
 992        assert_eq!(
 993            settings.formatter,
 994            Some(FormatterList::Vec(vec![
 995                Formatter::LanguageServer(LanguageServerFormatterSpecifier::Specific {
 996                    name: "ruff".to_string()
 997                }),
 998                Formatter::Prettier
 999            ]))
1000        );
1001
1002        assert_eq!(
1003            serde_json::to_string(&LanguageServerFormatterSpecifier::Current).unwrap(),
1004            "\"language_server\"",
1005        );
1006    }
1007
1008    #[test]
1009    fn test_formatter_deserialization_invalid() {
1010        let raw_auto = "{\"formatter\": {}}";
1011        let (_, result) = fallible_options::parse_json::<LanguageSettingsContent>(raw_auto);
1012        assert!(matches!(result, ParseStatus::Failed { .. }));
1013    }
1014
1015    #[test]
1016    fn test_prettier_options() {
1017        let raw_prettier = r#"{"allowed": false, "tabWidth": 4, "semi": false}"#;
1018        let result = serde_json::from_str::<PrettierSettingsContent>(raw_prettier)
1019            .expect("Failed to parse prettier options");
1020        assert!(
1021            result
1022                .options
1023                .as_ref()
1024                .expect("options were flattened")
1025                .contains_key("semi")
1026        );
1027        assert!(
1028            result
1029                .options
1030                .as_ref()
1031                .expect("options were flattened")
1032                .contains_key("tabWidth")
1033        );
1034    }
1035}