diff --git a/.changeset/delimiter-spacing-option.md b/.changeset/delimiter-spacing-option.md new file mode 100644 index 000000000000..93ca1fae0147 --- /dev/null +++ b/.changeset/delimiter-spacing-option.md @@ -0,0 +1,31 @@ +--- +"@biomejs/biome": minor +--- + +Added the `delimiterSpacing` formatter option. This option inserts spaces inside delimiters (after the opening delimiter and before the closing delimiter) when the content fits on a single line. Empty delimiters are not affected, and no space is added before the opening delimiter. The specific delimiters affected depend on the language. It can be configured globally via `formatter.delimiterSpacing` or per-language via `javascript.formatter.delimiterSpacing`, `json.formatter.delimiterSpacing`, and `css.formatter.delimiterSpacing`. Defaults to false. + +**JavaScript:** + +```diff +- if (condition) {} ++ if ( condition ) {} +``` + +```diff +- `Hello ${name}!` ++ `Hello ${ name }!` +``` + +**JSON:** + +```diff +- [1, 2, 3] ++ [ 1, 2, 3 ] +``` + +**CSS:** + +```diff +- rgba(0, 0, 0, 1) ++ rgba( 0, 0, 0, 1 ) +``` diff --git a/crates/biome_cli/src/execute/migrate/prettier.rs b/crates/biome_cli/src/execute/migrate/prettier.rs index e289338433f7..d62de81829ba 100644 --- a/crates/biome_cli/src/execute/migrate/prettier.rs +++ b/crates/biome_cli/src/execute/migrate/prettier.rs @@ -235,6 +235,7 @@ impl TryFrom for biome_configuration::Configuration { bracket_same_line: Some(value.bracket_line.into()), attribute_position: Some(AttributePosition::default()), bracket_spacing: Some(BracketSpacing::default()), + delimiter_spacing: None, expand: Some(value.object_wrap.into()), format_with_errors: Some(false.into()), includes: None, @@ -276,6 +277,7 @@ impl TryFrom for biome_configuration::Configuration { quote_style: Some(quote_style), quote_properties: Some(value.quote_props.into()), bracket_spacing: Some(value.bracket_spacing.into()), + delimiter_spacing: None, jsx_quote_style: Some(jsx_quote_style), attribute_position: Some(AttributePosition::default()), operator_linebreak: None, diff --git a/crates/biome_cli/src/execute/migrate/unsupported_rules.rs b/crates/biome_cli/src/execute/migrate/unsupported_rules.rs index 0723a90de9b3..96e4562409b0 100644 --- a/crates/biome_cli/src/execute/migrate/unsupported_rules.rs +++ b/crates/biome_cli/src/execute/migrate/unsupported_rules.rs @@ -9,7 +9,10 @@ use crate::execute::migrate::eslint_to_biome::UnsupportedRuleReason::*; /// The array is sorted to allow binary search. pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(Eslint("array-bracket-newline"), FormatterCovers), - UnsupportedRule(Eslint("array-bracket-spacing"), FormatterCovers), + UnsupportedRule( + Eslint("array-bracket-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(Eslint("array-element-newline"), FormatterCovers), UnsupportedRule(Eslint("arrow-parens"), FormatterOption("arrowParentheses")), UnsupportedRule(Eslint("arrow-spacing"), Stylistic), @@ -19,7 +22,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(Eslint("comma-dangle"), Stylistic), UnsupportedRule(Eslint("comma-spacing"), FormatterCovers), UnsupportedRule(Eslint("comma-style"), Stylistic), - UnsupportedRule(Eslint("computed-property-spacing"), Stylistic), + UnsupportedRule( + Eslint("computed-property-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(Eslint("dot-location"), Stylistic), UnsupportedRule(Eslint("eol-last"), FormatterCovers), UnsupportedRule(Eslint("func-call-spacing"), Stylistic), @@ -85,15 +91,24 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(Eslint("space-before-function-paren"), Stylistic), UnsupportedRule(Eslint("space-before-function-parentheses"), Stylistic), UnsupportedRule(Eslint("space-before-keywords"), FormatterCovers), - UnsupportedRule(Eslint("space-in-brackets"), Stylistic), - UnsupportedRule(Eslint("space-in-parens"), FormatterCovers), + UnsupportedRule( + Eslint("space-in-brackets"), + FormatterOption("delimiterSpacing"), + ), + UnsupportedRule( + Eslint("space-in-parens"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(Eslint("space-infix-ops"), FormatterCovers), UnsupportedRule(Eslint("space-return-throw-case"), FormatterCovers), UnsupportedRule(Eslint("space-unary-ops"), FormatterCovers), UnsupportedRule(Eslint("space-unary-word-ops"), FormatterCovers), UnsupportedRule(Eslint("spaced-comment"), Stylistic), UnsupportedRule(Eslint("switch-colon-spacing"), FormatterCovers), - UnsupportedRule(Eslint("template-curly-spacing"), FormatterCovers), + UnsupportedRule( + Eslint("template-curly-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(Eslint("template-tag-spacing"), FormatterCovers), UnsupportedRule(Eslint("wrap-iife"), Stylistic), UnsupportedRule(Eslint("wrap-regex"), Stylistic), @@ -110,7 +125,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(EslintReact("jsx-closing-bracket-location"), FormatterCovers), UnsupportedRule(EslintReact("jsx-closing-tag-location"), FormatterCovers), UnsupportedRule(EslintReact("jsx-curly-newline"), FormatterCovers), - UnsupportedRule(EslintReact("jsx-curly-spacing"), FormatterCovers), + UnsupportedRule( + EslintReact("jsx-curly-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintReact("jsx-equals-spacing"), FormatterCovers), UnsupportedRule(EslintReact("jsx-first-prop-new-line"), Stylistic), UnsupportedRule(EslintReact("jsx-indent"), FormatterOption("indentStyle")), @@ -123,7 +141,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(EslintReact("jsx-tag-spacing"), FormatterCovers), UnsupportedRule(EslintReact("jsx-wrap-multilines"), Stylistic), UnsupportedRule(EslintStylistic("array-bracket-newline"), FormatterCovers), - UnsupportedRule(EslintStylistic("array-bracket-spacing"), FormatterCovers), + UnsupportedRule( + EslintStylistic("array-bracket-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintStylistic("array-element-newline"), FormatterCovers), UnsupportedRule( EslintStylistic("arrow-parens"), @@ -135,7 +156,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(EslintStylistic("comma-dangle"), Stylistic), UnsupportedRule(EslintStylistic("comma-spacing"), FormatterCovers), UnsupportedRule(EslintStylistic("comma-style"), Stylistic), - UnsupportedRule(EslintStylistic("computed-property-spacing"), Stylistic), + UnsupportedRule( + EslintStylistic("computed-property-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintStylistic("dot-location"), Stylistic), UnsupportedRule(EslintStylistic("eol-last"), FormatterCovers), UnsupportedRule(EslintStylistic("func-call-spacing"), Stylistic), @@ -161,7 +185,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ ), UnsupportedRule(EslintStylistic("jsx-closing-tag-location"), FormatterCovers), UnsupportedRule(EslintStylistic("jsx-curly-newline"), FormatterCovers), - UnsupportedRule(EslintStylistic("jsx-curly-spacing"), FormatterCovers), + UnsupportedRule( + EslintStylistic("jsx-curly-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintStylistic("jsx-equals-spacing"), FormatterCovers), UnsupportedRule(EslintStylistic("jsx-first-prop-new-line"), Stylistic), UnsupportedRule(EslintStylistic("jsx-function-call-newline"), Stylistic), @@ -231,15 +258,24 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ Stylistic, ), UnsupportedRule(EslintStylistic("space-before-keywords"), FormatterCovers), - UnsupportedRule(EslintStylistic("space-in-brackets"), Stylistic), - UnsupportedRule(EslintStylistic("space-in-parens"), FormatterCovers), + UnsupportedRule( + EslintStylistic("space-in-brackets"), + FormatterOption("delimiterSpacing"), + ), + UnsupportedRule( + EslintStylistic("space-in-parens"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintStylistic("space-infix-ops"), FormatterCovers), UnsupportedRule(EslintStylistic("space-return-throw-case"), FormatterCovers), UnsupportedRule(EslintStylistic("space-unary-ops"), FormatterCovers), UnsupportedRule(EslintStylistic("space-unary-word-ops"), FormatterCovers), UnsupportedRule(EslintStylistic("spaced-comment"), Stylistic), UnsupportedRule(EslintStylistic("switch-colon-spacing"), FormatterCovers), - UnsupportedRule(EslintStylistic("template-curly-spacing"), FormatterCovers), + UnsupportedRule( + EslintStylistic("template-curly-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintStylistic("template-tag-spacing"), FormatterCovers), UnsupportedRule(EslintStylistic("type-annotation-spacing"), FormatterCovers), UnsupportedRule(EslintStylistic("type-generic-spacing"), FormatterCovers), @@ -263,7 +299,10 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ UnsupportedRule(EslintTypeScript("space-infix-ops"), FormatterCovers), UnsupportedRule(EslintUnicorn("empty-brace-spaces"), FormatterCovers), UnsupportedRule(EslintVueJs("array-bracket-newline"), FormatterCovers), - UnsupportedRule(EslintVueJs("array-bracket-spacing"), FormatterCovers), + UnsupportedRule( + EslintVueJs("array-bracket-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintVueJs("array-element-newline"), FormatterCovers), UnsupportedRule(EslintVueJs("arrow-spacing"), Stylistic), UnsupportedRule(EslintVueJs("block-spacing"), FormatterCovers), @@ -324,10 +363,16 @@ pub const UNSUPPORTED_RULES: &[UnsupportedRule] = &[ EslintVueJs("singleline-html-element-content-newline"), Stylistic, ), - UnsupportedRule(EslintVueJs("space-in-parens"), FormatterCovers), + UnsupportedRule( + EslintVueJs("space-in-parens"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(EslintVueJs("space-infix-ops"), FormatterCovers), UnsupportedRule(EslintVueJs("space-unary-ops"), FormatterCovers), - UnsupportedRule(EslintVueJs("template-curly-spacing"), FormatterCovers), + UnsupportedRule( + EslintVueJs("template-curly-spacing"), + FormatterOption("delimiterSpacing"), + ), UnsupportedRule(HtmlEslint("attrs-newline"), FormatterCovers), UnsupportedRule(HtmlEslint("element-newline"), FormatterCovers), UnsupportedRule(HtmlEslint("indent"), FormatterOption("indentWidth")), diff --git a/crates/biome_cli/tests/commands/format.rs b/crates/biome_cli/tests/commands/format.rs index 994c47a0580f..8e2149403b5a 100644 --- a/crates/biome_cli/tests/commands/format.rs +++ b/crates/biome_cli/tests/commands/format.rs @@ -112,6 +112,14 @@ const APPLY_BRACKET_SPACING_AFTER_GRAPHQL: &str = r#"{ } "#; +const APPLY_DELIMITER_SPACING_BEFORE: &str = r#"const arr = [1, 2, 3]; +function foo(a, b) {} +"#; + +const APPLY_DELIMITER_SPACING_AFTER: &str = r#"const arr = [ 1, 2, 3 ]; +function foo( a, b ) {} +"#; + const APPLY_BRACKET_SAME_LINE_BEFORE: &str = r#" Whether to insert spaces around brackets in object literals. Defaults to true. + --delimiter-spacing= Whether to insert spaces inside delimiters (after the + opening delimiter and before the closing delimiter), such as + parentheses, brackets, angle brackets, and template literal + interpolations. Spaces are not added before the opening delimiter, and + empty delimiters are not affected. Only applies when the content fits + on a single line. The specific delimiters affected depend on the + language. Defaults to false. --expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals are formatted on a @@ -99,6 +106,17 @@ The configuration that is contained inside the file `biome.json` JSX elements. Defaults to auto. --javascript-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --javascript-formatter-delimiter-spacing= Whether to insert spaces inside + delimiters (after the opening delimiter and before the closing + delimiter). Only applies when the content fits on a single line. + Spaces are not added before the opening delimiter (e.g., `function + f()` stays `function f()`, not `function f ()`), and empty delimiters + are not affected (e.g., `fn()` stays `fn()`). For JavaScript and + TypeScript, affects parentheses (e.g., `foo( a, b )`), square brackets + (e.g., `[ a, b ]`), template literal interpolations (e.g., `${ expr + }`), TypeScript angle brackets (e.g., `foo< T >()`), JSX expression + braces (e.g., `{ value }`), and the logical NOT operator (e.g., `! x`, + but in chains only after the last one: `!! x`). Defaults to false. --javascript-formatter-expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals @@ -152,6 +170,11 @@ The configuration that is contained inside the file `biome.json` Defaults to "auto". --json-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --json-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty brackets are + not affected. For JSON, affects square brackets (e.g., `[ 1, 2, 3 ]`). + Defaults to false. --json-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it @@ -183,6 +206,11 @@ The configuration that is contained inside the file `biome.json` super languages) files. Defaults to 80. --css-formatter-quote-style= The type of quotes used in CSS code. Defaults to double. + --css-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty delimiters + are not affected. For CSS, affects parentheses (e.g., `rgb( 0, 0, 0 + )`) and square brackets (e.g., `[ data-attr ]`). Defaults to false. --css-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it diff --git a/crates/biome_cli/tests/snapshots/main_cases_help/ci_help.snap b/crates/biome_cli/tests/snapshots/main_cases_help/ci_help.snap index 00cde9cc3cde..3a9cbb29d1bb 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_help/ci_help.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_help/ci_help.snap @@ -47,6 +47,13 @@ The configuration that is contained inside the file `biome.json` apply to self closing elements). --bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --delimiter-spacing= Whether to insert spaces inside delimiters (after the + opening delimiter and before the closing delimiter), such as + parentheses, brackets, angle brackets, and template literal + interpolations. Spaces are not added before the opening delimiter, and + empty delimiters are not affected. Only applies when the content fits + on a single line. The specific delimiters affected depend on the + language. Defaults to false. --expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals are formatted on a @@ -100,6 +107,17 @@ The configuration that is contained inside the file `biome.json` JSX elements. Defaults to auto. --javascript-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --javascript-formatter-delimiter-spacing= Whether to insert spaces inside + delimiters (after the opening delimiter and before the closing + delimiter). Only applies when the content fits on a single line. + Spaces are not added before the opening delimiter (e.g., `function + f()` stays `function f()`, not `function f ()`), and empty delimiters + are not affected (e.g., `fn()` stays `fn()`). For JavaScript and + TypeScript, affects parentheses (e.g., `foo( a, b )`), square brackets + (e.g., `[ a, b ]`), template literal interpolations (e.g., `${ expr + }`), TypeScript angle brackets (e.g., `foo< T >()`), JSX expression + braces (e.g., `{ value }`), and the logical NOT operator (e.g., `! x`, + but in chains only after the last one: `!! x`). Defaults to false. --javascript-formatter-expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals @@ -153,6 +171,11 @@ The configuration that is contained inside the file `biome.json` Defaults to "auto". --json-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --json-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty brackets are + not affected. For JSON, affects square brackets (e.g., `[ 1, 2, 3 ]`). + Defaults to false. --json-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it @@ -184,6 +207,11 @@ The configuration that is contained inside the file `biome.json` super languages) files. Defaults to 80. --css-formatter-quote-style= The type of quotes used in CSS code. Defaults to double. + --css-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty delimiters + are not affected. For CSS, affects parentheses (e.g., `rgb( 0, 0, 0 + )`) and square brackets (e.g., `[ data-attr ]`). Defaults to false. --css-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it diff --git a/crates/biome_cli/tests/snapshots/main_cases_help/format_help.snap b/crates/biome_cli/tests/snapshots/main_cases_help/format_help.snap index 7732dfcfae09..3099705c3518 100644 --- a/crates/biome_cli/tests/snapshots/main_cases_help/format_help.snap +++ b/crates/biome_cli/tests/snapshots/main_cases_help/format_help.snap @@ -23,6 +23,13 @@ Generic options applied to all files apply to self closing elements). --bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --delimiter-spacing= Whether to insert spaces inside delimiters (after the + opening delimiter and before the closing delimiter), such as + parentheses, brackets, angle brackets, and template literal + interpolations. Spaces are not added before the opening delimiter, and + empty delimiters are not affected. Only applies when the content fits + on a single line. The specific delimiters affected depend on the + language. Defaults to false. --expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals are formatted on a @@ -75,6 +82,17 @@ Formatting options specific to the JavaScript files JSX elements. Defaults to auto. --javascript-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --javascript-formatter-delimiter-spacing= Whether to insert spaces inside + delimiters (after the opening delimiter and before the closing + delimiter). Only applies when the content fits on a single line. + Spaces are not added before the opening delimiter (e.g., `function + f()` stays `function f()`, not `function f ()`), and empty delimiters + are not affected (e.g., `fn()` stays `fn()`). For JavaScript and + TypeScript, affects parentheses (e.g., `foo( a, b )`), square brackets + (e.g., `[ a, b ]`), template literal interpolations (e.g., `${ expr + }`), TypeScript angle brackets (e.g., `foo< T >()`), JSX expression + braces (e.g., `{ value }`), and the logical NOT operator (e.g., `! x`, + but in chains only after the last one: `!! x`). Defaults to false. --javascript-formatter-expand= Whether to expand arrays and objects on multiple lines. When set to `auto`, object literals are formatted on multiple lines if the first property has a newline, and array literals @@ -147,6 +165,11 @@ Options that changes how the CSS formatter behaves super languages) files. Defaults to 80. --css-formatter-quote-style= The type of quotes used in CSS code. Defaults to double. + --css-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty delimiters + are not affected. For CSS, affects parentheses (e.g., `rgb( 0, 0, 0 + )`) and square brackets (e.g., `[ data-attr ]`). Defaults to false. --css-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it @@ -293,6 +316,11 @@ Available options: Defaults to "auto". --json-formatter-bracket-spacing= Whether to insert spaces around brackets in object literals. Defaults to true. + --json-formatter-delimiter-spacing= Whether to insert spaces inside delimiters + (after the opening delimiter and before the closing delimiter). Only + applies when the content fits on a single line, and empty brackets are + not affected. For JSON, affects square brackets (e.g., `[ 1, 2, 3 ]`). + Defaults to false. --json-formatter-trailing-newline= Whether to add a trailing newline at the end of the file. Setting this option to `false` is **highly discouraged** because it diff --git a/crates/biome_cli/tests/snapshots/main_commands_format/applies_global_delimiter_spacing.snap b/crates/biome_cli/tests/snapshots/main_commands_format/applies_global_delimiter_spacing.snap new file mode 100644 index 000000000000..61fc34a20ab3 --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_commands_format/applies_global_delimiter_spacing.snap @@ -0,0 +1,17 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: redactor(content) +--- +## `file.js` + +```js +const arr = [ 1, 2, 3 ]; +function foo( a, b ) {} + +``` + +# Emitted Messages + +```block +Formatted 1 file in