language.rs

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