mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(oxfmt): make one state conditionalGroup fit up to first hardline (#27275)
Prepare for yaml(FM)-in-mdx-in-md case.
This commit is contained in:
1 parent
fd4ddce313
commit
16860185da
1 file changed
+71
-41
@@ -208,49 +208,73 @@ fn convert_group<'a>(
|
||||
let final_state = expanded_states.last().unwrap_or(contents);
|
||||
return convert_group_contents(Some(final_state), gid, true, out, ctx);
|
||||
}
|
||||
// A single state is just a regular group. BestFitting requires at least two variants
|
||||
let mut variants = ArenaVec::with_capacity_in(expanded_states.len().max(2), &ctx.allocator);
|
||||
if expanded_states.len() <= 1 {
|
||||
return convert_group_contents(Some(contents), gid, false, out, ctx);
|
||||
// A single state still goes through `BestFitting`, as `[flat, expanded]`:
|
||||
// Prettier never propagates a hardline's break into a conditional group,
|
||||
// it prints the state flat when it fits up to its first hardline (YAML's `key: >-`), expanded otherwise.
|
||||
// Without an id the two variants are the same elements, so convert once:
|
||||
// single-state groups nest per YAML mapping level, converting twice would double per level.
|
||||
let flat = convert_variant(contents, GroupMode::Flat, gid, ctx)?;
|
||||
let expanded = if gid.is_some() {
|
||||
convert_variant(contents, GroupMode::Expand, gid, ctx)?
|
||||
} else {
|
||||
flat
|
||||
};
|
||||
variants.push(flat);
|
||||
variants.push(expanded);
|
||||
} else {
|
||||
for (index, state) in
|
||||
std::iter::once(contents).chain(expanded_states.iter().skip(1)).enumerate()
|
||||
{
|
||||
let mode = if index + 1 == expanded_states.len() {
|
||||
GroupMode::Expand
|
||||
} else {
|
||||
GroupMode::Flat
|
||||
};
|
||||
variants.push(convert_variant(state, mode, gid, ctx)?);
|
||||
}
|
||||
}
|
||||
|
||||
let mut variants = ArenaVec::with_capacity_in(expanded_states.len(), &ctx.allocator);
|
||||
for (index, state) in
|
||||
std::iter::once(contents).chain(expanded_states.iter().skip(1)).enumerate()
|
||||
{
|
||||
let mode =
|
||||
if index + 1 == expanded_states.len() { GroupMode::Expand } else { GroupMode::Flat };
|
||||
let mut variant = ArenaVec::new_in(&ctx.allocator);
|
||||
variant.push(FormatElement::Tag(Tag::StartEntry));
|
||||
// `BestFitting` itself supplies the selected variant's print mode.
|
||||
// A wrapper group is only needed to publish that mode under Prettier's group ID for `if-break(groupId)` consumers.
|
||||
// Wrapping an ID-less variant would remeasure it after selection
|
||||
// and can incorrectly expand an intermediate state that Prettier prints flat.
|
||||
// (No core Prettier printer passes an id to `conditionalGroup`; this branch is defensive.
|
||||
// Note the same remeasure hazard reappears here via `propagate_expand` if a variant contains a top-level hard line,
|
||||
// so revisit before relying on it for real inputs.)
|
||||
if gid.is_some() {
|
||||
variant.push(FormatElement::Tag(Tag::StartGroup(
|
||||
Group::new().with_id(gid).with_mode(mode),
|
||||
)));
|
||||
}
|
||||
convert_doc(state, &mut variant, ctx)?;
|
||||
if gid.is_some() {
|
||||
variant.push(FormatElement::Tag(Tag::EndGroup));
|
||||
}
|
||||
variant.push(FormatElement::Tag(Tag::EndEntry));
|
||||
// The trailing `EndEntry` tag keeps postprocess's trailing-hardline strip from firing:
|
||||
// a variant retains its trailing hardline (content may follow the `BestFitting`).
|
||||
postprocess(&mut variant, ctx.allocator);
|
||||
variants.push(variant.into_arena_slice());
|
||||
}
|
||||
|
||||
// SAFETY: `expanded_states.len() > 1`, and the loop emits exactly that many variants.
|
||||
// SAFETY: Both branches emit at least two variants.
|
||||
out.push(FormatElement::BestFitting(unsafe {
|
||||
BestFittingElement::from_vec_unchecked(variants)
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// One `BestFitting` variant of a conditional group's `state`, printed in `mode`.
|
||||
fn convert_variant<'a>(
|
||||
state: &Value,
|
||||
mode: GroupMode,
|
||||
gid: Option<GroupId>,
|
||||
ctx: &mut FmtCtx<'a, '_>,
|
||||
) -> Result<&'a [FormatElement<'a>], String> {
|
||||
let mut variant = ArenaVec::new_in(&ctx.allocator);
|
||||
variant.push(FormatElement::Tag(Tag::StartEntry));
|
||||
// `BestFitting` itself supplies the selected variant's print mode.
|
||||
// A wrapper group is only needed to publish that mode under Prettier's group ID for `if-break(groupId)` consumers.
|
||||
// Wrapping an ID-less variant would remeasure it after selection
|
||||
// and can incorrectly expand an intermediate state that Prettier prints flat.
|
||||
//
|
||||
// (No core Prettier printer passes an id to `conditionalGroup`; this branch is defensive.
|
||||
// Note the same remeasure hazard reappears here via `propagate_expand` if a variant contains a top-level hard line,
|
||||
// so revisit before relying on it for real inputs.)
|
||||
if gid.is_some() {
|
||||
variant
|
||||
.push(FormatElement::Tag(Tag::StartGroup(Group::new().with_id(gid).with_mode(mode))));
|
||||
}
|
||||
convert_doc(state, &mut variant, ctx)?;
|
||||
if gid.is_some() {
|
||||
variant.push(FormatElement::Tag(Tag::EndGroup));
|
||||
}
|
||||
variant.push(FormatElement::Tag(Tag::EndEntry));
|
||||
// The trailing `EndEntry` tag keeps postprocess's trailing-hardline strip from firing:
|
||||
// a variant retains its trailing hardline (content may follow the `BestFitting`).
|
||||
postprocess(&mut variant, ctx.allocator);
|
||||
Ok(variant.into_arena_slice())
|
||||
}
|
||||
|
||||
fn convert_group_contents<'a>(
|
||||
contents: Option<&Value>,
|
||||
gid: Option<GroupId>,
|
||||
@@ -618,14 +642,20 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conditional_group_with_one_state_is_a_regular_group() {
|
||||
let group = json!({
|
||||
"type": "group",
|
||||
"contents": ["a", { "type": "line" }, "b"],
|
||||
"expandedStates": [["a", { "type": "line" }, "b"]]
|
||||
});
|
||||
fn conditional_group_with_one_state_fits_up_to_its_first_hardline() {
|
||||
let state = json!([
|
||||
"a:",
|
||||
{ "type": "line" },
|
||||
">-",
|
||||
{ "type": "line", "hard": true },
|
||||
{ "type": "break-parent" },
|
||||
"b"
|
||||
]);
|
||||
let group = json!({ "type": "group", "contents": state, "expandedStates": [state] });
|
||||
|
||||
assert_eq!(print_doc(&group, 80), "a b");
|
||||
// A hardline does not break a conditional group (Prettier's YAML block scalar key)
|
||||
assert_eq!(print_doc(&group, 80), "a: >-\nb");
|
||||
assert_eq!(print_doc(&group, 3), "a:\n>-\nb");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user