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

fix(css_formatter): keep @charset double quote in single quote config #4404

Merged
merged 2 commits into from
Oct 30, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
#### Bug fixes

- Fix [#4121](https://github.com/biomejs/biome/issues/4121). Respect line width when printing multiline strings. Contributed by @ah-yu
- Fix [#4384](https://github.com/biomejs/biome/issues/4384). Keep `@charset` dobule quote under any situation for css syntax rule. Contributed by @fireairforce
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ematipico here is my changelog, lol


### JavaScript APIs

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{prelude::*, utils::string_utils::FormatLiteralStringToken};
use crate::{
prelude::*,
utils::string_utils::{FormatLiteralStringToken, StringLiteralParentKind},
};
use biome_css_syntax::{
AnyCssAttributeMatcherValue, CssAttributeMatcherValue, CssAttributeMatcherValueFields,
};
Expand Down Expand Up @@ -35,7 +38,10 @@ impl FormatNodeRule<CssAttributeMatcherValue> for FormatCssAttributeMatcherValue
// 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()?),
FormatLiteralStringToken::new(
&ident.value_token()?,
StringLiteralParentKind::Others
),
format_trailing_comments(ident.syntax()),
format_dangling_comments(ident.syntax())
]
Expand Down
30 changes: 26 additions & 4 deletions crates/biome_css_formatter/src/css/value/string.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
use crate::{prelude::*, utils::string_utils::FormatLiteralStringToken};
use biome_css_syntax::{CssString, CssStringFields};
use crate::{
prelude::*,
utils::string_utils::{FormatLiteralStringToken, StringLiteralParentKind},
};
use biome_css_syntax::{CssString, CssStringFields, CssSyntaxKind};
use biome_formatter::write;
use biome_rowan::SyntaxNodeOptionExt;

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssString;
impl FormatNodeRule<CssString> for FormatCssString {
fn fmt_fields(&self, node: &CssString, f: &mut CssFormatter) -> FormatResult<()> {
let CssStringFields { value_token } = node.as_fields();

write!(f, [FormatLiteralStringToken::new(&value_token?)])
if matches!(
node.syntax().parent().kind(),
Some(CssSyntaxKind::CSS_CHARSET_AT_RULE)
) {
write!(
f,
[FormatLiteralStringToken::new(
&value_token?,
StringLiteralParentKind::CharsetAtRule
)]
)
} else {
write!(
f,
[FormatLiteralStringToken::new(
&value_token?,
StringLiteralParentKind::Others
)]
)
}
}
}
50 changes: 36 additions & 14 deletions crates/biome_css_formatter/src/utils/string_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,29 @@ impl Format<CssFormatContext> for FormatTokenAsLowercase {
}
}

#[derive(Eq, PartialEq, Debug)]
pub(crate) enum StringLiteralParentKind {
/// Variants to track tokens that are inside a CssCharasetRule
/// @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding
CharsetAtRule,
/// other types, will add more later
Others,
}

/// Data structure of convenience to format string literals. This is copied
/// from the JS formatter, but should eventually have the logic made generic
/// and reusable since many languages will have the same needs.
pub(crate) struct FormatLiteralStringToken<'token> {
/// The current token
token: &'token CssSyntaxToken,

// The parent that holds the token
parent_kind: StringLiteralParentKind,
}

impl<'token> FormatLiteralStringToken<'token> {
pub fn new(token: &'token CssSyntaxToken) -> Self {
Self { token }
pub fn new(token: &'token CssSyntaxToken, parent_kind: StringLiteralParentKind) -> Self {
Self { token, parent_kind }
}

fn token(&self) -> &'token CssSyntaxToken {
Expand Down Expand Up @@ -204,18 +216,28 @@ impl<'token> LiteralStringNormaliser<'token> {
}

fn normalise_text(&mut self) -> Cow<'token, str> {
let string_information = self
.token
.compute_string_information(self.chosen_quote_style);

// Normalize string token and non-string token.
//
// Add the chosen quotes to any non-string tokensto normalize them into strings.
//
// CSS has various places where "string-like" tokens can be used without quotes, but the
// semantics aren't affected by whether they are present or not. This function lets those
// tokens become string literals by safely adding quotes around them.
self.normalise_tokens(string_information)
match self.token.parent_kind {
StringLiteralParentKind::CharsetAtRule => {
let string_information = StringInformation {
preferred_quote: QuoteStyle::Double,
};
self.normalise_tokens(string_information)
}
StringLiteralParentKind::Others => {
let string_information = self
.token
.compute_string_information(self.chosen_quote_style);

// Normalize string token and non-string token.
//
// Add the chosen quotes to any non-string tokensto normalize them into strings.
//
// CSS has various places where "string-like" tokens can be used without quotes, but the
// semantics aren't affected by whether they are present or not. This function lets those
// tokens become string literals by safely adding quotes around them.
self.normalise_tokens(string_information)
}
}
}

fn get_token(&self) -> &'token CssSyntaxToken {
Expand Down
11 changes: 4 additions & 7 deletions crates/biome_css_formatter/tests/quick_test.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use biome_css_formatter::format_node;
use biome_css_formatter::{context::CssFormatOptions, CssFormatLanguage};
use biome_css_parser::{parse_css, CssParserOptions};
use biome_formatter::{IndentStyle, LineWidth};
use biome_formatter::{IndentStyle, LineWidth, QuoteStyle};
use biome_formatter_test::check_reformat::CheckReformat;

mod language {
Expand All @@ -13,18 +13,15 @@ mod language {
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
let src = r#"
.container {
&:has(.child) {
color: blue;
}
}
@charset "UTF-8";
"#;
let parse = parse_css(src, CssParserOptions::default());
println!("{parse:#?}");

let options = CssFormatOptions::default()
.with_line_width(LineWidth::try_from(80).unwrap())
.with_indent_style(IndentStyle::Space);
.with_indent_style(IndentStyle::Space)
.with_quote_style(QuoteStyle::Single);
let doc = format_node(options.clone(), &parse.syntax()).unwrap();
let result = doc.print().unwrap();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ div {
width: 0\eestays-unquoted;
--\eeunquoted: green;
color: var(--\eeunquoted);
}
}

@charset·"UTF-8";
@charset "iso-8859-15";
@charset "any-string-is-okay";
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ div {
--\eeunquoted: green;
color: var(--\eeunquoted);
}

@charset·"UTF-8";
@charset "iso-8859-15";
@charset "any-string-is-okay";
```


Expand Down Expand Up @@ -65,6 +69,10 @@ div {
--\eeunquoted: green;
color: var(--\eeunquoted);
}

@charset· "UTF-8";
@charset "iso-8859-15";
@charset "any-string-is-okay";
```

## Output 1
Expand Down Expand Up @@ -99,4 +107,8 @@ div {
--\eeunquoted: green;
color: var(--\eeunquoted);
}

@charset· "UTF-8";
@charset "iso-8859-15";
@charset "any-string-is-okay";
```