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