use crate::LanguageName;use collections::{HashMap, HashSet, IndexSet};use gpui_shared_string::SharedString;use regex::Regex;use schemars::{JsonSchema, SchemaGenerator, json_schema};use serde::{Deserialize, Deserializer, Serialize, Serializer, de};use std::{num::NonZeroU32, path::Path, sync::Arc}; /// Controls the soft-wrapping behavior in the editor.#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]#[serde(rename_all = "snake_case")]pub enum SoftWrap { /// Prefer a single line generally, unless an overly long line is encountered. None, /// Deprecated: use None instead. Left to avoid breaking existing users' configs. /// Prefer a single line generally, unless an overly long line is encountered. PreferLine, /// Soft wrap lines that exceed the editor width. EditorWidth, /// Soft wrap line at the preferred line length or the editor width (whichever is smaller). #[serde(alias = "preferred_line_length")] Bounded,} /// Top-level configuration for a language, typically loaded from a `config.toml`/// shipped alongside the grammar.#[derive(Clone, Debug, Deserialize, JsonSchema)]pub struct LanguageConfig { /// Human-readable name of the language. pub name: LanguageName, /// The name of this language for a Markdown code fence block pub code_fence_block_name: Option<Arc<str>>, /// Alternative language names that Jupyter kernels may report for this language. /// Used when a kernel's `language` field differs from Zed's language name. /// For example, the Nu extension would set this to `["nushell"]`. #[serde(default)] pub kernel_language_names: Vec<Arc<str>>, // The name of the grammar in a WASM bundle (experimental). pub grammar: Option<Arc<str>>, /// The criteria for matching this language to a given file. #[serde(flatten)] pub matcher: Arc<LanguageMatcher>, /// List of bracket types in a language. #[serde(default)] pub brackets: BracketPairConfig, /// If set to true, auto indentation uses last non empty line to determine /// the indentation level for a new line. #[serde(default = "default_true")] pub auto_indent_using_last_non_empty_line: bool, // Whether indentation of pasted content should be adjusted based on the context. #[serde(default)] pub auto_indent_on_paste: Option<bool>, /// A regex that is used to determine whether the indentation level should be /// increased in the following line. #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub increase_indent_pattern: Option<Regex>, /// A regex that is used to determine whether the indentation level should be /// decreased in the following line. #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub decrease_indent_pattern: Option<Regex>, /// A list of rules for decreasing indentation. Each rule pairs a regex with a set of valid /// "block-starting" tokens. When a line matches a pattern, its indentation is aligned with /// the most recent line that began with a corresponding token. This enables context-aware /// outdenting, like aligning an `else` with its `if`. #[serde(default)] pub decrease_indent_patterns: Vec<DecreaseIndentConfig>, /// A list of characters that trigger the automatic insertion of a closing /// bracket when they immediately precede the point where an opening /// bracket is inserted. #[serde(default)] pub autoclose_before: String, /// A placeholder used internally by Semantic Index. #[serde(default)] pub collapsed_placeholder: String, /// A line comment string that is inserted in e.g. `toggle comments` action. /// A language can have multiple flavours of line comments. All of the provided line comments are /// used for comment continuations on the next line, but only the first one is used for Editor::ToggleComments. #[serde(default)] pub line_comments: Vec<Arc<str>>, /// Delimiters and configuration for recognizing and formatting block comments. #[serde(default)] pub block_comment: Option<BlockCommentConfig>, /// Delimiters and configuration for recognizing and formatting documentation comments. #[serde(default, alias = "documentation")] pub documentation_comment: Option<BlockCommentConfig>, /// List markers that are inserted unchanged on newline (e.g., `- `, `* `, `+ `). #[serde(default)] pub unordered_list: Vec<Arc<str>>, /// Configuration for ordered lists with auto-incrementing numbers on newline (e.g., `1. ` becomes `2. `). #[serde(default)] pub ordered_list: Vec<OrderedListConfig>, /// Configuration for task lists where multiple markers map to a single continuation prefix (e.g., `- [x] ` continues as `- [ ] `). #[serde(default)] pub task_list: Option<TaskListConfig>, /// A list of additional regex patterns that should be treated as prefixes /// for creating boundaries during rewrapping, ensuring content from one /// prefixed section doesn't merge with another (e.g., markdown list items). /// By default, Zed treats as paragraph and comment prefixes as boundaries. #[serde(default, deserialize_with = "deserialize_regex_vec")] #[schemars(schema_with = "regex_vec_json_schema")] pub rewrap_prefixes: Vec<Regex>, /// A list of language servers that are allowed to run on subranges of a given language. #[serde(default)] pub scope_opt_in_language_servers: Vec<SharedString>, #[serde(default)] pub overrides: HashMap<String, LanguageConfigOverride>, /// A list of characters that Zed should treat as word characters for the /// purpose of features that operate on word boundaries, like 'move to next word end' /// or a whole-word search in buffer search. #[serde(default)] pub word_characters: HashSet<char>, /// Whether to indent lines using tab characters, as opposed to multiple /// spaces. #[serde(default)] pub hard_tabs: Option<bool>, /// How many columns a tab should occupy. #[serde(default)] #[schemars(range(min = 1, max = 128))] pub tab_size: Option<NonZeroU32>, /// How to soft-wrap long lines of text. #[serde(default)] pub soft_wrap: Option<SoftWrap>, /// When set, selections can be wrapped using prefix/suffix pairs on both sides. #[serde(default)] pub wrap_characters: Option<WrapCharactersConfig>, /// The name of a Prettier parser that will be used for this language when no file path is available. /// If there's a parser name in the language settings, that will be used instead. #[serde(default)] pub prettier_parser_name: Option<String>, /// If true, this language is only for syntax highlighting via an injection into other /// languages, but should not appear to the user as a distinct language. #[serde(default)] pub hidden: bool, /// If configured, this language contains JSX style tags, and should support auto-closing of those tags. #[serde(default)] pub jsx_tag_auto_close: Option<JsxTagAutoCloseConfig>, /// A list of characters that Zed should treat as word characters for completion queries. #[serde(default)] pub completion_query_characters: HashSet<char>, /// A list of characters that Zed should treat as word characters for linked edit operations. #[serde(default)] pub linked_edit_characters: HashSet<char>, /// A list of preferred debuggers for this language. #[serde(default)] pub debuggers: IndexSet<SharedString>,} impl LanguageConfig { pub const FILE_NAME: &str = "config.toml"; pub fn load(config_path: impl AsRef<Path>) -> anyhow::Result<Self> { let config = std::fs::read_to_string(config_path.as_ref())?; toml::from_str(&config).map_err(Into::into) }} impl Default for LanguageConfig { fn default() -> Self { Self { name: LanguageName::new_static(""), code_fence_block_name: None, kernel_language_names: Default::default(), grammar: None, matcher: Arc::default(), brackets: Default::default(), auto_indent_using_last_non_empty_line: default_true(), auto_indent_on_paste: None, increase_indent_pattern: Default::default(), decrease_indent_pattern: Default::default(), decrease_indent_patterns: Default::default(), autoclose_before: Default::default(), line_comments: Default::default(), block_comment: Default::default(), documentation_comment: Default::default(), unordered_list: Default::default(), ordered_list: Default::default(), task_list: Default::default(), rewrap_prefixes: Default::default(), scope_opt_in_language_servers: Default::default(), overrides: Default::default(), word_characters: Default::default(), collapsed_placeholder: Default::default(), hard_tabs: None, tab_size: None, soft_wrap: None, wrap_characters: None, prettier_parser_name: None, hidden: false, jsx_tag_auto_close: None, completion_query_characters: Default::default(), linked_edit_characters: Default::default(), debuggers: Default::default(), } }} #[derive(Clone, Debug, Deserialize, Default, JsonSchema)]pub struct DecreaseIndentConfig { #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub pattern: Option<Regex>, #[serde(default)] pub valid_after: Vec<String>,} /// Configuration for continuing ordered lists with auto-incrementing numbers.#[derive(Clone, Debug, Deserialize, JsonSchema)]pub struct OrderedListConfig { /// A regex pattern with a capture group for the number portion (e.g., `(\\d+)\\. `). pub pattern: String, /// A format string where `{1}` is replaced with the incremented number (e.g., `{1}. `). pub format: String,} /// Configuration for continuing task lists on newline.#[derive(Clone, Debug, Deserialize, JsonSchema)]pub struct TaskListConfig { /// The list markers to match (e.g., `- [ ] `, `- [x] `). pub prefixes: Vec<Arc<str>>, /// The marker to insert when continuing the list on a new line (e.g., `- [ ] `). pub continuation: Arc<str>,} #[derive(Debug, Serialize, Deserialize, Default, JsonSchema)]pub struct LanguageMatcher { /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`. #[serde(default)] pub path_suffixes: Vec<String>, /// A regex pattern that determines whether the language should be assigned to a file or not. #[serde( default, serialize_with = "serialize_regex", deserialize_with = "deserialize_regex" )] #[schemars(schema_with = "regex_json_schema")] pub first_line_pattern: Option<Regex>, /// Alternative names for this language used in vim/emacs modelines. /// These are matched case-insensitively against the `mode` (emacs) or /// `filetype`/`ft` (vim) specified in the modeline. #[serde(default)] pub modeline_aliases: Vec<String>,} impl Ord for LanguageMatcher { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.path_suffixes .cmp(&other.path_suffixes) .then_with(|| { self.first_line_pattern .as_ref() .map(Regex::as_str) .cmp(&other.first_line_pattern.as_ref().map(Regex::as_str)) }) .then_with(|| self.modeline_aliases.cmp(&other.modeline_aliases)) }} impl PartialOrd for LanguageMatcher { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }} impl Eq for LanguageMatcher {} impl PartialEq for LanguageMatcher { fn eq(&self, other: &Self) -> bool { self.path_suffixes == other.path_suffixes && self.first_line_pattern.as_ref().map(Regex::as_str) == other.first_line_pattern.as_ref().map(Regex::as_str) && self.modeline_aliases == other.modeline_aliases }} /// The configuration for JSX tag auto-closing.#[derive(Clone, Deserialize, JsonSchema, Debug)]pub struct JsxTagAutoCloseConfig { /// The name of the node for a opening tag pub open_tag_node_name: String, /// The name of the node for an closing tag pub close_tag_node_name: String, /// The name of the node for a complete element with children for open and close tags pub jsx_element_node_name: String, /// The name of the node found within both opening and closing /// tags that describes the tag name pub tag_name_node_name: String, /// Alternate Node names for tag names. /// Specifically needed as TSX represents the name in `<Foo.Bar>` /// as `member_expression` rather than `identifier` as usual #[serde(default)] pub tag_name_node_name_alternates: Vec<String>, /// Some grammars are smart enough to detect a closing tag /// that is not valid i.e. doesn't match it's corresponding /// opening tag or does not have a corresponding opening tag /// This should be set to the name of the node for invalid /// closing tags if the grammar contains such a node, otherwise /// detecting already closed tags will not work properly #[serde(default)] pub erroneous_close_tag_node_name: Option<String>, /// See above for erroneous_close_tag_node_name for details /// This should be set if the node used for the tag name /// within erroneous closing tags is different from the /// normal tag name node name #[serde(default)] pub erroneous_close_tag_name_node_name: Option<String>,} /// The configuration for block comments for this language.#[derive(Clone, Debug, JsonSchema, PartialEq)]pub struct BlockCommentConfig { /// A start tag of block comment. pub start: Arc<str>, /// A end tag of block comment. pub end: Arc<str>, /// A character to add as a prefix when a new line is added to a block comment. pub prefix: Arc<str>, /// A indent to add for prefix and end line upon new line. #[schemars(range(min = 1, max = 128))] pub tab_size: u32,} impl<'de> Deserialize<'de> for BlockCommentConfig { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum BlockCommentConfigHelper { New { start: Arc<str>, end: Arc<str>, prefix: Arc<str>, tab_size: u32, }, Old([Arc<str>; 2]), } match BlockCommentConfigHelper::deserialize(deserializer)? { BlockCommentConfigHelper::New { start, end, prefix, tab_size, } => Ok(BlockCommentConfig { start, end, prefix, tab_size, }), BlockCommentConfigHelper::Old([start, end]) => Ok(BlockCommentConfig { start, end, prefix: "".into(), tab_size: 0, }), } }} #[derive(Clone, Deserialize, Default, Debug, JsonSchema)]pub struct LanguageConfigOverride { #[serde(default)] pub line_comments: Override<Vec<Arc<str>>>, #[serde(default)] pub block_comment: Override<BlockCommentConfig>, #[serde(skip)] pub disabled_bracket_ixs: Vec<u16>, #[serde(default)] pub word_characters: Override<HashSet<char>>, #[serde(default)] pub completion_query_characters: Override<HashSet<char>>, #[serde(default)] pub linked_edit_characters: Override<HashSet<char>>, #[serde(default)] pub opt_into_language_servers: Vec<SharedString>, #[serde(default)] pub prefer_label_for_snippet: Option<bool>,} #[derive(Clone, Deserialize, Debug, Serialize, JsonSchema)]#[serde(untagged)]pub enum Override<T> { Remove { remove: bool }, Set(T),} impl<T> Default for Override<T> { fn default() -> Self { Override::Remove { remove: false } }} impl<T> Override<T> { pub fn as_option<'a>(this: Option<&'a Self>, original: Option<&'a T>) -> Option<&'a T> { match this { Some(Self::Set(value)) => Some(value), Some(Self::Remove { remove: true }) => None, Some(Self::Remove { remove: false }) | None => original, } }} /// Configuration of handling bracket pairs for a given language.////// This struct includes settings for defining which pairs of characters are considered brackets and/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.#[derive(Clone, Debug, Default, JsonSchema)]#[schemars(with = "Vec::<BracketPairContent>")]pub struct BracketPairConfig { /// A list of character pairs that should be treated as brackets in the context of a given language. pub pairs: Vec<BracketPair>, /// A list of tree-sitter scopes for which a given bracket should not be active. /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]` pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,} impl BracketPairConfig { pub fn is_closing_brace(&self, c: char) -> bool { self.pairs.iter().any(|pair| pair.end.starts_with(c)) }} #[derive(Deserialize, JsonSchema)]pub struct BracketPairContent { #[serde(flatten)] pub bracket_pair: BracketPair, #[serde(default)] pub not_in: Vec<String>,} impl<'de> Deserialize<'de> for BracketPairConfig { fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> where D: Deserializer<'de>, { let result = Vec::<BracketPairContent>::deserialize(deserializer)?; let (brackets, disabled_scopes_by_bracket_ix) = result .into_iter() .map(|entry| (entry.bracket_pair, entry.not_in)) .unzip(); Ok(BracketPairConfig { pairs: brackets, disabled_scopes_by_bracket_ix, }) }} /// Describes a single bracket pair and how an editor should react to e.g. inserting/// an opening bracket or to a newline character insertion in between `start` and `end` characters.#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema)]pub struct BracketPair { /// Starting substring for a bracket. pub start: String, /// Ending substring for a bracket. pub end: String, /// True if `end` should be automatically inserted right after `start` characters. pub close: bool, /// True if selected text should be surrounded by `start` and `end` characters. #[serde(default = "default_true")] pub surround: bool, /// True if an extra newline should be inserted while the cursor is in the middle /// of that bracket pair. pub newline: bool,} #[derive(Clone, Debug, Deserialize, JsonSchema)]pub struct WrapCharactersConfig { /// Opening token split into a prefix and suffix. The first caret goes /// after the prefix (i.e., between prefix and suffix). pub start_prefix: String, pub start_suffix: String, /// Closing token split into a prefix and suffix. The second caret goes /// after the prefix (i.e., between prefix and suffix). pub end_prefix: String, pub end_suffix: String,} pub fn default_true() -> bool { true} pub fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D::Error> { let source = Option::<String>::deserialize(d)?; if let Some(source) = source { Ok(Some(regex::Regex::new(&source).map_err(de::Error::custom)?)) } else { Ok(None) }} pub fn regex_json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { json_schema!({ "type": "string" })} Lines 1–500 of 525. Load more
use crate::LanguageName; use collections::{HashMap, HashSet, IndexSet}; use gpui_shared_string::SharedString; use regex::Regex; use schemars::{JsonSchema, SchemaGenerator, json_schema}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use std::{num::NonZeroU32, path::Path, sync::Arc}; /// Controls the soft-wrapping behavior in the editor. #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SoftWrap { /// Prefer a single line generally, unless an overly long line is encountered. None, /// Deprecated: use None instead. Left to avoid breaking existing users' configs. /// Prefer a single line generally, unless an overly long line is encountered. PreferLine, /// Soft wrap lines that exceed the editor width. EditorWidth, /// Soft wrap line at the preferred line length or the editor width (whichever is smaller). #[serde(alias = "preferred_line_length")] Bounded, } /// Top-level configuration for a language, typically loaded from a `config.toml` /// shipped alongside the grammar. #[derive(Clone, Debug, Deserialize, JsonSchema)] pub struct LanguageConfig { /// Human-readable name of the language. pub name: LanguageName, /// The name of this language for a Markdown code fence block pub code_fence_block_name: Option<Arc<str>>, /// Alternative language names that Jupyter kernels may report for this language. /// Used when a kernel's `language` field differs from Zed's language name. /// For example, the Nu extension would set this to `["nushell"]`. #[serde(default)] pub kernel_language_names: Vec<Arc<str>>, // The name of the grammar in a WASM bundle (experimental). pub grammar: Option<Arc<str>>, /// The criteria for matching this language to a given file. #[serde(flatten)] pub matcher: Arc<LanguageMatcher>, /// List of bracket types in a language. #[serde(default)] pub brackets: BracketPairConfig, /// If set to true, auto indentation uses last non empty line to determine /// the indentation level for a new line. #[serde(default = "default_true")] pub auto_indent_using_last_non_empty_line: bool, // Whether indentation of pasted content should be adjusted based on the context. #[serde(default)] pub auto_indent_on_paste: Option<bool>, /// A regex that is used to determine whether the indentation level should be /// increased in the following line. #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub increase_indent_pattern: Option<Regex>, /// A regex that is used to determine whether the indentation level should be /// decreased in the following line. #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub decrease_indent_pattern: Option<Regex>, /// A list of rules for decreasing indentation. Each rule pairs a regex with a set of valid /// "block-starting" tokens. When a line matches a pattern, its indentation is aligned with /// the most recent line that began with a corresponding token. This enables context-aware /// outdenting, like aligning an `else` with its `if`. #[serde(default)] pub decrease_indent_patterns: Vec<DecreaseIndentConfig>, /// A list of characters that trigger the automatic insertion of a closing /// bracket when they immediately precede the point where an opening /// bracket is inserted. #[serde(default)] pub autoclose_before: String, /// A placeholder used internally by Semantic Index. #[serde(default)] pub collapsed_placeholder: String, /// A line comment string that is inserted in e.g. `toggle comments` action. /// A language can have multiple flavours of line comments. All of the provided line comments are /// used for comment continuations on the next line, but only the first one is used for Editor::ToggleComments. #[serde(default)] pub line_comments: Vec<Arc<str>>, /// Delimiters and configuration for recognizing and formatting block comments. #[serde(default)] pub block_comment: Option<BlockCommentConfig>, /// Delimiters and configuration for recognizing and formatting documentation comments. #[serde(default, alias = "documentation")] pub documentation_comment: Option<BlockCommentConfig>, /// List markers that are inserted unchanged on newline (e.g., `- `, `* `, `+ `). #[serde(default)] pub unordered_list: Vec<Arc<str>>, /// Configuration for ordered lists with auto-incrementing numbers on newline (e.g., `1. ` becomes `2. `). #[serde(default)] pub ordered_list: Vec<OrderedListConfig>, /// Configuration for task lists where multiple markers map to a single continuation prefix (e.g., `- [x] ` continues as `- [ ] `). #[serde(default)] pub task_list: Option<TaskListConfig>, /// A list of additional regex patterns that should be treated as prefixes /// for creating boundaries during rewrapping, ensuring content from one /// prefixed section doesn't merge with another (e.g., markdown list items). /// By default, Zed treats as paragraph and comment prefixes as boundaries. #[serde(default, deserialize_with = "deserialize_regex_vec")] #[schemars(schema_with = "regex_vec_json_schema")] pub rewrap_prefixes: Vec<Regex>, /// A list of language servers that are allowed to run on subranges of a given language. #[serde(default)] pub scope_opt_in_language_servers: Vec<SharedString>, #[serde(default)] pub overrides: HashMap<String, LanguageConfigOverride>, /// A list of characters that Zed should treat as word characters for the /// purpose of features that operate on word boundaries, like 'move to next word end' /// or a whole-word search in buffer search. #[serde(default)] pub word_characters: HashSet<char>, /// Whether to indent lines using tab characters, as opposed to multiple /// spaces. #[serde(default)] pub hard_tabs: Option<bool>, /// How many columns a tab should occupy. #[serde(default)] #[schemars(range(min = 1, max = 128))] pub tab_size: Option<NonZeroU32>, /// How to soft-wrap long lines of text. #[serde(default)] pub soft_wrap: Option<SoftWrap>, /// When set, selections can be wrapped using prefix/suffix pairs on both sides. #[serde(default)] pub wrap_characters: Option<WrapCharactersConfig>, /// The name of a Prettier parser that will be used for this language when no file path is available. /// If there's a parser name in the language settings, that will be used instead. #[serde(default)] pub prettier_parser_name: Option<String>, /// If true, this language is only for syntax highlighting via an injection into other /// languages, but should not appear to the user as a distinct language. #[serde(default)] pub hidden: bool, /// If configured, this language contains JSX style tags, and should support auto-closing of those tags. #[serde(default)] pub jsx_tag_auto_close: Option<JsxTagAutoCloseConfig>, /// A list of characters that Zed should treat as word characters for completion queries. #[serde(default)] pub completion_query_characters: HashSet<char>, /// A list of characters that Zed should treat as word characters for linked edit operations. #[serde(default)] pub linked_edit_characters: HashSet<char>, /// A list of preferred debuggers for this language. #[serde(default)] pub debuggers: IndexSet<SharedString>, } impl LanguageConfig { pub const FILE_NAME: &str = "config.toml"; pub fn load(config_path: impl AsRef<Path>) -> anyhow::Result<Self> { let config = std::fs::read_to_string(config_path.as_ref())?; toml::from_str(&config).map_err(Into::into) } } impl Default for LanguageConfig { fn default() -> Self { Self { name: LanguageName::new_static(""), code_fence_block_name: None, kernel_language_names: Default::default(), grammar: None, matcher: Arc::default(), brackets: Default::default(), auto_indent_using_last_non_empty_line: default_true(), auto_indent_on_paste: None, increase_indent_pattern: Default::default(), decrease_indent_pattern: Default::default(), decrease_indent_patterns: Default::default(), autoclose_before: Default::default(), line_comments: Default::default(), block_comment: Default::default(), documentation_comment: Default::default(), unordered_list: Default::default(), ordered_list: Default::default(), task_list: Default::default(), rewrap_prefixes: Default::default(), scope_opt_in_language_servers: Default::default(), overrides: Default::default(), word_characters: Default::default(), collapsed_placeholder: Default::default(), hard_tabs: None, tab_size: None, soft_wrap: None, wrap_characters: None, prettier_parser_name: None, hidden: false, jsx_tag_auto_close: None, completion_query_characters: Default::default(), linked_edit_characters: Default::default(), debuggers: Default::default(), } } } #[derive(Clone, Debug, Deserialize, Default, JsonSchema)] pub struct DecreaseIndentConfig { #[serde(default, deserialize_with = "deserialize_regex")] #[schemars(schema_with = "regex_json_schema")] pub pattern: Option<Regex>, #[serde(default)] pub valid_after: Vec<String>, } /// Configuration for continuing ordered lists with auto-incrementing numbers. #[derive(Clone, Debug, Deserialize, JsonSchema)] pub struct OrderedListConfig { /// A regex pattern with a capture group for the number portion (e.g., `(\\d+)\\. `). pub pattern: String, /// A format string where `{1}` is replaced with the incremented number (e.g., `{1}. `). pub format: String, } /// Configuration for continuing task lists on newline. #[derive(Clone, Debug, Deserialize, JsonSchema)] pub struct TaskListConfig { /// The list markers to match (e.g., `- [ ] `, `- [x] `). pub prefixes: Vec<Arc<str>>, /// The marker to insert when continuing the list on a new line (e.g., `- [ ] `). pub continuation: Arc<str>, } #[derive(Debug, Serialize, Deserialize, Default, JsonSchema)] pub struct LanguageMatcher { /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`. #[serde(default)] pub path_suffixes: Vec<String>, /// A regex pattern that determines whether the language should be assigned to a file or not. #[serde( default, serialize_with = "serialize_regex", deserialize_with = "deserialize_regex" )] #[schemars(schema_with = "regex_json_schema")] pub first_line_pattern: Option<Regex>, /// Alternative names for this language used in vim/emacs modelines. /// These are matched case-insensitively against the `mode` (emacs) or /// `filetype`/`ft` (vim) specified in the modeline. #[serde(default)] pub modeline_aliases: Vec<String>, } impl Ord for LanguageMatcher { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.path_suffixes .cmp(&other.path_suffixes) .then_with(|| { self.first_line_pattern .as_ref() .map(Regex::as_str) .cmp(&other.first_line_pattern.as_ref().map(Regex::as_str)) }) .then_with(|| self.modeline_aliases.cmp(&other.modeline_aliases)) } } impl PartialOrd for LanguageMatcher { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } } impl Eq for LanguageMatcher {} impl PartialEq for LanguageMatcher { fn eq(&self, other: &Self) -> bool { self.path_suffixes == other.path_suffixes && self.first_line_pattern.as_ref().map(Regex::as_str) == other.first_line_pattern.as_ref().map(Regex::as_str) && self.modeline_aliases == other.modeline_aliases } } /// The configuration for JSX tag auto-closing. #[derive(Clone, Deserialize, JsonSchema, Debug)] pub struct JsxTagAutoCloseConfig { /// The name of the node for a opening tag pub open_tag_node_name: String, /// The name of the node for an closing tag pub close_tag_node_name: String, /// The name of the node for a complete element with children for open and close tags pub jsx_element_node_name: String, /// The name of the node found within both opening and closing /// tags that describes the tag name pub tag_name_node_name: String, /// Alternate Node names for tag names. /// Specifically needed as TSX represents the name in `<Foo.Bar>` /// as `member_expression` rather than `identifier` as usual #[serde(default)] pub tag_name_node_name_alternates: Vec<String>, /// Some grammars are smart enough to detect a closing tag /// that is not valid i.e. doesn't match it's corresponding /// opening tag or does not have a corresponding opening tag /// This should be set to the name of the node for invalid /// closing tags if the grammar contains such a node, otherwise /// detecting already closed tags will not work properly #[serde(default)] pub erroneous_close_tag_node_name: Option<String>, /// See above for erroneous_close_tag_node_name for details /// This should be set if the node used for the tag name /// within erroneous closing tags is different from the /// normal tag name node name #[serde(default)] pub erroneous_close_tag_name_node_name: Option<String>, } /// The configuration for block comments for this language. #[derive(Clone, Debug, JsonSchema, PartialEq)] pub struct BlockCommentConfig { /// A start tag of block comment. pub start: Arc<str>, /// A end tag of block comment. pub end: Arc<str>, /// A character to add as a prefix when a new line is added to a block comment. pub prefix: Arc<str>, /// A indent to add for prefix and end line upon new line. #[schemars(range(min = 1, max = 128))] pub tab_size: u32, } impl<'de> Deserialize<'de> for BlockCommentConfig { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum BlockCommentConfigHelper { New { start: Arc<str>, end: Arc<str>, prefix: Arc<str>, tab_size: u32, }, Old([Arc<str>; 2]), } match BlockCommentConfigHelper::deserialize(deserializer)? { BlockCommentConfigHelper::New { start, end, prefix, tab_size, } => Ok(BlockCommentConfig { start, end, prefix, tab_size, }), BlockCommentConfigHelper::Old([start, end]) => Ok(BlockCommentConfig { start, end, prefix: "".into(), tab_size: 0, }), } } } #[derive(Clone, Deserialize, Default, Debug, JsonSchema)] pub struct LanguageConfigOverride { #[serde(default)] pub line_comments: Override<Vec<Arc<str>>>, #[serde(default)] pub block_comment: Override<BlockCommentConfig>, #[serde(skip)] pub disabled_bracket_ixs: Vec<u16>, #[serde(default)] pub word_characters: Override<HashSet<char>>, #[serde(default)] pub completion_query_characters: Override<HashSet<char>>, #[serde(default)] pub linked_edit_characters: Override<HashSet<char>>, #[serde(default)] pub opt_into_language_servers: Vec<SharedString>, #[serde(default)] pub prefer_label_for_snippet: Option<bool>, } #[derive(Clone, Deserialize, Debug, Serialize, JsonSchema)] #[serde(untagged)] pub enum Override<T> { Remove { remove: bool }, Set(T), } impl<T> Default for Override<T> { fn default() -> Self { Override::Remove { remove: false } } } impl<T> Override<T> { pub fn as_option<'a>(this: Option<&'a Self>, original: Option<&'a T>) -> Option<&'a T> { match this { Some(Self::Set(value)) => Some(value), Some(Self::Remove { remove: true }) => None, Some(Self::Remove { remove: false }) | None => original, } } } /// Configuration of handling bracket pairs for a given language. /// /// This struct includes settings for defining which pairs of characters are considered brackets and /// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes. #[derive(Clone, Debug, Default, JsonSchema)] #[schemars(with = "Vec::<BracketPairContent>")] pub struct BracketPairConfig { /// A list of character pairs that should be treated as brackets in the context of a given language. pub pairs: Vec<BracketPair>, /// A list of tree-sitter scopes for which a given bracket should not be active. /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]` pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>, } impl BracketPairConfig { pub fn is_closing_brace(&self, c: char) -> bool { self.pairs.iter().any(|pair| pair.end.starts_with(c)) } } #[derive(Deserialize, JsonSchema)] pub struct BracketPairContent { #[serde(flatten)] pub bracket_pair: BracketPair, #[serde(default)] pub not_in: Vec<String>, } impl<'de> Deserialize<'de> for BracketPairConfig { fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> where D: Deserializer<'de>, { let result = Vec::<BracketPairContent>::deserialize(deserializer)?; let (brackets, disabled_scopes_by_bracket_ix) = result .into_iter() .map(|entry| (entry.bracket_pair, entry.not_in)) .unzip(); Ok(BracketPairConfig { pairs: brackets, disabled_scopes_by_bracket_ix, }) } } /// Describes a single bracket pair and how an editor should react to e.g. inserting /// an opening bracket or to a newline character insertion in between `start` and `end` characters. #[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema)] pub struct BracketPair { /// Starting substring for a bracket. pub start: String, /// Ending substring for a bracket. pub end: String, /// True if `end` should be automatically inserted right after `start` characters. pub close: bool, /// True if selected text should be surrounded by `start` and `end` characters. #[serde(default = "default_true")] pub surround: bool, /// True if an extra newline should be inserted while the cursor is in the middle /// of that bracket pair. pub newline: bool, } #[derive(Clone, Debug, Deserialize, JsonSchema)] pub struct WrapCharactersConfig { /// Opening token split into a prefix and suffix. The first caret goes /// after the prefix (i.e., between prefix and suffix). pub start_prefix: String, pub start_suffix: String, /// Closing token split into a prefix and suffix. The second caret goes /// after the prefix (i.e., between prefix and suffix). pub end_prefix: String, pub end_suffix: String, } pub fn default_true() -> bool { true } pub fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D::Error> { let source = Option::<String>::deserialize(d)?; if let Some(source) = source { Ok(Some(regex::Regex::new(&source).map_err(de::Error::custom)?)) } else { Ok(None) } } pub fn regex_json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { json_schema!({ "type": "string" }) }