Skip to content

Commit 02c78b9

Browse files
DunqingCopilot
authored andcommitted
perf(formatter): optimize formatting of JSX element/fragment with a single child (#16631)
Add `FormatSingleChild` for optimizing JSX elements/fragments with a single meaningful child. When there is only a single child, we do not need to write the child into a temporary buffer and then take it when formatting. Instead, we can directly write the child into the root buffer. Also, this can avoid calling costly `best_fitting!` formatting in some situations.
1 parent fe00500 commit 02c78b9

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

crates/oxc_formatter/src/write/jsx/child_list.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ impl FormatJsxChildList {
3131
self
3232
}
3333

34-
pub fn fmt_children<'a>(
34+
pub fn fmt_children<'a, 'b>(
3535
&self,
36-
children: &AstNode<'a, ArenaVec<'a, JSXChild<'a>>>,
36+
children: &'b AstNode<'a, ArenaVec<'a, JSXChild<'a>>>,
3737
f: &mut Formatter<'_, 'a>,
38-
) -> FormatChildrenResult<'a> {
38+
) -> FormatChildrenResult<'a, 'b> {
3939
// Use Biome's exact approach - no need for jsx_split_children at this stage
4040
let children_meta = Self::children_meta(children, f.context().comments());
4141
let layout = self.layout(children_meta);
@@ -57,6 +57,13 @@ impl FormatJsxChildList {
5757
children.pop();
5858
}
5959

60+
if children.len() == 1 {
61+
return FormatChildrenResult::SingleChild(FormatSingleChild {
62+
child: children.pop().unwrap(),
63+
force_multiline,
64+
});
65+
}
66+
6067
let mut is_next_child_suppressed = false;
6168
let mut last: Option<&JsxChild> = None;
6269
let mut children_iter = JsxChildrenIterator::new(children.iter());
@@ -390,7 +397,7 @@ impl FormatJsxChildList {
390397

391398
/// The result of formatting the children of a JSX child list.
392399
#[derive(Debug)]
393-
pub enum FormatChildrenResult<'a> {
400+
pub enum FormatChildrenResult<'a, 'b> {
394401
/// Force the children to be formatted over multiple lines.
395402
///
396403
/// For example:
@@ -409,6 +416,8 @@ pub enum FormatChildrenResult<'a> {
409416
flat_children: FormatFlatChildren<'a>,
410417
expanded_children: FormatMultilineChildren<'a>,
411418
},
419+
420+
SingleChild(FormatSingleChild<'a, 'b>),
412421
}
413422

414423
#[derive(Debug, Default, Copy, Clone)]
@@ -720,3 +729,42 @@ impl<'a> Format<'a> for FormatFlatChildren<'a> {
720729
}
721730
}
722731
}
732+
733+
/// Optimized formatting for a single JSX child.
734+
///
735+
/// When there is only a single child, we do not need to write the child into a temporary buffer
736+
/// and then take it when formatting. Instead, we can directly write the child into the root buffer.
737+
///
738+
/// Also, this can avoid calling costly `best_fitting!` formatting in some situations.
739+
#[derive(Debug)]
740+
pub struct FormatSingleChild<'a, 'b> {
741+
child: JsxChild<'a, 'b>,
742+
force_multiline: bool,
743+
}
744+
745+
impl<'a> Format<'a> for FormatSingleChild<'a, '_> {
746+
fn fmt(&self, f: &mut Formatter<'_, 'a>) {
747+
let format_inner = format_with(|f| match &self.child {
748+
JsxChild::Word(word) => {
749+
word.fmt(f);
750+
}
751+
JsxChild::Whitespace => {
752+
JsxSpace.fmt(f);
753+
}
754+
JsxChild::NonText(non_text) => {
755+
non_text.fmt(f);
756+
}
757+
JsxChild::Newline | JsxChild::EmptyLine => {
758+
unreachable!(
759+
"`Newline` or `EmptyLine` should have been trimmed for single child formatting"
760+
);
761+
}
762+
});
763+
764+
if self.force_multiline {
765+
write!(f, [block_indent(&format_inner)]);
766+
} else {
767+
write!(f, [soft_block_indent(&format_inner)]);
768+
}
769+
}
770+
}

crates/oxc_formatter/src/write/jsx/element.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ impl<'a> Format<'a> for AnyJsxTagWithChildren<'a, '_> {
138138
.fmt_children(children, f);
139139

140140
match format_children {
141+
FormatChildrenResult::SingleChild(child) => {
142+
write!(f, group(&format_args!(format_opening, child, format_closing)));
143+
}
141144
FormatChildrenResult::ForceMultiline(multiline) => {
142145
write!(f, [format_opening, multiline, format_closing]);
143146
}

0 commit comments

Comments
 (0)