From f680ee1f2ed3fbd927820ec17694093a7dc238ff Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Mon, 2 Nov 2026 08:26:18 +0000 Subject: [PATCH] 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. --- Cargo.lock | 4 +-- Cargo.toml | 2 +- .../src/print/mapping_item.rs | 22 +++++++-------- crates/oxc_formatter_yaml/src/print/mod.rs | 8 +++--- crates/oxc_formatter_yaml/src/print/scalar.rs | 27 ++++++++++++------- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75c7e8fa13..03940e2d34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/Cargo.toml b/Cargo.toml index ba28bf0393..f55d1731c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/crates/oxc_formatter_yaml/src/print/mapping_item.rs b/crates/oxc_formatter_yaml/src/print/mapping_item.rs index b753ab1636..486d571fcc 100644 --- a/crates/oxc_formatter_yaml/src/print/mapping_item.rs +++ b/crates/oxc_formatter_yaml/src/print/mapping_item.rs @@ -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) } diff --git a/crates/oxc_formatter_yaml/src/print/mod.rs b/crates/oxc_formatter_yaml/src/print/mod.rs index 1288c4170a..094d3c3a7e 100644 --- a/crates/oxc_formatter_yaml/src/print/mod.rs +++ b/crates/oxc_formatter_yaml/src/print/mod.rs @@ -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 diff --git a/crates/oxc_formatter_yaml/src/print/scalar.rs b/crates/oxc_formatter_yaml/src/print/scalar.rs index 7e0f295b3f..e5a577b8b3 100644 --- a/crates/oxc_formatter_yaml/src/print/scalar.rs +++ b/crates/oxc_formatter_yaml/src/print/scalar.rs @@ -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)); }