settings: Use a derive macro for refine (#38451)

When we refactored settings to not pass JSON blobs around, we ended up
needing
to write *a lot* of code that just merged things (like json merge used
to do).

Use a derive macro to prevent typos in this logic.

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin
2025-09-18 21:13:49 +00:00
committed by GitHub
parent 5f4f0a873e
commit b09764c54a
74 changed files with 1089 additions and 2818 deletions
+4 -4
View File
@@ -269,15 +269,15 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
cx.update(|cx| {
init_settings(cx, |settings| {
settings.file_types.extend([
("TypeScript".into(), vec!["js".into()]),
("TypeScript".into(), vec!["js".into()].into()),
(
"JavaScript".into(),
vec!["*longer.ts".into(), "ecmascript".into()],
vec!["*longer.ts".into(), "ecmascript".into()].into(),
),
("C++".into(), vec!["c".into(), "*.dev".into()]),
("C++".into(), vec!["c".into(), "*.dev".into()].into()),
(
"Dockerfile".into(),
vec!["Dockerfile".into(), "Dockerfile.*".into()],
vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),
),
]);
})
+95 -389
View File
@@ -9,22 +9,15 @@ use ec4rs::{
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use gpui::{App, Modifiers};
use itertools::{Either, Itertools};
use schemars::json_schema;
pub use settings::{
CompletionSettingsContent, EditPredictionProvider, EditPredictionsMode, FormatOnSave,
Formatter, FormatterList, InlayHintKind, LanguageSettingsContent, LspInsertMode,
RewrapBehavior, SelectedFormatter, ShowWhitespaceSetting, SoftWrap, WordsCompletionMode,
};
use settings::{
IndentGuideSettingsContent, LanguageTaskSettingsContent, ParameterizedJsonSchema,
PrettierSettingsContent, Settings, SettingsContent, SettingsLocation, SettingsStore,
SettingsUi,
};
use settings::{ExtendingVec, Settings, SettingsContent, SettingsLocation, SettingsStore};
use shellexpand;
use std::{borrow::Cow, num::NonZeroU32, path::Path, sync::Arc};
use util::MergeFrom;
use util::{ResultExt, schemars::replace_subschema};
/// Initializes the language settings.
pub fn init(cx: &mut App) {
@@ -64,7 +57,6 @@ pub struct AllLanguageSettings {
pub defaults: LanguageSettings,
languages: HashMap<LanguageName, LanguageSettings>,
pub(crate) file_types: FxHashMap<Arc<str>, GlobSet>,
pub(crate) file_globs: FxHashMap<Arc<str>, Vec<String>>,
}
/// The settings for a particular language.
@@ -189,18 +181,6 @@ pub struct CompletionSettings {
pub lsp_insert_mode: LspInsertMode,
}
impl CompletionSettings {
pub fn merge_from(&mut self, src: &Option<CompletionSettingsContent>) {
let Some(src) = src else { return };
self.words.merge_from(&src.words);
self.words_min_length.merge_from(&src.words_min_length);
self.lsp.merge_from(&src.lsp);
self.lsp_fetch_timeout_ms
.merge_from(&src.lsp_fetch_timeout_ms);
self.lsp_insert_mode.merge_from(&src.lsp_insert_mode);
}
}
/// The settings for indent guides.
#[derive(Debug, Clone, PartialEq)]
pub struct IndentGuideSettings {
@@ -226,19 +206,6 @@ pub struct IndentGuideSettings {
pub background_coloring: settings::IndentGuideBackgroundColoring,
}
impl IndentGuideSettings {
pub fn merge_from(&mut self, src: &Option<IndentGuideSettingsContent>) {
let Some(src) = src else { return };
self.enabled.merge_from(&src.enabled);
self.line_width.merge_from(&src.line_width);
self.active_line_width.merge_from(&src.active_line_width);
self.coloring.merge_from(&src.coloring);
self.background_coloring
.merge_from(&src.background_coloring);
}
}
#[derive(Debug, Clone)]
pub struct LanguageTaskSettings {
/// Extra task variables to set for a particular language.
@@ -254,17 +221,6 @@ pub struct LanguageTaskSettings {
pub prefer_lsp: bool,
}
impl LanguageTaskSettings {
pub fn merge_from(&mut self, src: &Option<LanguageTaskSettingsContent>) {
let Some(src) = src.clone() else {
return;
};
self.variables.extend(src.variables);
self.enabled.merge_from(&src.enabled);
self.prefer_lsp.merge_from(&src.prefer_lsp);
}
}
/// Allows to enable/disable formatting with Prettier
/// and configure default Prettier, used when no project-level Prettier installation is found.
/// Prettier formatting is disabled by default.
@@ -285,16 +241,6 @@ pub struct PrettierSettings {
pub options: HashMap<String, serde_json::Value>,
}
impl PrettierSettings {
pub fn merge_from(&mut self, src: &Option<PrettierSettingsContent>) {
let Some(src) = src.clone() else { return };
self.allowed.merge_from(&src.allowed);
self.parser = src.parser.clone();
self.plugins.extend(src.plugins);
self.options.extend(src.options);
}
}
impl LanguageSettings {
/// A token representing the rest of the available language servers.
const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
@@ -413,14 +359,13 @@ impl InlayHintSettings {
/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot)
/// or [Supermaven](https://supermaven.com).
#[derive(Clone, Debug, Default, SettingsUi)]
#[derive(Clone, Debug, Default)]
pub struct EditPredictionSettings {
/// The provider that supplies edit predictions.
pub provider: settings::EditPredictionProvider,
/// A list of globs representing files that edit predictions should be disabled for.
/// This list adds to a pre-existing, sensible default set of globs.
/// Any additional ones you add are combined with them.
#[settings_ui(skip)]
pub disabled_globs: Vec<DisabledGlob>,
/// Configures how edit predictions are displayed in the buffer.
pub mode: settings::EditPredictionsMode,
@@ -451,41 +396,16 @@ pub struct DisabledGlob {
is_absolute: bool,
}
#[derive(Clone, Debug, Default, SettingsUi)]
#[derive(Clone, Debug, Default)]
pub struct CopilotSettings {
/// HTTP/HTTPS proxy to use for Copilot.
#[settings_ui(skip)]
pub proxy: Option<String>,
/// Disable certificate verification for proxy (not recommended).
pub proxy_no_verify: Option<bool>,
/// Enterprise URI for Copilot.
#[settings_ui(skip)]
pub enterprise_uri: Option<String>,
}
inventory::submit! {
ParameterizedJsonSchema {
add_and_get_ref: |generator, params, _cx| {
let language_settings_content_ref = generator
.subschema_for::<LanguageSettingsContent>()
.to_value();
replace_subschema::<settings::LanguageToSettingsMap>(generator, || json_schema!({
"type": "object",
"properties": params
.language_names
.iter()
.map(|name| {
(
name.clone(),
language_settings_content_ref.clone(),
)
})
.collect::<serde_json::Map<_, _>>()
}))
}
}
}
impl AllLanguageSettings {
/// Returns the [`LanguageSettings`] for the language with the specified name.
pub fn language<'a>(
@@ -574,93 +494,99 @@ fn merge_with_editorconfig(settings: &mut LanguageSettings, cfg: &EditorconfigPr
}
impl settings::Settings for AllLanguageSettings {
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
let all_languages = &content.project.all_languages;
let defaults = all_languages.defaults.clone();
let inlay_hints = defaults.inlay_hints.unwrap();
let completions = defaults.completions.unwrap();
let prettier = defaults.prettier.unwrap();
let indent_guides = defaults.indent_guides.unwrap();
let tasks = defaults.tasks.unwrap();
let default_language_settings = LanguageSettings {
tab_size: defaults.tab_size.unwrap(),
hard_tabs: defaults.hard_tabs.unwrap(),
soft_wrap: defaults.soft_wrap.unwrap(),
preferred_line_length: defaults.preferred_line_length.unwrap(),
show_wrap_guides: defaults.show_wrap_guides.unwrap(),
wrap_guides: defaults.wrap_guides.unwrap(),
indent_guides: IndentGuideSettings {
enabled: indent_guides.enabled.unwrap(),
line_width: indent_guides.line_width.unwrap(),
active_line_width: indent_guides.active_line_width.unwrap(),
coloring: indent_guides.coloring.unwrap(),
background_coloring: indent_guides.background_coloring.unwrap(),
},
format_on_save: defaults.format_on_save.unwrap(),
remove_trailing_whitespace_on_save: defaults
.remove_trailing_whitespace_on_save
.unwrap(),
ensure_final_newline_on_save: defaults.ensure_final_newline_on_save.unwrap(),
formatter: defaults.formatter.unwrap(),
prettier: PrettierSettings {
allowed: prettier.allowed.unwrap(),
parser: prettier.parser,
plugins: prettier.plugins,
options: prettier.options,
},
jsx_tag_auto_close: defaults.jsx_tag_auto_close.unwrap().enabled.unwrap(),
enable_language_server: defaults.enable_language_server.unwrap(),
language_servers: defaults.language_servers.unwrap(),
allow_rewrap: defaults.allow_rewrap.unwrap(),
show_edit_predictions: defaults.show_edit_predictions.unwrap(),
edit_predictions_disabled_in: defaults.edit_predictions_disabled_in.unwrap(),
show_whitespaces: defaults.show_whitespaces.unwrap(),
whitespace_map: defaults.whitespace_map.unwrap(),
extend_comment_on_newline: defaults.extend_comment_on_newline.unwrap(),
inlay_hints: InlayHintSettings {
enabled: inlay_hints.enabled.unwrap(),
show_value_hints: inlay_hints.show_value_hints.unwrap(),
show_type_hints: inlay_hints.show_type_hints.unwrap(),
show_parameter_hints: inlay_hints.show_parameter_hints.unwrap(),
show_other_hints: inlay_hints.show_other_hints.unwrap(),
show_background: inlay_hints.show_background.unwrap(),
edit_debounce_ms: inlay_hints.edit_debounce_ms.unwrap(),
scroll_debounce_ms: inlay_hints.scroll_debounce_ms.unwrap(),
toggle_on_modifiers_press: inlay_hints.toggle_on_modifiers_press,
},
use_autoclose: defaults.use_autoclose.unwrap(),
use_auto_surround: defaults.use_auto_surround.unwrap(),
use_on_type_format: defaults.use_on_type_format.unwrap(),
auto_indent: defaults.auto_indent.unwrap(),
auto_indent_on_paste: defaults.auto_indent_on_paste.unwrap(),
always_treat_brackets_as_autoclosed: defaults
.always_treat_brackets_as_autoclosed
.unwrap(),
code_actions_on_format: defaults.code_actions_on_format.unwrap(),
linked_edits: defaults.linked_edits.unwrap(),
tasks: LanguageTaskSettings {
variables: tasks.variables,
enabled: tasks.enabled.unwrap(),
prefer_lsp: tasks.prefer_lsp.unwrap(),
},
show_completions_on_input: defaults.show_completions_on_input.unwrap(),
show_completion_documentation: defaults.show_completion_documentation.unwrap(),
completions: CompletionSettings {
words: completions.words.unwrap(),
words_min_length: completions.words_min_length.unwrap(),
lsp: completions.lsp.unwrap(),
lsp_fetch_timeout_ms: completions.lsp_fetch_timeout_ms.unwrap(),
lsp_insert_mode: completions.lsp_insert_mode.unwrap(),
},
debuggers: defaults.debuggers.unwrap(),
};
fn load_from_content(settings: LanguageSettingsContent) -> LanguageSettings {
let inlay_hints = settings.inlay_hints.unwrap();
let completions = settings.completions.unwrap();
let prettier = settings.prettier.unwrap();
let indent_guides = settings.indent_guides.unwrap();
let tasks = settings.tasks.unwrap();
LanguageSettings {
tab_size: settings.tab_size.unwrap(),
hard_tabs: settings.hard_tabs.unwrap(),
soft_wrap: settings.soft_wrap.unwrap(),
preferred_line_length: settings.preferred_line_length.unwrap(),
show_wrap_guides: settings.show_wrap_guides.unwrap(),
wrap_guides: settings.wrap_guides.unwrap(),
indent_guides: IndentGuideSettings {
enabled: indent_guides.enabled.unwrap(),
line_width: indent_guides.line_width.unwrap(),
active_line_width: indent_guides.active_line_width.unwrap(),
coloring: indent_guides.coloring.unwrap(),
background_coloring: indent_guides.background_coloring.unwrap(),
},
format_on_save: settings.format_on_save.unwrap(),
remove_trailing_whitespace_on_save: settings
.remove_trailing_whitespace_on_save
.unwrap(),
ensure_final_newline_on_save: settings.ensure_final_newline_on_save.unwrap(),
formatter: settings.formatter.unwrap(),
prettier: PrettierSettings {
allowed: prettier.allowed.unwrap(),
parser: prettier.parser,
plugins: prettier.plugins,
options: prettier.options,
},
jsx_tag_auto_close: settings.jsx_tag_auto_close.unwrap().enabled.unwrap(),
enable_language_server: settings.enable_language_server.unwrap(),
language_servers: settings.language_servers.unwrap(),
allow_rewrap: settings.allow_rewrap.unwrap(),
show_edit_predictions: settings.show_edit_predictions.unwrap(),
edit_predictions_disabled_in: settings.edit_predictions_disabled_in.unwrap(),
show_whitespaces: settings.show_whitespaces.unwrap(),
whitespace_map: settings.whitespace_map.unwrap(),
extend_comment_on_newline: settings.extend_comment_on_newline.unwrap(),
inlay_hints: InlayHintSettings {
enabled: inlay_hints.enabled.unwrap(),
show_value_hints: inlay_hints.show_value_hints.unwrap(),
show_type_hints: inlay_hints.show_type_hints.unwrap(),
show_parameter_hints: inlay_hints.show_parameter_hints.unwrap(),
show_other_hints: inlay_hints.show_other_hints.unwrap(),
show_background: inlay_hints.show_background.unwrap(),
edit_debounce_ms: inlay_hints.edit_debounce_ms.unwrap(),
scroll_debounce_ms: inlay_hints.scroll_debounce_ms.unwrap(),
toggle_on_modifiers_press: inlay_hints.toggle_on_modifiers_press,
},
use_autoclose: settings.use_autoclose.unwrap(),
use_auto_surround: settings.use_auto_surround.unwrap(),
use_on_type_format: settings.use_on_type_format.unwrap(),
auto_indent: settings.auto_indent.unwrap(),
auto_indent_on_paste: settings.auto_indent_on_paste.unwrap(),
always_treat_brackets_as_autoclosed: settings
.always_treat_brackets_as_autoclosed
.unwrap(),
code_actions_on_format: settings.code_actions_on_format.unwrap(),
linked_edits: settings.linked_edits.unwrap(),
tasks: LanguageTaskSettings {
variables: tasks.variables,
enabled: tasks.enabled.unwrap(),
prefer_lsp: tasks.prefer_lsp.unwrap(),
},
show_completions_on_input: settings.show_completions_on_input.unwrap(),
show_completion_documentation: settings.show_completion_documentation.unwrap(),
completions: CompletionSettings {
words: completions.words.unwrap(),
words_min_length: completions.words_min_length.unwrap(),
lsp: completions.lsp.unwrap(),
lsp_fetch_timeout_ms: completions.lsp_fetch_timeout_ms.unwrap(),
lsp_insert_mode: completions.lsp_insert_mode.unwrap(),
},
debuggers: settings.debuggers.unwrap(),
}
}
let default_language_settings = load_from_content(all_languages.defaults.clone());
let mut languages = HashMap::default();
for (language_name, settings) in &all_languages.languages.0 {
let mut language_settings = default_language_settings.clone();
merge_settings(&mut language_settings, settings);
languages.insert(LanguageName(language_name.clone()), language_settings);
let mut language_settings = all_languages.defaults.clone();
settings::merge_from::MergeFrom::merge_from(&mut language_settings, Some(settings));
languages.insert(
LanguageName(language_name.clone()),
load_from_content(language_settings),
);
}
let edit_prediction_provider = all_languages
@@ -688,17 +614,15 @@ impl settings::Settings for AllLanguageSettings {
let enabled_in_text_threads = edit_predictions.enabled_in_text_threads.unwrap();
let mut file_types: FxHashMap<Arc<str>, GlobSet> = FxHashMap::default();
let mut file_globs: FxHashMap<Arc<str>, Vec<String>> = FxHashMap::default();
for (language, patterns) in &all_languages.file_types {
let mut builder = GlobSetBuilder::new();
for pattern in patterns {
for pattern in &patterns.0 {
builder.add(Glob::new(pattern).unwrap());
}
file_types.insert(language.clone(), builder.build().unwrap());
file_globs.insert(language.clone(), patterns.clone());
}
Self {
@@ -725,110 +649,6 @@ impl settings::Settings for AllLanguageSettings {
defaults: default_language_settings,
languages,
file_types,
file_globs,
}
}
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
let all_languages = &content.project.all_languages;
if let Some(provider) = all_languages
.features
.as_ref()
.and_then(|f| f.edit_prediction_provider)
{
self.edit_predictions.provider = provider;
}
if let Some(edit_predictions) = all_languages.edit_predictions.as_ref() {
self.edit_predictions
.mode
.merge_from(&edit_predictions.mode);
self.edit_predictions
.enabled_in_text_threads
.merge_from(&edit_predictions.enabled_in_text_threads);
if let Some(disabled_globs) = edit_predictions.disabled_globs.as_ref() {
self.edit_predictions
.disabled_globs
.extend(disabled_globs.iter().filter_map(|g| {
let expanded_g = shellexpand::tilde(g).into_owned();
Some(DisabledGlob {
matcher: globset::Glob::new(&expanded_g).ok()?.compile_matcher(),
is_absolute: Path::new(&expanded_g).is_absolute(),
})
}));
}
}
if let Some(proxy) = all_languages
.edit_predictions
.as_ref()
.and_then(|settings| settings.copilot.as_ref()?.proxy.clone())
{
self.edit_predictions.copilot.proxy = Some(proxy);
}
if let Some(proxy_no_verify) = all_languages
.edit_predictions
.as_ref()
.and_then(|settings| settings.copilot.as_ref()?.proxy_no_verify)
{
self.edit_predictions.copilot.proxy_no_verify = Some(proxy_no_verify);
}
if let Some(enterprise_uri) = all_languages
.edit_predictions
.as_ref()
.and_then(|settings| settings.copilot.as_ref()?.enterprise_uri.clone())
{
self.edit_predictions.copilot.enterprise_uri = Some(enterprise_uri);
}
// A user's global settings override the default global settings and
// all default language-specific settings.
merge_settings(&mut self.defaults, &all_languages.defaults);
for language_settings in self.languages.values_mut() {
merge_settings(language_settings, &all_languages.defaults);
}
// A user's language-specific settings override default language-specific settings.
for (language_name, user_language_settings) in &all_languages.languages.0 {
merge_settings(
self.languages
.entry(LanguageName(language_name.clone()))
.or_insert_with(|| self.defaults.clone()),
user_language_settings,
);
}
for (language, patterns) in &all_languages.file_types {
let mut builder = GlobSetBuilder::new();
let default_value = self.file_globs.get(&language.clone());
// Merge the default value with the user's value.
if let Some(patterns) = default_value {
for pattern in patterns {
if let Some(glob) = Glob::new(pattern).log_err() {
builder.add(glob);
}
}
}
for pattern in patterns {
if let Some(glob) = Glob::new(pattern).log_err() {
builder.add(glob);
}
}
self.file_globs
.entry(language.clone())
.or_default()
.extend(patterns.clone());
if let Some(matcher) = builder.build().log_err() {
self.file_types.insert(language.clone(), matcher);
}
}
}
@@ -917,14 +737,14 @@ impl settings::Settings for AllLanguageSettings {
// TODO: pull ^ out into helper and reuse for per-language settings
// vscodes file association map is inverted from ours, so we flip the mapping before merging
let mut associations: HashMap<Arc<str>, Vec<String>> = HashMap::default();
let mut associations: HashMap<Arc<str>, ExtendingVec<String>> = HashMap::default();
if let Some(map) = vscode
.read_value("files.associations")
.and_then(|v| v.as_object())
{
for (k, v) in map {
let Some(v) = v.as_str() else { continue };
associations.entry(v.into()).or_default().push(k.clone());
associations.entry(v.into()).or_default().0.push(k.clone());
}
}
@@ -957,121 +777,7 @@ impl settings::Settings for AllLanguageSettings {
}
}
fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
settings.tab_size.merge_from(&src.tab_size);
settings.tab_size = settings
.tab_size
.clamp(NonZeroU32::new(1).unwrap(), NonZeroU32::new(16).unwrap());
settings.hard_tabs.merge_from(&src.hard_tabs);
settings.soft_wrap.merge_from(&src.soft_wrap);
settings.use_autoclose.merge_from(&src.use_autoclose);
settings
.use_auto_surround
.merge_from(&src.use_auto_surround);
settings
.use_on_type_format
.merge_from(&src.use_on_type_format);
settings.auto_indent.merge_from(&src.auto_indent);
settings
.auto_indent_on_paste
.merge_from(&src.auto_indent_on_paste);
settings
.always_treat_brackets_as_autoclosed
.merge_from(&src.always_treat_brackets_as_autoclosed);
settings.show_wrap_guides.merge_from(&src.show_wrap_guides);
settings.wrap_guides.merge_from(&src.wrap_guides);
settings.indent_guides.merge_from(&src.indent_guides);
settings
.code_actions_on_format
.merge_from(&src.code_actions_on_format.clone());
settings.linked_edits.merge_from(&src.linked_edits);
settings.tasks.merge_from(&src.tasks);
settings
.preferred_line_length
.merge_from(&src.preferred_line_length);
settings.formatter.merge_from(&src.formatter.clone());
settings.prettier.merge_from(&src.prettier.clone());
settings
.jsx_tag_auto_close
.merge_from(&src.jsx_tag_auto_close.as_ref().and_then(|v| v.enabled));
settings
.format_on_save
.merge_from(&src.format_on_save.clone());
settings
.remove_trailing_whitespace_on_save
.merge_from(&src.remove_trailing_whitespace_on_save);
settings
.ensure_final_newline_on_save
.merge_from(&src.ensure_final_newline_on_save);
settings
.enable_language_server
.merge_from(&src.enable_language_server);
settings
.language_servers
.merge_from(&src.language_servers.clone());
settings.allow_rewrap.merge_from(&src.allow_rewrap);
settings
.show_edit_predictions
.merge_from(&src.show_edit_predictions);
settings
.edit_predictions_disabled_in
.merge_from(&src.edit_predictions_disabled_in.clone());
settings.show_whitespaces.merge_from(&src.show_whitespaces);
settings
.whitespace_map
.merge_from(&src.whitespace_map.clone());
settings
.extend_comment_on_newline
.merge_from(&src.extend_comment_on_newline);
if let Some(inlay_hints) = &src.inlay_hints {
settings
.inlay_hints
.enabled
.merge_from(&inlay_hints.enabled);
settings
.inlay_hints
.show_value_hints
.merge_from(&inlay_hints.show_value_hints);
settings
.inlay_hints
.show_type_hints
.merge_from(&inlay_hints.show_type_hints);
settings
.inlay_hints
.show_parameter_hints
.merge_from(&inlay_hints.show_parameter_hints);
settings
.inlay_hints
.show_other_hints
.merge_from(&inlay_hints.show_other_hints);
settings
.inlay_hints
.show_background
.merge_from(&inlay_hints.show_background);
settings
.inlay_hints
.edit_debounce_ms
.merge_from(&inlay_hints.edit_debounce_ms);
settings
.inlay_hints
.scroll_debounce_ms
.merge_from(&inlay_hints.scroll_debounce_ms);
if let Some(toggle_on_modifiers_press) = &inlay_hints.toggle_on_modifiers_press {
settings.inlay_hints.toggle_on_modifiers_press = Some(*toggle_on_modifiers_press);
}
}
settings
.show_completions_on_input
.merge_from(&src.show_completions_on_input);
settings
.show_completion_documentation
.merge_from(&src.show_completion_documentation);
settings.completions.merge_from(&src.completions);
}
#[derive(Default, Debug, Clone, PartialEq, Eq, SettingsUi)]
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct JsxTagAutoCloseSettings {
/// Enables or disables auto-closing of JSX tags.
pub enabled: bool,