fix(formatter_css): hug a spaced ident ( only at the at-rule prelude head (#27283)

Do not glue `not (`, which changes the meanings.
This commit is contained in:
leaysgur committed 2026-10-02 11:36:17 +00:00
1 parent 892ee3a4ee
commit 1967302ca3
5 files changed
+66 -14

No files matched your search

+27 -14
View File
@@ -1055,15 +1055,15 @@ fn write_token_value<'a>(
&& (matches!(&only[0].token, Token::LParen(_))
|| (matches!(&only[0].token, Token::Ident(_))
&& matches!(&only[1].token, Token::LParen(_))));
if top_level && !whole_call {
if whole_call {
write_token_comma_group(only, top_level, f);
} else if top_level {
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
write_token_comma_group(only, f);
write_token_comma_group(only, true, f);
});
write!(f, group(&indent(&body)));
} else if whole_call {
write_token_comma_group(only, f);
} else {
write_token_comma_group_grouped(only, f);
write_token_comma_group_grouped(only, false, f);
}
return;
}
@@ -1075,7 +1075,7 @@ fn write_token_value<'a>(
for (i, group_tokens) in groups_ref.iter().enumerate() {
let is_last = i + 1 == groups_ref.len();
let content = format_with(move |f: &mut CssFormatter<'_, 'a>| {
write_token_comma_group_grouped(group_tokens, f);
write_token_comma_group_grouped(group_tokens, i == 0, f);
if !is_last {
write!(f, ",");
}
@@ -1106,15 +1106,22 @@ fn write_token_value<'a>(
write!(f, soft_line_break_or_space());
}
}
write_token_comma_group_grouped(group_tokens, f);
write_token_comma_group_grouped(group_tokens, false, f);
}
}
}
/// Space-separated tokens within one comma group;
/// balanced paren regions are printed as breakable groups.
fn write_token_comma_group<'a>(tokens: &[TokenWithSpan<'a>], f: &mut CssFormatter<'_, 'a>) {
write_token_comma_group_after(None, tokens, f);
/// `prelude_head`: the group starts the at-rule prelude,
/// so a spaced `name (` hugs like Prettier's `@include name (...)` rewrite (only the first call);
/// elsewhere the source gap decides.
fn write_token_comma_group<'a>(
tokens: &[TokenWithSpan<'a>],
prelude_head: bool,
f: &mut CssFormatter<'_, 'a>,
) {
write_token_comma_group_after(None, tokens, prelude_head, f);
}
/// `write_token_comma_group` with `head` as the fill's first entry
@@ -1122,11 +1129,13 @@ fn write_token_comma_group<'a>(tokens: &[TokenWithSpan<'a>], f: &mut CssFormatte
fn write_token_comma_group_after<'a>(
head: Option<&dyn Format<'a, CssFormatContext<'a>>>,
tokens: &[TokenWithSpan<'a>],
prelude_head: bool,
f: &mut CssFormatter<'_, 'a>,
) {
let source = f.context().source_text();
let hug_lparen = tokens.len() > 1
let hug_lparen = prelude_head
&& tokens.len() > 1
&& matches!(&tokens[1].token, Token::LParen(_))
&& matches!(&tokens[0].token, Token::Ident(_))
&& !source.text_for(&to_span(&tokens[0].span)).eq_ignore_ascii_case("if");
@@ -1219,7 +1228,11 @@ fn write_token_comma_group_after<'a>(
/// Wrapper: a comma group is its own breakable group with indent.
/// Except when it contains paren regions,
/// which provide their own indentation (avoids double-indenting `name(...)` contents).
fn write_token_comma_group_grouped<'a>(tokens: &[TokenWithSpan<'a>], f: &mut CssFormatter<'_, 'a>) {
fn write_token_comma_group_grouped<'a>(
tokens: &[TokenWithSpan<'a>],
prelude_head: bool,
f: &mut CssFormatter<'_, 'a>,
) {
// A group that IS one call/paren region delegates breaking to the parens
let whole_region = !tokens.is_empty()
&& matches!(&tokens[tokens.len() - 1].token, Token::RParen(_))
@@ -1237,11 +1250,11 @@ fn write_token_comma_group_grouped<'a>(tokens: &[TokenWithSpan<'a>], f: &mut Css
})
.any(|t| matches!(&t.token, Token::Colon(_)));
if whole_region || kv_region {
write_token_comma_group(tokens, f);
write_token_comma_group(tokens, prelude_head, f);
return;
}
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
write_token_comma_group(tokens, f);
write_token_comma_group(tokens, prelude_head, f);
});
write!(f, group(&indent(&body)));
}
@@ -1789,7 +1802,7 @@ fn write_import_modifier_group<'a>(
_ => None,
})
.collect();
write_token_comma_group_after(head.filter(|_| run_start == 0), &tokens, f);
write_token_comma_group_after(head.filter(|_| run_start == 0), &tokens, false, f);
} else {
value::write_component_value(&values[i], ValueContext::default(), f);
i += 1;
@@ -11,3 +11,6 @@
/* a wrap inside a comma group sits one indent deeper than a wrap between comma groups */
@import "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css" some-modifier another-modifier yet-another-one, "bbbbbbbbbbbbbbbbbb.css" tail-word;
@import url("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css") tail-word, "b.css" x y;
/* an unparsed `supports()` condition keeps the source's `ident (` gap */
@import url("a.css") supports(not (display: grid) and (display: flex));
@import url("a.css") supports(foo (display: grid));
@@ -15,6 +15,9 @@ source: crates/oxc_formatter_css/tests/fixtures/mod.rs
/* a wrap inside a comma group sits one indent deeper than a wrap between comma groups */
@import "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css" some-modifier another-modifier yet-another-one, "bbbbbbbbbbbbbbbbbb.css" tail-word;
@import url("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css") tail-word, "b.css" x y;
/* an unparsed `supports()` condition keeps the source's `ident (` gap */
@import url("a.css") supports(not (display: grid) and (display: flex));
@import url("a.css") supports(foo (display: grid));
==================== Output ====================
------------------
@@ -38,6 +41,9 @@ source: crates/oxc_formatter_css/tests/fixtures/mod.rs
@import url("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css")
tail-word,
"b.css" x y;
/* an unparsed `supports()` condition keeps the source's `ident (` gap */
@import url("a.css") supports(not (display: grid) and (display: flex));
@import url("a.css") supports(foo (display: grid));
-------------------
{ printWidth: 100 }
@@ -57,5 +63,8 @@ source: crates/oxc_formatter_css/tests/fixtures/mod.rs
"bbbbbbbbbbbbbbbbbb.css" tail-word;
@import url("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.css") tail-word,
"b.css" x y;
/* an unparsed `supports()` condition keeps the source's `ident (` gap */
@import url("a.css") supports(not (display: grid) and (display: flex));
@import url("a.css") supports(foo (display: grid));
===================== End =====================
@@ -0,0 +1,3 @@
/* only the prelude's first `name (` hugs, nested ones keep the gap */
@include foo (a (b), c (d));
@include foo (a), bar (b);
@@ -0,0 +1,24 @@
---
source: crates/oxc_formatter_css/tests/fixtures/mod.rs
---
==================== Input ====================
/* only the prelude's first `name (` hugs, nested ones keep the gap */
@include foo (a (b), c (d));
@include foo (a), bar (b);
==================== Output ====================
------------------
{ printWidth: 80 }
------------------
/* only the prelude's first `name (` hugs, nested ones keep the gap */
@include foo(a (b), c (d));
@include foo(a), bar (b);
-------------------
{ printWidth: 100 }
-------------------
/* only the prelude's first `name (` hugs, nested ones keep the gap */
@include foo(a (b), c (d));
@include foo(a), bar (b);
===================== End =====================