From 138637ca852823d1699c87aba10ffeeb27ab00e4 Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 19 Jan 2026 01:32:37 +0000 Subject: [PATCH] perf(formatter): use VecDeque for member chain groups (#18094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Replace `Vec` with `VecDeque` for `TailChainGroups` storage - `pop_first()` now uses `pop_front()` which is O(1) instead of O(n) `remove(0)` ## Changes | Operation | Before | After | |-----------|--------|-------| | `pop_first()` | `remove(0)` - O(n) shift | `pop_front()` - O(1) | | `first()` | `first()` | `front()` | | `last()` | `last()` | `back()` | | `push` | `push()` | `push_back()` | ## Impact Improves performance when formatting long member chains like `a.b().c().d().e()` where the first group may be merged with the head. ## Test plan - [x] `cargo test -p oxc_formatter` passes (144 tests) - [x] Prettier conformance unchanged (97.77% js, 96.37% ts) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../src/utils/member_chain/groups.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/oxc_formatter/src/utils/member_chain/groups.rs b/crates/oxc_formatter/src/utils/member_chain/groups.rs index 9405527a41b4d..a76f69125f996 100644 --- a/crates/oxc_formatter/src/utils/member_chain/groups.rs +++ b/crates/oxc_formatter/src/utils/member_chain/groups.rs @@ -1,4 +1,5 @@ use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; use oxc_span::GetSpan; @@ -8,7 +9,7 @@ use crate::formatter::{Format, Formatter, prelude::*}; #[derive(Default)] pub(super) struct MemberChainGroupsBuilder<'a, 'b> { /// keeps track of the groups created - groups: Vec>, + groups: VecDeque>, /// keeps track of the current group that is being created/updated current_group: Option>, } @@ -33,7 +34,7 @@ impl<'a, 'b> MemberChainGroupsBuilder<'a, 'b> { /// clears the current group, and adds it to the groups collection pub fn close_group(&mut self) { if let Some(group) = self.current_group.take() { - self.groups.push(group); + self.groups.push_back(group); } } @@ -41,7 +42,7 @@ impl<'a, 'b> MemberChainGroupsBuilder<'a, 'b> { let mut groups = self.groups; if let Some(group) = self.current_group { - groups.push(group); + groups.push_back(group); } TailChainGroups { groups } @@ -53,7 +54,7 @@ impl<'a, 'b> MemberChainGroupsBuilder<'a, 'b> { /// May be empty if all members are part of the head group #[derive(Debug)] pub(super) struct TailChainGroups<'a, 'b> { - groups: Vec>, + groups: VecDeque>, } impl<'a, 'b> TailChainGroups<'a, 'b> { @@ -69,17 +70,17 @@ impl<'a, 'b> TailChainGroups<'a, 'b> { /// Returns the first group pub(crate) fn first(&self) -> Option<&MemberChainGroup<'a, 'b>> { - self.groups.first() + self.groups.front() } /// Returns the last group pub(crate) fn last(&self) -> Option<&MemberChainGroup<'a, 'b>> { - self.groups.last() + self.groups.back() } /// Removes the first group and returns it pub(super) fn pop_first(&mut self) -> Option> { - if self.groups.is_empty() { None } else { Some(self.groups.remove(0)) } + self.groups.pop_front() } /// Here we check if the length of the groups exceeds the cutoff or there are comments @@ -96,7 +97,8 @@ impl<'a, 'b> TailChainGroups<'a, 'b> { /// Test if any group except the last group [break](FormatElements::will_break). pub(super) fn any_except_last_will_break(&self, f: &Formatter<'_, 'a>) -> bool { - for group in &self.groups[..self.groups.len().saturating_sub(1)] { + let count = self.groups.len().saturating_sub(1); + for group in self.groups.iter().take(count) { if group.will_break(f) { return true; }