Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(css_formatter): Add quoteStyle for css formatting #1384

Merged
merged 5 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/biome_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,9 @@ pub(crate) fn validate_configuration_diagnostics(
{if verbose { PrintDiagnostic::verbose(diagnostic) } else { PrintDiagnostic::simple(diagnostic) }}
});
}

if loaded_configuration.has_errors() {
println!("{:#?}", loaded_configuration);
return Err(CliDiagnostic::workspace_error(
WorkspaceError::Configuration(ConfigurationDiagnostic::invalid_configuration(
"Biome exited because the configuration resulted in errors. Please fix them.",
Expand Down
19 changes: 13 additions & 6 deletions crates/biome_cli/tests/cases/overrides_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const FORMATTED_LINE_WIDTH: &str = "const a = [\"loreum\", \"ipsum\"];\n";
const FORMATTED_WITH_SINGLE_QUOTES: &str = "const a = ['loreum', 'ipsum'];\n";
const FORMATTED_WITH_NO_SEMICOLONS: &str = "const a = [\"loreum\", \"ipsum\"]\n";

const CSS_UNFORMATTED_QUOTES: &str =
r#"[class='foo'] { background-image: url("/path/to/file.jpg")}"#;
const CSS_FORMATTED_SINGLE_QUOTES_AND_SPACES: &str =
"[class='foo'] {\n background-image: url('/path/to/file.jpg');\n}\n";

#[test]
fn does_handle_included_file_and_disable_formatter() {
let mut console = BufferConsole::default();
Expand Down Expand Up @@ -239,10 +244,10 @@ fn does_include_file_with_different_languages() {
r#"{
"overrides": [
{ "include": ["test.js"], "formatter": { "lineWidth": 120 }, "javascript": { "formatter": { "quoteStyle": "single" } } },
{ "include": ["test2.js"], "formatter": { "lineWidth": 120, "indentStyle": "space" }, "javascript": { "formatter": { "semicolons": "asNeeded" } } }
{ "include": ["test2.js"], "formatter": { "lineWidth": 120, "indentStyle": "space" }, "javascript": { "formatter": { "semicolons": "asNeeded" } } },
{ "include": ["test.css"], "formatter": { "lineWidth": 120, "indentStyle": "space" }, "css": { "formatter": { "quoteStyle": "single" } } }
]
}

"#
.as_bytes(),
);
Expand All @@ -252,6 +257,8 @@ fn does_include_file_with_different_languages() {

let test2 = Path::new("test2.js");
fs.insert(test2.into(), UNFORMATTED_LINE_WIDTH.as_bytes());
let test_css = Path::new("test.css");
fs.insert(test_css.into(), CSS_UNFORMATTED_QUOTES.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
Expand All @@ -262,6 +269,7 @@ fn does_include_file_with_different_languages() {
("--write"),
test.as_os_str().to_str().unwrap(),
test2.as_os_str().to_str().unwrap(),
test_css.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
Expand All @@ -271,6 +279,7 @@ fn does_include_file_with_different_languages() {

assert_file_contents(&fs, test, FORMATTED_WITH_SINGLE_QUOTES);
assert_file_contents(&fs, test2, FORMATTED_WITH_NO_SEMICOLONS);
assert_file_contents(&fs, test_css, CSS_FORMATTED_SINGLE_QUOTES_AND_SPACES);

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
Expand All @@ -295,14 +304,12 @@ fn does_include_file_with_different_languages_and_files() {
"include": ["test2.js"],
"formatter": { "lineWidth": 120, "indentStyle": "space" },
"javascript": { "formatter": { "semicolons": "asNeeded" } },
"json": { "formatter": { "indentStyle": "space", "lineWidth": 20, "indentWidth": 4 } },
"css": { "formatter": { "indentStyle": "space", "lineWidth": 30, "indentWidth": 3 } }
"json": { "formatter": { "indentStyle": "space", "lineWidth": 20, "indentWidth": 4 } }
},
{
"include": ["test3.json"],
"formatter": { "lineWidth": 120, "indentStyle": "space" },
"json": { "formatter": { "indentStyle": "space", "lineWidth": 20, "indentWidth": 4 } },
"css": { "formatter": { "indentStyle": "space", "lineWidth": 30, "indentWidth": 3 } }
"json": { "formatter": { "indentStyle": "space", "lineWidth": 20, "indentWidth": 4 } }
}
]
}
Expand Down
45 changes: 45 additions & 0 deletions crates/biome_cli/tests/commands/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ let b = {
const APPLY_QUOTE_STYLE_AFTER: &str = "let a = 'something';
let b = {\n\t'hey': 'hello',\n};\n";

const APPLY_CSS_QUOTE_STYLE_BEFORE: &str =
r#"[class='foo'] { background-image: url("/path/to/file.jpg")}"#;

const APPLY_CSS_QUOTE_STYLE_AFTER: &str =
"[class='foo'] {\n\tbackground-image: url('/path/to/file.jpg');\n}\n";

const APPLY_TRAILING_COMMA_BEFORE: &str = r#"
const a = [
longlonglonglongItem1longlonglonglongItem1,
Expand Down Expand Up @@ -616,6 +622,45 @@ fn applies_custom_quote_style() {
));
}

#[test]
fn applies_custom_css_quote_style() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let css_file_path = Path::new("file.css");
fs.insert(
css_file_path.into(),
APPLY_CSS_QUOTE_STYLE_BEFORE.as_bytes(),
);

let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from(
[
("format"),
("--css-formatter-quote-style"),
("single"),
("--write"),
css_file_path.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
);

assert!(result.is_ok(), "run_cli returned {result:?}");

assert_file_contents(&fs, css_file_path, APPLY_CSS_QUOTE_STYLE_AFTER);

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"applies_custom_css_quote_style",
fs,
console,
result,
));
}

#[test]
fn applies_custom_trailing_comma() {
let mut fs = MemoryFileSystem::default();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@ expression: content
"include": ["test2.js"],
"formatter": { "lineWidth": 120, "indentStyle": "space" },
"javascript": { "formatter": { "semicolons": "asNeeded" } }
},
{
"include": ["test.css"],
"formatter": { "lineWidth": 120, "indentStyle": "space" },
"css": { "formatter": { "quoteStyle": "single" } }
}
]
}
```

## `test.css`

```css
[class='foo'] {
background-image: url('/path/to/file.jpg');
}

```

## `test.js`

```js
Expand All @@ -38,7 +52,7 @@ const a = ["loreum", "ipsum"]
# Emitted Messages

```block
Formatted 2 file(s) in <TIME>
Formatted 3 file(s) in <TIME>
```


Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ expression: content
"lineWidth": 20,
"indentWidth": 4
}
},
"css": {
"formatter": {
"indentStyle": "space",
"lineWidth": 30,
"indentWidth": 3
}
}
},
{
Expand All @@ -40,13 +33,6 @@ expression: content
"lineWidth": 20,
"indentWidth": 4
}
},
"css": {
"formatter": {
"indentStyle": "space",
"lineWidth": 30,
"indentWidth": 3
}
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ The configuration that is contained inside the file `biome.json`
--indent-width=NUMBER The size of the indentation, 2 by default
--line-ending=<lf|crlf|cr> The type of line ending.
--line-width=NUMBER What's the max width of a line. Defaults to 80.
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.
--jsx-quote-style=<double|single> The type of quotes used in JSX. Defaults to double.
--quote-properties=<preserve|as-needed> When properties in objects are quoted. Defaults to asNeeded.
--trailing-comma=<all|es5|none> Print trailing commas wherever possible in multi-line comma-separated
Expand All @@ -55,6 +54,7 @@ The configuration that is contained inside the file `biome.json`
(and its super languages) files.
--javascript-formatter-line-width=NUMBER What's the max width of a line applied to JavaScript
(and its super languages) files. Defaults to 80.
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.
--json-formatter-enabled=<true|false> Control the formatter for JSON (and its super languages)
files.
--json-formatter-indent-style=<tab|space> The indent style applied to JSON (and its super languages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ The configuration that is contained inside the file `biome.json`
--indent-width=NUMBER The size of the indentation, 2 by default
--line-ending=<lf|crlf|cr> The type of line ending.
--line-width=NUMBER What's the max width of a line. Defaults to 80.
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.
--jsx-quote-style=<double|single> The type of quotes used in JSX. Defaults to double.
--quote-properties=<preserve|as-needed> When properties in objects are quoted. Defaults to asNeeded.
--trailing-comma=<all|es5|none> Print trailing commas wherever possible in multi-line comma-separated
Expand All @@ -57,6 +56,7 @@ The configuration that is contained inside the file `biome.json`
(and its super languages) files.
--javascript-formatter-line-width=NUMBER What's the max width of a line applied to JavaScript
(and its super languages) files. Defaults to 80.
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.
--json-formatter-enabled=<true|false> Control the formatter for JSON (and its super languages)
files.
--json-formatter-indent-style=<tab|space> The indent style applied to JSON (and its super languages)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: content
---
## `file.css`

```css
[class='foo'] {
background-image: url('/path/to/file.jpg');
}

```

# Emitted Messages

```block
Formatted 1 file(s) in <TIME>
```


Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Generic options applied to all files
--line-width=NUMBER What's the max width of a line. Defaults to 80.

Formatting options specific to the JavaScript files
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.
--jsx-quote-style=<double|single> The type of quotes used in JSX. Defaults to double.
--quote-properties=<preserve|as-needed> When properties in objects are quoted. Defaults to asNeeded.
--trailing-comma=<all|es5|none> Print trailing commas wherever possible in multi-line comma-separated
Expand All @@ -43,6 +42,7 @@ Formatting options specific to the JavaScript files
(and its super languages) files.
--javascript-formatter-line-width=NUMBER What's the max width of a line applied to JavaScript
(and its super languages) files. Defaults to 80.
--quote-style=<double|single> The type of quotes used in JavaScript code. Defaults to double.

Set of properties to integrate Biome with a VCS software.
--vcs-client-kind=<git> The kind of client.
Expand Down
20 changes: 18 additions & 2 deletions crates/biome_css_formatter/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::CssCommentStyle;
use biome_formatter::{prelude::*, IndentWidth};
use biome_formatter::{prelude::*, IndentWidth, QuoteStyle};
use biome_formatter::{
CstFormatContext, FormatContext, FormatOptions, IndentStyle, LineEnding, LineWidth,
TransformSourceMap,
Expand Down Expand Up @@ -61,6 +61,7 @@ pub struct CssFormatOptions {
indent_width: IndentWidth,
line_ending: LineEnding,
line_width: LineWidth,
quote_style: QuoteStyle,
_file_source: CssFileSource,
}

Expand All @@ -72,6 +73,7 @@ impl CssFormatOptions {
indent_width: IndentWidth::default(),
line_ending: LineEnding::default(),
line_width: LineWidth::default(),
quote_style: QuoteStyle::default(),
}
}

Expand All @@ -95,6 +97,11 @@ impl CssFormatOptions {
self
}

pub fn with_quote_style(mut self, quote_style: QuoteStyle) -> Self {
self.quote_style = quote_style;
self
}

pub fn set_indent_style(&mut self, indent_style: IndentStyle) {
self.indent_style = indent_style;
}
Expand All @@ -110,6 +117,14 @@ impl CssFormatOptions {
pub fn set_line_width(&mut self, line_width: LineWidth) {
self.line_width = line_width;
}

pub fn set_quote_style(&mut self, quote_style: QuoteStyle) {
self.quote_style = quote_style;
}

pub fn quote_style(&self) -> QuoteStyle {
self.quote_style
}
}

impl FormatOptions for CssFormatOptions {
Expand Down Expand Up @@ -139,6 +154,7 @@ impl fmt::Display for CssFormatOptions {
writeln!(f, "Indent style: {}", self.indent_style)?;
writeln!(f, "Indent width: {}", self.indent_width.value())?;
writeln!(f, "Line ending: {}", self.line_ending)?;
writeln!(f, "Line width: {}", self.line_width.get())
writeln!(f, "Line width: {}", self.line_width.get())?;
writeln!(f, "Quote style: {}", self.quote_style)
}
}
2 changes: 1 addition & 1 deletion crates/biome_css_formatter/src/css/any/dimension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ impl FormatRule<AnyCssDimension> for FormatAnyCssDimension {
fn fmt(&self, node: &AnyCssDimension, f: &mut CssFormatter) -> FormatResult<()> {
match node {
AnyCssDimension::CssRegularDimension(node) => node.format().fmt(f),
AnyCssDimension::CssPercentage(node) => node.format().fmt(f),
AnyCssDimension::CssUnknownDimension(node) => node.format().fmt(f),
AnyCssDimension::CssPercentage(node) => node.format().fmt(f),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::borrow::Cow;

use crate::prelude::*;
use crate::{prelude::*, utils::string_utils::FormatLiteralStringToken};
use biome_css_syntax::{
AnyCssAttributeMatcherValue, CssAttributeMatcherValue, CssAttributeMatcherValueFields,
};
Expand Down Expand Up @@ -28,26 +26,16 @@ impl FormatNodeRule<CssAttributeMatcherValue> for FormatCssAttributeMatcherValue
return write!(f, [ident.format()]);
}

// Unlike almost all other usages of regular identifiers,
// attribute values are case-sensitive, so the identifier here
// does not get converted to lowercase. Once it's quoted, it
// will be parsed as a CssString on the next pass, at which
// point casing is preserved no matter what.
let value = ident.value_token()?;
let quoted = std::format!("\"{}\"", value.text_trimmed());

write!(
f,
[
format_leading_comments(ident.syntax()),
format_replaced(
&value,
&syntax_token_cow_slice(
Cow::Owned(quoted),
&value,
value.text_trimmed_range().start()
)
),
// Unlike almost all other usages of regular identifiers,
// attribute values are case-sensitive, so the identifier here
// does not get converted to lowercase. Once it's quoted, it
// will be parsed as a CssString on the next pass, at which
// point casing is preserved no matter what.
FormatLiteralStringToken::new(&ident.value_token()?),
format_trailing_comments(ident.syntax()),
format_dangling_comments(ident.syntax())
]
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_css_formatter/src/css/value/string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::prelude::*;
use crate::{prelude::*, utils::string_utils::FormatLiteralStringToken};
use biome_css_syntax::{CssString, CssStringFields};
use biome_formatter::write;

Expand All @@ -8,6 +8,6 @@ impl FormatNodeRule<CssString> for FormatCssString {
fn fmt_fields(&self, node: &CssString, f: &mut CssFormatter) -> FormatResult<()> {
let CssStringFields { value_token } = node.as_fields();

write!(f, [value_token.format()])
write!(f, [FormatLiteralStringToken::new(&value_token?)])
}
}
Loading