mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(formatter_css): use u32 spans (#28708)
- bump oxc-css-parser 0.0.19 which changes `Span` from `usize` to `u32` - update conversion codes Peak memory is reduced 5% ~ 20% in large CSS file.
This commit is contained in:
1 parent
f680ee1f2e
commit
633ee960da
11 files changed
+212
-254
No files matched your search
Generated
+2
-2
@@ -1679,9 +1679,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxc-css-parser"
|
||||
version = "0.0.18"
|
||||
version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72213601ce5dff6b59b5fcecf15877018a6ed2f90662823e08241c4f04aa0a16"
|
||||
checksum = "21bff6681f56d41c2c45ffd87ef7bfc572e4dff44885e1a4797f0294cd6c21e9"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
]
|
||||
|
||||
+1
-1
@@ -241,7 +241,7 @@ nodejs-built-in-modules = "1.0.0" # Node.js built-in modules
|
||||
nonmax = "0.5.5" # Non-maximum numbers
|
||||
num-bigint = "0.5.0" # Big integers
|
||||
num-traits = "0.2.19" # Numeric traits
|
||||
oxc-css-parser = "0.0.18" # CSS/SCSS/Less parser (raffia 0.12.3 fork: adds `template_placeholder` typed placeholders for css-in-js + valid-syntax coverage fixes (see crates/oxc_formatter_css/AGENTS.md))
|
||||
oxc-css-parser = "0.0.19" # CSS/SCSS/Less parser (raffia 0.12.3 fork: adds `template_placeholder` typed placeholders for css-in-js + valid-syntax coverage fixes (see crates/oxc_formatter_css/AGENTS.md))
|
||||
oxc-graphql-parser = "0.0.9" # GraphQL parser (direct AST; apollo-parser 0.8.6 fork aligned with graphql-js 17 syntax)
|
||||
oxc-htmlish-parser = "0.0.2" # HTML / Vue / Angular parser (loose, span-faithful AST)
|
||||
oxc-markdown-parser = "0.0.3" # CommonMark + GFM parser (span-faithful, style-preserving typed AST)
|
||||
|
||||
@@ -267,7 +267,7 @@ fn parse_stylesheet<'a>(
|
||||
|
||||
let comments: &'a [CssComment] = ArenaVec::from_iter_in(
|
||||
parser.comments().iter().map(|c| CssComment {
|
||||
span: to_span(&c.span),
|
||||
span: to_span(c.span),
|
||||
inline: matches!(c.kind, oxc_css_parser::token::CommentKind::Line),
|
||||
}),
|
||||
&allocator,
|
||||
@@ -278,14 +278,11 @@ fn parse_stylesheet<'a>(
|
||||
}
|
||||
|
||||
fn to_diagnostic(error: &oxc_css_parser::error::Error) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("Syntax error: {}", error.kind)).with_label(to_span(&error.span))
|
||||
OxcDiagnostic::error(format!("Syntax error: {}", error.kind)).with_label(to_span(error.span))
|
||||
}
|
||||
|
||||
pub fn to_span(span: &oxc_css_parser::Span) -> Span {
|
||||
Span::new(
|
||||
u32::try_from(span.start).unwrap_or(u32::MAX),
|
||||
u32::try_from(span.end).unwrap_or(u32::MAX),
|
||||
)
|
||||
pub fn to_span(span: oxc_css_parser::Span) -> Span {
|
||||
Span::new(span.start, span.end)
|
||||
}
|
||||
|
||||
fn write_front_matter<'a>(fm: &FrontMatter<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
|
||||
@@ -15,7 +15,6 @@ use oxc_css_parser::{
|
||||
SupportsConditionKind, SupportsInParens, SupportsInParensKind, SupportsNot, SupportsOr,
|
||||
TokenSeq, UnknownAtRulePrelude,
|
||||
},
|
||||
pos::Span,
|
||||
token::{Token, TokenWithSpan},
|
||||
};
|
||||
|
||||
@@ -48,8 +47,7 @@ pub(super) fn write_at_rule<'a>(at_rule: &AtRule<'a>, f: &mut CssFormatter<'_, '
|
||||
|
||||
// Comments inside the params:
|
||||
// postcss keeps them embedded in the params string / media tokens; reconstruct from the source.
|
||||
let region_end =
|
||||
statement::params_region_end(at_rule.block.as_ref(), to_span(at_rule.span()).end, f);
|
||||
let region_end = statement::params_region_end(at_rule.block.as_ref(), at_rule.span().end, f);
|
||||
let has_params_comments = f
|
||||
.context()
|
||||
.comments()
|
||||
@@ -90,7 +88,7 @@ pub(super) fn write_at_rule<'a>(at_rule: &AtRule<'a>, f: &mut CssFormatter<'_, '
|
||||
// so we route them through the normal structured printer
|
||||
// for internal consistency over Prettier byte-equality.
|
||||
if unknown_string_params {
|
||||
let prelude_start = to_span(prelude.span()).start;
|
||||
let prelude_start = prelude.span().start;
|
||||
write_verbatim_at_rule_tail(
|
||||
name_span.end,
|
||||
prelude_start,
|
||||
@@ -173,7 +171,7 @@ pub(super) fn write_at_rule<'a>(at_rule: &AtRule<'a>, f: &mut CssFormatter<'_, '
|
||||
if let Some(prelude) = &at_rule.prelude {
|
||||
// postcss folds a no-gap, non-paren prelude into the at-rule NAME
|
||||
// (`@page:first` stays tight); reproduce by checking the source gap.
|
||||
let name_end = to_span(at_rule.name.span()).end;
|
||||
let name_end = at_rule.name.span().end;
|
||||
let prelude_span = to_span(prelude.span());
|
||||
let fused =
|
||||
name_end == prelude_span.start && !source.text_for(&prelude_span).starts_with('(');
|
||||
@@ -204,7 +202,7 @@ pub(super) fn write_at_rule<'a>(at_rule: &AtRule<'a>, f: &mut CssFormatter<'_, '
|
||||
// is written here, so nothing inside the prelude is lost to the statement loop's cursor guard.
|
||||
if let Some(block) = &at_rule.block {
|
||||
let prelude_end =
|
||||
at_rule.prelude.as_ref().map_or(name_span.end, |prelude| to_span(prelude.span()).end);
|
||||
at_rule.prelude.as_ref().map_or(name_span.end, |prelude| prelude.span().end);
|
||||
value::write_comments_before_block(prelude_end, region_end, f);
|
||||
if !is_control_directive {
|
||||
write!(f, space());
|
||||
@@ -764,17 +762,16 @@ fn write_at_rule_prelude<'a>(prelude: &AtRulePrelude<'a>, f: &mut CssFormatter<'
|
||||
// oxc-css-parser's CustomSelector span excludes the leading `:`
|
||||
write!(f, ":");
|
||||
write!(f, text(source.text_for(&custom_span)));
|
||||
head_separator(to_span(custom.selector.span()).end, f);
|
||||
head_separator(custom.selector.span().end, f);
|
||||
// Selectors share THIS group: when the prelude breaks,
|
||||
// each selector gets its own line.
|
||||
for (i, complex) in custom.selector.selectors.iter().enumerate() {
|
||||
if i > 0 {
|
||||
let comma =
|
||||
custom.selector.comma_spans.get(i - 1).map(|sp| to_span(sp).start);
|
||||
let comma = custom.selector.comma_spans.get(i - 1).map(|sp| sp.start);
|
||||
value::write_group_comma(comma, f);
|
||||
write!(f, soft_line_break_or_space());
|
||||
}
|
||||
value::flush_value_comments(to_span(complex.span()).start, f);
|
||||
value::flush_value_comments(complex.span().start, f);
|
||||
selector::write_complex_selector(complex, f);
|
||||
}
|
||||
});
|
||||
@@ -791,12 +788,12 @@ fn write_at_rule_prelude<'a>(prelude: &AtRulePrelude<'a>, f: &mut CssFormatter<'
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
if let Some(prefix) = &namespace.prefix {
|
||||
let span = to_span(prefix.span());
|
||||
let prelude_end = to_span(namespace.span()).end;
|
||||
let prelude_end = namespace.span().end;
|
||||
value::write_with_comments(span, prelude_end, f, |f| {
|
||||
write!(f, text(source.text_for(&span)));
|
||||
});
|
||||
write!(f, space());
|
||||
value::flush_value_comments(to_span(namespace.uri.span()).start, f);
|
||||
value::flush_value_comments(namespace.uri.span().start, f);
|
||||
}
|
||||
match &namespace.uri {
|
||||
NamespacePreludeUri::Str(InterpolableStr::Literal(str)) => {
|
||||
@@ -821,7 +818,7 @@ fn write_at_rule_prelude<'a>(prelude: &AtRulePrelude<'a>, f: &mut CssFormatter<'
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
for (i, layer) in layers.names.iter().enumerate() {
|
||||
if i > 0 {
|
||||
let comma = layers.comma_spans.get(i - 1).map(|sp| to_span(sp).start);
|
||||
let comma = layers.comma_spans.get(i - 1).map(|sp| sp.start);
|
||||
value::write_group_comma(comma, f);
|
||||
write!(f, space());
|
||||
}
|
||||
@@ -868,10 +865,10 @@ fn write_at_rule_prelude<'a>(prelude: &AtRulePrelude<'a>, f: &mut CssFormatter<'
|
||||
.map(|(i, path)| ImportPath {
|
||||
span: path.span,
|
||||
raw: path.raw,
|
||||
comma_start: import.comma_spans.get(i).map(|sp| to_span(sp).start),
|
||||
comma_start: import.comma_spans.get(i).map(|sp| sp.start),
|
||||
})
|
||||
.collect();
|
||||
write_import_path_list(&paths, to_span(import.span()).end, f);
|
||||
write_import_path_list(&paths, import.span().end, f);
|
||||
}
|
||||
// Only reached for SCSS-family names parsed AS CSS (see `is_value_parsed_at_rule`);
|
||||
// other `Unknown` preludes print verbatim via the `write_at_rule` early return.
|
||||
@@ -897,7 +894,7 @@ fn write_at_rule_prelude<'a>(prelude: &AtRulePrelude<'a>, f: &mut CssFormatter<'
|
||||
|
||||
/// One `@import` path with the start of its trailing comma (see [`value::write_group_comma`]'s pairing).
|
||||
struct ImportPath<'a> {
|
||||
span: Span,
|
||||
span: oxc_css_parser::Span,
|
||||
raw: &'a str,
|
||||
comma_start: Option<u32>,
|
||||
}
|
||||
@@ -928,9 +925,9 @@ fn write_import_path_list<'a>(
|
||||
let mut trailing: Vec<Option<u32>> = vec![None; n];
|
||||
let mut leads: Vec<Vec<comments::CssComment>> = Vec::with_capacity(n);
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
let path_start = to_span(&path.span).start;
|
||||
let path_start = path.span.start;
|
||||
let prev = (i > 0).then(|| &paths[i - 1]);
|
||||
let prev_end = prev.map(|prev| to_span(&prev.span).end);
|
||||
let prev_end = prev.map(|prev| prev.span.end);
|
||||
let mut lead: Vec<comments::CssComment> = all
|
||||
.iter()
|
||||
.filter(|c| c.span.end <= path_start)
|
||||
@@ -953,10 +950,7 @@ fn write_import_path_list<'a>(
|
||||
let chunk_w: Vec<u32> = paths
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, p)| {
|
||||
let span = to_span(&p.span);
|
||||
span.end - span.start + u32::from(i + 1 < n)
|
||||
})
|
||||
.map(|(i, p)| p.span.end - p.span.start + u32::from(i + 1 < n))
|
||||
.collect();
|
||||
let mut breaks_before = vec![false; n];
|
||||
let mut x = 8; // after `@import `
|
||||
@@ -999,7 +993,7 @@ fn write_import_path_list<'a>(
|
||||
write!(f, indent(&body));
|
||||
} else if has_comments {
|
||||
let path = &paths[0];
|
||||
let path_start = to_span(&path.span).start;
|
||||
let path_start = path.span.start;
|
||||
let lead: Vec<comments::CssComment> =
|
||||
f.context().comments().take_before(path_start).to_vec();
|
||||
for &comment in &lead {
|
||||
@@ -1095,9 +1089,8 @@ fn write_token_value<'a>(
|
||||
write!(f, ",");
|
||||
let sep_blank =
|
||||
groups_ref[i - 1].iter().any(|t| matches!(&t.token, Token::Colon(_))) && {
|
||||
let prev_end = groups_ref[i - 1].last().map_or(0, |t| to_span(&t.span).end);
|
||||
let next_start =
|
||||
group_tokens.first().map_or(prev_end, |t| to_span(&t.span).start);
|
||||
let prev_end = groups_ref[i - 1].last().map_or(0, |t| t.span.end);
|
||||
let next_start = group_tokens.first().map_or(prev_end, |t| t.span.start);
|
||||
comments::classify_gap(source.bytes_range(prev_end, next_start))
|
||||
== comments::Gap::Blank
|
||||
};
|
||||
@@ -1139,7 +1132,7 @@ fn write_token_comma_group_after<'a>(
|
||||
&& 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");
|
||||
&& !source.text_for(&to_span(tokens[0].span)).eq_ignore_ascii_case("if");
|
||||
|
||||
let mut filler = f.fill();
|
||||
if let Some(head) = head {
|
||||
@@ -1169,7 +1162,7 @@ fn write_token_comma_group_after<'a>(
|
||||
if matches!(&curr.token, Token::LParen(_) | Token::LBracket(_) | Token::LBrace(_)) {
|
||||
// Opener glues to the run when fused in source,
|
||||
// after a colon (`$arg: (...)`), or as a call (`name(...)`).
|
||||
let glued = to_span(&prev.span).end == to_span(&curr.span).start
|
||||
let glued = is_glued(prev.span, curr.span)
|
||||
|| matches!(&prev.token, Token::Colon(_))
|
||||
|| (run_end == 1 && hug_lparen);
|
||||
if !glued {
|
||||
@@ -1188,7 +1181,7 @@ fn write_token_comma_group_after<'a>(
|
||||
run_end = j;
|
||||
continue;
|
||||
}
|
||||
let tight = to_span(&prev.span).end == to_span(&curr.span).start
|
||||
let tight = is_glued(prev.span, curr.span)
|
||||
|| matches!(
|
||||
&curr.token,
|
||||
Token::Comma(_)
|
||||
@@ -1297,7 +1290,7 @@ fn write_token_run<'a>(run: &[TokenWithSpan<'a>], hug_lparen: bool, f: &mut CssF
|
||||
if j > 0 {
|
||||
write_token_pair_space(run, j, hug_lparen, f);
|
||||
}
|
||||
let span = to_span(&tok.span);
|
||||
let span = to_span(tok.span);
|
||||
write!(f, text(source.text_for(&span)));
|
||||
j += 1;
|
||||
continue;
|
||||
@@ -1334,7 +1327,7 @@ fn write_token_run<'a>(run: &[TokenWithSpan<'a>], hug_lparen: bool, f: &mut CssF
|
||||
if j > 0 {
|
||||
write_token_pair_space(run, j, hug_lparen, f);
|
||||
}
|
||||
let span = to_span(&tok.span);
|
||||
let span = to_span(tok.span);
|
||||
match &tok.token {
|
||||
Token::Str(_) => {
|
||||
write_raw_str(source.text_for(&span), f);
|
||||
@@ -1370,7 +1363,7 @@ fn write_token_pair_space<'a>(
|
||||
let source = f.context().source_text();
|
||||
let prev = &run[j - 1];
|
||||
let tok = &run[j];
|
||||
let gap = to_span(&prev.span).end != to_span(&tok.span).start;
|
||||
let gap = !is_glued(prev.span, tok.span);
|
||||
let wordish = |t: &Token<'_>| {
|
||||
matches!(t, Token::Ident(_) | Token::DollarVar(_) | Token::AtKeyword(_) | Token::RParen(_))
|
||||
};
|
||||
@@ -1392,7 +1385,7 @@ fn write_token_pair_space<'a>(
|
||||
(_, Token::LParen(_)) if j == 1 && hug_lparen => false,
|
||||
(Token::Colon(_) | Token::Comma(_), _) => true,
|
||||
(Token::Ident(_), Token::Asterisk(_))
|
||||
if !gap && source.text_for(&to_span(&prev.span)).ends_with('-') =>
|
||||
if !gap && source.text_for(&to_span(prev.span)).ends_with('-') =>
|
||||
{
|
||||
false
|
||||
}
|
||||
@@ -1446,11 +1439,11 @@ fn write_import_prelude<'a>(import: &ImportPrelude<'a>, f: &mut CssFormatter<'_,
|
||||
// `oxc-css-parser` routes Scss/Sass there directly).
|
||||
// Rebuild the typed path list and print it through the same comment-aware fill.
|
||||
if let Some(paths) = import_as_path_list(import, f) {
|
||||
write_import_path_list(&paths, to_span(import.span()).end, f);
|
||||
write_import_path_list(&paths, import.span().end, f);
|
||||
return;
|
||||
}
|
||||
|
||||
let prelude_end = to_span(import.span()).end;
|
||||
let prelude_end = import.span().end;
|
||||
// Non-standard import tail (`@import "a", "b"` / `@import "a" b c(d)`):
|
||||
// reference compilers accept an arbitrary post-URL mix of idents,
|
||||
// functions, parens, and comma-chained imports; oxc-css-parser keeps it as raw tokens
|
||||
@@ -1638,7 +1631,7 @@ fn push_media_condition_words<'b, 'a>(
|
||||
for kind in kinds {
|
||||
let (keyword, in_parens) = split_media_condition_kind(kind);
|
||||
match keyword {
|
||||
Some(keyword) if is_glued(keyword.span(), &in_parens.span) => {
|
||||
Some(keyword) if is_glued(keyword.span(), in_parens.span) => {
|
||||
words.push(ImportWord::GluedCall(keyword, in_parens));
|
||||
}
|
||||
Some(keyword) => {
|
||||
@@ -1679,13 +1672,13 @@ fn import_as_path_list<'a>(
|
||||
Token::Comma(_) if expect_comma => {
|
||||
expect_comma = false;
|
||||
if let Some(last) = paths.last_mut() {
|
||||
last.comma_start = Some(to_span(&tok.span).start);
|
||||
last.comma_start = Some(tok.span.start);
|
||||
}
|
||||
}
|
||||
Token::Str(_) if !expect_comma => {
|
||||
paths.push(ImportPath {
|
||||
span: tok.span,
|
||||
raw: source.text_for(&to_span(&tok.span)),
|
||||
raw: source.text_for(&to_span(tok.span)),
|
||||
comma_start: None,
|
||||
});
|
||||
expect_comma = true;
|
||||
@@ -1700,7 +1693,7 @@ fn import_as_path_list<'a>(
|
||||
fn write_import_prelude_inner<'a>(import: &ImportPrelude<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
let source = f.context().source_text();
|
||||
// A `//` between the parts breaks the prelude either way
|
||||
let prelude_end = to_span(import.span()).end;
|
||||
let prelude_end = import.span().end;
|
||||
value::write_with_comments(to_span(import.href.span()), prelude_end, f, |f| {
|
||||
write_import_href(&import.href, f);
|
||||
});
|
||||
@@ -1718,7 +1711,7 @@ fn write_import_prelude_inner<'a>(import: &ImportPrelude<'a>, f: &mut CssFormatt
|
||||
});
|
||||
}
|
||||
if let Some(media) = &import.media {
|
||||
let media_span = to_span(media.span());
|
||||
let media_span = media.span();
|
||||
head_separator(media_span.end, f);
|
||||
value::flush_value_comments(media_span.start, f);
|
||||
// No own group/indent: the queries share the prelude-level group
|
||||
@@ -1781,7 +1774,7 @@ fn write_import_modifiers<'a>(
|
||||
for (i, &(group_values, comma)) in groups.iter().enumerate().skip(1) {
|
||||
let content = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
if let Some(first) = group_values.first() {
|
||||
value::flush_value_comments(to_span(first.span()).start, f);
|
||||
value::flush_value_comments(first.span().start, f);
|
||||
}
|
||||
let words = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
write_import_modifier_group(None, group_values, f);
|
||||
@@ -1868,7 +1861,7 @@ fn write_import_href<'a>(href: &ImportPreludeHref<'a>, f: &mut CssFormatter<'_,
|
||||
/// oxc-css-parser parses the options form as a dedicated `LessImportPrelude`,
|
||||
/// which otherwise falls into the verbatim catch-all and skips quote normalization.
|
||||
fn write_less_import_prelude<'a>(import: &LessImportPrelude<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
if !has_unclaimed_comments_before(to_span(&import.span).end, f) {
|
||||
if !has_unclaimed_comments_before(import.span.end, f) {
|
||||
let mut groups: Vec<Vec<ImportWord<'_, 'a>>> = vec![vec![]];
|
||||
if !import.options.names.is_empty() {
|
||||
groups[0].push(ImportWord::LessOptions(&import.options));
|
||||
@@ -1917,8 +1910,7 @@ fn write_media_query_list<'a>(list: &MediaQueryList<'a>, f: &mut CssFormatter<'_
|
||||
// The indent governs the inter-query `,`-breaks and a `//` break inside a query;
|
||||
// without either a single query never breaks, and dropping the indent avoids
|
||||
// leaking a level into an embedded `${}` placeholder break (a media value is flat text, like Prettier).
|
||||
let breaks_inside =
|
||||
f.context().comments().iter_before(to_span(list.span()).end).any(|c| c.inline);
|
||||
let breaks_inside = f.context().comments().iter_before(list.span().end).any(|c| c.inline);
|
||||
if list.queries.len() > 1 || breaks_inside {
|
||||
write!(f, group(&indent(&body)));
|
||||
} else {
|
||||
@@ -1931,7 +1923,7 @@ fn write_media_query_list<'a>(list: &MediaQueryList<'a>, f: &mut CssFormatter<'_
|
||||
fn write_media_query_list_inner<'a>(list: &MediaQueryList<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
for (i, query) in list.queries.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value::write_group_comma(list.comma_spans.get(i - 1).map(|sp| to_span(sp).start), f);
|
||||
value::write_group_comma(list.comma_spans.get(i - 1).map(|sp| sp.start), f);
|
||||
write!(f, soft_line_break_or_space());
|
||||
}
|
||||
write_media_query(query, f);
|
||||
@@ -1944,7 +1936,7 @@ fn write_media_query<'a>(query: &MediaQuery<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
MediaQuery::ConditionOnly(condition) => write_media_condition(condition, f),
|
||||
MediaQuery::WithType(with_type) => {
|
||||
// A `//` among the words breaks the query
|
||||
let query_end = to_span(query.span()).end;
|
||||
let query_end = query.span().end;
|
||||
let write_word = |span: oxc_span::Span, f: &mut CssFormatter<'_, 'a>| {
|
||||
value::write_with_comments(span, query_end, f, |f| {
|
||||
write_maybe_lowercase(source.text_for(&span), f);
|
||||
@@ -1964,16 +1956,13 @@ fn write_media_query<'a>(query: &MediaQuery<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
if is_glued_media_type_and(condition)
|
||||
&& let Some(first) = condition.condition.conditions.first()
|
||||
{
|
||||
let word = to_span(&oxc_css_parser::pos::Span {
|
||||
start: condition.and.span().start,
|
||||
end: first.span().end,
|
||||
});
|
||||
let word = oxc_span::Span::new(condition.and.span().start, first.span().end);
|
||||
value::write_adjusted_verbatim(source.text_for(&word), f);
|
||||
write_media_condition_from(&condition.condition, 1, f);
|
||||
} else {
|
||||
write_word(to_span(condition.and.span()), f);
|
||||
write!(f, space());
|
||||
value::flush_value_comments(to_span(condition.condition.span()).start, f);
|
||||
value::flush_value_comments(condition.condition.span().start, f);
|
||||
write_media_condition(&condition.condition, f);
|
||||
}
|
||||
}
|
||||
@@ -2005,7 +1994,7 @@ fn write_media_condition_from<'a>(
|
||||
skip: usize,
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let condition_end = to_span(condition.span()).end;
|
||||
let condition_end = condition.span().end;
|
||||
for (i, kind) in condition.conditions.iter().enumerate().skip(skip) {
|
||||
if i > 0 {
|
||||
write!(f, space());
|
||||
@@ -2024,11 +2013,8 @@ fn write_media_condition_kind<'a>(kind: &MediaConditionKind<'a>, f: &mut CssForm
|
||||
let source = f.context().source_text();
|
||||
let (keyword, in_parens) = split_media_condition_kind(kind);
|
||||
if let Some(keyword) = keyword {
|
||||
if is_glued(keyword.span(), &in_parens.span) {
|
||||
let word = to_span(&oxc_css_parser::pos::Span {
|
||||
start: keyword.span().start,
|
||||
end: in_parens.span.end,
|
||||
});
|
||||
if is_glued(keyword.span(), in_parens.span) {
|
||||
let word = oxc_span::Span::new(keyword.span().start, in_parens.span.end);
|
||||
value::write_adjusted_verbatim(source.text_for(&word), f);
|
||||
return;
|
||||
}
|
||||
@@ -2050,7 +2036,7 @@ fn split_media_condition_kind<'b, 'a>(
|
||||
}
|
||||
|
||||
fn is_glued_media_type_and(condition: &MediaConditionAfterMediaType<'_>) -> bool {
|
||||
is_glued(condition.and.span(), &condition.condition.span)
|
||||
is_glued(condition.and.span(), condition.condition.span)
|
||||
}
|
||||
|
||||
fn write_media_in_parens<'a>(in_parens: &MediaInParens<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
@@ -2078,7 +2064,7 @@ fn write_media_in_parens<'a>(in_parens: &MediaInParens<'a>, f: &mut CssFormatter
|
||||
// Lenient bare operand (`print and speech`):
|
||||
// `oxc-css-parser` accepts an unparenthesized ident as <general-enclosed>;
|
||||
// the source shape must survive, so only reprint parens that exist.
|
||||
if f.context().source_text().text_for(&to_span(&in_parens.span)).starts_with('(') {
|
||||
if f.context().source_text().text_for(&to_span(in_parens.span)).starts_with('(') {
|
||||
write_general_enclosed(seq, f);
|
||||
} else {
|
||||
write_token_seq_normalized(seq, f);
|
||||
@@ -2116,13 +2102,13 @@ fn write_token_seq_normalized<'a>(seq: &TokenSeq<'a>, f: &mut CssFormatter<'_, '
|
||||
| Token::Colon(_),
|
||||
) => false,
|
||||
(Token::Comma(_) | Token::Colon(_), _) => true,
|
||||
_ => to_span(&prev.span).end != to_span(&tok.span).start,
|
||||
_ => !is_glued(prev.span, tok.span),
|
||||
};
|
||||
if needs_space {
|
||||
write!(f, space());
|
||||
}
|
||||
}
|
||||
let span = to_span(&tok.span);
|
||||
let span = to_span(tok.span);
|
||||
write!(f, text(source.text_for(&span)));
|
||||
}
|
||||
}
|
||||
@@ -2189,7 +2175,7 @@ fn write_supports_condition<'a>(condition: &SupportsCondition<'a>, f: &mut CssFo
|
||||
// A fill of keywords and parenthesized terms:
|
||||
// a long condition breaks AFTER `and`/`or`, one indent in
|
||||
// (`postcss-values` prints the params as a value group, so each word/paren is its own fill entry).
|
||||
let end = to_span(condition.span()).end;
|
||||
let end = condition.span().end;
|
||||
let keyword = |keyword| format_with(move |f| write_supports_keyword(keyword, end, f));
|
||||
let term = |in_parens| format_with(move |f| write_supports_term(in_parens, end, f));
|
||||
let not = |not| format_with(move |f| write_supports_not(not, end, f));
|
||||
@@ -2270,7 +2256,7 @@ fn write_supports_in_parens<'a>(in_parens: &SupportsInParens<'a>, f: &mut CssFor
|
||||
// An inline comment inside `selector(...)` forces the parens
|
||||
// open and trails the selector.
|
||||
// oxc-css-parser's span may stop at the selector; scan to the `)`.
|
||||
let span_end = to_span(in_parens.span()).end;
|
||||
let span_end = in_parens.span().end;
|
||||
let bytes = source.as_bytes();
|
||||
let mut scan = span_end.saturating_sub(1) as usize;
|
||||
while scan < bytes.len() && bytes[scan] != b')' {
|
||||
|
||||
@@ -38,10 +38,10 @@ pub(super) fn write_less_variable_declaration<'a>(
|
||||
write!(f, "@");
|
||||
let name_span = to_span(decl.name.name.span());
|
||||
write!(f, text(source.text_for(&name_span)));
|
||||
let colon_end = to_span(&decl.colon_span).end;
|
||||
let colon_end = decl.colon_span.end;
|
||||
// Inline comments around the colon make postcss-less treat this as a plain at-rule:
|
||||
// the raw text is kept and the block loses its `;`.
|
||||
let value_start = to_span(decl.value.span()).start;
|
||||
let value_start = decl.value.span().start;
|
||||
let inline_before_colon = f.context().comments().iter_before(colon_end).any(|c| c.inline);
|
||||
let inline_after_colon = f
|
||||
.context()
|
||||
@@ -90,7 +90,7 @@ pub(super) fn write_less_variable_declaration<'a>(
|
||||
} else {
|
||||
write!(f, body);
|
||||
}
|
||||
write_terminator_tail_comments(to_span(&decl.span).end, f);
|
||||
write_terminator_tail_comments(decl.span.end, f);
|
||||
true
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ pub(super) fn write_less_mixin_definition<'a>(
|
||||
def: &LessMixinDefinition<'a>,
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let start = to_span(def.name.span()).start;
|
||||
if has_comments_between(start, to_span(&def.block.span).start, f) {
|
||||
let start = def.name.span().start;
|
||||
if has_comments_between(start, def.block.span.start, f) {
|
||||
write_verbatim_prelude_rule(start, &def.block, true, f);
|
||||
return;
|
||||
}
|
||||
@@ -118,11 +118,11 @@ pub(super) fn write_less_mixin_definition<'a>(
|
||||
// The prelude breaks like a selector: at its word gaps, all at once, one indent in
|
||||
let prelude = format_with(|f: &mut CssFormatter<'_, 'a>| {
|
||||
write_mixin_name(&def.name, f);
|
||||
if !is_glued(def.name.span(), &def.params.span) {
|
||||
if !is_glued(def.name.span(), def.params.span) {
|
||||
write!(f, soft_line_break_or_space());
|
||||
}
|
||||
let source = f.context().source_text();
|
||||
value::write_adjusted_verbatim(source.text_for(&to_span(&def.params.span)), f);
|
||||
value::write_adjusted_verbatim(source.text_for(&to_span(def.params.span)), f);
|
||||
if let Some(guard) = &def.guard {
|
||||
write_less_guard(guard, f);
|
||||
}
|
||||
@@ -138,8 +138,8 @@ pub(super) fn write_less_conditional_qualified_rule<'a>(
|
||||
rule: &LessConditionalQualifiedRule<'a>,
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let start = to_span(&rule.span).start;
|
||||
if has_comments_between(start, to_span(&rule.block.span).start, f) {
|
||||
let start = rule.span.start;
|
||||
if has_comments_between(start, rule.block.span.start, f) {
|
||||
write_verbatim_prelude_rule(start, &rule.block, true, f);
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ fn write_less_guard<'a>(guard: &LessConditions<'a>, f: &mut CssFormatter<'_, 'a>
|
||||
for (i, condition) in guard.conditions.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, [",", soft_line_break_or_space()]);
|
||||
} else if !is_glued(&guard.when_span, condition.span()) {
|
||||
} else if !is_glued(guard.when_span, condition.span()) {
|
||||
write!(f, space());
|
||||
}
|
||||
value::write_adjusted_verbatim(source.text_for(&to_span(condition.span())), f);
|
||||
@@ -181,8 +181,8 @@ pub(super) fn write_less_mixin_call_statement<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let source = f.context().source_text();
|
||||
let span = to_span(&call.span);
|
||||
let end = call.important.as_ref().map_or(span.end, |imp| to_span(&imp.span).start);
|
||||
let span = call.span;
|
||||
let end = call.important.as_ref().map_or(span.end, |imp| imp.span.start);
|
||||
let raw = source.slice_range(span.start, end).trim_end();
|
||||
let _ = f.context().comments().take_before(end);
|
||||
value::write_adjusted_verbatim(raw, f);
|
||||
@@ -197,7 +197,7 @@ fn write_less_mixin_call<'a>(call: &LessMixinCall<'a>, f: &mut CssFormatter<'_,
|
||||
let source = f.context().source_text();
|
||||
for child in &call.callee.children {
|
||||
if let Some(combinator) = &child.combinator {
|
||||
let span = to_span(&combinator.span);
|
||||
let span = to_span(combinator.span);
|
||||
write!(f, [space(), text(source.text_for(&span)), space()]);
|
||||
}
|
||||
write_mixin_name(&child.name, f);
|
||||
@@ -236,7 +236,7 @@ fn write_less_condition<'a>(condition: &LessCondition<'a>, f: &mut CssFormatter<
|
||||
match condition {
|
||||
LessCondition::Binary(binary) => {
|
||||
write_less_condition(&binary.left, f);
|
||||
let op_span = to_span(&binary.op.span);
|
||||
let op_span = to_span(binary.op.span);
|
||||
write!(f, [space(), text(source.text_for(&op_span)), space()]);
|
||||
write_less_condition(&binary.right, f);
|
||||
}
|
||||
@@ -287,7 +287,7 @@ fn write_less_detached_ruleset<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
// Block comments before `{` stay inline; `//` ends its line.
|
||||
let block_start = to_span(&ruleset.block.span).start;
|
||||
let block_start = ruleset.block.span.start;
|
||||
for &comment in f.context().comments().take_before(block_start) {
|
||||
write!(f, FormatCommentBeforeContent::new(comment, BlockCommentAfter::Space));
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ fn write_placeholder<'a>(placeholder: &Placeholder<'a>, f: &mut CssFormatter<'_,
|
||||
|
||||
/// No source gap between two spans (postcss's `hasEmptyRawBefore`):
|
||||
/// the signal for a glued `and(` / `not(`, a `(` that makes a function, and the like.
|
||||
pub fn is_glued(left: &oxc_css_parser::pos::Span, right: &oxc_css_parser::pos::Span) -> bool {
|
||||
pub fn is_glued(left: oxc_css_parser::pos::Span, right: oxc_css_parser::pos::Span) -> bool {
|
||||
left.end == right.start
|
||||
}
|
||||
|
||||
|
||||
@@ -26,17 +26,17 @@ pub(super) fn write_postcss_simple_var_declaration<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
write_postcss_simple_var(&decl.name, f);
|
||||
statement::write_colon_run(to_span(&decl.name.span).end, to_span(&decl.colon_span).end, f);
|
||||
statement::write_colon_run(decl.name.span.end, decl.colon_span.end, f);
|
||||
|
||||
// An empty value prints as `$x:;`, as Prettier does.
|
||||
let end = to_span(&decl.span).end;
|
||||
let end = decl.span.end;
|
||||
if !decl.value.is_empty() {
|
||||
write!(f, space());
|
||||
if decl.value_is_raw {
|
||||
// postcss-simple-vars substitutes the value textually,
|
||||
// so re-spacing a raw fallback would change the substituted token stream
|
||||
// (DIVERGENCES.md#postcss-simple-var-raw-verbatim).
|
||||
let value_start = to_span(decl.value[0].span()).start;
|
||||
let value_start = decl.value[0].span().start;
|
||||
value::write_verbatim_value(Span::new(value_start, end), f);
|
||||
} else {
|
||||
// The typed value stream (including any trailing `ImportantAnnotation` pushed by the parser)
|
||||
@@ -53,6 +53,6 @@ pub(super) fn write_postcss_simple_var<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let source = f.context().source_text();
|
||||
let span = to_span(&variable.span);
|
||||
let span = to_span(variable.span);
|
||||
write!(f, text(source.text_for(&span)));
|
||||
}
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
comments::{self, BlockCommentAfter, FormatCommentBeforeContent, FormatLineCommentSuffix},
|
||||
format::to_span,
|
||||
print::{
|
||||
CssFormatter, format_with, statement,
|
||||
CssFormatter, format_with, is_glued, statement,
|
||||
value::{self, ValueContext},
|
||||
},
|
||||
};
|
||||
@@ -38,7 +38,7 @@ pub(super) fn write_sass_variable_declaration<'a>(
|
||||
write!(f, "$");
|
||||
let name_span = to_span(decl.name.name.span());
|
||||
write!(f, text(source.text_for(&name_span)));
|
||||
statement::write_colon_run(name_span.end, to_span(&decl.colon_span).end, f);
|
||||
statement::write_colon_run(name_span.end, decl.colon_span.end, f);
|
||||
write!(f, space());
|
||||
|
||||
let ctx = ValueContext { decl_prop: Some("$"), map_break: true, ..ValueContext::default() };
|
||||
@@ -46,7 +46,7 @@ pub(super) fn write_sass_variable_declaration<'a>(
|
||||
// a same-line `//` stays on the colon's (`$x: // c`), an own-line one stays own-line,
|
||||
// and the value then continues one level under the name.
|
||||
// A hard-broken comma list indents itself and claims an own-line lead inside that indent.
|
||||
let value_start = to_span(decl.value.span()).start;
|
||||
let value_start = decl.value.span().start;
|
||||
let inline_lead = f.context().comments().iter_before(value_start).any(|c| c.inline);
|
||||
let own_line_lead = f
|
||||
.context()
|
||||
@@ -78,7 +78,7 @@ pub(super) fn write_sass_variable_declaration<'a>(
|
||||
write!(f, space());
|
||||
write!(f, text(source.text_for(&span)));
|
||||
}
|
||||
statement::write_terminator_tail_comments(to_span(decl.span()).end, f);
|
||||
statement::write_terminator_tail_comments(decl.span().end, f);
|
||||
}
|
||||
|
||||
/// `("a",)` / `$x: "a",`: a single element followed by a comma is a one-element LIST in Sass,
|
||||
@@ -92,7 +92,7 @@ pub(super) fn is_single_item_list(list: &SassList<'_>) -> bool {
|
||||
|
||||
/// Start offset of the source comma after element `i` (for [`value::write_group_comma`]).
|
||||
pub(super) fn list_comma_start(list: &SassList<'_>, i: usize) -> Option<u32> {
|
||||
list.comma_spans.as_ref().and_then(|s| s.get(i)).map(|sp| to_span(sp).start)
|
||||
list.comma_spans.as_ref().and_then(|s| s.get(i)).map(|sp| sp.start)
|
||||
}
|
||||
|
||||
/// Whether [`write_top_level_value`] prints `value` one comma entry per line:
|
||||
@@ -111,7 +111,7 @@ pub(super) fn top_level_value_breaks_hard<'a>(
|
||||
if elements.len() < 2 || ctx.decl_prop.is_some_and(|p| p.starts_with("--")) {
|
||||
return false;
|
||||
}
|
||||
let value_span = to_span(value.span());
|
||||
let value_span = value.span();
|
||||
let has_comments = f
|
||||
.context()
|
||||
.comments()
|
||||
@@ -168,7 +168,7 @@ pub(super) fn write_top_level_value<'a>(
|
||||
}
|
||||
other => std::slice::from_ref(other),
|
||||
};
|
||||
(group, comma_spans.get(i).map(|sp| to_span(sp).start))
|
||||
(group, comma_spans.get(i).map(|sp| sp.start))
|
||||
})
|
||||
.collect();
|
||||
let force_hard_line = top_level_value_breaks_hard(value, ctx, f);
|
||||
@@ -185,7 +185,7 @@ pub(super) fn write_top_level_list_element<'a>(
|
||||
ctx: ValueContext<'a>,
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
value::flush_value_comments(to_span(value.span()).start, f);
|
||||
value::flush_value_comments(value.span().start, f);
|
||||
write_top_level_value(value, ctx, f);
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ pub(super) fn write_sass_map<'a>(
|
||||
// Block comments stay inline when they fit (`$map: (/* c */);`);
|
||||
// a `//` comment glues to the current line but forces the `)` onto its own line,
|
||||
// like Prettier's `lineSuffix` + `lineSuffixBoundary` (Prettier #18535).
|
||||
let r_paren = to_span(map.span()).end.saturating_sub(1);
|
||||
let r_paren = map.span().end.saturating_sub(1);
|
||||
let tail: Vec<comments::CssComment> = f.context().comments().take_before(r_paren).to_vec();
|
||||
if tail.is_empty() {
|
||||
write!(f, ["(", ")"]);
|
||||
@@ -243,14 +243,11 @@ pub(super) fn write_sass_map<'a>(
|
||||
write!(f, soft_line_break());
|
||||
for (i, item) in map.items.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value::write_group_comma(
|
||||
map.comma_spans.get(i - 1).map(|sp| to_span(sp).start),
|
||||
f,
|
||||
);
|
||||
value::write_group_comma(map.comma_spans.get(i - 1).map(|sp| sp.start), f);
|
||||
// A blank line between items in the source is preserved
|
||||
// (Prettier's isNextLineEmpty → hardline).
|
||||
let prev_end = to_span(map.items[i - 1].span()).end;
|
||||
let start = to_span(item.span()).start;
|
||||
let prev_end = map.items[i - 1].span().end;
|
||||
let start = item.span().start;
|
||||
if comments::classify_gap(source.bytes_range(prev_end, start))
|
||||
== comments::Gap::Blank
|
||||
{
|
||||
@@ -293,17 +290,17 @@ pub(super) fn write_sass_map<'a>(
|
||||
return;
|
||||
}
|
||||
let trailing = f.options().allow_trailing_comma();
|
||||
let r_paren = to_span(map.span()).end.saturating_sub(1);
|
||||
let r_paren = map.span().end.saturating_sub(1);
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
write!(f, hard_line_break());
|
||||
let source = f.context().source_text();
|
||||
for (i, item) in map.items.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value::write_group_comma(map.comma_spans.get(i - 1).map(|sp| to_span(sp).start), f);
|
||||
value::write_group_comma(map.comma_spans.get(i - 1).map(|sp| sp.start), f);
|
||||
write!(f, hard_line_break());
|
||||
// Preserve one blank line between items
|
||||
let prev_end = to_span(map.items[i - 1].span()).end;
|
||||
let item_start = to_span(item.span()).start;
|
||||
let prev_end = map.items[i - 1].span().end;
|
||||
let item_start = item.span().start;
|
||||
let next_start = f
|
||||
.context()
|
||||
.comments()
|
||||
@@ -326,8 +323,8 @@ pub(super) fn write_sass_map<'a>(
|
||||
// Comments between items:
|
||||
// block comments join the item when the pair fits on one line (Prettier's fill);
|
||||
// `//` comments and pairs that don't fit keep their own line.
|
||||
let item_start = to_span(item.span()).start;
|
||||
let item_width = to_span(item.span()).end - item_start;
|
||||
let item_start = item.span().start;
|
||||
let item_width = item.span().end - item_start;
|
||||
for &comment in f.context().comments().take_before(item_start) {
|
||||
let comment_width = comment.span.end - comment.span.start;
|
||||
let fits = !value_is_block
|
||||
@@ -394,14 +391,14 @@ pub(super) fn write_sass_map<'a>(
|
||||
// and the source comma preceded the comment (true next-slot comment).
|
||||
if let Some(last) = map.items.last() {
|
||||
let source_comma_start =
|
||||
map.comma_spans.get(map.items.len() - 1).map_or(u32::MAX, |sp| to_span(sp).start);
|
||||
map.comma_spans.get(map.items.len() - 1).map_or(u32::MAX, |sp| sp.start);
|
||||
// Only inside function/include arguments does the comment move
|
||||
// to the next slot; `$map:` declarations keep it attached.
|
||||
let next_slot = trailing
|
||||
&& ctx.in_args
|
||||
&& f.context().comments().peek().is_some_and(|c| c.span.start > source_comma_start);
|
||||
if !next_slot {
|
||||
value::flush_same_line_comments(to_span(last.span()).end, r_paren, f);
|
||||
value::flush_same_line_comments(last.span().end, r_paren, f);
|
||||
}
|
||||
}
|
||||
// The rest goes below the last item, one line per `//`;
|
||||
@@ -458,8 +455,8 @@ pub(super) fn write_sass_list<'a>(
|
||||
/// (`$k, $v in (a), (b), (c)`), filling and indenting like a value.
|
||||
pub(super) fn write_sass_each<'a>(each: &SassEach<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
let source = f.context().source_text();
|
||||
let in_span = to_span(&each.in_span);
|
||||
let in_tight = in_span.end == to_span(each.expr.span()).start;
|
||||
let in_span = each.in_span;
|
||||
let in_tight = is_glued(in_span, each.expr.span());
|
||||
|
||||
// Comma-list expr: the first element joins the `... in` entry,
|
||||
// the rest are separate fill entries (mirrors postcss's flat comma groups).
|
||||
@@ -470,7 +467,7 @@ pub(super) fn write_sass_each<'a>(each: &SassEach<'a>, f: &mut CssFormatter<'_,
|
||||
expr => (std::slice::from_ref(expr), None),
|
||||
};
|
||||
let expr_comma_start =
|
||||
move |i: usize| expr_comma_spans.and_then(|s| s.get(i)).map(|sp| to_span(sp).start);
|
||||
move |i: usize| expr_comma_spans.and_then(|s| s.get(i)).map(|sp| sp.start);
|
||||
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
let mut filler = f.fill();
|
||||
@@ -513,10 +510,7 @@ pub(super) fn write_sass_each<'a>(each: &SassEach<'a>, f: &mut CssFormatter<'_,
|
||||
write!(f, group(&indent(&tail)));
|
||||
} else {
|
||||
write!(f, text(source.text_for(&span)));
|
||||
value::write_group_comma(
|
||||
each.comma_spans.get(i).map(|sp| to_span(sp).start),
|
||||
f,
|
||||
);
|
||||
value::write_group_comma(each.comma_spans.get(i).map(|sp| sp.start), f);
|
||||
}
|
||||
});
|
||||
filler.entry(&soft_line_break_or_space(), &content);
|
||||
@@ -569,9 +563,8 @@ pub(super) fn write_sass_function<'a>(function: &SassFunction<'a>, f: &mut CssFo
|
||||
}
|
||||
|
||||
fn write_sass_parameters<'a>(parameters: &SassParameters<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
let comma_start =
|
||||
|i: usize| parameters.comma_spans.get(i).map(|sp: &oxc_css_parser::Span| to_span(sp).start);
|
||||
let r_paren = to_span(¶meters.span).end.saturating_sub(1);
|
||||
let comma_start = |i: usize| parameters.comma_spans.get(i).map(|sp| sp.start);
|
||||
let r_paren = parameters.span.end.saturating_sub(1);
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
write!(f, soft_line_break());
|
||||
for (i, param) in parameters.params.iter().enumerate() {
|
||||
@@ -614,7 +607,7 @@ pub(super) fn write_sass_include<'a>(include: &SassInclude<'a>, f: &mut CssForma
|
||||
if let Some(arguments) = &include.arguments {
|
||||
let args = &arguments.args;
|
||||
let comma_spans = &arguments.comma_spans;
|
||||
let r_paren = to_span(&arguments.span).end.saturating_sub(1);
|
||||
let r_paren = arguments.span.end.saturating_sub(1);
|
||||
// Same first-argument gate as `write_function` (which see), over typed args
|
||||
let first_arg_is_kw =
|
||||
args.first().is_some_and(|a| matches!(a, ComponentValue::SassKeywordArgument(_)));
|
||||
@@ -623,7 +616,7 @@ pub(super) fn write_sass_include<'a>(include: &SassInclude<'a>, f: &mut CssForma
|
||||
write!(f, soft_line_break());
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value::write_group_comma(comma_spans.get(i - 1).map(|sp| to_span(sp).start), f);
|
||||
value::write_group_comma(comma_spans.get(i - 1).map(|sp| sp.start), f);
|
||||
// Preserve a blank line, but only after a multi-part argument
|
||||
// (Prettier checks `value-comma_group`s only).
|
||||
let prev = &args[i - 1];
|
||||
@@ -631,8 +624,8 @@ pub(super) fn write_sass_include<'a>(include: &SassInclude<'a>, f: &mut CssForma
|
||||
prev,
|
||||
ComponentValue::SassKeywordArgument(_) | ComponentValue::SassList(_)
|
||||
);
|
||||
let prev_end = to_span(prev.span()).end;
|
||||
let start = to_span(arg.span()).start;
|
||||
let prev_end = prev.span().end;
|
||||
let start = arg.span().start;
|
||||
if prev_is_group
|
||||
&& comments::classify_gap(source.bytes_range(prev_end, start))
|
||||
== comments::Gap::Blank
|
||||
@@ -673,9 +666,9 @@ pub(super) fn write_sass_include<'a>(include: &SassInclude<'a>, f: &mut CssForma
|
||||
pub(super) fn write_sass_if_at_rule<'a>(if_rule: &SassIfAtRule<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
write!(f, ["@if", space()]);
|
||||
write_control_condition(&if_rule.if_clause.condition, f);
|
||||
write_block_after(to_span(if_rule.if_clause.condition.span()).end, &if_rule.if_clause.block, f);
|
||||
write_block_after(if_rule.if_clause.condition.span().end, &if_rule.if_clause.block, f);
|
||||
for (clause, else_span) in if_rule.else_if_clauses.iter().zip(&if_rule.else_spans) {
|
||||
write_else_join(to_span(else_span).start, f);
|
||||
write_else_join(else_span.start, f);
|
||||
// `if` is a value word in postcss, so the condition may break after it
|
||||
if matches!(clause.condition, ComponentValue::SassParenthesizedExpression(_)) {
|
||||
write!(f, ["if", space()]);
|
||||
@@ -683,11 +676,12 @@ pub(super) fn write_sass_if_at_rule<'a>(if_rule: &SassIfAtRule<'a>, f: &mut CssF
|
||||
} else {
|
||||
write_condition_chain(Some("if"), &clause.condition, f);
|
||||
}
|
||||
write_block_after(to_span(clause.condition.span()).end, &clause.block, f);
|
||||
write_block_after(clause.condition.span().end, &clause.block, f);
|
||||
}
|
||||
if let Some(else_block) = &if_rule.else_clause {
|
||||
let block_start = to_span(&else_block.span).start;
|
||||
let else_span = if_rule.else_spans.last().map_or(Span::empty(block_start), to_span);
|
||||
let block_start = else_block.span.start;
|
||||
let else_span =
|
||||
if_rule.else_spans.last().map_or(Span::empty(block_start), |&sp| to_span(sp));
|
||||
write_else_join(else_span.start, f);
|
||||
write_block_after(else_span.end, else_block, f);
|
||||
}
|
||||
@@ -696,7 +690,7 @@ pub(super) fn write_sass_if_at_rule<'a>(if_rule: &SassIfAtRule<'a>, f: &mut CssF
|
||||
/// A clause's block, with the comments between its head (ending at `head_end`)
|
||||
/// and `{` placed first (`@if $a // c\n{`), so they do not fall into the block as its first leading comment.
|
||||
fn write_block_after<'a>(head_end: u32, block: &SimpleBlock<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
value::write_comments_before_block(head_end, to_span(&block.span).start, f);
|
||||
value::write_comments_before_block(head_end, block.span.start, f);
|
||||
statement::write_block(block, f);
|
||||
}
|
||||
|
||||
@@ -756,13 +750,13 @@ fn flatten_condition<'b, 'a>(cond: &'b ComponentValue<'a>, out: &mut Vec<CondPar
|
||||
match cond {
|
||||
ComponentValue::SassBinaryExpression(binary) => {
|
||||
flatten_condition(&binary.left, out);
|
||||
out.push(CondPart::Op(to_span(&binary.op.span)));
|
||||
out.push(CondPart::Op(to_span(binary.op.span)));
|
||||
flatten_condition(&binary.right, out);
|
||||
}
|
||||
ComponentValue::SassUnaryExpression(unary)
|
||||
if matches!(unary.op.kind, SassUnaryOperatorKind::Not) =>
|
||||
{
|
||||
out.push(CondPart::Op(to_span(&unary.op.span)));
|
||||
out.push(CondPart::Op(to_span(unary.op.span)));
|
||||
flatten_condition(&unary.expr, out);
|
||||
}
|
||||
other => out.push(CondPart::Value(other)),
|
||||
@@ -840,7 +834,7 @@ pub(super) fn write_sass_use<'a>(sass_use: &SassUse<'a>, f: &mut CssFormatter<'_
|
||||
filler.entry(&soft_line_break_or_space(), &path);
|
||||
if let Some(namespace) = &sass_use.namespace {
|
||||
let as_kw = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
value::flush_value_comments(to_span(&namespace.as_span).start, f);
|
||||
value::flush_value_comments(namespace.as_span.start, f);
|
||||
write!(f, "as");
|
||||
});
|
||||
filler.entry(&soft_line_break_or_space(), &as_kw);
|
||||
@@ -849,7 +843,7 @@ pub(super) fn write_sass_use<'a>(sass_use: &SassUse<'a>, f: &mut CssFormatter<'_
|
||||
value::write_text_with_leading_comments(to_span(ident.span()), f);
|
||||
}
|
||||
SassUseNamespaceKind::Unnamed(star) => {
|
||||
value::flush_value_comments(to_span(&star.span).start, f);
|
||||
value::flush_value_comments(star.span.start, f);
|
||||
write!(f, "*");
|
||||
}
|
||||
});
|
||||
@@ -877,7 +871,7 @@ pub(super) fn write_sass_forward<'a>(forward: &SassForward<'a>, f: &mut CssForma
|
||||
filler.entry(&soft_line_break_or_space(), &path);
|
||||
if let Some(prefix) = &forward.prefix {
|
||||
let as_kw = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
value::flush_value_comments(to_span(&prefix.as_span).start, f);
|
||||
value::flush_value_comments(prefix.as_span.start, f);
|
||||
write!(f, "as");
|
||||
});
|
||||
filler.entry(&soft_line_break_or_space(), &as_kw);
|
||||
@@ -889,7 +883,7 @@ pub(super) fn write_sass_forward<'a>(forward: &SassForward<'a>, f: &mut CssForma
|
||||
}
|
||||
if let Some(visibility) = &forward.visibility {
|
||||
let keyword = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
value::flush_value_comments(to_span(&visibility.modifier.span).start, f);
|
||||
value::flush_value_comments(visibility.modifier.span.start, f);
|
||||
match visibility.modifier.kind {
|
||||
SassForwardVisibilityModifierKind::Show => write!(f, "show"),
|
||||
SassForwardVisibilityModifierKind::Hide => write!(f, "hide"),
|
||||
@@ -901,10 +895,7 @@ pub(super) fn write_sass_forward<'a>(forward: &SassForward<'a>, f: &mut CssForma
|
||||
let entry = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
value::write_text_with_leading_comments(to_span(member.span()), f);
|
||||
if i + 1 < members.len() {
|
||||
value::write_group_comma(
|
||||
Some(to_span(&visibility.comma_spans[i]).start),
|
||||
f,
|
||||
);
|
||||
value::write_group_comma(Some(visibility.comma_spans[i].start), f);
|
||||
}
|
||||
});
|
||||
filler.entry(&soft_line_break_or_space(), &entry);
|
||||
@@ -945,16 +936,16 @@ fn write_sass_module_config<'a>(config: &SassModuleConfig<'a>, f: &mut CssFormat
|
||||
let source = f.context().source_text();
|
||||
// Comments between the module path and `with` stay glued to the head
|
||||
// (`@use "a" /* c */ with (`).
|
||||
write_same_line_trailing_comments(to_span(&config.with_span).start, f);
|
||||
write_same_line_trailing_comments(config.with_span.start, f);
|
||||
write!(f, [space(), "with", space(), "("]);
|
||||
let body = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
write!(f, hard_line_break());
|
||||
for (i, item) in config.items.iter().enumerate() {
|
||||
let item_span = to_span(&item.span);
|
||||
let item_span = item.span;
|
||||
if i > 0 {
|
||||
// Prettier's `isNextLineEmpty` after an item:
|
||||
// a blank line after the comma survives, measured up to the next item or its first leading comment.
|
||||
let comma_end = to_span(&config.comma_spans[i - 1]).end;
|
||||
let comma_end = config.comma_spans[i - 1].end;
|
||||
// Clamped: an own-line comment left pending by the previous
|
||||
// item's trailing flush can start BEFORE the comma.
|
||||
let next_start = f
|
||||
@@ -987,11 +978,11 @@ fn write_sass_module_config<'a>(config: &SassModuleConfig<'a>, f: &mut CssFormat
|
||||
write!(f, text(source.text_for(&span)));
|
||||
}
|
||||
if i + 1 < config.items.len() {
|
||||
value::write_group_comma(Some(to_span(&config.comma_spans[i]).start), f);
|
||||
value::write_group_comma(Some(config.comma_spans[i].start), f);
|
||||
} else {
|
||||
// Comments before `)` (past a trailing comma, which is dropped):
|
||||
// same-line ones glue to the last item, own-line ones keep their line.
|
||||
let bound = to_span(&config.span).end;
|
||||
let bound = config.span.end;
|
||||
let mut prev_end =
|
||||
write_same_line_trailing_comments(bound, f).unwrap_or(item_span.end);
|
||||
for &comment in f.context().comments().take_before(bound) {
|
||||
|
||||
@@ -87,12 +87,12 @@ pub(super) fn write_selector_list<'a>(
|
||||
// A comment trailing the comma stays on the same line:
|
||||
// a lone block comment (`a, /* b */ c`), or a run ending in a `//` (`a, /* b */ // c`).
|
||||
// A `//` ends the line itself; the separator below then merges into its break.
|
||||
let next_start = to_span(complex.span()).start;
|
||||
let next_start = complex.span().start;
|
||||
if let Some(comment) = f.context().comments().peek()
|
||||
&& comment.span.end <= next_start
|
||||
{
|
||||
let source = f.context().source_text();
|
||||
let prev_end = to_span(list.selectors[i - 1].span()).end;
|
||||
let prev_end = list.selectors[i - 1].span().end;
|
||||
if comments::classify_gap(source.bytes_range(prev_end, comment.span.start))
|
||||
== comments::Gap::None
|
||||
{
|
||||
@@ -111,13 +111,13 @@ pub(super) fn write_selector_list<'a>(
|
||||
}
|
||||
}
|
||||
// Comments on their own line between selectors
|
||||
let start = to_span(complex.span()).start;
|
||||
let start = complex.span().start;
|
||||
for &comment in f.context().comments().take_before(start) {
|
||||
write!(f, FormatCommentBeforeContent::new(comment, BlockCommentAfter::HardLine));
|
||||
}
|
||||
if placeholder_idx == Some(i) {
|
||||
let source = f.context().source_text();
|
||||
let end = to_span(list.selectors[list.selectors.len() - 1].span()).end;
|
||||
let end = list.selectors[list.selectors.len() - 1].span().end;
|
||||
let raw = source.slice_range(start, end);
|
||||
// Comments inside the chunk are part of the verbatim text
|
||||
let _ = f.context().comments().take_before(end);
|
||||
@@ -213,7 +213,7 @@ fn write_combinator(
|
||||
// Deprecated shadow-DOM combinators (`/deep/` and less.js's `/name/` (`/shadow/`));
|
||||
// print as written: the tools give a meaning match it case-sensitively
|
||||
CombinatorKind::Deep | CombinatorKind::Slashed => {
|
||||
let span = to_span(&combinator.span);
|
||||
let span = to_span(combinator.span);
|
||||
write!(f, text(f.context().source_text().text_for(&span)));
|
||||
}
|
||||
CombinatorKind::ShadowChild => write!(f, "^"),
|
||||
@@ -526,10 +526,10 @@ fn write_nth<'a>(nth: &Nth<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
}
|
||||
if let Some(matcher) = &nth.matcher {
|
||||
write!(f, " ");
|
||||
let matcher_span = to_span(&matcher.span);
|
||||
let matcher_span = to_span(matcher.span);
|
||||
if let Some(selector) = &matcher.selector {
|
||||
// The `of` keyword as written, then the selector list
|
||||
let keyword_end = to_span(selector.span()).start;
|
||||
let keyword_end = selector.span().start;
|
||||
let keyword = source.slice_range(matcher_span.start, keyword_end).trim_ascii_end();
|
||||
write!(f, [text(keyword), " "]);
|
||||
write_selector_list_inline(selector, f);
|
||||
|
||||
@@ -23,23 +23,18 @@ use crate::{
|
||||
},
|
||||
format::to_span,
|
||||
print::{
|
||||
CssFormatter, at_rule, format_with, has_comments_between, less, postcss_simple_vars, scss,
|
||||
selector,
|
||||
CssFormatter, at_rule, format_with, has_comments_between, is_glued, less,
|
||||
postcss_simple_vars, scss, selector,
|
||||
value::{self, ValueContext},
|
||||
write_maybe_lowercase,
|
||||
},
|
||||
};
|
||||
|
||||
/// Start offset of a statement.
|
||||
pub(super) fn stmt_start(stmt: &Statement<'_>) -> u32 {
|
||||
to_span(stmt.span()).start
|
||||
}
|
||||
|
||||
/// End offset of a statement, extended over a trailing semicolon
|
||||
/// (oxc-css-parser's spans exclude it; postcss's `locEnd` includes it,
|
||||
/// and blank-line detection counts from after the `;`).
|
||||
pub(super) fn stmt_end(stmt: &Statement<'_>, f: &CssFormatter<'_, '_>) -> u32 {
|
||||
end_with_semicolon(to_span(stmt.span()).end, f)
|
||||
end_with_semicolon(stmt.span().end, f)
|
||||
}
|
||||
|
||||
/// Extends `end` over any whitespace, comments and a final `;` in the source.
|
||||
@@ -55,7 +50,7 @@ pub(super) fn params_region_end<'a>(
|
||||
let with_semi = end_with_semicolon(span_end, f);
|
||||
if with_semi > span_end { with_semi - 1 } else { span_end }
|
||||
},
|
||||
|block| to_span(&block.span).start,
|
||||
|block| block.span.start,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,7 +99,7 @@ pub(super) fn write_statement_sequence_bounded<'a>(
|
||||
// Only the attribute's own declarations, not a block's
|
||||
let style_attribute = upper == u32::MAX && f.context().is_style_attribute();
|
||||
for (i, stmt) in statements.iter().enumerate() {
|
||||
let start = stmt_start(stmt);
|
||||
let start = stmt.span().start;
|
||||
if i > 0 {
|
||||
let prev_end = stmt_end(&statements[i - 1], f);
|
||||
// Trailing comment on the same line as the previous statement
|
||||
@@ -146,7 +141,7 @@ pub(super) fn write_statement_sequence_bounded<'a>(
|
||||
let end = stmt_end(stmt, f);
|
||||
// `stmt_end` already consumes a trailing `;`,
|
||||
// so a source `;` is present exactly when it advanced past the raw statement span.
|
||||
let has_source_semicolon = end > to_span(stmt.span()).end;
|
||||
let has_source_semicolon = end > stmt.span().end;
|
||||
if is_suppressed {
|
||||
write!(f, text(source.slice_range(start, end)));
|
||||
} else if swallowed && let Statement::Declaration(decl) = stmt {
|
||||
@@ -191,7 +186,7 @@ pub(super) fn write_statement<'a>(stmt: &Statement<'a>, f: &mut CssFormatter<'_,
|
||||
if decl.name.has_placeholder() {
|
||||
// A css-in-js `${foo}: ${bar}` declaration keeps the source `;`
|
||||
// (Prettier preserves it: `${foo}: ${bar};` vs `${foo}: ${bar}`).
|
||||
let end = to_span(decl.span()).end;
|
||||
let end = decl.span().end;
|
||||
if end_with_semicolon(end, f) > end {
|
||||
write!(f, ";");
|
||||
}
|
||||
@@ -213,7 +208,7 @@ pub(super) fn write_statement<'a>(stmt: &Statement<'a>, f: &mut CssFormatter<'_,
|
||||
// Preserve a trailing `;` from the source
|
||||
// (Prettier keeps `${foo};` as `${foo};` and `${foo}` as `${foo}`):
|
||||
// the `;` is consumed by the statement separator, so recover it from the source gap.
|
||||
let end = to_span(&placeholder.span).end;
|
||||
let end = placeholder.span.end;
|
||||
if end_with_semicolon(end, f) > end {
|
||||
write!(f, ";");
|
||||
}
|
||||
@@ -247,12 +242,9 @@ pub(super) fn write_statement<'a>(stmt: &Statement<'a>, f: &mut CssFormatter<'_,
|
||||
write!(f, "@");
|
||||
let name_span = to_span(unknown.name.span());
|
||||
write_maybe_lowercase(source.text_for(&name_span), f);
|
||||
let region_end =
|
||||
params_region_end(unknown.block.as_ref(), to_span(&unknown.span).end, f);
|
||||
let prelude_start = unknown
|
||||
.prelude
|
||||
.as_ref()
|
||||
.map_or(region_end, |prelude| to_span(prelude.span()).start);
|
||||
let region_end = params_region_end(unknown.block.as_ref(), unknown.span.end, f);
|
||||
let prelude_start =
|
||||
unknown.prelude.as_ref().map_or(region_end, |prelude| prelude.span().start);
|
||||
at_rule::write_verbatim_at_rule_tail(
|
||||
name_span.end,
|
||||
prelude_start,
|
||||
@@ -267,16 +259,16 @@ pub(super) fn write_statement<'a>(stmt: &Statement<'a>, f: &mut CssFormatter<'_,
|
||||
for (i, sel) in keyframe_block.selectors.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value::write_group_comma(
|
||||
keyframe_block.comma_spans.get(i - 1).map(|sp| to_span(sp).start),
|
||||
keyframe_block.comma_spans.get(i - 1).map(|sp| sp.start),
|
||||
f,
|
||||
);
|
||||
write!(f, hard_line_break());
|
||||
}
|
||||
value::flush_value_comments(to_span(sel.span()).start, f);
|
||||
value::flush_value_comments(sel.span().start, f);
|
||||
selector::write_keyframe_selector(sel, f);
|
||||
}
|
||||
// Trailing comments before `{` stay on the selector line
|
||||
value::flush_trailing_value_comments(to_span(keyframe_block.block.span()).start, f);
|
||||
value::flush_trailing_value_comments(keyframe_block.block.span().start, f);
|
||||
write!(f, space());
|
||||
write_block(&keyframe_block.block, f);
|
||||
}
|
||||
@@ -298,13 +290,13 @@ pub(super) fn write_statement<'a>(stmt: &Statement<'a>, f: &mut CssFormatter<'_,
|
||||
|
||||
/// Mirrors Prettier's `css-rule`.
|
||||
fn write_qualified_rule<'a>(rule: &QualifiedRule<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
let sel_span = to_span(rule.selector.span());
|
||||
let block_start = to_span(rule.block.span()).start;
|
||||
let sel_start = rule.selector.span().start;
|
||||
let block_start = rule.block.span().start;
|
||||
// Comments inside the selector (both `//` and `/* */`) make Prettier print the raw selector verbatim (`selector-unknown`).
|
||||
// Reordering them would change which compound they annotate.
|
||||
// A trailing `//` comment pushes `{` to the next line.
|
||||
if has_comments_between(sel_span.start, block_start, f) {
|
||||
write_verbatim_prelude_rule(sel_span.start, &rule.block, false, f);
|
||||
if has_comments_between(sel_start, block_start, f) {
|
||||
write_verbatim_prelude_rule(sel_start, &rule.block, false, f);
|
||||
return;
|
||||
}
|
||||
selector::write_selector_list(&rule.selector, selector::SelectorListStyle::Hard, f);
|
||||
@@ -320,7 +312,7 @@ fn write_qualified_rule<'a>(rule: &QualifiedRule<'a>, f: &mut CssFormatter<'_, '
|
||||
/// Prettier prints the prelude verbatim, interior whitespace and newlines included,
|
||||
/// unlike selector garbage mode's collapsing.
|
||||
fn write_unknown_qualified_rule<'a>(rule: &UnknownQualifiedRule<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
write_verbatim_prelude_rule(to_span(&rule.span).start, &rule.block, false, f);
|
||||
write_verbatim_prelude_rule(rule.span.start, &rule.block, false, f);
|
||||
}
|
||||
|
||||
/// Prelude printed verbatim from `start` to the block,
|
||||
@@ -336,7 +328,7 @@ pub(super) fn write_verbatim_prelude_rule<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let source = f.context().source_text();
|
||||
let block_start = to_span(&block.span).start;
|
||||
let block_start = block.span.start;
|
||||
let raw = source.slice_range(start, block_start).trim_end();
|
||||
let _ = f.context().comments().take_before(block_start);
|
||||
if adjust {
|
||||
@@ -381,9 +373,8 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
// Prettier prints the WHOLE `raws.between`
|
||||
// (prop → value, `:` and any comments included) trimmed but otherwise verbatim,
|
||||
// with a space before a leading `//` comment and a space before the value.
|
||||
let colon_end = to_span(&decl.colon_span).end;
|
||||
let between_upper =
|
||||
if decl.value.is_empty() { colon_end } else { to_span(decl.value[0].span()).start };
|
||||
let colon_end = decl.colon_span.end;
|
||||
let between_upper = if decl.value.is_empty() { colon_end } else { decl.value[0].span().start };
|
||||
let between = source.slice_range(name_span.end, between_upper);
|
||||
let trimmed_between = between.trim_ascii();
|
||||
// `lastLineHasInlineComment`: the value drops to the next line,
|
||||
@@ -395,7 +386,7 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
// (Prettier 3.9.5 #19517; the SPACED `+_` is ours alone, postcss-less cannot parse it).
|
||||
// Comments around the marker keep the verbatim path.
|
||||
let merge_marker =
|
||||
decl.less_property_merge.as_ref().map(|merge| to_span(&merge.span)).filter(|span| {
|
||||
decl.less_property_merge.as_ref().map(|merge| to_span(merge.span)).filter(|span| {
|
||||
source.slice_range(name_span.end, span.start).trim_ascii().is_empty()
|
||||
&& source.slice_range(span.end, between_upper).trim_ascii() == ":"
|
||||
});
|
||||
@@ -439,7 +430,7 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
// which is its value: `--x: ;` substitutes a space where `--x:;` substitutes nothing, so it is kept verbatim.
|
||||
// Scan up to the `;` in the source.
|
||||
if is_custom_property {
|
||||
let colon_end = to_span(&decl.colon_span).end;
|
||||
let colon_end = decl.colon_span.end;
|
||||
let bytes = source.as_bytes();
|
||||
let mut i = colon_end as usize;
|
||||
while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') {
|
||||
@@ -454,8 +445,8 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
|
||||
// `filter: progid:...` values are printed verbatim
|
||||
// (the parser accepts the prefix case-insensitively; postcss rejects an uppercase one).
|
||||
let value_start = to_span(decl.value[0].span()).start;
|
||||
let value_end = to_span(decl.value[decl.value.len() - 1].span()).end;
|
||||
let value_start = decl.value[0].span().start;
|
||||
let value_end = decl.value[decl.value.len() - 1].span().end;
|
||||
let value_text = source.slice_range(value_start, value_end);
|
||||
if value_text
|
||||
.as_bytes()
|
||||
@@ -475,7 +466,7 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
&& decl.value.len() > 1
|
||||
// Only when oxc-css-parser split ONE string apart (no gaps between parts).
|
||||
&& decl.value.windows(2).all(|w| {
|
||||
to_span(w[0].span()).end == to_span(w[1].span()).start
|
||||
is_glued(w[0].span(), w[1].span())
|
||||
})
|
||||
{
|
||||
// A string containing SCSS interpolation that `oxc-css-parser` tokenized apart (`"#{".5"}"`):
|
||||
@@ -529,7 +520,7 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
|
||||
// The `;` position bounds wrapped trailing comments.
|
||||
// Only when such comments exist (keeps simple declarations on the single-component fast path).
|
||||
let decl_end_pre = to_span(decl.span()).end;
|
||||
let decl_end_pre = decl.span().end;
|
||||
let semi = end_with_semicolon(decl_end_pre, f);
|
||||
let bound = if semi > decl_end_pre { semi - 1 } else { decl_end_pre };
|
||||
// A single interpolated component is exempt,
|
||||
@@ -567,7 +558,7 @@ pub(super) fn write_declaration<'a>(decl: &Declaration<'a>, f: &mut CssFormatter
|
||||
if let Some(important) = &decl.important {
|
||||
value::write_trailing_important(important, f);
|
||||
}
|
||||
write_terminator_tail_comments(to_span(decl.span()).end, f);
|
||||
write_terminator_tail_comments(decl.span().end, f);
|
||||
}
|
||||
|
||||
/// The run between a variable name and its `:`: normally just the colon,
|
||||
@@ -725,7 +716,7 @@ fn respace_value_tokens(v: &str) -> String {
|
||||
/// Mirrors Prettier's block printing: `{` + indented statements + `}`.
|
||||
/// An empty block prints as `{\n}`.
|
||||
pub(super) fn write_block<'a>(block: &SimpleBlock<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
let block_span = to_span(&block.span);
|
||||
let block_span = block.span;
|
||||
let r_curly = block_span.end.saturating_sub(1);
|
||||
|
||||
write!(f, "{");
|
||||
|
||||
@@ -429,7 +429,7 @@ fn is_word_glued_number(values: &[ComponentValue<'_>], i: usize) -> bool {
|
||||
i > 0
|
||||
&& number_ish(&values[i])
|
||||
&& (is_word_like(&values[i - 1]) || number_ish(&values[i - 1]))
|
||||
&& to_span(values[i - 1].span()).end == to_span(values[i].span()).start
|
||||
&& is_glued(values[i - 1].span(), values[i].span())
|
||||
}
|
||||
|
||||
/// Is this number-ish or a font-size-capable function? (Prettier's `isPossibleFontSize`)
|
||||
@@ -524,7 +524,7 @@ pub(super) fn split_comma_groups<'b, 'a>(
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
if let ComponentValue::TokenWithSpan(tok) = v {
|
||||
if depth == 0 && matches!(&tok.token, Token::Comma(_)) {
|
||||
groups.push((&values[start..i], Some(to_span(v.span()).start)));
|
||||
groups.push((&values[start..i], Some(v.span().start)));
|
||||
start = i + 1;
|
||||
} else {
|
||||
depth += token_depth_delta(&tok.token);
|
||||
@@ -532,7 +532,7 @@ pub(super) fn split_comma_groups<'b, 'a>(
|
||||
continue;
|
||||
}
|
||||
if is_comma(v) {
|
||||
groups.push((&values[start..i], Some(to_span(v.span()).start)));
|
||||
groups.push((&values[start..i], Some(v.span().start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
@@ -570,8 +570,8 @@ pub(super) fn write_declaration_value<'a>(
|
||||
// `shouldBreakList` forces one per line when any group has multiple elements
|
||||
// (a real `value-comma_group` survives flattening, comments count as members)
|
||||
// and the property is not a custom property.
|
||||
let value_start = to_span(values[0].span()).start;
|
||||
let value_end = to_span(values[values.len() - 1].span()).end;
|
||||
let value_start = values[0].span().start;
|
||||
let value_end = values[values.len() - 1].span().end;
|
||||
let has_comments =
|
||||
f.context().comments().iter_before(value_end).any(|c| c.span.start >= value_start);
|
||||
let force_hard_line = !ctx.decl_prop.is_some_and(|p| p.starts_with("--"))
|
||||
@@ -775,9 +775,9 @@ pub(super) fn write_value_groups<'a>(
|
||||
// Comments between groups lead the group they precede
|
||||
// (a `//` on the previous comma's line already went with the comma, see `write_group_comma`);
|
||||
// own-line ones keep their line.
|
||||
let lead: &'a [comments::CssComment] = group_values.first().map_or(&[], |first| {
|
||||
f.context().comments().take_before(to_span(first.span()).start)
|
||||
});
|
||||
let lead: &'a [comments::CssComment] = group_values
|
||||
.first()
|
||||
.map_or(&[], |first| f.context().comments().take_before(first.span().start));
|
||||
if lead.is_empty() {
|
||||
write_comma_group(group_values, gctx, f);
|
||||
} else if i == 0 {
|
||||
@@ -801,8 +801,8 @@ pub(super) fn write_value_groups<'a>(
|
||||
let width = u32::from(f.options().line_width.value());
|
||||
let source = f.context().source_text();
|
||||
let chunk_len = run_end(group_values, 0, gctx, source);
|
||||
let group_w = to_span(group_values[chunk_len - 1].span()).end
|
||||
- to_span(group_values[0].span()).start
|
||||
let group_w = group_values[chunk_len - 1].span().end
|
||||
- group_values[0].span().start
|
||||
+ u32::from(!is_last && chunk_len == group_values.len());
|
||||
let mut x = 4u32; // hardline indent under the value
|
||||
for (k, &comment) in lead.iter().enumerate() {
|
||||
@@ -918,7 +918,7 @@ fn write_bracket_block<'a>(
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
let groups = split_comma_groups(&bracket.value);
|
||||
let r_bracket = to_span(bracket.span()).end.saturating_sub(1);
|
||||
let r_bracket = bracket.span().end.saturating_sub(1);
|
||||
// `[1,]` is a COMMA list, `[1]` a space list (dart-sass `list.separator`):
|
||||
// the source comma is the value, like `(1,)` (see `scss::is_single_item_list`).
|
||||
// A multi-element list drops it: the separator is comma either way.
|
||||
@@ -1027,13 +1027,12 @@ pub(super) fn write_important_annotation(
|
||||
important: &ImportantAnnotation<'_>,
|
||||
f: &mut CssFormatter<'_, '_>,
|
||||
) {
|
||||
let span = to_span(&important.span);
|
||||
let ident_start = to_span(&important.ident.span).start;
|
||||
let ident_start = important.ident.span.start;
|
||||
let has_inner_comment = f
|
||||
.context()
|
||||
.comments()
|
||||
.peek()
|
||||
.is_some_and(|c| c.span.start >= span.start && c.span.end <= ident_start);
|
||||
.is_some_and(|c| c.span.start >= important.span.start && c.span.end <= ident_start);
|
||||
if !has_inner_comment {
|
||||
write!(f, "!important");
|
||||
return;
|
||||
@@ -1049,7 +1048,7 @@ pub(super) fn write_trailing_important(
|
||||
important: &ImportantAnnotation<'_>,
|
||||
f: &mut CssFormatter<'_, '_>,
|
||||
) {
|
||||
flush_trailing_value_comments(to_span(&important.span).start, f);
|
||||
flush_trailing_value_comments(important.span.start, f);
|
||||
write!(f, space());
|
||||
write_important_annotation(important, f);
|
||||
}
|
||||
@@ -1077,7 +1076,7 @@ pub(super) fn write_list_element<'a>(
|
||||
ctx: ValueContext<'a>,
|
||||
f: &mut CssFormatter<'_, 'a>,
|
||||
) {
|
||||
flush_value_comments(to_span(el.span()).start, f);
|
||||
flush_value_comments(el.span().start, f);
|
||||
write_component_value(el, ctx, f);
|
||||
}
|
||||
|
||||
@@ -1094,7 +1093,7 @@ pub(super) fn write_comma_group<'a>(
|
||||
// Comments leading the FIRST value flush at this level, before any group/indent opens:
|
||||
// `//` comment's forced hardline inside `indent(&body)` would drop the value one level deeper
|
||||
// and break the fill run after it.
|
||||
flush_value_comments(to_span(values[0].span()).start, f);
|
||||
flush_value_comments(values[0].span().start, f);
|
||||
// Prettier's `flattenGroups`:
|
||||
// a single-element comma group collapses to the element itself (no extra group/indent level).
|
||||
if values.len() == 1 && ctx.tail_bound.is_none() {
|
||||
@@ -1116,7 +1115,7 @@ pub(super) fn write_comma_group<'a>(
|
||||
// A single value WITH a tail bound routes through the fill below
|
||||
// so its trailing comments wrap.
|
||||
let source = f.context().source_text();
|
||||
let upper_bound = to_span(values[values.len() - 1].span()).end;
|
||||
let upper_bound = values[values.len() - 1].span().end;
|
||||
// Grid values that break across source lines start on their own line
|
||||
// (Prettier's `didBreak` + `parts.unshift(hardline)`),
|
||||
// but breaks caused by inline comments in the gap don't count.
|
||||
@@ -1124,8 +1123,8 @@ pub(super) fn write_comma_group<'a>(
|
||||
let grid_did_break = ctx.is_grid()
|
||||
&& (1..values.len()).any(|i| {
|
||||
separator_between(values, i, ctx, source) == Separator::Hard && {
|
||||
let prev_end = to_span(values[i - 1].span()).end;
|
||||
let start = to_span(values[i].span()).start;
|
||||
let prev_end = values[i - 1].span().end;
|
||||
let start = values[i].span().start;
|
||||
!f.context()
|
||||
.comments()
|
||||
.iter_before(upper_bound)
|
||||
@@ -1161,8 +1160,8 @@ pub(super) fn write_comma_group<'a>(
|
||||
if i == 0 { Separator::Line } else { separator_between(values, i, ctx, source) };
|
||||
// An inline comment trailing the PREVIOUS run forces a break
|
||||
if i > 0 {
|
||||
let prev_end = to_span(values[i - 1].span()).end;
|
||||
let start = to_span(values[i].span()).start;
|
||||
let prev_end = values[i - 1].span().end;
|
||||
let start = values[i].span().start;
|
||||
if pending.iter().any(|c| {
|
||||
c.inline
|
||||
&& c.span.start >= prev_end
|
||||
@@ -1175,8 +1174,8 @@ pub(super) fn write_comma_group<'a>(
|
||||
let run_end = run_end(values, i, ctx, source);
|
||||
let run_start = i;
|
||||
let run = &values[i..run_end];
|
||||
let start = to_span(values[i].span()).start;
|
||||
let run_end_pos = to_span(values[run_end - 1].span()).end;
|
||||
let start = values[i].span().start;
|
||||
let run_end_pos = values[run_end - 1].span().end;
|
||||
let is_last_run = run_end == values.len();
|
||||
let content = format_with(move |f: &mut CssFormatter<'_, 'a>| {
|
||||
flush_value_comments(start, f);
|
||||
@@ -1198,7 +1197,7 @@ pub(super) fn write_comma_group<'a>(
|
||||
// Same-line `//` comments stay attached to this entry
|
||||
// (a separate entry would break with the expanded group).
|
||||
if !is_last_run {
|
||||
let next_start = to_span(values[run_end].span()).start;
|
||||
let next_start = values[run_end].span().start;
|
||||
while let Some(comment) = f.context().comments().peek() {
|
||||
if !comment.inline
|
||||
|| comment.span.start < run_end_pos
|
||||
@@ -1233,7 +1232,7 @@ pub(super) fn write_comma_group<'a>(
|
||||
// Block comments between runs become their own fill items, own-line ones included:
|
||||
// keeping those own-line would freeze a previously wrapped layout (= not idempotent).
|
||||
if !is_last_run {
|
||||
let next_start = to_span(values[run_end].span()).start;
|
||||
let next_start = values[run_end].span().start;
|
||||
for &comment in pending.iter().filter(|c| {
|
||||
!c.inline && c.span.start >= run_end_pos && c.span.end <= next_start
|
||||
}) {
|
||||
@@ -1337,8 +1336,8 @@ fn separator_between(
|
||||
// a glued pair (`Tight`/`Word`) is part of ONE postcss word (`sandstone.10`, `1fr/2fr`)
|
||||
// and survives verbatim in grid values too, grid never splits what other properties keep glued.
|
||||
if ctx.is_grid() && matches!(sep, Separator::Line | Separator::Space) {
|
||||
let prev_end = to_span(values[i - 1].span()).end;
|
||||
let curr_start = to_span(values[i].span()).start;
|
||||
let prev_end = values[i - 1].span().end;
|
||||
let curr_start = values[i].span().start;
|
||||
return if source.bytes_contain(prev_end, curr_start, b'\n') {
|
||||
Separator::Hard
|
||||
} else {
|
||||
@@ -1352,12 +1351,10 @@ fn separator_between(
|
||||
fn base_separator(values: &[ComponentValue<'_>], i: usize, ctx: ValueContext<'_>) -> Separator {
|
||||
let prev = &values[i - 1];
|
||||
let curr = &values[i];
|
||||
let prev_span = to_span(prev.span());
|
||||
let curr_span = to_span(curr.span());
|
||||
// `hasEmptyRawBefore(iNextNode)`
|
||||
let gap_empty = prev_span.end == curr_span.start;
|
||||
let gap_empty = is_glued(prev.span(), curr.span());
|
||||
// `hasEmptyRawBefore(iNode)`
|
||||
let prev_gap_empty = i >= 2 && to_span(values[i - 2].span()).end == prev_span.start;
|
||||
let prev_gap_empty = i >= 2 && is_glued(values[i - 2].span(), prev.span());
|
||||
|
||||
// Solidus (`/`) spacing rules
|
||||
if is_solidus(curr) || is_solidus(prev) {
|
||||
@@ -1368,7 +1365,7 @@ fn base_separator(values: &[ComponentValue<'_>], i: usize, ctx: ValueContext<'_>
|
||||
// Fully glued `/` (`center/80%`, `12px/1.5`): one postcss word
|
||||
if is_solidus(curr)
|
||||
&& gap_empty
|
||||
&& values.get(i + 1).is_none_or(|n| to_span(curr.span()).end == to_span(n.span()).start)
|
||||
&& values.get(i + 1).is_none_or(|n| is_glued(curr.span(), n.span()))
|
||||
{
|
||||
return Separator::Tight;
|
||||
}
|
||||
@@ -1621,11 +1618,11 @@ pub(super) fn write_component_value<'a>(
|
||||
// The spacing around `/` prints as written, as in a declaration value
|
||||
ComponentValue::Ratio(ratio) => {
|
||||
write_number(&ratio.numerator, f);
|
||||
if !is_glued(&ratio.numerator.span, &ratio.solidus_span) {
|
||||
if !is_glued(ratio.numerator.span, ratio.solidus_span) {
|
||||
write!(f, space());
|
||||
}
|
||||
write!(f, "/");
|
||||
if !is_glued(&ratio.solidus_span, &ratio.denominator.span) {
|
||||
if !is_glued(ratio.solidus_span, ratio.denominator.span) {
|
||||
write!(f, space());
|
||||
}
|
||||
write_number(&ratio.denominator, f);
|
||||
@@ -1700,7 +1697,7 @@ pub(super) fn write_component_value<'a>(
|
||||
});
|
||||
let inner_ctx = ValueContext { paren_break: false, ..ctx };
|
||||
let trailing = f.options().allow_trailing_comma();
|
||||
let r_paren = to_span(paren.span()).end.saturating_sub(1);
|
||||
let r_paren = paren.span().end.saturating_sub(1);
|
||||
if ctx.paren_break
|
||||
&& !single_scalar_item
|
||||
&& let Some(list) = comma_list
|
||||
@@ -1773,7 +1770,7 @@ pub(super) fn write_component_value<'a>(
|
||||
// `not` already wrote its space.
|
||||
if !keyword_not
|
||||
&& is_func_like(&unary.expr)
|
||||
&& to_span(&unary.op.span).end != to_span(unary.expr.span()).start
|
||||
&& !is_glued(unary.op.span, unary.expr.span())
|
||||
{
|
||||
write!(f, " ");
|
||||
}
|
||||
@@ -1906,7 +1903,7 @@ fn write_sass_binary<'a>(
|
||||
if matches!(op.kind, SassBinaryOperatorKind::Multiply) {
|
||||
break;
|
||||
}
|
||||
let op_span = to_span(&op.span);
|
||||
let op_span = to_span(op.span);
|
||||
let next = &parts[run_end + 1].0;
|
||||
let operand = &parts[run_end].0;
|
||||
// Division mirrors postcss-values lexing:
|
||||
@@ -1916,7 +1913,7 @@ fn write_sass_binary<'a>(
|
||||
// while plain numbers keep the source gap (`10px/8px`, `1/2`).
|
||||
if matches!(op.kind, SassBinaryOperatorKind::Division) {
|
||||
let wf = |v: &ComponentValue<'_>| is_word_like(v) || is_func_like(v);
|
||||
let left_absorbs = to_span(operand.span()).end == op_span.start && wf(operand);
|
||||
let left_absorbs = operand.span().end == op_span.start && wf(operand);
|
||||
if !left_absorbs {
|
||||
let near_wordish = wf(operand)
|
||||
|| wf(next)
|
||||
@@ -1928,7 +1925,7 @@ fn write_sass_binary<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
let next_start = to_span(next.span()).start;
|
||||
let next_start = next.span().start;
|
||||
if matches!(source.text_for(&op_span), "+" | "-")
|
||||
&& (is_word_like(operand)
|
||||
|| is_func_like(operand)
|
||||
@@ -1939,7 +1936,7 @@ fn write_sass_binary<'a>(
|
||||
// Prettier keeps it glued (`$a -$b` stays `$a -$b`, NOT `$a - $b`).
|
||||
// It matters for the ambiguous Sass `margin: -$a -$b` list/subtraction case (dart-sass deprecates the binary reading).
|
||||
// Merge it into this run so no fill space lands after the operator.
|
||||
if to_span(operand.span()).end != op_span.start && op_span.end == next_start {
|
||||
if operand.span().end != op_span.start && op_span.end == next_start {
|
||||
run_end += 1;
|
||||
continue;
|
||||
}
|
||||
@@ -1956,9 +1953,9 @@ fn write_sass_binary<'a>(
|
||||
for (j, (operand, op)) in run.iter().enumerate() {
|
||||
write_component_value(operand, ctx, f);
|
||||
if let Some(op) = op {
|
||||
let op_span = to_span(&op.span);
|
||||
let operand_end = to_span(operand.span()).end;
|
||||
let next_start = run.get(j + 1).map(|(next, _)| to_span(next.span()).start);
|
||||
let op_span = to_span(op.span);
|
||||
let operand_end = operand.span().end;
|
||||
let next_start = run.get(j + 1).map(|(next, _)| next.span().start);
|
||||
// `op(paren)` fuses like a postcss function token,
|
||||
// which always gets a space before it;
|
||||
// word-like operands force spaces (`$a + $b` even when glued).
|
||||
@@ -2034,16 +2031,12 @@ fn write_calc<'a>(calc: &Calc<'a>, ctx: ValueContext<'a>, f: &mut CssFormatter<'
|
||||
// Sass modulo inside a math function's arguments (`max(1px, 7px % 4)`)
|
||||
CalcOperatorKind::Modulo => "%",
|
||||
};
|
||||
let left_end = to_span(calc.left.span()).end;
|
||||
let op_span = to_span(&calc.op.span);
|
||||
let right_start = to_span(calc.right.span()).start;
|
||||
|
||||
push_operand(&calc.left, chunks, current);
|
||||
if left_end != op_span.start {
|
||||
if !is_glued(calc.left.span(), calc.op.span) {
|
||||
current.push(Piece::Space);
|
||||
}
|
||||
current.push(Piece::Op(op_str));
|
||||
if op_span.end != right_start {
|
||||
if !is_glued(calc.op.span, calc.right.span()) {
|
||||
chunks.push(std::mem::take(current));
|
||||
}
|
||||
push_operand(&calc.right, chunks, current);
|
||||
@@ -2153,8 +2146,8 @@ fn write_less_binary_operation<'a>(
|
||||
Some(Piece::Operand(v)) => Some(*v),
|
||||
_ => None,
|
||||
};
|
||||
let source_before = !is_glued(op.left.span(), &op.op.span);
|
||||
let source_after = !is_glued(&op.op.span, op.right.span());
|
||||
let source_before = !is_glued(op.left.span(), op.op.span);
|
||||
let source_after = !is_glued(op.op.span, op.right.span());
|
||||
// Spaces on both sides regardless of the source (see the doc comment), else the source glue
|
||||
let forced = match op.op.kind {
|
||||
LessOperationOperatorKind::Multiply => {
|
||||
@@ -2336,7 +2329,7 @@ pub(super) fn write_function<'a>(
|
||||
}
|
||||
|
||||
let groups_ref = &groups;
|
||||
let r_paren = to_span(func.span()).end.saturating_sub(1);
|
||||
let r_paren = func.span().end.saturating_sub(1);
|
||||
// `var(--baz,)`: an empty fallback is meaningful,
|
||||
// so a source trailing comma is preserved for `var()` ONLY
|
||||
// (Prettier's `printTrailingComma` checks `isVarFunctionNode`; every other function drops it).
|
||||
@@ -2365,8 +2358,8 @@ pub(super) fn write_function<'a>(
|
||||
if i > 0 {
|
||||
// Preserve a blank line between argument groups,
|
||||
// but only after a multi-part group (Prettier checks `comma_groups`).
|
||||
let prev_end = groups_ref[i - 1].0.last().map_or(0, |v| to_span(v.span()).end);
|
||||
let next_start = group_values.first().map_or(prev_end, |v| to_span(v.span()).start);
|
||||
let prev_end = groups_ref[i - 1].0.last().map_or(0, |v| v.span().end);
|
||||
let next_start = group_values.first().map_or(prev_end, |v| v.span().start);
|
||||
let next_start = f
|
||||
.context()
|
||||
.comments()
|
||||
@@ -2477,8 +2470,8 @@ pub(super) fn write_url<'a>(url: &Url<'a>, f: &mut CssFormatter<'_, 'a>) {
|
||||
}
|
||||
// Unquoted url contents are printed verbatim, including inner spaces
|
||||
_ => {
|
||||
let url_span = to_span(url.span());
|
||||
let inner_start = to_span(url.name.span()).end + 1;
|
||||
let url_span = url.span();
|
||||
let inner_start = url.name.span().end + 1;
|
||||
// oxc-css-parser's url span may stop before trailing padding; scan to `)`
|
||||
let bytes = source.as_bytes();
|
||||
let mut close = url_span.end.saturating_sub(1) as usize;
|
||||
|
||||
Reference in new issue
Block a user