Skip to content

Commit

Permalink
feat(css_formatter): Add quoteStyle for css formatting (#1384)
Browse files Browse the repository at this point in the history
  • Loading branch information
faultyserver authored Dec 31, 2023
1 parent 8cea760 commit 658ffe9
Show file tree
Hide file tree
Showing 91 changed files with 937 additions and 245 deletions.
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

0 comments on commit 658ffe9

Please sign in to comment.