diff --git a/crates/biome_analyze/src/context.rs b/crates/biome_analyze/src/context.rs index 7c4ad906b16c..55b06164a720 100644 --- a/crates/biome_analyze/src/context.rs +++ b/crates/biome_analyze/src/context.rs @@ -1,4 +1,4 @@ -use crate::options::{JsxRuntime, PreferredQuote}; +use crate::options::{JsxRuntime, PreferredIndentation, PreferredQuote}; use crate::{FromServices, Queryable, Rule, RuleKey, ServiceBag, registry::RuleRoot}; use crate::{GroupCategory, RuleCategory, RuleGroup, RuleMetadata}; use biome_diagnostics::{Error, Result}; @@ -16,8 +16,9 @@ pub struct RuleContext<'a, R: Rule> { globals: &'a [&'a str], file_path: &'a Utf8Path, options: &'a R::Options, - preferred_quote: &'a PreferredQuote, - preferred_jsx_quote: &'a PreferredQuote, + preferred_quote: PreferredQuote, + preferred_jsx_quote: PreferredQuote, + preferred_indentation: PreferredIndentation, jsx_runtime: Option, css_modules: bool, } @@ -34,8 +35,9 @@ where globals: &'a [&'a str], file_path: &'a Utf8Path, options: &'a R::Options, - preferred_quote: &'a PreferredQuote, - preferred_jsx_quote: &'a PreferredQuote, + preferred_quote: PreferredQuote, + preferred_jsx_quote: PreferredQuote, + preferred_indentation: PreferredIndentation, jsx_runtime: Option, css_modules: bool, ) -> Result { @@ -50,6 +52,7 @@ where options, preferred_quote, preferred_jsx_quote, + preferred_indentation, jsx_runtime, css_modules, }) @@ -170,15 +173,20 @@ where } /// Returns the preferred quote that should be used when providing code actions - pub fn as_preferred_quote(&self) -> &PreferredQuote { + pub fn preferred_quote(&self) -> PreferredQuote { self.preferred_quote } /// Returns the preferred JSX quote that should be used when providing code actions - pub fn as_preferred_jsx_quote(&self) -> &PreferredQuote { + pub fn preferred_jsx_quote(&self) -> PreferredQuote { self.preferred_jsx_quote } + /// Returns the preferred indentation style that should be when providing code actions. + pub fn preferred_indentation(&self) -> PreferredIndentation { + self.preferred_indentation + } + pub fn is_css_modules(&self) -> bool { self.css_modules } diff --git a/crates/biome_analyze/src/options.rs b/crates/biome_analyze/src/options.rs index 6cb105f7a4f4..4d3653d49fec 100644 --- a/crates/biome_analyze/src/options.rs +++ b/crates/biome_analyze/src/options.rs @@ -3,6 +3,7 @@ use rustc_hash::FxHashMap; use crate::{FixKind, Rule, RuleKey}; use std::any::{Any, TypeId}; +use std::borrow::Cow; use std::sync::Arc; /// A convenient new type data structure to store the options that belong to a rule @@ -68,6 +69,9 @@ pub struct AnalyzerConfiguration { /// Allows choosing a different JSX quote when applying fixes inside the lint rules pub preferred_jsx_quote: PreferredQuote, + /// Allows applying the right indentation in fix suggestions. + preferred_indentation: PreferredIndentation, + /// Indicates the type of runtime or transformation used for interpreting JSX. jsx_runtime: Option, @@ -101,6 +105,14 @@ impl AnalyzerConfiguration { self } + pub fn with_preferred_indentation( + mut self, + preferred_indentation: PreferredIndentation, + ) -> Self { + self.preferred_indentation = preferred_indentation; + self + } + pub fn with_css_modules(mut self, css_modules: bool) -> Self { self.css_modules = css_modules; self @@ -171,12 +183,16 @@ impl AnalyzerOptions { .get_rule_fix_kind(&RuleKey::rule::()) } - pub fn preferred_quote(&self) -> &PreferredQuote { - &self.configuration.preferred_quote + pub fn preferred_quote(&self) -> PreferredQuote { + self.configuration.preferred_quote } - pub fn preferred_jsx_quote(&self) -> &PreferredQuote { - &self.configuration.preferred_jsx_quote + pub fn preferred_jsx_quote(&self) -> PreferredQuote { + self.configuration.preferred_jsx_quote + } + + pub fn preferred_indentation(&self) -> PreferredIndentation { + self.configuration.preferred_indentation } pub fn css_modules(&self) -> bool { @@ -184,7 +200,28 @@ impl AnalyzerOptions { } } -#[derive(Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] +pub enum PreferredIndentation { + /// Use tabs for indentation. + #[default] + Tab, + /// Use the given amount of spaces for indentation. + Spaces(u8), +} + +impl PreferredIndentation { + /// Returns the indentation in its string form. + pub fn to_string(self) -> Cow<'static, str> { + match self { + Self::Tab => Cow::Borrowed("\t"), + Self::Spaces(tab_width) => { + Cow::Owned(std::iter::repeat_n(' ', tab_width as usize).collect()) + } + } + } +} + +#[derive(Clone, Copy, Debug, Default)] pub enum PreferredQuote { /// Double quotes #[default] diff --git a/crates/biome_analyze/src/registry.rs b/crates/biome_analyze/src/registry.rs index f687406337c4..ec23df343b80 100644 --- a/crates/biome_analyze/src/registry.rs +++ b/crates/biome_analyze/src/registry.rs @@ -404,6 +404,7 @@ impl RegistryRule { let globals = params.options.globals(); let preferred_quote = params.options.preferred_quote(); let preferred_jsx_quote = params.options.preferred_jsx_quote(); + let preferred_indentation = params.options.preferred_indentation(); let jsx_runtime = params.options.jsx_runtime(); let css_modules = params.options.css_modules(); let options = params.options.rule_options::().unwrap_or_default(); @@ -416,6 +417,7 @@ impl RegistryRule { &options, preferred_quote, preferred_jsx_quote, + preferred_indentation, jsx_runtime, css_modules, )?; diff --git a/crates/biome_analyze/src/signals.rs b/crates/biome_analyze/src/signals.rs index 3b4b1e574b2c..5985eea79c95 100644 --- a/crates/biome_analyze/src/signals.rs +++ b/crates/biome_analyze/src/signals.rs @@ -357,6 +357,7 @@ where let globals = self.options.globals(); let preferred_quote = self.options.preferred_quote(); let preferred_jsx_quote = self.options.preferred_jsx_quote(); + let preferred_indentation = self.options.preferred_indentation(); let options = self.options.rule_options::().unwrap_or_default(); let ctx = RuleContext::new( &self.query_result, @@ -367,6 +368,7 @@ where &options, preferred_quote, preferred_jsx_quote, + preferred_indentation, self.options.jsx_runtime(), self.options.css_modules(), ) @@ -403,6 +405,7 @@ where &options, self.options.preferred_quote(), self.options.preferred_jsx_quote(), + self.options.preferred_indentation(), self.options.jsx_runtime(), self.options.css_modules(), ) @@ -467,6 +470,7 @@ where &options, self.options.preferred_quote(), self.options.preferred_jsx_quote(), + self.options.preferred_indentation(), self.options.jsx_runtime(), self.options.css_modules(), ) diff --git a/crates/biome_configuration/src/analyzer/linter/mod.rs b/crates/biome_configuration/src/analyzer/linter/mod.rs index 34b6a9419671..b978f76d36eb 100644 --- a/crates/biome_configuration/src/analyzer/linter/mod.rs +++ b/crates/biome_configuration/src/analyzer/linter/mod.rs @@ -92,7 +92,3 @@ impl LinterConfiguration { self.rules.clone().unwrap_or_default() } } - -impl Rules { - pub fn get_rule(&self) {} -} diff --git a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs index 0357a07b44c4..77aee31d5c1f 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs @@ -174,7 +174,7 @@ impl Rule for UseLiteralKeys { token.clone() } else { let identifier = inner_string_text(token); - if ctx.as_preferred_quote().is_double() { + if ctx.preferred_quote().is_double() { make::js_string_literal(&identifier) } else { make::js_string_literal_single_quotes(&identifier) diff --git a/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs b/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs index b5ac90e7bff3..000044412dd9 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_string_case_mismatch.rs @@ -102,7 +102,7 @@ impl Rule for NoStringCaseMismatch { state.literal.clone(), AnyJsExpression::AnyJsLiteralExpression( AnyJsLiteralExpression::JsStringLiteralExpression( - make::js_string_literal_expression(if ctx.as_preferred_quote().is_double() { + make::js_string_literal_expression(if ctx.preferred_quote().is_double() { make::js_string_literal(&expected_value) } else { make::js_string_literal_single_quotes(&expected_value) diff --git a/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs b/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs index ee6470842543..924fafcdf9e1 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs @@ -169,7 +169,7 @@ impl Rule for UseImportExtensions { let mut mutation = ctx.root().begin(); let (suggested_path, extension) = state.suggestion.clone()?; - let new_module_name = if ctx.as_preferred_quote().is_double() { + let new_module_name = if ctx.preferred_quote().is_double() { make::js_string_literal(&suggested_path) } else { make::js_string_literal_single_quotes(&suggested_path) diff --git a/crates/biome_js_analyze/src/lint/correctness/use_valid_typeof.rs b/crates/biome_js_analyze/src/lint/correctness/use_valid_typeof.rs index 5c9c6ede95a6..35e9bbc368c4 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_valid_typeof.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_valid_typeof.rs @@ -191,7 +191,7 @@ impl Rule for UseValidTypeof { mutation.replace_node( other.clone(), AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::from( - make::js_string_literal_expression(if ctx.as_preferred_quote().is_double() { + make::js_string_literal_expression(if ctx.preferred_quote().is_double() { make::js_string_literal(suggestion) } else { make::js_string_literal_single_quotes(suggestion) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_sorted_classes.rs b/crates/biome_js_analyze/src/lint/nursery/use_sorted_classes.rs index a852cd17f180..f9824db35fcd 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_sorted_classes.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_sorted_classes.rs @@ -214,7 +214,7 @@ impl Rule for UseSortedClasses { let is_double_quote = string_literal .value_token() .map(|token| token.text_trimmed().starts_with('"')) - .unwrap_or(ctx.as_preferred_quote().is_double()); + .unwrap_or(ctx.preferred_quote().is_double()); let replacement = js_string_literal_expression(if is_double_quote { js_string_literal(state) } else { @@ -223,7 +223,7 @@ impl Rule for UseSortedClasses { mutation.replace_node(string_literal.clone(), replacement); } AnyClassStringLike::JsLiteralMemberName(string_literal) => { - let replacement = js_literal_member_name(if ctx.as_preferred_quote().is_double() { + let replacement = js_literal_member_name(if ctx.preferred_quote().is_double() { js_string_literal(state) } else { js_string_literal_single_quotes(state) @@ -234,7 +234,7 @@ impl Rule for UseSortedClasses { let is_double_quote = jsx_string_node .value_token() .map(|token| token.text_trimmed().starts_with('"')) - .unwrap_or(ctx.as_preferred_jsx_quote().is_double()); + .unwrap_or(ctx.preferred_jsx_quote().is_double()); let replacement = jsx_string(if is_double_quote { js_string_literal(state) } else { diff --git a/crates/biome_js_analyze/src/lint/style/no_unused_template_literal.rs b/crates/biome_js_analyze/src/lint/style/no_unused_template_literal.rs index 9bd91a880ff5..322fee58a504 100644 --- a/crates/biome_js_analyze/src/lint/style/no_unused_template_literal.rs +++ b/crates/biome_js_analyze/src/lint/style/no_unused_template_literal.rs @@ -98,7 +98,7 @@ impl Rule for NoUnusedTemplateLiteral { AnyJsExpression::JsTemplateExpression(node.clone()), AnyJsExpression::AnyJsLiteralExpression( AnyJsLiteralExpression::JsStringLiteralExpression( - make::js_string_literal_expression(if ctx.as_preferred_quote().is_double() { + make::js_string_literal_expression(if ctx.preferred_quote().is_double() { make::js_string_literal(&inner_content) } else { make::js_string_literal_single_quotes(&inner_content) diff --git a/crates/biome_js_analyze/src/lint/style/use_enum_initializers.rs b/crates/biome_js_analyze/src/lint/style/use_enum_initializers.rs index fe4848872a64..51fdd729ebab 100644 --- a/crates/biome_js_analyze/src/lint/style/use_enum_initializers.rs +++ b/crates/biome_js_analyze/src/lint/style/use_enum_initializers.rs @@ -163,7 +163,7 @@ impl Rule for UseEnumInitializers { let enum_name = enum_name.text(); Some(AnyJsLiteralExpression::JsStringLiteralExpression( make::js_string_literal_expression( - if ctx.as_preferred_quote().is_double() { + if ctx.preferred_quote().is_double() { make::js_string_literal(enum_name) } else { make::js_string_literal_single_quotes(enum_name) diff --git a/crates/biome_js_analyze/src/lint/suspicious/use_strict_mode.rs b/crates/biome_js_analyze/src/lint/suspicious/use_strict_mode.rs index 20a7a267ddeb..9c5665d845ea 100644 --- a/crates/biome_js_analyze/src/lint/suspicious/use_strict_mode.rs +++ b/crates/biome_js_analyze/src/lint/suspicious/use_strict_mode.rs @@ -92,7 +92,7 @@ impl Rule for UseStrictMode { fn action(ctx: &RuleContext, _state: &Self::State) -> Option { let node = ctx.query().clone(); let mut mutation = ctx.root().begin(); - let value = match ctx.as_preferred_quote() { + let value = match ctx.preferred_quote() { PreferredQuote::Double => "\"use strict\"", PreferredQuote::Single => "'use strict'", }; diff --git a/crates/biome_service/src/file_handlers/javascript.rs b/crates/biome_service/src/file_handlers/javascript.rs index 7dbb8ff64456..acfd74795e74 100644 --- a/crates/biome_service/src/file_handlers/javascript.rs +++ b/crates/biome_service/src/file_handlers/javascript.rs @@ -18,7 +18,7 @@ use crate::{ RenameResult, }, }; -use biome_analyze::options::PreferredQuote; +use biome_analyze::options::{PreferredIndentation, PreferredQuote}; use biome_analyze::{ AnalysisFilter, AnalyzerConfiguration, AnalyzerOptions, ControlFlow, Never, QueryMatch, RuleCategoriesBuilder, RuleError, RuleFilter, @@ -260,12 +260,9 @@ impl ServiceLanguage for JsLanguage { .javascript .formatter .quote_style - .map(|quote_style: QuoteStyle| { - if quote_style == QuoteStyle::Single { - PreferredQuote::Single - } else { - PreferredQuote::Double - } + .map(|quote_style: QuoteStyle| match quote_style { + QuoteStyle::Single => PreferredQuote::Single, + QuoteStyle::Double => PreferredQuote::Double, }) .unwrap_or_default(); let preferred_jsx_quote = global @@ -273,14 +270,26 @@ impl ServiceLanguage for JsLanguage { .javascript .formatter .jsx_quote_style - .map(|quote_style: QuoteStyle| { - if quote_style == QuoteStyle::Single { - PreferredQuote::Single - } else { - PreferredQuote::Double - } + .map(|quote_style: QuoteStyle| match quote_style { + QuoteStyle::Single => PreferredQuote::Single, + QuoteStyle::Double => PreferredQuote::Double, }) .unwrap_or_default(); + let preferred_indentation = global.languages.javascript.formatter.indent_style.map_or( + PreferredIndentation::default(), + |indent_style| match indent_style { + IndentStyle::Tab => PreferredIndentation::Tab, + IndentStyle::Space => PreferredIndentation::Spaces( + global + .languages + .javascript + .formatter + .indent_width + .unwrap_or_default() + .value(), + ), + }, + ); let mut configuration = AnalyzerConfiguration::default(); let mut globals = Vec::new(); @@ -349,7 +358,8 @@ impl ServiceLanguage for JsLanguage { .with_rules(to_analyzer_rules(global, path.as_path())) .with_globals(globals) .with_preferred_quote(preferred_quote) - .with_preferred_jsx_quote(preferred_jsx_quote); + .with_preferred_jsx_quote(preferred_jsx_quote) + .with_preferred_indentation(preferred_indentation); AnalyzerOptions::default() .with_file_path(path.as_path())