perf(formatter_yaml): use multiline flag for flow scalars (#28707)

- bump `oxc-yaml-parser` which has `multiline` flag
- eliminate all `\n` contains check from formatter

Reduce 5~7% CPU instructions.
This commit is contained in:
leaysgur committed 2026-11-02 08:26:18 +00:00
1 parent ff7c203711
commit f680ee1f2e
5 files changed
+36 -27

No files matched your search

Generated
+2 -2
View File
@@ -1738,9 +1738,9 @@ checksum = "f321c22ccee109a6fe0aef08d1521af693e4b178dcb7259137ff08f8ce5388d8"
[[package]]
name = "oxc-yaml-parser"
version = "0.0.7"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ca5cfdf8b83491f53dd1fe583f43923100eacf76d3b8e6bce9d24dec93356c"
checksum = "f7909b626031c9f06dc94fdcdf16b439196d209399fa094db85148c4098c6fc3"
dependencies = [
"oxc_allocator",
]
+1 -1
View File
@@ -246,7 +246,7 @@ oxc-graphql-parser = "0.0.9" # GraphQL parser (direct AST; apollo-parser 0.8.6 f
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)
oxc-toml = "0.15.1" # TOML parser and formatter
oxc-yaml-parser = "0.0.7" # YAML 1.2 parser (comment-preserving typed AST, node naming from `yaml-unist-parser`)
oxc-yaml-parser = "0.0.8" # YAML 1.2 parser (comment-preserving typed AST, node naming from `yaml-unist-parser`)
papaya = { version = "0.2.5", default-features = false } # Concurrent hash map
percent-encoding = "2.3.2" # URL encoding
petgraph = { version = "0.8.3", default-features = false } # Graph data structures
@@ -391,14 +391,14 @@ fn is_inline(node: Option<&Node<'_>>) -> bool {
/// The raw source of a flow scalar (plain/quoted); `None` for aliases,
/// collections and block scalars.
fn scalar_raw<'a>(node: &Node<'_>, f: &YamlFormatter<'_, 'a>) -> Option<&'a str> {
let span = match &node.content {
Content::Plain(p) => p.span,
Content::QuoteSingle(s) => s.span,
Content::QuoteDouble(d) => d.span,
fn scalar_raw<'a>(node: &Node<'_>, f: &YamlFormatter<'_, 'a>) -> Option<(&'a str, bool)> {
let (span, multiline) = match &node.content {
Content::Plain(p) => (p.span, p.multiline),
Content::QuoteSingle(s) => (s.span, s.multiline),
Content::QuoteDouble(d) => (d.span, d.multiline),
_ => return None,
};
Some(f.context().source_text().text_for(&to_span(span)))
Some((f.context().source_text().text_for(&to_span(span)), multiline))
}
/// `isSingleLineNode`: the node occupies one source line.
@@ -406,7 +406,7 @@ fn is_single_line(node: Option<&Node<'_>>, f: &YamlFormatter<'_, '_>) -> bool {
match node {
None => true,
Some(node) if matches!(node.content, Content::Alias(_)) => true,
Some(node) => scalar_raw(node, f).is_some_and(|raw| !raw.contains('\n')),
Some(node) => scalar_raw(node, f).is_some_and(|(_, multiline)| !multiline),
}
}
@@ -417,11 +417,11 @@ fn is_absolutely_single_line(node: Option<&Node<'_>>, f: &YamlFormatter<'_, '_>)
if matches!(node.content, Content::Alias(_)) {
return true;
}
let Some(raw) = scalar_raw(node, f) else { return false };
let Some((raw, multiline)) = scalar_raw(node, f) else { return false };
let prose_wrap = f.options().prose_wrap;
if prose_wrap == ProseWrap::Preserve {
return !raw.contains('\n');
return !multiline;
}
if has_backslash_continuation(raw) {
return false;
@@ -432,7 +432,7 @@ fn is_absolutely_single_line(node: Option<&Node<'_>>, f: &YamlFormatter<'_, '_>)
!has_blank_line(raw)
} else {
// `always` may wrap at any space
!raw.contains('\n') && !raw.contains(' ')
!multiline && !raw.contains(' ')
}
}
@@ -453,7 +453,7 @@ fn has_backslash_continuation(raw: &str) -> bool {
fn has_forced_break_when_folded(node: Option<&Node<'_>>, f: &YamlFormatter<'_, '_>) -> bool {
let Some(node) = node else { return false };
// Defensive: non-scalars never re-fold
let Some(raw) = scalar_raw(node, f) else { return true };
let Some((raw, _)) = scalar_raw(node, f) else { return true };
has_blank_line(raw) || has_backslash_continuation(raw)
}
+5 -3
View File
@@ -155,12 +155,14 @@ pub fn write_node<'a>(node: &'a Node<'a>, f: &mut YamlFormatter<'_, 'a>) {
}
match content {
Content::Plain(scalar) => scalar::write_plain(to_span(scalar.span), f),
Content::Plain(scalar) => scalar::write_plain(to_span(scalar.span), scalar.multiline, f),
Content::QuoteSingle(scalar) => {
scalar::write_quoted(scalar::FlowScalarKind::QuoteSingle, to_span(scalar.span), f);
let kind = scalar::FlowScalarKind::QuoteSingle;
scalar::write_quoted(kind, to_span(scalar.span), scalar.multiline, f);
}
Content::QuoteDouble(scalar) => {
scalar::write_quoted(scalar::FlowScalarKind::QuoteDouble, to_span(scalar.span), f);
let kind = scalar::FlowScalarKind::QuoteDouble;
scalar::write_quoted(kind, to_span(scalar.span), scalar.multiline, f);
}
Content::Alias(alias) => {
// Verbatim: `*name` has no layout freedom, and no comment can lie inside
+17 -10
View File
@@ -23,13 +23,18 @@ pub enum FlowScalarKind {
}
/// Emits a plain scalar: raw slice content, line-folded per `proseWrap`.
pub fn write_plain(span: Span, f: &mut YamlFormatter<'_, '_>) {
pub fn write_plain(span: Span, multiline: bool, f: &mut YamlFormatter<'_, '_>) {
let content = f.context().source_text().text_for(&span);
write_flow_scalar_content(FlowScalarKind::Plain, content, f);
write_flow_scalar_content(FlowScalarKind::Plain, content, multiline, f);
}
/// Emits a quoted scalar with Prettier's `quoteDouble`/`quoteSingle` case.
pub fn write_quoted(kind: FlowScalarKind, span: Span, f: &mut YamlFormatter<'_, '_>) {
pub fn write_quoted(
kind: FlowScalarKind,
span: Span,
multiline: bool,
f: &mut YamlFormatter<'_, '_>,
) {
let source = f.context().source_text();
// Inner content between the quotes
let raw = source.text_for(&Span::new(span.start + 1, span.end - 1));
@@ -47,7 +52,7 @@ pub fn write_quoted(kind: FlowScalarKind, span: Span, f: &mut YamlFormatter<'_,
if has_unreencodable_escape {
let quote = if kind == FlowScalarKind::QuoteDouble { "\"" } else { "'" };
write!(f, text(quote));
write_flow_scalar_content(kind, raw, f);
write_flow_scalar_content(kind, raw, multiline, f);
write!(f, text(quote));
return;
}
@@ -60,9 +65,9 @@ pub fn write_quoted(kind: FlowScalarKind, span: Span, f: &mut YamlFormatter<'_,
// (The guarding `contains('"')` means the first replace always matches,
// so the chain is owned; copy it into the arena once.)
let content = raw.cow_replace("\\\"", "\"").cow_replace('\'', "''").into_owned();
write_flow_scalar_content(kind, f.allocator().alloc_str(&content), f);
write_flow_scalar_content(kind, f.allocator().alloc_str(&content), multiline, f);
} else {
write_flow_scalar_content(kind, raw, f);
write_flow_scalar_content(kind, raw, multiline, f);
}
write!(f, text("'"));
return;
@@ -74,9 +79,9 @@ pub fn write_quoted(kind: FlowScalarKind, span: Span, f: &mut YamlFormatter<'_,
if kind == FlowScalarKind::QuoteSingle {
// `''` unescapes to `'` (nothing else needs escaping in double quotes here)
let content = raw.cow_replace("''", "'");
write_flow_scalar_content(kind, arena_cow_str(&content, f), f);
write_flow_scalar_content(kind, arena_cow_str(&content, f), multiline, f);
} else {
write_flow_scalar_content(kind, raw, f);
write_flow_scalar_content(kind, raw, multiline, f);
}
write!(f, text("\""));
return;
@@ -84,22 +89,24 @@ pub fn write_quoted(kind: FlowScalarKind, span: Span, f: &mut YamlFormatter<'_,
let quote = f.options().single_quote.as_str();
write!(f, text(quote));
write_flow_scalar_content(kind, raw, f);
write_flow_scalar_content(kind, raw, multiline, f);
write!(f, text(quote));
}
/// Prettier's `printFlowScalarContent`:
/// each reflow group becomes a `fill(join(line, words))`, groups joined by hardlines.
/// `multiline` is the parser's flag for the scalar crossing a line break.
pub fn write_flow_scalar_content<'a>(
kind: FlowScalarKind,
content: &'a str,
multiline: bool,
f: &mut YamlFormatter<'_, 'a>,
) {
let prose_wrap = f.options().prose_wrap;
// Fast path:
// a single-line scalar under `preserve` (the default) prints verbatim,
// skip the line-contents machinery and its allocations.
if prose_wrap == ProseWrap::Preserve && !content.contains('\n') {
if prose_wrap == ProseWrap::Preserve && !multiline {
if !content.is_empty() {
write!(f, text(content));
}