mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(formatter): keep AstNodes on the stack, and only chain links in the arena (#27963)
Stop allocating every `AstNode` wrapper in the arena, following the approach of #21931. ### What changes - `AstNode<'parent, 'a, T> { inner, parent, following_span_start }` is `Copy` and has no `&Allocator`. - `'parent` is the lifetime of the parent `AstNode` that `parent` refers to. - Generated child accessors return an owned `AstNode` that borrows its parent. - The parent's `write` keeps its children on its stack frame. - The unsafe lifetime extension (`transmute_self`) is gone. - The arena-backed `as_ast_nodes()` is replaced by a generated owned `AstNodeKind` (`as_kind()`). - `NeedsParentheses for AstNode<Expression>` is generated as one direct match. ### Sites that are not a plain recursive descent A loop that walks down a chain cannot keep each node on the stack, as the next node refers to it as its parent. #21931 hit this at about 10 sites. - Chain walks (binary-like flattening, member chains, arrow chains, assignment-like, nested namespaces) keep their loops. - Only the links that the walk continues past go into the arena, through `AstNode::in_arena` / `AstNodeKind::in_arena`. - The rule is documented once on `AstNode`. - Checks that never ask for a parent read the AST directly, with no wrapper. - Left sides: one table, `LeftSide::of`, which `left_span_of`, `leftmost` and the `AstNode` walk `left_of` derive from. - The shape checks of assignment-like chains. - The binary-like split and the flattened logical expression are loops too, so long chains need no more stack than on `main`. ### Compared to #21931 (Jim's PoC) - Enum nodes resolve through the generated `as_kind()`, so call sites keep the `match` shape of `as_ast_nodes()`. - PoC rewrote each of them by hand, as a `match` on `inner` with `with_inner`. - The arena is used only through `in_arena`, and only for the links a chain walk continues past. - PoC put an `f.allocator().alloc(..)` at each site that broke, kept the recursion, and allocated at the entry of a flatten. - Checks that never ask for a parent use no wrapper. - PoC allocated a wrapper at each step of e.g. `get_innermost_expression`. - `ImportExpression::arguments` takes the allocator, instead of leaving `to_arguments` as a TODO. ### Alternatives tried Both follow directions suggested in #21931. - #27958: no arena at all - As suggested "replace loop with recursive function call". - The chain walks recurse, and a walk that needs the whole chain keeps it in a `StackList` on the stack, consumed through continuation callbacks. - The recursion uses stack in proportion to the chain length: with 2 MiB workers, `return a.b().b()…` overflows at 4000 calls, where `main` formats 32000. - So it needs a workaround, e.g. 8 MiB `oxfmt` workers, which still leaves the napi API and the LSP on default stacks. - About 0.5 points faster than this PR on CodSpeed, but more code. - #27959: no parent pointer - As suggested "Alternative approach". - Ancestors on a `NonEmptyStack` like `Traverse`, and `following_span_start` computed on demand. - 2-4% more instructions than #27958, quadratic on long chains, and the ancestry is only checked in debug builds.
This commit is contained in:
1 parent
4c0fa90abe
commit
788af22210
68 files changed
+6832
-7381
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -4,7 +4,7 @@ use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::ast_nodes::AstNodes;
|
||||
|
||||
impl<'a> AstNodes<'a> {
|
||||
impl<'parent, 'a> AstNodes<'parent, 'a> {
|
||||
/// Returns an iterator over all ancestor nodes in the AST, starting from self.
|
||||
///
|
||||
/// The iteration includes the current node and proceeds upward through the tree,
|
||||
@@ -17,7 +17,7 @@ impl<'a> AstNodes<'a> {
|
||||
/// └─ ExpressionStatement <- self
|
||||
/// ```
|
||||
/// For `self` as ExpressionStatement, this yields: [ExpressionStatement, BlockStatement, Program]
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = &AstNodes<'a>> {
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = &AstNodes<'parent, 'a>> {
|
||||
// Start with the current node and walk up the tree, including Program
|
||||
std::iter::successors(Some(self), |node| {
|
||||
// Continue iteration until we've yielded Program (root node)
|
||||
@@ -28,7 +28,7 @@ impl<'a> AstNodes<'a> {
|
||||
|
||||
/// If the node is a ChainExpression, recursively skip to its parent until a non-ChainExpression node is found.
|
||||
/// This is useful for analyses that want to ignore the presence of ChainExpressions in the AST.
|
||||
pub fn without_chain_expression(&self) -> &AstNodes<'a> {
|
||||
pub fn without_chain_expression(&self) -> &AstNodes<'parent, 'a> {
|
||||
match self {
|
||||
AstNodes::ChainExpression(chain_expression) => {
|
||||
chain_expression.parent.without_chain_expression()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! The span getter returns `None` for elements the formatter never prints (dropped empty statements, elision holes),
|
||||
//! which are skipped when computing `following_span_start`.
|
||||
|
||||
use oxc_allocator::{Allocator, ArenaVec};
|
||||
use oxc_allocator::ArenaVec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
@@ -13,10 +13,9 @@ use crate::utils::{is_dropped_statement, statement_span};
|
||||
use super::{AstNode, AstNodes};
|
||||
|
||||
/// Iterator for `AstNode<ArenaVec<T>>`.
|
||||
pub struct AstNodeIterator<'a, T> {
|
||||
pub struct AstNodeIterator<'parent, 'a, T> {
|
||||
inner: std::slice::Iter<'a, T>,
|
||||
parent: AstNodes<'a>,
|
||||
allocator: &'a Allocator,
|
||||
parent: AstNodes<'parent, 'a>,
|
||||
/// The `following_span_start` for the last element when there's no next element in this iterator.
|
||||
///
|
||||
/// This is essential for [`Comments::get_trailing_comments`] to correctly distinguish trailing
|
||||
@@ -62,12 +61,11 @@ macro_rules! impl_ast_node_vec {
|
||||
impl_ast_node_vec!($type, true, $get_span);
|
||||
};
|
||||
($type:ty, $has_following_span_in_the_last_item:tt, $get_span:expr) => {
|
||||
impl<'a> AstNode<'a, ArenaVec<'a, $type>> {
|
||||
pub fn iter(&self) -> AstNodeIterator<'a, $type> {
|
||||
impl<'parent, 'a> AstNode<'parent, 'a, ArenaVec<'a, $type>> {
|
||||
pub fn iter(&self) -> AstNodeIterator<'parent, 'a, $type> {
|
||||
AstNodeIterator {
|
||||
inner: self.inner.iter(),
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: if $has_following_span_in_the_last_item {
|
||||
self.following_span_start
|
||||
} else {
|
||||
@@ -77,74 +75,42 @@ macro_rules! impl_ast_node_vec {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first(&self) -> Option<&'a AstNode<'a, $type>> {
|
||||
let following = if $has_following_span_in_the_last_item {
|
||||
self.following_span_start
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let get_span: fn(&$type) -> Option<u32> = $get_span;
|
||||
let mut inner_iter = self.inner.iter();
|
||||
self.allocator
|
||||
.alloc(inner_iter.next().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: inner_iter.find_map(get_span).unwrap_or(following),
|
||||
}))
|
||||
.as_ref()
|
||||
pub fn first(&self) -> Option<AstNode<'parent, 'a, $type>> {
|
||||
self.iter().next()
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<&'a AstNode<'a, $type>> {
|
||||
pub fn last(&self) -> Option<AstNode<'parent, 'a, $type>> {
|
||||
let following = if $has_following_span_in_the_last_item {
|
||||
self.following_span_start
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.allocator
|
||||
.alloc(self.inner.last().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: following,
|
||||
}))
|
||||
.as_ref()
|
||||
self.inner.last().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
following_span_start: following,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for AstNodeIterator<'a, $type> {
|
||||
type Item = &'a AstNode<'a, $type>;
|
||||
impl<'parent, 'a> Iterator for AstNodeIterator<'parent, 'a, $type> {
|
||||
type Item = AstNode<'parent, 'a, $type>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let allocator = self.allocator;
|
||||
let following = self.following_span_start;
|
||||
let get_span = self.get_following_span_start;
|
||||
allocator
|
||||
.alloc(self.inner.next().map(|inner| AstNode {
|
||||
parent: self.parent,
|
||||
inner,
|
||||
allocator,
|
||||
following_span_start:
|
||||
self.inner.clone().find_map(get_span).unwrap_or(following),
|
||||
}))
|
||||
.as_ref()
|
||||
let inner = self.inner.next()?;
|
||||
let following_span_start = self
|
||||
.inner
|
||||
.clone()
|
||||
.find_map(self.get_following_span_start)
|
||||
.unwrap_or(self.following_span_start);
|
||||
Some(AstNode { inner, parent: self.parent, following_span_start })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &AstNode<'a, ArenaVec<'a, $type>> {
|
||||
type Item = &'a AstNode<'a, $type>;
|
||||
type IntoIter = AstNodeIterator<'a, $type>;
|
||||
impl<'parent, 'a> IntoIterator for &AstNode<'parent, 'a, ArenaVec<'a, $type>> {
|
||||
type Item = AstNode<'parent, 'a, $type>;
|
||||
type IntoIter = AstNodeIterator<'parent, 'a, $type>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
AstNodeIterator {
|
||||
inner: self.inner.iter(),
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: if $has_following_span_in_the_last_item {
|
||||
self.following_span_start
|
||||
} else {
|
||||
0
|
||||
},
|
||||
get_following_span_start: $get_span,
|
||||
}
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,6 @@ pub mod impls;
|
||||
mod iterator;
|
||||
mod node;
|
||||
|
||||
pub use generated::ast_nodes::AstNodes;
|
||||
pub use generated::ast_nodes::{AstNodeKind, AstNodes};
|
||||
pub use iterator::AstNodeIterator;
|
||||
pub use node::AstNode;
|
||||
@@ -1,24 +1,41 @@
|
||||
use std::{
|
||||
fmt,
|
||||
mem::{transmute, transmute_copy},
|
||||
ops::Deref,
|
||||
};
|
||||
use std::{fmt, mem::transmute_copy, ops::Deref};
|
||||
|
||||
use oxc_allocator::{Allocator, ArenaVec};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use super::AstNodes;
|
||||
use super::{AstNodeKind, AstNodes};
|
||||
|
||||
pub struct AstNode<'a, T> {
|
||||
/// An AST node with its parent, created on the stack while the formatter descends the AST.
|
||||
///
|
||||
/// `'parent` is the lifetime of the parent node it refers to, `'a` is the lifetime of the AST.
|
||||
/// An accessor for a child borrows `self`, so the parent must outlive the child.
|
||||
///
|
||||
/// # Links in the arena
|
||||
///
|
||||
/// A loop that walks down a chain (`a.b.c`, `a && b && c`) cannot keep each node on the stack,
|
||||
/// as the next node refers to it as its parent.
|
||||
/// Such a link is moved into the arena with `in_arena`, only when the walk continues past it:
|
||||
/// - [`AstNodeKind::in_arena`] when the next step matches on its kind
|
||||
/// - [`AstNode::in_arena`] when a match has already selected the variant,
|
||||
/// as the specific node (e.g. `AstNode<CallExpression>`) is smaller than the kind
|
||||
pub struct AstNode<'parent, 'a, T> {
|
||||
pub(super) inner: &'a T,
|
||||
pub(super) parent: AstNodes<'a>,
|
||||
pub(super) allocator: &'a Allocator,
|
||||
pub(super) parent: AstNodes<'parent, 'a>,
|
||||
/// The start position of the following sibling node, or 0 if none.
|
||||
pub(super) following_span_start: u32,
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for AstNode<'_, T> {
|
||||
// Not derived, to not require `T: Copy`
|
||||
impl<T> Clone for AstNode<'_, '_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for AstNode<'_, '_, T> {}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for AstNode<'_, '_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AstNode")
|
||||
.field("inner", &self.inner)
|
||||
@@ -28,7 +45,7 @@ impl<T: fmt::Debug> fmt::Debug for AstNode<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for AstNode<'a, T> {
|
||||
impl<'a, T> Deref for AstNode<'_, 'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &'a Self::Target {
|
||||
@@ -36,38 +53,41 @@ impl<'a, T> Deref for AstNode<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<T> for AstNode<'a, T> {
|
||||
impl<'a, T> AsRef<T> for AstNode<'_, 'a, T> {
|
||||
fn as_ref(&self) -> &'a T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AstNode<'a, Option<T>> {
|
||||
pub fn as_ref(&self) -> Option<&'a AstNode<'a, T>> {
|
||||
self.allocator
|
||||
.alloc(self.inner.as_ref().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}))
|
||||
.as_ref()
|
||||
impl<'parent, 'a, T> AstNode<'parent, 'a, Option<T>> {
|
||||
pub fn as_ref(&self) -> Option<AstNode<'parent, 'a, T>> {
|
||||
self.inner.as_ref().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
following_span_start: self.following_span_start,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetSpan> GetSpan for AstNode<'_, T> {
|
||||
impl<T: GetSpan> GetSpan for AstNode<'_, '_, T> {
|
||||
fn span(&self) -> Span {
|
||||
self.inner.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetSpan> GetSpan for &AstNode<'_, T> {
|
||||
impl<T: GetSpan> GetSpan for &AstNode<'_, '_, T> {
|
||||
fn span(&self) -> Span {
|
||||
self.inner.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AstNode<'a, T> {
|
||||
impl<'parent, 'a, T> AstNode<'parent, 'a, T> {
|
||||
/// Returns the AST node, for the lifetime of the AST rather than the borrow of `self` (unlike [`Deref`] and [`AsRef`]).
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &'a T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Returns an iterator over all ancestor nodes in the AST, starting from self.
|
||||
///
|
||||
/// The iteration includes the current node and proceeds upward through the tree,
|
||||
@@ -97,13 +117,13 @@ impl<'a, T> AstNode<'a, T> {
|
||||
/// let in_arrow_fn = self.ancestors()
|
||||
/// .any(|p| matches!(p, AstNodes::ArrowFunctionExpression(_)));
|
||||
/// ```
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = &AstNodes<'a>> {
|
||||
pub fn ancestors(&self) -> impl Iterator<Item = &AstNodes<'parent, 'a>> {
|
||||
self.parent().ancestors()
|
||||
}
|
||||
|
||||
/// Returns the parent node.
|
||||
#[inline]
|
||||
pub fn parent(&self) -> &AstNodes<'a> {
|
||||
pub fn parent(&self) -> &AstNodes<'parent, 'a> {
|
||||
&self.parent
|
||||
}
|
||||
|
||||
@@ -116,18 +136,30 @@ impl<'a, T> AstNode<'a, T> {
|
||||
/// Returns the grandparent node (parent's parent).
|
||||
///
|
||||
/// This is a convenience method equivalent to `self.parent().parent()`.
|
||||
pub fn grand_parent(&self) -> &AstNodes<'a> {
|
||||
pub fn grand_parent(&self) -> &AstNodes<'parent, 'a> {
|
||||
self.parent().parent()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AstNode<'a, T> {
|
||||
pub fn new(inner: &'a T, parent: AstNodes<'a>, allocator: &'a Allocator) -> Self {
|
||||
AstNode { inner, parent, allocator, following_span_start: 0 }
|
||||
pub fn new(inner: &'a T, parent: AstNodes<'parent, 'a>) -> Self {
|
||||
AstNode { inner, parent, following_span_start: 0 }
|
||||
}
|
||||
|
||||
/// Moves this node into the arena, see [links in the arena](AstNode#links-in-the-arena).
|
||||
#[inline]
|
||||
pub fn in_arena(self, allocator: &'parent Allocator) -> &'parent Self {
|
||||
allocator.alloc(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetSpan> AstNode<'_, T> {
|
||||
impl<'parent> AstNodeKind<'parent, '_> {
|
||||
/// Moves this node into the arena, see [links in the arena](AstNode#links-in-the-arena).
|
||||
#[inline]
|
||||
pub fn in_arena(self, allocator: &'parent Allocator) -> &'parent Self {
|
||||
allocator.alloc(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GetSpan> AstNode<'_, '_, T> {
|
||||
/// Check if this node is the callee of a CallExpression or NewExpression
|
||||
pub fn is_call_like_callee(&self) -> bool {
|
||||
let callee = match self.parent() {
|
||||
@@ -145,29 +177,29 @@ impl<T: GetSpan> AstNode<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, ArrowFunctionBody<'a>> {
|
||||
impl<'parent, 'a> AstNode<'parent, 'a, ArrowFunctionBody<'a>> {
|
||||
/// Returns the concise expression body as an [`AstNode`].
|
||||
pub fn as_expression(&self) -> Option<&AstNode<'a, Expression<'a>>> {
|
||||
self.allocator
|
||||
.alloc(self.inner.as_expression().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}))
|
||||
.as_ref()
|
||||
pub fn as_expression(&self) -> Option<AstNode<'parent, 'a, Expression<'a>>> {
|
||||
self.inner.as_expression().map(|inner| AstNode {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
following_span_start: self.following_span_start,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, ImportExpression<'a>> {
|
||||
impl<'a> AstNode<'_, 'a, ImportExpression<'a>> {
|
||||
/// Converts the arguments of the ImportExpression into an `AstNode` representing a `Vec` of `Argument`.
|
||||
#[inline]
|
||||
pub fn to_arguments(&self) -> &AstNode<'a, ArenaVec<'a, Argument<'a>>> {
|
||||
pub fn arguments(
|
||||
&self,
|
||||
allocator: &'a Allocator,
|
||||
) -> AstNode<'_, 'a, ArenaVec<'a, Argument<'a>>> {
|
||||
// Convert ImportExpression's source and options to ArenaVec<'a, Argument<'a>>.
|
||||
// This allows us to reuse CallExpression's argument formatting logic when printing
|
||||
// import expressions, since import(source, options) has the same structure as
|
||||
// a function call with arguments.
|
||||
let mut arguments = ArenaVec::new_in(&self.allocator);
|
||||
let mut arguments = ArenaVec::new_in(&allocator);
|
||||
|
||||
// SAFETY: Argument inherits all Expression variants through the inherit_variants! macro,
|
||||
// so Expression and Argument have identical memory layout for shared variants.
|
||||
@@ -187,27 +219,15 @@ impl<'a> AstNode<'a, ImportExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
let arguments_ref = self.allocator.alloc(arguments);
|
||||
let following_span_start = self.following_span_start;
|
||||
|
||||
self.allocator.alloc(AstNode {
|
||||
inner: arguments_ref,
|
||||
allocator: self.allocator,
|
||||
parent: AstNodes::ImportExpression({
|
||||
// SAFETY: `self` is already allocated in Arena, so transmute from `&` to `&'a` is safe.
|
||||
unsafe {
|
||||
transmute::<
|
||||
&AstNode<'_, ImportExpression<'_>>,
|
||||
&'a AstNode<'a, ImportExpression<'a>>,
|
||||
>(self)
|
||||
}
|
||||
}),
|
||||
following_span_start,
|
||||
})
|
||||
AstNode {
|
||||
inner: allocator.alloc(arguments),
|
||||
parent: AstNodes::ImportExpression(self),
|
||||
following_span_start: self.following_span_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, CallExpression<'a>> {
|
||||
impl AstNode<'_, '_, CallExpression<'_>> {
|
||||
/// Check if the passing span is the callee of this CallExpression
|
||||
pub fn is_callee_span(&self, span: Span) -> bool {
|
||||
self.inner.callee.span() == span
|
||||
@@ -219,7 +239,7 @@ impl<'a> AstNode<'a, CallExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AstNode<'a, NewExpression<'a>> {
|
||||
impl AstNode<'_, '_, NewExpression<'_>> {
|
||||
/// Check if the passing span is the callee of this NewExpression
|
||||
pub fn is_callee_span(&self, span: Span) -> bool {
|
||||
self.inner.callee.span() == span
|
||||
|
||||
@@ -130,7 +130,7 @@ pub fn format_to_ir<'a>(
|
||||
) -> Result<EmbeddedIr<'a>, OxcDiagnostic> {
|
||||
let allocator = session.allocator();
|
||||
let program = parse(allocator, allocator.alloc_str(source_text), source_type)?;
|
||||
let node = AstNode::new(program, AstNodes::Dummy(), allocator);
|
||||
let node = AstNode::new(program, AstNodes::Dummy());
|
||||
let mut context =
|
||||
JsFormatContext::new(program.source_text, program.source_type, &program.comments, options);
|
||||
|
||||
@@ -195,7 +195,7 @@ pub fn format_fragment<'a>(
|
||||
// Parens are forced only in the binding-LHS context with multiple params or a rest element.
|
||||
let with_parens = matches!(context, FragmentContext::FunctionParamsAsBindingLhs)
|
||||
&& (1 < params.items.len() || params.rest.is_some());
|
||||
let node = AstNode::new(params, AstNodes::Dummy(), allocator);
|
||||
let node = AstNode::new(params, AstNodes::Dummy());
|
||||
let content = FormatFunctionParams::new(&node, with_parens);
|
||||
format_node(
|
||||
&session,
|
||||
@@ -215,7 +215,7 @@ pub fn format_fragment<'a>(
|
||||
let Some(type_params) = decl.type_parameters.as_deref() else {
|
||||
return Err(OxcDiagnostic::error("Expected type parameters in wrapped fragment"));
|
||||
};
|
||||
let node = AstNode::new(type_params, AstNodes::Dummy(), allocator);
|
||||
let node = AstNode::new(type_params, AstNodes::Dummy());
|
||||
let content = FormatTypeParameters::new(&node);
|
||||
format_node(
|
||||
&session,
|
||||
@@ -266,7 +266,7 @@ fn format_program_with_session<'a>(
|
||||
program: &'a Program<'a>,
|
||||
options: JsFormatOptions,
|
||||
) -> Formatted<'a, JsFormatContext<'a>> {
|
||||
let node = AstNode::new(program, AstNodes::Dummy(), session.allocator());
|
||||
let node = AstNode::new(program, AstNodes::Dummy());
|
||||
format_node(
|
||||
session,
|
||||
options,
|
||||
|
||||
@@ -12,14 +12,8 @@ use crate::{
|
||||
|
||||
use super::NeedsParentheses;
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, Expression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
self.as_ast_nodes().needs_parentheses(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Covers expression nodes, including the ones reachable as an ancestor (e.g. a member expression as an assignment target).
|
||||
impl NeedsParentheses<'_> for AstNodes<'_> {
|
||||
impl NeedsParentheses<'_> for AstNodes<'_, '_> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
match self {
|
||||
AstNodes::BooleanLiteral(it) => it.needs_parentheses(f),
|
||||
@@ -74,7 +68,7 @@ impl NeedsParentheses<'_> for AstNodes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, IdentifierReference<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, IdentifierReference<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -142,63 +136,63 @@ impl NeedsParentheses<'_> for AstNode<'_, IdentifierReference<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, BooleanLiteral> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, BooleanLiteral> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, NullLiteral> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, NullLiteral> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, BigIntLiteral<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, BigIntLiteral<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, RegExpLiteral<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, RegExpLiteral<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TemplateLiteral<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TemplateLiteral<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ImportMeta> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ImportMeta> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, NewTarget> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, NewTarget> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, Super> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, Super> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, NumericLiteral<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, NumericLiteral<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -211,7 +205,7 @@ impl NeedsParentheses<'_> for AstNode<'_, NumericLiteral<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, StringLiteral<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, StringLiteral<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -237,14 +231,14 @@ impl NeedsParentheses<'_> for AstNode<'_, StringLiteral<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ThisExpression> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ThisExpression> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ArrayExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ArrayExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
// Wrap array expressions in for-in initializers
|
||||
// e.g., `for (var a = ([b in c]) in {})`
|
||||
@@ -252,7 +246,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ArrayExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ObjectExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ObjectExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -276,7 +270,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ObjectExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TaggedTemplateExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TaggedTemplateExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
// `class A extends (tag`x`) {}`; keep in sync with the wrapped-in-`!` case
|
||||
@@ -288,20 +282,20 @@ impl NeedsParentheses<'_> for AstNode<'_, TaggedTemplateExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, MemberExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, MemberExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ComputedMemberExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ComputedMemberExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
self.is_new_callee() && (self.optional || member_chain_callee_needs_parens(&self.object))
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, StaticMemberExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, StaticMemberExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -311,14 +305,14 @@ impl NeedsParentheses<'_> for AstNode<'_, StaticMemberExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, PrivateFieldExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, PrivateFieldExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
self.is_new_callee() && (self.optional || member_chain_callee_needs_parens(&self.object))
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, CallExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, CallExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -328,7 +322,7 @@ impl NeedsParentheses<'_> for AstNode<'_, CallExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, NewExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, NewExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -338,7 +332,7 @@ impl NeedsParentheses<'_> for AstNode<'_, NewExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, UpdateExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, UpdateExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -359,7 +353,7 @@ impl NeedsParentheses<'_> for AstNode<'_, UpdateExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, UnaryExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, UnaryExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -383,7 +377,7 @@ impl NeedsParentheses<'_> for AstNode<'_, UnaryExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, BinaryExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, BinaryExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -413,7 +407,7 @@ impl NeedsParentheses<'_> for AstNode<'_, BinaryExpression<'_>> {
|
||||
/// separately at the VariableDeclarator level (see `is_for_in_statement_init`).
|
||||
///
|
||||
/// <https://github.com/prettier/prettier/issues/907#issuecomment-284304321>
|
||||
fn is_in_for_initializer(expr: &AstNode<'_, BinaryExpression<'_>>) -> bool {
|
||||
fn is_in_for_initializer(expr: &AstNode<'_, '_, BinaryExpression<'_>>) -> bool {
|
||||
let ancestors = expr.ancestors();
|
||||
|
||||
for parent in ancestors {
|
||||
@@ -450,7 +444,7 @@ fn is_in_for_initializer(expr: &AstNode<'_, BinaryExpression<'_>>) -> bool {
|
||||
///
|
||||
/// Legacy syntax: `for (var a = 1 in b);`
|
||||
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_for-in_initializer>
|
||||
fn is_for_in_statement_init<T: GetSpan>(node: &T, parent: &AstNodes<'_>) -> bool {
|
||||
fn is_for_in_statement_init<T: GetSpan>(node: &T, parent: &AstNodes<'_, '_>) -> bool {
|
||||
let AstNodes::VariableDeclarator(declarator) = parent else { return false };
|
||||
let Some(init) = &declarator.init else { return false };
|
||||
if init.span() != node.span() {
|
||||
@@ -463,7 +457,7 @@ fn is_for_in_statement_init<T: GetSpan>(node: &T, parent: &AstNodes<'_>) -> bool
|
||||
if matches!(&stmt.left, ForStatementLeft::VariableDeclaration(d) if ptr::eq(d.as_ref(), decl.as_ref())))
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, PrivateInExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, PrivateInExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
@@ -475,7 +469,7 @@ impl NeedsParentheses<'_> for AstNode<'_, PrivateInExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, LogicalExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, LogicalExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -501,7 +495,7 @@ impl NeedsParentheses<'_> for AstNode<'_, LogicalExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ConditionalExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ConditionalExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -530,7 +524,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ConditionalExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, Function<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, Function<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if self.r#type() != FunctionType::FunctionExpression {
|
||||
return false;
|
||||
@@ -559,7 +553,7 @@ impl NeedsParentheses<'_> for AstNode<'_, Function<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, AssignmentExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, AssignmentExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -634,7 +628,7 @@ impl NeedsParentheses<'_> for AstNode<'_, AssignmentExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, SequenceExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, SequenceExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -651,7 +645,7 @@ impl NeedsParentheses<'_> for AstNode<'_, SequenceExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, AwaitExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, AwaitExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -661,7 +655,7 @@ impl NeedsParentheses<'_> for AstNode<'_, AwaitExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ChainExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ChainExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -680,7 +674,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ChainExpression<'_>> {
|
||||
/// - The callee of a new expression
|
||||
/// - The object of a non-optional member expression
|
||||
/// - The tag of a tagged template expression
|
||||
fn chain_expression_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn chain_expression_needs_parens(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
AstNodes::TSNonNullExpression(_) | AstNodes::TaggedTemplateExpression(_) => true,
|
||||
AstNodes::NewExpression(new) => new.is_callee_span(span),
|
||||
@@ -693,7 +687,7 @@ fn chain_expression_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, Class<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, Class<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if self.r#type() != ClassType::ClassExpression {
|
||||
return false;
|
||||
@@ -723,13 +717,13 @@ impl NeedsParentheses<'_> for AstNode<'_, Class<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ParenthesizedExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ParenthesizedExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
unreachable!("Already disabled `preserveParens` option in the parser")
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ArrowFunctionExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ArrowFunctionExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -764,7 +758,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ArrowFunctionExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, YieldExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, YieldExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -776,7 +770,7 @@ impl NeedsParentheses<'_> for AstNode<'_, YieldExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, ImportExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, ImportExpression<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -786,47 +780,47 @@ impl NeedsParentheses<'_> for AstNode<'_, ImportExpression<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, V8IntrinsicExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, V8IntrinsicExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, JSXMemberExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, JSXMemberExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, JSXExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, JSXExpression<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, JSXEmptyExpression> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, JSXEmptyExpression> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSAsExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSAsExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
ts_as_or_satisfies_needs_parens(self.span(), self.parent())
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSSatisfiesExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSSatisfiesExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
ts_as_or_satisfies_needs_parens(self.span(), self.parent())
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSTypeAssertion<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSTypeAssertion<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
match self.parent() {
|
||||
AstNodes::TSAsExpression(_) | AstNodes::TSSatisfiesExpression(_) => true,
|
||||
@@ -843,7 +837,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSTypeAssertion<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
#[expect(clippy::match_same_arms)] // for better readability
|
||||
match parent {
|
||||
AstNodes::TSTypeAssertion(_)
|
||||
@@ -872,7 +866,7 @@ fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSNonNullExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSNonNullExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
(self.is_new_callee() && member_chain_callee_needs_parens(&self.expression))
|
||||
// `class A extends ({}!) {}` needs parens, `class A extends B! {}` does not:
|
||||
@@ -915,7 +909,7 @@ fn class_extends_needs_parens_through_non_null(expression: &Expression<'_>) -> b
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSInstantiationExpression<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSInstantiationExpression<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
let object = match self.parent() {
|
||||
AstNodes::StaticMemberExpression(expr) => &expr.object,
|
||||
@@ -938,7 +932,9 @@ impl NeedsParentheses<'_> for AstNode<'_, TSInstantiationExpression<'_>> {
|
||||
/// Whether the instantiation expression ends the left operand of `+`, `-`, `<` or `>`-led operators:
|
||||
/// TypeScript reads `f<T> + x` as `f < T > +x` and rejects `f<T> < x`,
|
||||
/// also when `f<T>` only ends the operand (`(a + f<T>) - x`, `(typeof f<T>) + x`).
|
||||
fn is_followed_by_comparison_or_sign(node: &AstNode<'_, TSInstantiationExpression<'_>>) -> bool {
|
||||
fn is_followed_by_comparison_or_sign(
|
||||
node: &AstNode<'_, '_, TSInstantiationExpression<'_>>,
|
||||
) -> bool {
|
||||
let mut span = node.span;
|
||||
let mut parent = node.parent();
|
||||
loop {
|
||||
@@ -1048,12 +1044,12 @@ fn member_chain_callee_needs_parens(e: &Expression) -> bool {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum UnaryLike<'a, 'b> {
|
||||
UpdateExpression(&'b AstNode<'a, UpdateExpression<'a>>),
|
||||
UnaryExpression(&'b AstNode<'a, UnaryExpression<'a>>),
|
||||
UpdateExpression(&'b AstNode<'b, 'a, UpdateExpression<'a>>),
|
||||
UnaryExpression(&'b AstNode<'b, 'a, UnaryExpression<'a>>),
|
||||
}
|
||||
|
||||
impl UnaryLike<'_, '_> {
|
||||
fn parent(&self) -> &AstNodes<'_> {
|
||||
fn parent(&self) -> &AstNodes<'_, '_> {
|
||||
match self {
|
||||
Self::UpdateExpression(e) => e.parent(),
|
||||
Self::UnaryExpression(e) => e.parent(),
|
||||
@@ -1082,7 +1078,7 @@ fn unary_like_expression_needs_parens(node: UnaryLike<'_, '_>) -> bool {
|
||||
/// Returns `true` if an expression with lower precedence than an update expression needs parentheses.
|
||||
///
|
||||
/// This is generally the case if the expression is used in a left hand side, or primary expression context.
|
||||
fn update_or_lower_expression_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn update_or_lower_expression_needs_parens(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
AstNodes::TSNonNullExpression(_)
|
||||
| AstNodes::StaticMemberExpression(_)
|
||||
@@ -1110,7 +1106,7 @@ pub enum FirstInStatementMode {
|
||||
/// Returns `true` if the node at `span` starts a statement (or an arrow body / `export default`, per `mode`).
|
||||
fn is_first_in_statement(
|
||||
span: Span,
|
||||
parent: &AstNodes<'_>,
|
||||
parent: &AstNodes<'_, '_>,
|
||||
mode: FirstInStatementMode,
|
||||
f: &JsFormatter<'_, '_>,
|
||||
) -> bool {
|
||||
@@ -1132,9 +1128,9 @@ fn is_first_in_statement(
|
||||
/// since the node then starts nothing outside them.
|
||||
fn leftmost_container<'b, 'a>(
|
||||
mut span: Span,
|
||||
parent: &'b AstNodes<'a>,
|
||||
parent: &'b AstNodes<'b, 'a>,
|
||||
f: &JsFormatter<'_, '_>,
|
||||
) -> Option<(&'b AstNodes<'a>, Span)> {
|
||||
) -> Option<(&'b AstNodes<'b, 'a>, Span)> {
|
||||
for ancestor in parent.ancestors() {
|
||||
// The chain and its element share a span, `left_of` would skip the element
|
||||
let is_led = matches!(ancestor, AstNodes::ChainExpression(_))
|
||||
@@ -1152,7 +1148,7 @@ fn leftmost_container<'b, 'a>(
|
||||
|
||||
/// Whether `node` is printed inside parentheses,
|
||||
/// including the ones `needs_parentheses` does not report.
|
||||
fn prints_own_parens(node: &AstNodes<'_>, f: &JsFormatter<'_, '_>) -> bool {
|
||||
fn prints_own_parens(node: &AstNodes<'_, '_>, f: &JsFormatter<'_, '_>) -> bool {
|
||||
node.needs_parentheses(f)
|
||||
// A type cast target prints them itself, see `utils/typecast.rs`
|
||||
|| is_cast_target(node.span(), f)
|
||||
@@ -1161,7 +1157,7 @@ fn prints_own_parens(node: &AstNodes<'_>, f: &JsFormatter<'_, '_>) -> bool {
|
||||
&& matches!(node.parent(), AstNodes::ArrowFunctionExpression(_)))
|
||||
}
|
||||
|
||||
fn await_or_yield_needs_parens(span: Span, node: &AstNodes<'_>) -> bool {
|
||||
fn await_or_yield_needs_parens(span: Span, node: &AstNodes<'_, '_>) -> bool {
|
||||
if matches!(
|
||||
node,
|
||||
AstNodes::UnaryExpression(_)
|
||||
@@ -1182,7 +1178,7 @@ fn await_or_yield_needs_parens(span: Span, node: &AstNodes<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn ts_as_or_satisfies_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn ts_as_or_satisfies_needs_parens(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
matches!(
|
||||
parent,
|
||||
AstNodes::ConditionalExpression(_)
|
||||
@@ -1191,14 +1187,14 @@ fn ts_as_or_satisfies_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
) || type_cast_like_needs_parens(span, parent)
|
||||
}
|
||||
|
||||
fn is_class_extends(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn is_class_extends(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
if let AstNodes::Class(c) = parent {
|
||||
return c.heritage().is_some_and(|heritage| heritage.expression().span() == span);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
if is_class_extends(span, parent) {
|
||||
return true;
|
||||
}
|
||||
@@ -1225,7 +1221,7 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_>) -> boo
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, JSXElement<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, JSXElement<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
@@ -1235,7 +1231,7 @@ impl NeedsParentheses<'_> for AstNode<'_, JSXElement<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, JSXFragment<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, JSXFragment<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
if f.comments().is_marked_as_type_cast_node(self) {
|
||||
return false;
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxc_span::GetSpan;
|
||||
|
||||
use super::NeedsParentheses;
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::JsFormatter,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
/// `(| number)[]`) don't exist — the parser unwraps them. In oxc's AST, they do exist, so inner
|
||||
/// types need to "see through" them when checking `needs_parentheses` to get the correct parent
|
||||
/// context.
|
||||
fn effective_parent<'a>(parent: &'a AstNodes<'a>) -> &'a AstNodes<'a> {
|
||||
fn effective_parent<'b, 'a>(parent: &'b AstNodes<'b, 'a>) -> &'b AstNodes<'b, 'a> {
|
||||
match parent {
|
||||
AstNodes::TSUnionType(union) if union.types.len() <= 1 => effective_parent(union.parent()),
|
||||
AstNodes::TSIntersectionType(intersection) if intersection.types.len() <= 1 => {
|
||||
@@ -23,17 +23,17 @@ fn effective_parent<'a>(parent: &'a AstNodes<'a>) -> &'a AstNodes<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSType<'_>> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, '_>) -> bool {
|
||||
match self.as_ast_nodes() {
|
||||
AstNodes::TSFunctionType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSInferType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSConstructorType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSUnionType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSIntersectionType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSConditionalType(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSTypeOperator(it) => it.needs_parentheses(f),
|
||||
AstNodes::TSTypeQuery(it) => it.needs_parentheses(f),
|
||||
match self.as_kind() {
|
||||
AstNodeKind::TSFunctionType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSInferType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSConstructorType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSUnionType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSIntersectionType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSConditionalType(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSTypeOperator(it) => it.needs_parentheses(f),
|
||||
AstNodeKind::TSTypeQuery(it) => it.needs_parentheses(f),
|
||||
_ => {
|
||||
// TODO: incomplete
|
||||
false
|
||||
@@ -42,7 +42,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSFunctionType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSFunctionType<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
let parent = effective_parent(self.parent());
|
||||
@@ -62,7 +62,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSFunctionType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSInferType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSInferType<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
let parent = effective_parent(self.parent());
|
||||
match parent {
|
||||
@@ -73,7 +73,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSInferType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSConstructorType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSConstructorType<'_>> {
|
||||
#[inline]
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
function_like_type_needs_parentheses(
|
||||
@@ -84,7 +84,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSConstructorType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSUnionType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSUnionType<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
// Single-member unions are transparent (formatted as just the member).
|
||||
// In Prettier/Babel, these don't exist in the AST.
|
||||
@@ -109,7 +109,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSUnionType<'_>> {
|
||||
/// Ported from Biome's function_like_type_needs_parentheses
|
||||
fn function_like_type_needs_parentheses<'a>(
|
||||
span: Span,
|
||||
parent: &'a AstNodes<'a>,
|
||||
parent: &'a AstNodes<'_, 'a>,
|
||||
return_type: Option<&'a TSTypeAnnotation<'a>>,
|
||||
) -> bool {
|
||||
match parent {
|
||||
@@ -159,7 +159,7 @@ fn operator_type_or_higher_needs_parens(span: Span, parent: &AstNodes) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSIntersectionType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSIntersectionType<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
// Single-member intersections are transparent (formatted as just the member).
|
||||
if self.types.len() <= 1 {
|
||||
@@ -173,7 +173,7 @@ impl NeedsParentheses<'_> for AstNode<'_, TSIntersectionType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSConditionalType<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSConditionalType<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
let parent = effective_parent(self.parent());
|
||||
match parent {
|
||||
@@ -193,13 +193,13 @@ impl NeedsParentheses<'_> for AstNode<'_, TSConditionalType<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSTypeOperator<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSTypeOperator<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
operator_type_or_higher_needs_parens(self.span(), effective_parent(self.parent()))
|
||||
}
|
||||
}
|
||||
|
||||
impl NeedsParentheses<'_> for AstNode<'_, TSTypeQuery<'_>> {
|
||||
impl NeedsParentheses<'_> for AstNode<'_, '_, TSTypeQuery<'_>> {
|
||||
fn needs_parentheses(&self, _f: &JsFormatter<'_, '_>) -> bool {
|
||||
match effective_parent(self.parent()) {
|
||||
AstNodes::TSArrayType(_) => true,
|
||||
|
||||
@@ -12,13 +12,13 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct ArrayElementList<'a, 'b> {
|
||||
elements: &'b AstNode<'a, ArenaVec<'a, ArrayExpressionElement<'a>>>,
|
||||
elements: &'b AstNode<'b, 'a, ArenaVec<'a, ArrayExpressionElement<'a>>>,
|
||||
group_id: Option<GroupId>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ArrayElementList<'a, 'b> {
|
||||
pub fn new(
|
||||
elements: &'b AstNode<'a, ArenaVec<'a, ArrayExpressionElement<'a>>>,
|
||||
elements: &'b AstNode<'b, 'a, ArenaVec<'a, ArrayExpressionElement<'a>>>,
|
||||
group_id: GroupId,
|
||||
) -> Self {
|
||||
Self { elements, group_id: Some(group_id) }
|
||||
|
||||
@@ -11,12 +11,12 @@ pub struct FormatArrayExpressionOptions {
|
||||
}
|
||||
|
||||
pub struct FormatArrayExpression<'a, 'b> {
|
||||
array: &'b AstNode<'a, ArrayExpression<'a>>,
|
||||
array: &'b AstNode<'b, 'a, ArrayExpression<'a>>,
|
||||
options: FormatArrayExpressionOptions,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatArrayExpression<'a, 'b> {
|
||||
pub fn new(array: &'b AstNode<'a, ArrayExpression<'a>>) -> Self {
|
||||
pub fn new(array: &'b AstNode<'b, 'a, ArrayExpression<'a>>) -> Self {
|
||||
Self { array, options: FormatArrayExpressionOptions::default() }
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatArrayExpression<'a, '_> {
|
||||
let should_expand = !self.options.is_force_flat_mode
|
||||
&& (should_break(self.array) || has_trailing_line_comment());
|
||||
|
||||
let elements = ArrayElementList::new(self.array.elements(), group_id);
|
||||
let array_elements = self.array.elements();
|
||||
let elements = ArrayElementList::new(&array_elements, group_id);
|
||||
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -13,10 +13,10 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
struct FormatArrayPattern<'a, 'b>(&'b AstNode<'a, ArrayPattern<'a>>);
|
||||
struct FormatArrayPattern<'a, 'b>(&'b AstNode<'b, 'a, ArrayPattern<'a>>);
|
||||
|
||||
impl<'a> Deref for FormatArrayPattern<'a, '_> {
|
||||
type Target = AstNode<'a, ArrayPattern<'a>>;
|
||||
impl<'a, 'b> Deref for FormatArrayPattern<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, ArrayPattern<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
@@ -38,7 +38,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatArrayPattern<'a, '_> {
|
||||
write_array_node(
|
||||
self.span(),
|
||||
self.rest.as_ref().map(|rest| rest.span.start),
|
||||
self.elements().iter().map(AstNode::as_ref),
|
||||
self.elements().iter().map(|element| element.as_ref()),
|
||||
f,
|
||||
);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatArrayPattern<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ArrayPattern<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ArrayPattern<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
FormatArrayPattern(self).fmt(f);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxc_formatter_core::{Format, SourceText};
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
format_args,
|
||||
formatter::{JsFormatContext, JsFormatter, prelude::*, trivia::FormatTrailingComments},
|
||||
options::FormatTrailingCommas,
|
||||
@@ -23,7 +23,7 @@ use super::{
|
||||
sequence_expression::sequence_leading_comments_start,
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ArrowFunctionExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ArrowFunctionExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let options = FormatJsArrowFunctionExpressionOptions {
|
||||
// Handed over by `WithAssignmentLayout` when this arrow is the RHS of an assignment-like;
|
||||
@@ -37,7 +37,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ArrowFunctionExpression<'a>> {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FormatJsArrowFunctionExpression<'a, 'b> {
|
||||
arrow: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
arrow: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
options: FormatJsArrowFunctionExpressionOptions,
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ pub enum FunctionCacheMode {
|
||||
|
||||
impl<'a, 'b> FormatJsArrowFunctionExpression<'a, 'b> {
|
||||
pub fn new_with_options(
|
||||
arrow: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
arrow: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
options: FormatJsArrowFunctionExpressionOptions,
|
||||
) -> Self {
|
||||
Self { arrow, options }
|
||||
@@ -229,7 +229,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatJsArrowFunctionExpression<'a,
|
||||
|
||||
enum ArrowFunctionLayout<'a, 'b> {
|
||||
/// Arrow function with a non-arrow function body
|
||||
Single(&'b AstNode<'a, ArrowFunctionExpression<'a>>),
|
||||
Single(&'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>),
|
||||
|
||||
/// A chain of at least two arrow functions.
|
||||
///
|
||||
@@ -253,7 +253,7 @@ impl<'a, 'b> ArrowFunctionLayout<'a, 'b> {
|
||||
/// Determines the layout for the passed arrow function. See [ArrowFunctionLayout] for a description
|
||||
/// of the different layouts.
|
||||
fn for_arrow(
|
||||
arrow: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
arrow: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
options: FormatJsArrowFunctionExpressionOptions,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> ArrowFunctionLayout<'a, 'b> {
|
||||
@@ -268,13 +268,13 @@ impl<'a, 'b> ArrowFunctionLayout<'a, 'b> {
|
||||
|
||||
loop {
|
||||
if is_non_grouped_or_grouped_last_argument
|
||||
&& let AstNodes::ArrowFunctionExpression(next) = current.body().as_ast_nodes()
|
||||
&& let AstNodeKind::ArrowFunctionExpression(next) = current.body().as_kind()
|
||||
// A type cast keeps its parens: `(a) => /** @type {T} */ ((b) => c)`
|
||||
&& !is_cast_target(next.span(), f)
|
||||
{
|
||||
should_break = should_break || Self::should_break_chain(current);
|
||||
|
||||
should_break = should_break || Self::should_break_chain(next);
|
||||
should_break = should_break || Self::should_break_chain(&next);
|
||||
|
||||
if head.is_none() {
|
||||
head = Some(current);
|
||||
@@ -282,7 +282,8 @@ impl<'a, 'b> ArrowFunctionLayout<'a, 'b> {
|
||||
middle.push(current);
|
||||
}
|
||||
|
||||
current = next;
|
||||
// In the arena, as it is the parent of the next arrow function
|
||||
current = next.in_arena(f.allocator());
|
||||
continue;
|
||||
}
|
||||
break match head {
|
||||
@@ -370,14 +371,14 @@ pub fn is_multiline_template_starting_on_same_line(
|
||||
|
||||
struct ArrowChain<'a, 'b> {
|
||||
/// The top most arrow function in the chain
|
||||
head: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
head: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
|
||||
/// The arrow functions in the chain that are neither the first nor the last.
|
||||
/// Empty for chains consisting only of two arrow functions.
|
||||
middle: Vec<&'b AstNode<'a, ArrowFunctionExpression<'a>>>,
|
||||
middle: Vec<&'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>>,
|
||||
|
||||
/// The last arrow function in the chain
|
||||
tail: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
tail: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
|
||||
options: FormatJsArrowFunctionExpressionOptions,
|
||||
|
||||
@@ -387,7 +388,7 @@ struct ArrowChain<'a, 'b> {
|
||||
|
||||
impl<'a, 'b> ArrowChain<'a, 'b> {
|
||||
/// Returns an iterator over all arrow functions in this chain
|
||||
fn arrows(&self) -> impl Iterator<Item = &'b AstNode<'a, ArrowFunctionExpression<'a>>> {
|
||||
fn arrows(&self) -> impl Iterator<Item = &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>> {
|
||||
use std::iter::once;
|
||||
once(self.head).chain(self.middle.iter().copied()).chain(once(self.tail))
|
||||
}
|
||||
@@ -673,7 +674,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ArrowChain<'a, '_> {
|
||||
/// An own-line comment cannot appear before the `=>` (no line terminator is allowed there),
|
||||
/// so any hit in this range belongs to the body.
|
||||
fn has_own_line_comment_before_body<'a>(
|
||||
arrow: &AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
arrow: &AstNode<'_, 'a, ArrowFunctionExpression<'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let signature_end =
|
||||
@@ -681,13 +682,13 @@ fn has_own_line_comment_before_body<'a>(
|
||||
f.comments().has_own_line_comment_in_range(signature_end, arrow.body().span().start)
|
||||
}
|
||||
|
||||
fn should_add_parens(expression: &AstNode<'_, Expression<'_>>) -> bool {
|
||||
fn should_add_parens(expression: AstNode<'_, '_, Expression<'_>>) -> bool {
|
||||
// Add parentheses to avoid confusion between `a => b ? c : d` and `a <= b ? c : d`
|
||||
// but only if the body isn't an object/function or class expression because parentheses are always required in that
|
||||
// case and added by the object expression itself
|
||||
if matches!(&**expression, Expression::ConditionalExpression(_)) {
|
||||
if matches!(&*expression, Expression::ConditionalExpression(_)) {
|
||||
!matches!(
|
||||
ExpressionLeftSide::leftmost(expression).as_ref(),
|
||||
ExpressionLeftSide::leftmost(&expression),
|
||||
Expression::ObjectExpression(_)
|
||||
| Expression::FunctionExpression(_)
|
||||
| Expression::ClassExpression(_)
|
||||
@@ -707,7 +708,7 @@ fn has_rest_object_or_array_parameter(params: &FormalParameters) -> bool {
|
||||
/// Formats the parameters and return type annotation without any soft line breaks if `is_grouped_call_argument` is `true`
|
||||
/// so that the parameters and return type are kept on the same line.
|
||||
fn format_signature<'a, 'b>(
|
||||
arrow: &'b AstNode<'a, ArrowFunctionExpression<'a>>,
|
||||
arrow: &'b AstNode<'b, 'a, ArrowFunctionExpression<'a>>,
|
||||
is_grouped_call_argument: bool,
|
||||
is_first_in_chain: bool,
|
||||
cache_mode: FunctionCacheMode,
|
||||
@@ -718,7 +719,7 @@ fn format_signature<'a, 'b>(
|
||||
arrow.r#async().then_some("async "),
|
||||
arrow.type_parameters(),
|
||||
arrow.params(),
|
||||
arrow.return_type().map(FormatNodeWithoutTrailingComments),
|
||||
arrow.return_type().as_ref().map(FormatNodeWithoutTrailingComments),
|
||||
))
|
||||
.fmt(f);
|
||||
});
|
||||
|
||||
@@ -9,12 +9,12 @@ use crate::{
|
||||
write,
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSAsExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSAsExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let is_callee_or_object = is_callee_or_object_context(self.span(), self.parent());
|
||||
format_as_or_satisfies_expression(
|
||||
self.expression(),
|
||||
self.type_annotation(),
|
||||
&self.expression(),
|
||||
&self.type_annotation(),
|
||||
is_callee_or_object,
|
||||
"as",
|
||||
f,
|
||||
@@ -22,12 +22,12 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSAsExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSSatisfiesExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSSatisfiesExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let is_callee_or_object = is_callee_or_object_context(self.span(), self.parent());
|
||||
format_as_or_satisfies_expression(
|
||||
self.expression(),
|
||||
self.type_annotation(),
|
||||
&self.expression(),
|
||||
&self.type_annotation(),
|
||||
is_callee_or_object,
|
||||
"satisfies",
|
||||
f,
|
||||
@@ -36,8 +36,8 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSSatisfiesExpression<'a>> {
|
||||
}
|
||||
|
||||
fn format_as_or_satisfies_expression<'a>(
|
||||
expression: &AstNode<'a, Expression>,
|
||||
type_annotation: &AstNode<'a, TSType>,
|
||||
expression: &AstNode<'_, 'a, Expression>,
|
||||
type_annotation: &AstNode<'_, 'a, TSType>,
|
||||
is_callee_or_object: bool,
|
||||
operation: &'static str,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
@@ -131,7 +131,7 @@ fn format_as_or_satisfies_expression<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_callee_or_object_context(span: Span, parent: &AstNodes<'_>) -> bool {
|
||||
fn is_callee_or_object_context(span: Span, parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
// Static member
|
||||
AstNodes::StaticMemberExpression(_) => true,
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::{
|
||||
};
|
||||
|
||||
enum AssignmentTargetPropertyListNode<'a, 'b> {
|
||||
Property(&'b AstNode<'a, AssignmentTargetProperty<'a>>),
|
||||
Rest(&'b AstNode<'a, AssignmentTargetRest<'a>>),
|
||||
Property(AstNode<'b, 'a, AssignmentTargetProperty<'a>>),
|
||||
Rest(AstNode<'b, 'a, AssignmentTargetRest<'a>>),
|
||||
}
|
||||
|
||||
impl GetSpan for AssignmentTargetPropertyListNode<'_, '_> {
|
||||
@@ -33,8 +33,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AssignmentTargetPropertyListNode<'a
|
||||
}
|
||||
|
||||
struct AssignmentTargetPropertyListIter<'a, 'b> {
|
||||
properties: AstNodeIterator<'a, AssignmentTargetProperty<'a>>,
|
||||
rest: Option<&'b AstNode<'a, AssignmentTargetRest<'a>>>,
|
||||
properties: AstNodeIterator<'b, 'a, AssignmentTargetProperty<'a>>,
|
||||
rest: Option<AstNode<'b, 'a, AssignmentTargetRest<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Iterator for AssignmentTargetPropertyListIter<'a, 'b> {
|
||||
@@ -50,14 +50,14 @@ impl<'a, 'b> Iterator for AssignmentTargetPropertyListIter<'a, 'b> {
|
||||
}
|
||||
|
||||
pub struct AssignmentTargetPropertyList<'a, 'b> {
|
||||
properties: &'b AstNode<'a, ArenaVec<'a, AssignmentTargetProperty<'a>>>,
|
||||
rest: Option<&'b AstNode<'a, AssignmentTargetRest<'a>>>,
|
||||
properties: AstNode<'b, 'a, ArenaVec<'a, AssignmentTargetProperty<'a>>>,
|
||||
rest: Option<AstNode<'b, 'a, AssignmentTargetRest<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> AssignmentTargetPropertyList<'a, 'b> {
|
||||
pub fn new(
|
||||
properties: &'b AstNode<'a, ArenaVec<'a, AssignmentTargetProperty<'a>>>,
|
||||
rest: Option<&'b AstNode<'a, AssignmentTargetRest<'a>>>,
|
||||
properties: AstNode<'b, 'a, ArenaVec<'a, AssignmentTargetProperty<'a>>>,
|
||||
rest: Option<AstNode<'b, 'a, AssignmentTargetRest<'a>>>,
|
||||
) -> Self {
|
||||
Self { properties, rest }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::precedence::{GetPrecedence, Precedence};
|
||||
|
||||
use crate::{
|
||||
Format,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::JsFormatter,
|
||||
print::unary_argument_takes_comment_parens,
|
||||
utils::typecast::is_cast_target,
|
||||
@@ -56,13 +57,13 @@ impl BinaryLikeOperator {
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BinaryLikeExpression<'a, 'b> {
|
||||
LogicalExpression(&'b AstNode<'a, LogicalExpression<'a>>),
|
||||
BinaryExpression(&'b AstNode<'a, BinaryExpression<'a>>),
|
||||
LogicalExpression(&'b AstNode<'b, 'a, LogicalExpression<'a>>),
|
||||
BinaryExpression(&'b AstNode<'b, 'a, BinaryExpression<'a>>),
|
||||
}
|
||||
|
||||
impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
/// Returns the left hand side of the binary expression.
|
||||
fn left(&self) -> &'b AstNode<'a, Expression<'a>> {
|
||||
fn left(&self) -> AstNode<'b, 'a, Expression<'a>> {
|
||||
match self {
|
||||
Self::LogicalExpression(expr) => expr.left(),
|
||||
Self::BinaryExpression(expr) => expr.left(),
|
||||
@@ -70,14 +71,14 @@ impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
}
|
||||
|
||||
/// Returns the right hand side of the binary expression.
|
||||
pub fn right(&self) -> &'b AstNode<'a, Expression<'a>> {
|
||||
pub fn right(&self) -> AstNode<'b, 'a, Expression<'a>> {
|
||||
match self {
|
||||
Self::LogicalExpression(expr) => expr.right(),
|
||||
Self::BinaryExpression(expr) => expr.right(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> &AstNodes<'a> {
|
||||
pub fn parent(&self) -> &AstNodes<'b, 'a> {
|
||||
match self {
|
||||
Self::LogicalExpression(expr) => expr.parent(),
|
||||
Self::BinaryExpression(expr) => expr.parent(),
|
||||
@@ -93,7 +94,7 @@ impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
/// if (true) { a + b } // false
|
||||
/// switch (a + b) {} // true
|
||||
/// ```
|
||||
fn is_inside_condition(&self, parent: &AstNodes<'_>) -> bool {
|
||||
fn is_inside_condition(&self, parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
AstNodes::IfStatement(stmt) => stmt.test().span() == self.span(),
|
||||
AstNodes::DoWhileStatement(stmt) => stmt.test().span() == self.span(),
|
||||
@@ -125,6 +126,20 @@ impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
should_flatten(self.operator(), left_operator) && !is_cast_target(left.span(), f)
|
||||
}
|
||||
|
||||
/// Returns the left hand side, which [Self::can_flatten] checked to be a binary like expression.
|
||||
///
|
||||
/// It is in the arena, as it is the parent of the sides formatted after it,
|
||||
/// so a chain is walked without recursion.
|
||||
fn flattened_left(&self, allocator: &'b Allocator) -> Self {
|
||||
match self.left().as_kind() {
|
||||
AstNodeKind::LogicalExpression(left) => {
|
||||
Self::LogicalExpression(left.in_arena(allocator))
|
||||
}
|
||||
AstNodeKind::BinaryExpression(left) => Self::BinaryExpression(left.in_arena(allocator)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_inline_logical_expression(&self) -> bool {
|
||||
let Self::LogicalExpression(logical) = self else {
|
||||
return false;
|
||||
@@ -146,7 +161,7 @@ impl<'a, 'b> BinaryLikeExpression<'a, 'b> {
|
||||
/// There are some cases where the indentation is done by the parent, so if the parent is already doing
|
||||
/// the indentation, then there's no need to do a second indentation.
|
||||
/// [Prettier applies]: <https://github.com/prettier/prettier/blob/b0201e01ef99db799eb3716f15b7dfedb0a2e62b/src/language-js/print/binaryish.js#L122-L125>
|
||||
pub fn should_not_indent_if_parent_indents(&self, parent: &AstNodes<'a>) -> bool {
|
||||
pub fn should_not_indent_if_parent_indents(&self, parent: &AstNodes<'_, 'a>) -> bool {
|
||||
match parent {
|
||||
AstNodes::ReturnStatement(_)
|
||||
| AstNodes::ThrowStatement(_)
|
||||
@@ -186,13 +201,13 @@ impl GetSpan for BinaryLikeExpression<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> TryFrom<&'b AstNode<'a, Expression<'a>>> for BinaryLikeExpression<'a, 'b> {
|
||||
impl<'a, 'b> TryFrom<&'b AstNodeKind<'b, 'a>> for BinaryLikeExpression<'a, 'b> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &'b AstNode<'a, Expression<'a>>) -> Result<Self, Self::Error> {
|
||||
match value.as_ast_nodes() {
|
||||
AstNodes::LogicalExpression(expr) => Ok(Self::LogicalExpression(expr)),
|
||||
AstNodes::BinaryExpression(expr) => Ok(Self::BinaryExpression(expr)),
|
||||
fn try_from(value: &'b AstNodeKind<'b, 'a>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
AstNodeKind::LogicalExpression(expr) => Ok(Self::LogicalExpression(expr)),
|
||||
AstNodeKind::BinaryExpression(expr) => Ok(Self::BinaryExpression(expr)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
@@ -352,32 +367,32 @@ fn format_flattened_logical_expression<'a>(
|
||||
inside_condition: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
fn format_recursive<'a>(
|
||||
binary: BinaryLikeExpression<'a, '_>,
|
||||
inside_condition: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
let left = binary.left();
|
||||
|
||||
if binary.can_flatten(f) {
|
||||
// Recursively format nested binary expressions
|
||||
format_recursive(BinaryLikeExpression::try_from(left).unwrap(), inside_condition, f);
|
||||
} else {
|
||||
// Format the left terminal
|
||||
write!(f, [group(left)]);
|
||||
}
|
||||
|
||||
// Format the right side with operator
|
||||
BinaryLeftOrRightSide::Right { parent: binary, inside_condition }.fmt(f);
|
||||
let mut current = binary;
|
||||
let mut depth = 0;
|
||||
while current.can_flatten(f) {
|
||||
current = current.flattened_left(f.allocator());
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
format_recursive(binary, inside_condition, f);
|
||||
// Format the left terminal
|
||||
write!(f, [group(¤t.left())]);
|
||||
|
||||
// Format the right sides with operator, from the innermost one back up `depth` levels to `binary`
|
||||
BinaryLeftOrRightSide::Right { parent: current, inside_condition }.fmt(f);
|
||||
for _ in 0..depth {
|
||||
current = match current.parent() {
|
||||
AstNodes::LogicalExpression(expr) => BinaryLikeExpression::LogicalExpression(expr),
|
||||
AstNodes::BinaryExpression(expr) => BinaryLikeExpression::BinaryExpression(expr),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
BinaryLeftOrRightSide::Right { parent: current, inside_condition }.fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
match self {
|
||||
Self::Left { parent } => write!(f, group(parent.left())),
|
||||
Self::Left { parent } => write!(f, group(&parent.left())),
|
||||
Self::Right {
|
||||
parent: binary_like_expression,
|
||||
inside_condition: inside_parenthesis,
|
||||
@@ -422,8 +437,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
// Based on Prettier's rebalancing logic for LogicalExpressions:
|
||||
// <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/parse/postprocess/index.js#L64-L69>
|
||||
loop {
|
||||
if let AstNodes::LogicalExpression(right_logical) =
|
||||
binary_like_expression.right().as_ast_nodes()
|
||||
if let AstNodeKind::LogicalExpression(right_logical) =
|
||||
binary_like_expression.right().as_kind()
|
||||
&& let Some(operator) = logical_operator
|
||||
&& operator == right_logical.operator()
|
||||
// A type cast keeps its parens: `a && /** @type {T} */ (b && c)`
|
||||
@@ -435,12 +450,13 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
// we need to recursively format it inline.
|
||||
// This way, we can ensure that all parts are in the same group.
|
||||
// We format directly instead of allocating a Vec via `split_into_left_and_right_sides`.
|
||||
let right_binary = BinaryLikeExpression::LogicalExpression(right_logical);
|
||||
let right_binary = BinaryLikeExpression::LogicalExpression(&right_logical);
|
||||
if right_binary.can_flatten(f) {
|
||||
// Format the nested logical expression inline without Vec allocation
|
||||
format_flattened_logical_expression(
|
||||
// SAFETY: `can_flatten()` checked it is a binary like expression
|
||||
BinaryLikeExpression::try_from(right_logical.left()).unwrap(),
|
||||
BinaryLikeExpression::try_from(&right_logical.left().as_kind())
|
||||
.unwrap(),
|
||||
*inside_parenthesis,
|
||||
f,
|
||||
);
|
||||
@@ -448,7 +464,10 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
right_logical.left().fmt(f);
|
||||
}
|
||||
|
||||
binary_like_expression = right_binary;
|
||||
// The parent of the nodes formatted after the loop
|
||||
binary_like_expression = BinaryLikeExpression::LogicalExpression(
|
||||
right_logical.in_arena(f.allocator()),
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -463,7 +482,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
if !right.is_jsx()
|
||||
&& f.comments().has_leading_own_line_comment(right.span().start)
|
||||
{
|
||||
return write!(f, soft_line_indent_or_space(right));
|
||||
return write!(f, soft_line_indent_or_space(&right));
|
||||
}
|
||||
} else {
|
||||
format_operator_and_break(
|
||||
@@ -473,12 +492,13 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
);
|
||||
}
|
||||
|
||||
write!(f, right);
|
||||
write!(f, &right);
|
||||
});
|
||||
|
||||
// Cache as_ast_nodes() calls to avoid repeated conversions
|
||||
let left_ast_nodes = binary_like_expression.left().as_ast_nodes();
|
||||
let right_ast_nodes = right.as_ast_nodes();
|
||||
let left_kind = binary_like_expression.left().as_kind();
|
||||
let right_kind = right.as_kind();
|
||||
let left_ast_nodes = &left_kind.as_ast_nodes();
|
||||
let right_ast_nodes = &right_kind.as_ast_nodes();
|
||||
|
||||
// Doesn't match prettier that only distinguishes between logical and binary
|
||||
let should_group =
|
||||
@@ -489,11 +509,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BinaryLeftOrRightSide<'a, '_> {
|
||||
|| is_same_binary_expression_kind(binary_like_expression, right_ast_nodes)
|
||||
|| (*inside_parenthesis && logical_operator.is_some()));
|
||||
|
||||
match left_ast_nodes {
|
||||
AstNodes::LogicalExpression(logical) => {
|
||||
match left_kind {
|
||||
AstNodeKind::LogicalExpression(logical) => {
|
||||
logical.format_trailing_comments(f);
|
||||
}
|
||||
AstNodes::BinaryExpression(binary) => {
|
||||
AstNodeKind::BinaryExpression(binary) => {
|
||||
binary.format_trailing_comments(f);
|
||||
}
|
||||
_ => {}
|
||||
@@ -557,42 +577,29 @@ impl GetSpan for BinaryLeftOrRightSide<'_, '_> {
|
||||
/// It then traverses upwards from the left most node and creates [BinaryLeftOrRightSide::Right]s for
|
||||
/// every [BinaryLikeExpression] until it reaches the root again.
|
||||
fn split_into_left_and_right_sides<'a, 'b>(
|
||||
binary: BinaryLikeExpression<'a, 'b>,
|
||||
mut binary: BinaryLikeExpression<'a, 'b>,
|
||||
inside_condition: bool,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> Vec<BinaryLeftOrRightSide<'a, 'b>> {
|
||||
fn split_into_left_and_right_sides_inner<'a, 'b>(
|
||||
binary: BinaryLikeExpression<'a, 'b>,
|
||||
inside_condition: bool,
|
||||
items: &mut Vec<BinaryLeftOrRightSide<'a, 'b>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) {
|
||||
let left = binary.left();
|
||||
|
||||
if binary.can_flatten(f) {
|
||||
// We can flatten the left hand side, so we need to check if we have a nested binary expression
|
||||
// that we can flatten.
|
||||
split_into_left_and_right_sides_inner(
|
||||
// SAFETY: `left` is guaranteed to be a valid binary like expression in `can_flatten()`.
|
||||
BinaryLikeExpression::try_from(left).unwrap(),
|
||||
inside_condition,
|
||||
items,
|
||||
f,
|
||||
);
|
||||
} else {
|
||||
items.push(BinaryLeftOrRightSide::Left { parent: binary });
|
||||
}
|
||||
|
||||
items.push(BinaryLeftOrRightSide::Right { parent: binary, inside_condition });
|
||||
}
|
||||
|
||||
// Stores the left and right parts of the binary expression in sequence (rather than nested as they
|
||||
// appear in the tree).
|
||||
// Stores the left and right parts of the binary expression in sequence
|
||||
// (rather than nested as they appear in the tree), walking down the left sides in reverse order.
|
||||
// `with_capacity(2)` because we expect at least 2 items (left and right).
|
||||
let mut items = Vec::with_capacity(2);
|
||||
|
||||
split_into_left_and_right_sides_inner(binary, inside_condition, &mut items, f);
|
||||
loop {
|
||||
items.push(BinaryLeftOrRightSide::Right { parent: binary, inside_condition });
|
||||
|
||||
if !binary.can_flatten(f) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We can flatten the left hand side,
|
||||
// so we need to check if we have a nested binary expression that we can flatten.
|
||||
binary = binary.flattened_left(f.allocator());
|
||||
}
|
||||
|
||||
items.push(BinaryLeftOrRightSide::Left { parent: binary });
|
||||
items.reverse();
|
||||
items
|
||||
}
|
||||
|
||||
@@ -627,7 +634,7 @@ fn format_operator_and_break(
|
||||
/// these cases the decide to actually break on a new line and indent it.
|
||||
///
|
||||
/// This function checks what the parents adheres to this behaviour
|
||||
fn should_indent_if_parent_inlines(parent: &AstNodes<'_>) -> bool {
|
||||
fn should_indent_if_parent_inlines(parent: &AstNodes<'_, '_>) -> bool {
|
||||
matches!(
|
||||
parent,
|
||||
AstNodes::AssignmentExpression(_)
|
||||
@@ -639,7 +646,7 @@ fn should_indent_if_parent_inlines(parent: &AstNodes<'_>) -> bool {
|
||||
|
||||
fn is_same_binary_expression_kind(
|
||||
binary: BinaryLikeExpression<'_, '_>,
|
||||
other: &AstNodes<'_>,
|
||||
other: &AstNodes<'_, '_>,
|
||||
) -> bool {
|
||||
match binary {
|
||||
BinaryLikeExpression::LogicalExpression(_) => {
|
||||
|
||||
@@ -10,13 +10,13 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct BindingPropertyList<'a, 'b> {
|
||||
properties: &'b AstNode<'a, ArenaVec<'a, BindingProperty<'a>>>,
|
||||
rest: Option<&'b AstNode<'a, BindingRestElement<'a>>>,
|
||||
properties: AstNode<'b, 'a, ArenaVec<'a, BindingProperty<'a>>>,
|
||||
rest: Option<AstNode<'b, 'a, BindingRestElement<'a>>>,
|
||||
}
|
||||
|
||||
enum BindingPropertyListNode<'a, 'b> {
|
||||
Property(&'b AstNode<'a, BindingProperty<'a>>),
|
||||
Rest(&'b AstNode<'a, BindingRestElement<'a>>),
|
||||
Property(AstNode<'b, 'a, BindingProperty<'a>>),
|
||||
Rest(AstNode<'b, 'a, BindingRestElement<'a>>),
|
||||
}
|
||||
|
||||
impl GetSpan for BindingPropertyListNode<'_, '_> {
|
||||
@@ -38,8 +38,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for BindingPropertyListNode<'a, '_> {
|
||||
}
|
||||
|
||||
struct BindingPropertyListIter<'a, 'b> {
|
||||
properties: AstNodeIterator<'a, BindingProperty<'a>>,
|
||||
rest: Option<&'b AstNode<'a, BindingRestElement<'a>>>,
|
||||
properties: AstNodeIterator<'b, 'a, BindingProperty<'a>>,
|
||||
rest: Option<AstNode<'b, 'a, BindingRestElement<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Iterator for BindingPropertyListIter<'a, 'b> {
|
||||
@@ -56,8 +56,8 @@ impl<'a, 'b> Iterator for BindingPropertyListIter<'a, 'b> {
|
||||
|
||||
impl<'a, 'b> BindingPropertyList<'a, 'b> {
|
||||
pub fn new(
|
||||
properties: &'b AstNode<'a, ArenaVec<'a, BindingProperty<'a>>>,
|
||||
rest: Option<&'b AstNode<'a, BindingRestElement<'a>>>,
|
||||
properties: AstNode<'b, 'a, ArenaVec<'a, BindingProperty<'a>>>,
|
||||
rest: Option<AstNode<'b, 'a, BindingRestElement<'a>>>,
|
||||
) -> Self {
|
||||
Self { properties, rest }
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ use crate::{
|
||||
write,
|
||||
};
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, Statement<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, Statement<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
f.join_nodes_with_hardline()
|
||||
.entries(self.iter().filter(|stmt| !is_dropped_statement(stmt.as_ref())));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, BlockStatement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, BlockStatement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, "{");
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn is_empty_block(block: &[Statement<'_>]) -> bool {
|
||||
/// * empty block that is the 'bons' or 'alt' of an if statement: two lines `{\n}`
|
||||
/// * non empty block: put each stmt on its own line: `{\nstmt1;\nstmt2;\n}`
|
||||
/// * non empty block with comments (trailing comments on {, or leading comments on })
|
||||
fn is_non_collapsible(parent: &AstNodes<'_>) -> bool {
|
||||
fn is_non_collapsible(parent: &AstNodes<'_, '_>) -> bool {
|
||||
match parent {
|
||||
AstNodes::FunctionBody(_)
|
||||
| AstNodes::ForStatement(_)
|
||||
|
||||
@@ -5,7 +5,7 @@ use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
Format, FormatTrailingCommas, TrailingSeparator,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
format_args,
|
||||
formatter::{
|
||||
Comments, JoinBuilderJsExt as _, JsFormatContext, JsFormatter, JsFormatterExt as _,
|
||||
@@ -35,7 +35,7 @@ use crate::{
|
||||
write,
|
||||
};
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, Argument<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, Argument<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let l_paren_token = "(";
|
||||
let r_paren_token = ")";
|
||||
@@ -216,7 +216,7 @@ pub fn is_function_composition_args(args: &[Argument<'_>]) -> bool {
|
||||
}
|
||||
|
||||
fn format_all_elements_broken_out<'a>(
|
||||
node: &AstNode<'a, ArenaVec<'a, Argument<'a>>>,
|
||||
node: &AstNode<'_, 'a, ArenaVec<'a, Argument<'a>>>,
|
||||
elements: impl Iterator<Item = (Option<FormatElement<'a>>, usize)>,
|
||||
expand: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
@@ -252,7 +252,7 @@ fn format_all_elements_broken_out<'a>(
|
||||
}
|
||||
|
||||
fn format_all_args_broken_out<'a>(
|
||||
node: &AstNode<'a, ArenaVec<'a, Argument<'a>>>,
|
||||
node: &AstNode<'_, 'a, ArenaVec<'a, Argument<'a>>>,
|
||||
expand: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -610,7 +610,7 @@ fn can_group_arrow_function_expression_argument(
|
||||
}
|
||||
|
||||
fn write_grouped_arguments<'a>(
|
||||
node: &AstNode<'a, ArenaVec<'a, Argument<'a>>>,
|
||||
node: &AstNode<'_, 'a, ArenaVec<'a, Argument<'a>>>,
|
||||
group_layout: GroupedCallArgumentLayout,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -636,8 +636,8 @@ fn write_grouped_arguments<'a>(
|
||||
|
||||
let interned = f.intern(&format_once(|f| {
|
||||
if is_grouped_argument {
|
||||
match argument.as_ast_nodes() {
|
||||
AstNodes::Function(function)
|
||||
match &argument.as_kind() {
|
||||
AstNodeKind::Function(function)
|
||||
if !group_layout.is_grouped_first()
|
||||
&& (!only_one_argument
|
||||
|| function_has_only_simple_parameters(function)) =>
|
||||
@@ -645,7 +645,7 @@ fn write_grouped_arguments<'a>(
|
||||
has_cached = true;
|
||||
return write!(f, [FormatFunction::new_cached(function), comma]);
|
||||
}
|
||||
AstNodes::ArrowFunctionExpression(arrow) => {
|
||||
AstNodeKind::ArrowFunctionExpression(arrow) => {
|
||||
has_cached = true;
|
||||
return write!(
|
||||
f,
|
||||
@@ -705,9 +705,9 @@ fn write_grouped_arguments<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let function_params = match argument.as_ast_nodes() {
|
||||
AstNodes::ArrowFunctionExpression(arrow) => Some(&arrow.params),
|
||||
AstNodes::Function(function) => Some(&function.params),
|
||||
let function_params = match argument.as_ref() {
|
||||
Argument::ArrowFunctionExpression(arrow) => Some(&arrow.params),
|
||||
Argument::FunctionExpression(function) => Some(&function.params),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -729,7 +729,7 @@ fn write_grouped_arguments<'a>(
|
||||
// For decorated function patterns like `decorator("name")((props: {...}) => {...})`,
|
||||
// the arrow function should be kept hugged even if its signature breaks.
|
||||
// <https://github.com/prettier/prettier/blob/0273e33fc691e28e4ab3f3c8ee86918b65cf823d/src/language-js/print/function-parameters.js#L241-L292>
|
||||
let is_decorated = is_decorated_function(argument);
|
||||
let is_decorated = is_decorated_function(&argument);
|
||||
|
||||
// Remove soft lines from the cached parameters and check if they would break.
|
||||
// If they break even without soft lines, we need to use the expanded layout.
|
||||
@@ -755,11 +755,11 @@ fn write_grouped_arguments<'a>(
|
||||
|
||||
*grouped_element = if group_layout.is_grouped_first() {
|
||||
f.intern(&format_with(|f| {
|
||||
FormatGroupedFirstArgument { argument }.fmt(f);
|
||||
FormatGroupedFirstArgument { argument: &argument }.fmt(f);
|
||||
write!(f, (last_index != 0).then_some(","));
|
||||
}))
|
||||
} else {
|
||||
f.intern(&FormatGroupedLastArgument { argument, is_only: only_one_argument })
|
||||
f.intern(&FormatGroupedLastArgument { argument: &argument, is_only: only_one_argument })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,15 +843,15 @@ fn write_grouped_arguments<'a>(
|
||||
|
||||
/// Helper for formatting the first grouped argument (see [should_group_first_argument]).
|
||||
struct FormatGroupedFirstArgument<'a, 'b> {
|
||||
argument: &'b AstNode<'a, Argument<'a>>,
|
||||
argument: &'b AstNode<'b, 'a, Argument<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for FormatGroupedFirstArgument<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
match self.argument.as_ast_nodes() {
|
||||
match &self.argument.as_kind() {
|
||||
// Call the arrow function formatting but explicitly passes the call argument layout down
|
||||
// so that the arrow function formatting removes any soft line breaks between parameters and the return type.
|
||||
AstNodes::ArrowFunctionExpression(arrow) => {
|
||||
AstNodeKind::ArrowFunctionExpression(arrow) => {
|
||||
FormatJsArrowFunctionExpression::new_with_options(
|
||||
arrow,
|
||||
FormatJsArrowFunctionExpressionOptions {
|
||||
@@ -872,7 +872,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatGroupedFirstArgument<'a, '_>
|
||||
/// Helper for formatting the last grouped argument (see [should_group_last_argument]).
|
||||
struct FormatGroupedLastArgument<'a, 'b> {
|
||||
/// The argument to format
|
||||
argument: &'b AstNode<'a, Argument<'a>>,
|
||||
argument: &'b AstNode<'b, 'a, Argument<'a>>,
|
||||
/// Is this the only argument in the arguments list
|
||||
is_only: bool,
|
||||
}
|
||||
@@ -882,13 +882,13 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatGroupedLastArgument<'a, '_> {
|
||||
// For function and arrow expressions, re-format the node and pass the argument that it is the
|
||||
// last grouped argument. This changes the formatting of parameters, type parameters, and return types
|
||||
// to remove any soft line breaks.
|
||||
match self.argument.as_ast_nodes() {
|
||||
AstNodes::Function(function)
|
||||
match &self.argument.as_kind() {
|
||||
AstNodeKind::Function(function)
|
||||
if !self.is_only || function_has_only_simple_parameters(function) =>
|
||||
{
|
||||
FormatFunction::new_cached(function).fmt(f);
|
||||
}
|
||||
AstNodes::ArrowFunctionExpression(arrow) => {
|
||||
AstNodeKind::ArrowFunctionExpression(arrow) => {
|
||||
FormatJsArrowFunctionExpression::new_with_options(
|
||||
arrow,
|
||||
FormatJsArrowFunctionExpressionOptions {
|
||||
@@ -910,7 +910,7 @@ fn function_has_only_simple_parameters(function: &Function<'_>) -> bool {
|
||||
|
||||
/// Tests if this a simple module import like `import("module-name")` or `require("module-name")`.
|
||||
pub fn is_simple_module_import(
|
||||
arguments: &AstNode<'_, ArenaVec<'_, Argument<'_>>>,
|
||||
arguments: &AstNode<'_, '_, ArenaVec<'_, Argument<'_>>>,
|
||||
comments: &Comments,
|
||||
) -> bool {
|
||||
if arguments.len() != 1 {
|
||||
@@ -959,7 +959,7 @@ pub fn is_simple_module_import(
|
||||
/// Tests if amd's [`define`](https://github.com/amdjs/amdjs-api/wiki/AMD#define-function-) function.
|
||||
fn is_commonjs_or_amd_call(
|
||||
arguments: &[Argument<'_>],
|
||||
call: &AstNode<'_, CallExpression<'_>>,
|
||||
call: &AstNode<'_, '_, CallExpression<'_>>,
|
||||
f: &JsFormatter<'_, '_>,
|
||||
) -> bool {
|
||||
let Expression::Identifier(ident) = &call.callee else {
|
||||
@@ -1041,7 +1041,7 @@ pub fn is_verbatim_multiline_template_sole_arg(
|
||||
/// useMemo(() => {}, [])
|
||||
/// ```
|
||||
fn is_react_hook_with_deps_array(
|
||||
arguments: &AstNode<'_, ArenaVec<'_, Argument<'_>>>,
|
||||
arguments: &AstNode<'_, '_, ArenaVec<'_, Argument<'_>>>,
|
||||
comments: &Comments,
|
||||
) -> bool {
|
||||
if arguments.len() > 3 || arguments.len() < 2 {
|
||||
@@ -1095,9 +1095,9 @@ fn is_react_hook_with_deps_array(
|
||||
/// ```
|
||||
///
|
||||
/// <https://github.com/prettier/prettier/blob/0273e33fc691e28e4ab3f3c8ee86918b65cf823d/src/language-js/print/function-parameters.js#L240-L291>
|
||||
fn is_decorated_function(argument: &AstNode<'_, Argument<'_>>) -> bool {
|
||||
fn is_decorated_function(argument: &AstNode<'_, '_, Argument<'_>>) -> bool {
|
||||
// Check if the argument is an arrow function with a block body
|
||||
let AstNodes::ArrowFunctionExpression(arrow) = argument.as_ast_nodes() else {
|
||||
let AstNodeKind::ArrowFunctionExpression(arrow) = argument.as_kind() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use arguments::{is_simple_module_import, is_verbatim_multiline_template_sole_arg
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, CallExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, CallExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let callee = self.callee();
|
||||
let type_arguments = self.type_arguments();
|
||||
@@ -46,7 +46,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, CallExpression<'a>> {
|
||||
callee.as_ref(),
|
||||
Expression::StaticMemberExpression(_) | Expression::ComputedMemberExpression(_)
|
||||
) && !is_verbatim_multiline_template_sole_arg(arguments.as_slice(), Some(self), f)
|
||||
&& !is_simple_module_import(self.arguments(), f.comments())
|
||||
&& !is_simple_module_import(&self.arguments(), f.comments())
|
||||
&& !is_test_call_expression(self, f.comments())
|
||||
{
|
||||
MemberChain::from_call_expression(self, f).fmt(f);
|
||||
@@ -55,7 +55,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, CallExpression<'a>> {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
FormatBeforeOpener(callee, callee_opener(self)),
|
||||
FormatBeforeOpener(&callee, callee_opener(self)),
|
||||
self.optional.then_some("?."),
|
||||
type_arguments
|
||||
]
|
||||
@@ -99,11 +99,11 @@ impl<'a> FormatWrite<'a> for AstNode<'a, CallExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, NewExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, NewExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["new", space()]);
|
||||
if let Some(type_arguments) = self.type_arguments() {
|
||||
write!(f, [FormatBeforeOpener(self.callee(), b'<'), type_arguments]);
|
||||
write!(f, [FormatBeforeOpener(&self.callee(), b'<'), type_arguments]);
|
||||
} else {
|
||||
write!(f, [self.callee()]);
|
||||
}
|
||||
@@ -111,7 +111,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, NewExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["import"]);
|
||||
if let Some(phase) = &self.phase() {
|
||||
@@ -119,6 +119,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ImportExpression<'a>> {
|
||||
}
|
||||
|
||||
// Use the same logic as CallExpression arguments formatting
|
||||
write!(f, self.to_arguments());
|
||||
let arguments = self.arguments(f.allocator());
|
||||
write!(f, arguments);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ use super::{
|
||||
type_parameters::{FormatTSTypeParameters, FormatTSTypeParametersOptions},
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ClassBody<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ClassBody<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if f.options().quote_properties.is_consistent() {
|
||||
let quote_needed = self.body.iter().any(|element| {
|
||||
@@ -65,7 +65,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ClassBody<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ClassElement<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, ClassElement<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// Join class elements with hard line breaks between them
|
||||
let mut join = f.join_nodes_with_hardline();
|
||||
@@ -79,14 +79,14 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ClassEleme
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>>
|
||||
for (&AstNode<'a, ClassElement<'a>>, Option<&AstNode<'a, ClassElement<'a>>>)
|
||||
for (AstNode<'_, 'a, ClassElement<'a>>, Option<AstNode<'_, 'a, ClassElement<'a>>>)
|
||||
{
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
FormatClassElementWithSemicolon::new(self.0, self.1).fmt(f);
|
||||
FormatClassElementWithSemicolon::new(&self.0, self.1.as_ref()).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, MethodDefinition<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, MethodDefinition<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.decorators()]);
|
||||
|
||||
@@ -119,7 +119,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, MethodDefinition<'a>> {
|
||||
if value.generator {
|
||||
write!(f, "*");
|
||||
}
|
||||
format_property_key(self.key(), self.computed, f);
|
||||
format_property_key(&self.key(), self.computed, f);
|
||||
|
||||
if self.optional {
|
||||
write!(f, "?");
|
||||
@@ -128,7 +128,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, MethodDefinition<'a>> {
|
||||
format_grouped_parameters_with_return_type_for_method(
|
||||
value.type_parameters(),
|
||||
value.this_param.as_deref(),
|
||||
value.params(),
|
||||
&value.params(),
|
||||
value.return_type(),
|
||||
f,
|
||||
);
|
||||
@@ -147,7 +147,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, MethodDefinition<'a>> {
|
||||
let node_end = self.span.end;
|
||||
let comments = f.context().comments().comments_before(node_end);
|
||||
if !comments.is_empty() {
|
||||
let content_end = function_signature_end(value, f);
|
||||
let content_end = function_signature_end(&value, f);
|
||||
if let Some(trailing_comments) =
|
||||
trailing_comments_to_move_behind_semicolon(f, content_end, node_end)
|
||||
{
|
||||
@@ -159,19 +159,19 @@ impl<'a> FormatWrite<'a> for AstNode<'a, MethodDefinition<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, PropertyDefinition<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PropertyDefinition<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
AssignmentLike::PropertyDefinition(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, PrivateIdentifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PrivateIdentifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["#", text_without_whitespace(self.name().as_str())]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, StaticBlock<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, StaticBlock<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["static", space(), "{"]);
|
||||
|
||||
@@ -185,13 +185,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, StaticBlock<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, AccessorProperty<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, AccessorProperty<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
AssignmentLike::AccessorProperty(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSIndexSignature<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSIndexSignature<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if self.r#static {
|
||||
write!(f, ["static", space()]);
|
||||
@@ -204,7 +204,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSIndexSignature<'a>> {
|
||||
f,
|
||||
[
|
||||
"[",
|
||||
group(&soft_block_indent(self.parameter())),
|
||||
group(&soft_block_indent(&self.parameter())),
|
||||
"]",
|
||||
self.type_annotation(),
|
||||
is_class.then_some(OptionalSemicolon)
|
||||
@@ -213,13 +213,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSIndexSignature<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSIndexSignatureName<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSIndexSignatureName<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [text_without_whitespace(self.name().as_str()), self.type_annotation()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSClassImplements<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, TSClassImplements<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let last_index = self.len().saturating_sub(1);
|
||||
let mut joiner = f.join_with(soft_line_break_or_space());
|
||||
@@ -238,19 +238,19 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSClassImp
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSClassImplements<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSClassImplements<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.expression(), self.type_arguments()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ClassHeritage<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ClassHeritage<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.expression(), self.type_arguments()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Class<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Class<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if self.r#type == ClassType::ClassExpression
|
||||
&& (!self.decorators.is_empty() && self.needs_parentheses(f))
|
||||
@@ -262,10 +262,10 @@ impl<'a> FormatWrite<'a> for AstNode<'a, Class<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
struct FormatClass<'a, 'b>(pub &'b AstNode<'a, Class<'a>>);
|
||||
struct FormatClass<'a, 'b>(pub &'b AstNode<'b, 'a, Class<'a>>);
|
||||
|
||||
impl<'a> Deref for FormatClass<'a, '_> {
|
||||
type Target = AstNode<'a, Class<'a>>;
|
||||
impl<'a, 'b> Deref for FormatClass<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, Class<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
@@ -277,8 +277,9 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClass<'a, '_> {
|
||||
let decorators = self.decorators();
|
||||
let type_parameters = self.type_parameters();
|
||||
let heritage = self.heritage();
|
||||
let super_class = heritage.map(AstNode::<ClassHeritage<'a>>::expression);
|
||||
let super_type_arguments = heritage.and_then(AstNode::<ClassHeritage<'a>>::type_arguments);
|
||||
let super_class = heritage.as_ref().map(AstNode::<ClassHeritage<'a>>::expression);
|
||||
let super_type_arguments =
|
||||
heritage.as_ref().and_then(AstNode::<ClassHeritage<'a>>::type_arguments);
|
||||
let implements = self.implements();
|
||||
let body = self.body();
|
||||
|
||||
@@ -309,7 +310,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClass<'a, '_> {
|
||||
if type_parameters.is_none() && super_class.is_none() && implements.is_empty() {
|
||||
// The id's trailing pass would claim an end-of-line comment
|
||||
// and flush it past the body's `{`; leave it for the head-body separator.
|
||||
FormatNodeWithoutTrailingComments(id).fmt(f);
|
||||
FormatNodeWithoutTrailingComments(&id).fmt(f);
|
||||
} else {
|
||||
write!(f, id);
|
||||
}
|
||||
@@ -381,7 +382,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClass<'a, '_> {
|
||||
type_arguments.fmt(f);
|
||||
}
|
||||
} else if implements.is_empty() {
|
||||
FormatNodeWithoutTrailingComments(extends).fmt(f);
|
||||
FormatNodeWithoutTrailingComments(&extends).fmt(f);
|
||||
// Line comments are handled separately to ensure proper placement
|
||||
if !has_trailing_line_comments {
|
||||
FormatTrailingComments::Comments(comments).fmt(f);
|
||||
@@ -431,7 +432,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClass<'a, '_> {
|
||||
FormatLeadingComments::Comments(leading_comments),
|
||||
(!leading_comments.is_empty()).then_some(hard_line_break()),
|
||||
"implements",
|
||||
group(&soft_line_indent_or_space(implements))
|
||||
group(&soft_line_indent_or_space(&implements))
|
||||
]
|
||||
);
|
||||
} else {
|
||||
@@ -492,7 +493,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClass<'a, '_> {
|
||||
/// 3. Implements is a qualified name and has no type arguments
|
||||
/// 4. There are comments in the heritage clause area
|
||||
/// 5. There are trailing line comments after type parameters
|
||||
fn should_group<'a>(class: &AstNode<'a, Class<'a>>, f: &JsFormatter<'_, 'a>) -> bool {
|
||||
fn should_group<'a>(class: &AstNode<'_, 'a, Class<'a>>, f: &JsFormatter<'_, 'a>) -> bool {
|
||||
let heritage = class.heritage.as_ref();
|
||||
|
||||
if usize::from(heritage.is_some()) + class.implements.len() > 1 {
|
||||
@@ -545,14 +546,14 @@ fn should_group<'a>(class: &AstNode<'a, Class<'a>>, f: &JsFormatter<'_, 'a>) ->
|
||||
}
|
||||
|
||||
pub struct FormatClassElementWithSemicolon<'a, 'b> {
|
||||
element: &'b AstNode<'a, ClassElement<'a>>,
|
||||
next_element: Option<&'b AstNode<'a, ClassElement<'a>>>,
|
||||
element: &'b AstNode<'b, 'a, ClassElement<'a>>,
|
||||
next_element: Option<&'b AstNode<'b, 'a, ClassElement<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatClassElementWithSemicolon<'a, 'b> {
|
||||
pub fn new(
|
||||
element: &'b AstNode<'a, ClassElement<'a>>,
|
||||
next_element: Option<&'b AstNode<'a, ClassElement<'a>>>,
|
||||
element: &'b AstNode<'b, 'a, ClassElement<'a>>,
|
||||
next_element: Option<&'b AstNode<'b, 'a, ClassElement<'a>>>,
|
||||
) -> Self {
|
||||
Self { element, next_element }
|
||||
}
|
||||
@@ -687,17 +688,18 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatClassElementWithSemicolon<'a,
|
||||
|
||||
/// Based on <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/print/function.js#L160-L176>
|
||||
pub fn format_grouped_parameters_with_return_type_for_method<'a>(
|
||||
type_parameters: Option<&AstNode<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
type_parameters: Option<AstNode<'_, 'a, TSTypeParameterDeclaration<'a>>>,
|
||||
this_param: Option<&TSThisParameter<'a>>,
|
||||
params: &AstNode<'a, FormalParameters<'a>>,
|
||||
return_type: Option<&AstNode<'a, TSTypeAnnotation<'a>>>,
|
||||
params: &AstNode<'_, 'a, FormalParameters<'a>>,
|
||||
return_type: Option<AstNode<'_, 'a, TSTypeAnnotation<'a>>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
write!(f, type_parameters);
|
||||
|
||||
group(&format_with(|f| {
|
||||
let format_parameters = params.memoized();
|
||||
let format_return_type = return_type.map(FormatNodeWithoutTrailingComments).memoized();
|
||||
let format_return_type =
|
||||
return_type.as_ref().map(FormatNodeWithoutTrailingComments).memoized();
|
||||
|
||||
// Inspect early, in case the `return_type` is formatted before `parameters`
|
||||
// in `should_group_function_parameters`.
|
||||
@@ -706,9 +708,9 @@ pub fn format_grouped_parameters_with_return_type_for_method<'a>(
|
||||
let should_break_parameters = should_break_function_parameters(params);
|
||||
let should_group_parameters = should_break_parameters
|
||||
|| should_group_function_parameters(
|
||||
type_parameters.map(AsRef::as_ref),
|
||||
type_parameters.as_ref().map(AsRef::as_ref),
|
||||
params.parameters_count() + usize::from(this_param.is_some()),
|
||||
return_type.map(AsRef::as_ref),
|
||||
return_type.as_ref().map(AsRef::as_ref),
|
||||
&format_return_type,
|
||||
f,
|
||||
);
|
||||
@@ -751,6 +753,6 @@ pub fn format_grouped_parameters_with_return_type_for_method<'a>(
|
||||
/// ```ts
|
||||
/// constructor(private id: string) {}
|
||||
/// ```
|
||||
fn should_break_function_parameters<'a>(params: &AstNode<'a, FormalParameters<'a>>) -> bool {
|
||||
fn should_break_function_parameters<'a>(params: &AstNode<'_, 'a, FormalParameters<'a>>) -> bool {
|
||||
params.parameters_count() > 1 && params.items().iter().any(|param| param.has_modifier())
|
||||
}
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, Decorator<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, Decorator<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if self.is_empty() {
|
||||
return;
|
||||
@@ -67,7 +67,7 @@ fn is_identifier_or_static_member_only(callee: &Expression) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Decorator<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Decorator<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["@"]);
|
||||
|
||||
@@ -98,7 +98,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, Decorator<'a>> {
|
||||
/// Check if decorators should expand (have newlines between them)
|
||||
#[inline]
|
||||
fn should_expand_decorators<'a>(
|
||||
decorators: &AstNode<'a, ArenaVec<'a, Decorator<'a>>>,
|
||||
decorators: &AstNode<'_, 'a, ArenaVec<'a, Decorator<'a>>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
decorators
|
||||
|
||||
@@ -4,7 +4,7 @@ use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
FormatTrailingCommas,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind},
|
||||
format_args,
|
||||
formatter::{
|
||||
JsFormatter,
|
||||
@@ -28,7 +28,7 @@ use super::FormatWrite;
|
||||
fn format_export_keyword_with_class_decorators<'a>(
|
||||
span: Span,
|
||||
keyword: &'static str,
|
||||
declaration: &AstNodes<'a>,
|
||||
declaration: &AstNodeKind<'_, 'a>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
// `@decorator export class Cls {}`
|
||||
@@ -40,7 +40,7 @@ fn format_export_keyword_with_class_decorators<'a>(
|
||||
FormatLeadingComments::CommentsOfNode(comments, span.start).fmt(f);
|
||||
});
|
||||
|
||||
if let AstNodes::Class(class) = declaration
|
||||
if let AstNodeKind::Class(class) = declaration
|
||||
&& !class.decorators.is_empty()
|
||||
&& !class.is_expression()
|
||||
{
|
||||
@@ -78,7 +78,7 @@ fn format_export_keyword_with_class_decorators<'a>(
|
||||
fn format_export_specifiers_block<'a>(
|
||||
span: Span,
|
||||
export_kind: ImportOrExportKind,
|
||||
specifiers: &AstNode<'a, ArenaVec<'a, ExportSpecifier<'a>>>,
|
||||
specifiers: &AstNode<'_, 'a, ArenaVec<'a, ExportSpecifier<'a>>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
let needs_space = f.options().bracket_spacing.value();
|
||||
@@ -119,13 +119,13 @@ fn format_export_specifiers_block<'a>(
|
||||
write!(f, "}");
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportDefaultDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportDefaultDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let declaration = self.declaration();
|
||||
format_export_keyword_with_class_decorators(
|
||||
self.span,
|
||||
"export default",
|
||||
declaration.as_ast_nodes(),
|
||||
&declaration.as_kind(),
|
||||
f,
|
||||
);
|
||||
|
||||
@@ -137,7 +137,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportDefaultDeclaration<'a>> {
|
||||
self.span.end,
|
||||
false,
|
||||
);
|
||||
write!(f, FormatContentWithSemicolon::new(declaration, content_end, self.span.end));
|
||||
write!(f, FormatContentWithSemicolon::new(&declaration, content_end, self.span.end));
|
||||
} else {
|
||||
write!(f, declaration);
|
||||
}
|
||||
@@ -146,7 +146,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportDefaultDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportAllDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportAllDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let prefix = format_with(|f| {
|
||||
write!(f, ["export", space(), self.export_kind(), "*", space()]);
|
||||
@@ -157,7 +157,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportAllDeclaration<'a>> {
|
||||
});
|
||||
format_source_with_clause_and_semicolon(
|
||||
&prefix,
|
||||
self.source(),
|
||||
&self.source(),
|
||||
self.with_clause(),
|
||||
self.span.end,
|
||||
f,
|
||||
@@ -165,15 +165,10 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportAllDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let declaration = self.declaration();
|
||||
format_export_keyword_with_class_decorators(
|
||||
self.span,
|
||||
"export",
|
||||
declaration.as_ast_nodes(),
|
||||
f,
|
||||
);
|
||||
format_export_keyword_with_class_decorators(self.span, "export", &declaration.as_kind(), f);
|
||||
write!(f, declaration);
|
||||
// No semicolon here: the declaration prints its own,
|
||||
// together with its trailing comments
|
||||
@@ -181,25 +176,25 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportNamedDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportNamedDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
self.format_leading_comments(f);
|
||||
write!(f, ["export", space()]);
|
||||
format_export_specifiers_block(self.span, self.export_kind(), self.specifiers(), f);
|
||||
format_export_specifiers_block(self.span, self.export_kind(), &self.specifiers(), f);
|
||||
write!(f, OptionalSemicolon);
|
||||
self.format_trailing_comments(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportFromDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportFromDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
self.format_leading_comments(f);
|
||||
write!(f, ["export", space()]);
|
||||
format_export_specifiers_block(self.span, self.export_kind(), self.specifiers(), f);
|
||||
format_export_specifiers_block(self.span, self.export_kind(), &self.specifiers(), f);
|
||||
|
||||
format_source_with_clause_and_semicolon(
|
||||
&format_args!(space(), "from", space()),
|
||||
self.source(),
|
||||
&self.source(),
|
||||
self.with_clause(),
|
||||
self.span.end,
|
||||
f,
|
||||
@@ -208,7 +203,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportFromDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ExportSpecifier<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, ExportSpecifier<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let trailing_separator = FormatTrailingCommas::ES5.trailing_separator(f.options());
|
||||
f.join_with(soft_line_break_or_space()).entries(
|
||||
@@ -231,7 +226,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ExportSpec
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ExportSpecifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ExportSpecifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments().line_comments_before(self.exported.span().end);
|
||||
write!(f, [FormatLeadingComments::Comments(comments)]);
|
||||
@@ -245,13 +240,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ExportSpecifier<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSExportAssignment<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSExportAssignment<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["export = ", self.expression(), OptionalSemicolon]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSNamespaceExportDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSNamespaceExportDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["export as namespace ", self.id(), OptionalSemicolon]);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ use super::parameters::{ParameterLayout, ParameterList};
|
||||
///
|
||||
/// For a plain binding position (e.g., `{ item }`), use the default (no parens).
|
||||
pub struct FormatFunctionParams<'a, 'b> {
|
||||
node: &'b AstNode<'a, FormalParameters<'a>>,
|
||||
node: &'b AstNode<'b, 'a, FormalParameters<'a>>,
|
||||
with_parens: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatFunctionParams<'a, 'b> {
|
||||
pub fn new(node: &'b AstNode<'a, FormalParameters<'a>>, with_parens: bool) -> Self {
|
||||
pub fn new(node: &'b AstNode<'b, 'a, FormalParameters<'a>>, with_parens: bool) -> Self {
|
||||
Self { node, with_parens }
|
||||
}
|
||||
}
|
||||
@@ -67,11 +67,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatFunctionParams<'a, '_> {
|
||||
/// Does NOT include angle bracket delimiters `<`/`>` or group wrapping.
|
||||
/// And trailing commas are suppressed.
|
||||
pub struct FormatTypeParameters<'a, 'b> {
|
||||
decl: &'b AstNode<'a, TSTypeParameterDeclaration<'a>>,
|
||||
decl: &'b AstNode<'b, 'a, TSTypeParameterDeclaration<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatTypeParameters<'a, 'b> {
|
||||
pub fn new(decl: &'b AstNode<'a, TSTypeParameterDeclaration<'a>>) -> Self {
|
||||
pub fn new(decl: &'b AstNode<'b, 'a, TSTypeParameterDeclaration<'a>>) -> Self {
|
||||
Self { decl }
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatTypeParameters<'a, '_> {
|
||||
|
||||
soft_block_indent(&format_with(|f| {
|
||||
f.join_with(soft_line_break_or_space()).entries_with_trailing_separator(
|
||||
params,
|
||||
¶ms,
|
||||
",",
|
||||
TrailingSeparator::Omit,
|
||||
);
|
||||
|
||||
@@ -10,19 +10,19 @@ use crate::{
|
||||
utils::statement_body::write_head_body_separator, write,
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Function<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Function<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
FormatFunction::new(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FormatFunction<'a, 'b> {
|
||||
pub function: &'b AstNode<'a, Function<'a>>,
|
||||
pub function: &'b AstNode<'b, 'a, Function<'a>>,
|
||||
pub cache_mode: FunctionCacheMode,
|
||||
}
|
||||
|
||||
impl<'a> Deref for FormatFunction<'a, '_> {
|
||||
type Target = AstNode<'a, Function<'a>>;
|
||||
impl<'a, 'b> Deref for FormatFunction<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, Function<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.function
|
||||
@@ -30,11 +30,11 @@ impl<'a> Deref for FormatFunction<'a, '_> {
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatFunction<'a, 'b> {
|
||||
pub fn new(function: &'b AstNode<'a, Function<'a>>) -> Self {
|
||||
pub fn new(function: &'b AstNode<'b, 'a, Function<'a>>) -> Self {
|
||||
Self { function, cache_mode: FunctionCacheMode::NoCache }
|
||||
}
|
||||
|
||||
pub fn new_cached(function: &'b AstNode<'a, Function<'a>>) -> Self {
|
||||
pub fn new_cached(function: &'b AstNode<'b, 'a, Function<'a>>) -> Self {
|
||||
Self { function, cache_mode: FunctionCacheMode::Cache }
|
||||
}
|
||||
|
||||
@@ -114,13 +114,13 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatFunction<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, FunctionBody<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, FunctionBody<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write_head_body_separator(self.span.start, f);
|
||||
|
||||
let statements = self.statements();
|
||||
let directives = self.directives();
|
||||
if is_empty_block(statements) && directives.is_empty() {
|
||||
if is_empty_block(&statements) && directives.is_empty() {
|
||||
write!(f, ["{", format_dangling_comments(self.span).with_block_indent(), "}"]);
|
||||
} else {
|
||||
write!(f, ["{", block_indent(&format_args!(directives, statements)), "}"]);
|
||||
|
||||
@@ -14,12 +14,12 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSFunctionType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSFunctionType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
format_grouped_parameters_with_return_type(
|
||||
self.type_parameters(),
|
||||
self.this_param.as_deref(),
|
||||
self.params(),
|
||||
&self.params(),
|
||||
Some(self.return_type()),
|
||||
/* is_function_or_constructor_type */ true,
|
||||
f,
|
||||
@@ -27,7 +27,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSFunctionType<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructorType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSConstructorType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let r#abstract = self.r#abstract();
|
||||
|
||||
@@ -41,7 +41,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructorType<'a>> {
|
||||
format_grouped_parameters_with_return_type(
|
||||
self.type_parameters(),
|
||||
None,
|
||||
self.params(),
|
||||
&self.params(),
|
||||
Some(self.return_type()),
|
||||
/* is_function_or_constructor_type */ true,
|
||||
f,
|
||||
@@ -52,12 +52,12 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructorType<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSCallSignatureDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSCallSignatureDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
format_grouped_parameters_with_return_type(
|
||||
self.type_parameters(),
|
||||
self.this_param.as_deref(),
|
||||
self.params(),
|
||||
&self.params(),
|
||||
self.return_type(),
|
||||
/* is_function_or_constructor_type */ false,
|
||||
f,
|
||||
@@ -65,7 +65,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSCallSignatureDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSMethodSignature<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSMethodSignature<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let format_inner = format_with(|f| {
|
||||
match self.kind() {
|
||||
@@ -77,7 +77,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSMethodSignature<'a>> {
|
||||
write!(f, ["set", space()]);
|
||||
}
|
||||
}
|
||||
format_property_key(self.key(), self.computed(), f);
|
||||
format_property_key(&self.key(), self.computed(), f);
|
||||
if self.optional() {
|
||||
write!(f, "?");
|
||||
}
|
||||
@@ -110,7 +110,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSMethodSignature<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructSignatureDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSConstructSignatureDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(
|
||||
f,
|
||||
@@ -121,7 +121,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructSignatureDeclaration<'a>> {
|
||||
format_grouped_parameters_with_return_type(
|
||||
self.type_parameters(),
|
||||
None,
|
||||
self.params(),
|
||||
&self.params(),
|
||||
self.return_type(),
|
||||
/* is_function_or_constructor_type */ false,
|
||||
f,
|
||||
@@ -134,17 +134,18 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSConstructSignatureDeclaration<'a>> {
|
||||
|
||||
/// Based on <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/print/type-annotation.js#L274-L331>
|
||||
pub fn format_grouped_parameters_with_return_type<'a>(
|
||||
type_parameters: Option<&AstNode<'a, TSTypeParameterDeclaration<'a>>>,
|
||||
type_parameters: Option<AstNode<'_, 'a, TSTypeParameterDeclaration<'a>>>,
|
||||
this_param: Option<&TSThisParameter<'a>>,
|
||||
params: &AstNode<'a, FormalParameters<'a>>,
|
||||
return_type: Option<&AstNode<'a, TSTypeAnnotation<'a>>>,
|
||||
params: &AstNode<'_, 'a, FormalParameters<'a>>,
|
||||
return_type: Option<AstNode<'_, 'a, TSTypeAnnotation<'a>>>,
|
||||
is_function_or_constructor_type: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
group(&format_with(|f| {
|
||||
let format_type_parameters = type_parameters.memoized();
|
||||
let format_parameters = params.memoized();
|
||||
let format_return_type = return_type.map(FormatNodeWithoutTrailingComments).memoized();
|
||||
let format_return_type =
|
||||
return_type.as_ref().map(FormatNodeWithoutTrailingComments).memoized();
|
||||
|
||||
// Inspect early, in case the `return_type` is formatted before `parameters`
|
||||
// in `should_group_function_parameters`.
|
||||
@@ -152,9 +153,9 @@ pub fn format_grouped_parameters_with_return_type<'a>(
|
||||
format_parameters.inspect(f);
|
||||
|
||||
let group_parameters = should_group_function_parameters(
|
||||
type_parameters.map(AsRef::as_ref),
|
||||
type_parameters.as_ref().map(AsRef::as_ref),
|
||||
params.parameters_count() + usize::from(this_param.is_some()),
|
||||
return_type.map(AsRef::as_ref),
|
||||
return_type.as_ref().map(AsRef::as_ref),
|
||||
&format_return_type,
|
||||
f,
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
Format, FormatTrailingCommas, JsLabels, TrailingSeparator,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind},
|
||||
format_args,
|
||||
formatter::{
|
||||
JsFormatter, prelude::*, separated::FormatSeparatedIter, trivia::FormatLeadingComments,
|
||||
@@ -37,8 +37,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ImportPhase {
|
||||
/// then the semicolon, moving a same-line trailing comment behind it.
|
||||
pub fn format_source_with_clause_and_semicolon<'a>(
|
||||
prefix: &impl Format<'a, JsFormatContext<'a>>,
|
||||
source: &AstNode<'a, StringLiteral>,
|
||||
with_clause: Option<&AstNode<'a, WithClause>>,
|
||||
source: &AstNode<'_, 'a, StringLiteral>,
|
||||
with_clause: Option<AstNode<'_, 'a, WithClause>>,
|
||||
span_end: u32,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -54,7 +54,7 @@ pub fn format_source_with_clause_and_semicolon<'a>(
|
||||
write!(f, [with_clause]);
|
||||
}
|
||||
});
|
||||
let content_end = module_source_end(source, with_clause.map(AsRef::as_ref));
|
||||
let content_end = module_source_end(source, with_clause.as_ref().map(AsRef::as_ref));
|
||||
write!(f, FormatContentWithSemicolon::new(&content, content_end, span_end));
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub fn module_source_end(source: &StringLiteral, with_clause: Option<&WithClause
|
||||
with_clause.map_or(source.span.end, |with| with.span.end)
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let prefix = format_with(|f| {
|
||||
write!(f, ["import", space()]);
|
||||
@@ -80,7 +80,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ImportDeclaration<'a>> {
|
||||
let decl = format_with(|f| {
|
||||
format_source_with_clause_and_semicolon(
|
||||
&prefix,
|
||||
self.source(),
|
||||
&self.source(),
|
||||
self.with_clause(),
|
||||
self.span.end,
|
||||
f,
|
||||
@@ -96,7 +96,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ImportDeclaration<'a>> {
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>>
|
||||
for AstNode<'a, ArenaVec<'a, ImportDeclarationSpecifier<'a>>>
|
||||
for AstNode<'_, 'a, ArenaVec<'a, ImportDeclarationSpecifier<'a>>>
|
||||
{
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let mut specifiers_iter = self.iter().peekable();
|
||||
@@ -176,7 +176,7 @@ impl<'a> Format<'a, JsFormatContext<'a>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportSpecifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportSpecifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments().line_comments_before(self.local.span.end);
|
||||
write!(f, [FormatLeadingComments::Comments(comments), self.import_kind()]);
|
||||
@@ -188,19 +188,19 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ImportSpecifier<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportDefaultSpecifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportDefaultSpecifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.local()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportNamespaceSpecifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportNamespaceSpecifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["*", space(), "as", space(), self.local()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, WithClause<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, WithClause<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if f.options().quote_properties.is_consistent() {
|
||||
let quote_needed = self.with_entries.iter().any(|attribute| {
|
||||
@@ -225,7 +225,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, WithClause<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ImportAttribute<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, ImportAttribute<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if self.is_empty() {
|
||||
return write!(f, "{}");
|
||||
@@ -288,9 +288,9 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, ImportAttr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ImportAttribute<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ImportAttribute<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if let AstNodes::StringLiteral(string) = self.key().as_ast_nodes() {
|
||||
if let AstNodeKind::StringLiteral(string) = &self.key().as_kind() {
|
||||
let format = FormatLiteralStringToken::new(
|
||||
f.source_text().text_for(string),
|
||||
false,
|
||||
@@ -317,7 +317,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ImportAttribute<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSImportEqualsDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSImportEqualsDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(
|
||||
f,
|
||||
@@ -336,12 +336,12 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSImportEqualsDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSExternalModuleReference<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSExternalModuleReference<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["require("]);
|
||||
|
||||
if f.comments().has_comment_in_span(self.span) {
|
||||
write!(f, [block_indent(self.expression())]);
|
||||
write!(f, [block_indent(&self.expression())]);
|
||||
} else {
|
||||
write!(f, [self.expression()]);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::{
|
||||
utils::typescript::is_object_like_type, write,
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSIntersectionType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSIntersectionType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let content = format_with(|f| format_intersection_types(self.types(), f));
|
||||
let content = format_with(|f| format_intersection_types(&self.types(), f));
|
||||
write!(f, [group(&content)]);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSIntersectionType<'a>> {
|
||||
// One member per line with leading `&`, object members treated like any other member (NO hug special case).
|
||||
// Until that lands, this follows Prettier's current output.
|
||||
fn format_intersection_types<'a>(
|
||||
node: &AstNode<'a, ArenaVec<'a, TSType<'a>>>,
|
||||
node: &AstNode<'_, 'a, ArenaVec<'a, TSType<'a>>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
let operator_leads_break = f.options().operator_position.is_start();
|
||||
|
||||
@@ -34,7 +34,7 @@ impl FormatJsxChildList {
|
||||
|
||||
pub fn fmt_children<'a, 'b>(
|
||||
&self,
|
||||
children: &'b AstNode<'a, ArenaVec<'a, JSXChild<'a>>>,
|
||||
children: &'b AstNode<'b, 'a, ArenaVec<'a, JSXChild<'a>>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> FormatChildrenResult<'a, 'b> {
|
||||
// Use Biome's exact approach - no need for jsx_split_children at this stage
|
||||
@@ -371,7 +371,7 @@ impl FormatJsxChildList {
|
||||
}
|
||||
|
||||
fn children_meta(
|
||||
children: &AstNode<'_, ArenaVec<'_, JSXChild<'_>>>,
|
||||
children: &AstNode<'_, '_, ArenaVec<'_, JSXChild<'_>>>,
|
||||
comments: &Comments<'_>,
|
||||
) -> ChildrenMeta {
|
||||
let mut meta = ChildrenMeta::default();
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxc_ast::ast::{JSXChild, JSXElement, JSXExpression, JSXExpressionContainer,
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
best_fitting, format_args,
|
||||
formatter::{prelude::*, trivia::FormatTrailingComments},
|
||||
parentheses::NeedsParentheses,
|
||||
@@ -20,8 +20,8 @@ use super::{FormatJsxChildList, JsxChildListLayout};
|
||||
/// Union type for JSX elements and fragments that have children
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AnyJsxTagWithChildren<'a, 'b> {
|
||||
Element(&'b AstNode<'a, JSXElement<'a>>),
|
||||
Fragment(&'b AstNode<'a, JSXFragment<'a>>),
|
||||
Element(&'b AstNode<'b, 'a, JSXElement<'a>>),
|
||||
Fragment(&'b AstNode<'b, 'a, JSXFragment<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> AnyJsxTagWithChildren<'a, '_> {
|
||||
@@ -161,7 +161,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AnyJsxTagWithChildren<'a, '_> {
|
||||
let children = self.children();
|
||||
let format_children = FormatJsxChildList::default()
|
||||
.with_options(list_layout)
|
||||
.fmt_children(children, f);
|
||||
.fmt_children(&children, f);
|
||||
|
||||
match format_children {
|
||||
FormatChildrenResult::SingleChild(child) => {
|
||||
@@ -246,7 +246,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AnyJsxTagWithChildren<'a, '_> {
|
||||
/// // As JSX attribute:
|
||||
/// <Tooltip title={[].map(name => (<Foo>{name}</Foo>))} />;
|
||||
/// ```
|
||||
pub fn should_expand(parent: &AstNodes<'_>) -> bool {
|
||||
pub fn should_expand(parent: &AstNodes<'_, '_>) -> bool {
|
||||
let maybe_jsx_expression_container = match parent {
|
||||
AstNodes::ArrowFunctionExpression(arrow) if arrow.is_expression() => match arrow.parent() {
|
||||
AstNodes::CallExpression(call) => call.parent(),
|
||||
@@ -265,9 +265,7 @@ impl<'a, 'b> AnyJsxTagWithChildren<'a, 'b> {
|
||||
match self {
|
||||
Self::Element(element) => {
|
||||
let is_self_closing = element.closing_element().is_none();
|
||||
let opening_formatter =
|
||||
FormatOpeningElement::new(element.opening_element(), is_self_closing);
|
||||
write!(f, opening_formatter);
|
||||
write!(f, FormatOpeningElement::new(&element.opening_element(), is_self_closing));
|
||||
}
|
||||
Self::Fragment(fragment) => {
|
||||
write!(f, fragment.opening_fragment());
|
||||
@@ -286,14 +284,14 @@ impl<'a, 'b> AnyJsxTagWithChildren<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn children(&self) -> &'b AstNode<'a, ArenaVec<'a, JSXChild<'a>>> {
|
||||
fn children(&self) -> AstNode<'b, 'a, ArenaVec<'a, JSXChild<'a>>> {
|
||||
match self {
|
||||
Self::Element(element) => element.children(),
|
||||
Self::Fragment(fragment) => fragment.children(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parent(&self) -> &'b AstNodes<'a> {
|
||||
fn parent(&self) -> &'b AstNodes<'b, 'a> {
|
||||
match self {
|
||||
Self::Element(element) => element.parent(),
|
||||
Self::Fragment(fragment) => fragment.parent(),
|
||||
@@ -316,15 +314,16 @@ impl<'a, 'b> AnyJsxTagWithChildren<'a, 'b> {
|
||||
// Safe because of length check above
|
||||
let child = children.first().unwrap();
|
||||
|
||||
match child.as_ast_nodes() {
|
||||
AstNodes::JSXText(text) => {
|
||||
match child.as_kind() {
|
||||
AstNodeKind::JSXText(text) => {
|
||||
if is_meaningful_jsx_text(&text.value) {
|
||||
ElementLayout::Default
|
||||
} else {
|
||||
ElementLayout::NoChildren
|
||||
}
|
||||
}
|
||||
AstNodes::JSXExpressionContainer(expression) => match &expression.expression {
|
||||
AstNodeKind::JSXExpressionContainer(expression) => match &expression.expression
|
||||
{
|
||||
JSXExpression::TemplateLiteral(_) => ElementLayout::Template(expression),
|
||||
JSXExpression::TaggedTemplateExpression(_) => {
|
||||
ElementLayout::Template(expression)
|
||||
@@ -360,7 +359,7 @@ pub enum ElementLayout<'a, 'b> {
|
||||
/// } that will eventually break across multiple lines ${(40 / 3) * 45}`}
|
||||
/// </div>;
|
||||
/// ```
|
||||
Template(&'b AstNode<'a, JSXExpressionContainer<'a>>),
|
||||
Template(AstNode<'b, 'a, JSXExpressionContainer<'a>>),
|
||||
|
||||
/// Default layout used for all elements that have children and [ElementLayout::Template] does not apply.
|
||||
///
|
||||
|
||||
@@ -28,19 +28,19 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXElement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXElement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
AnyJsxTagWithChildren::Element(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXOpeningElement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXOpeningElement<'a>> {
|
||||
fn write(&self, _f: &mut JsFormatter<'_, 'a>) {
|
||||
unreachable!("`AnyJsxTagWithChildren` will print it.")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXClosingElement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXClosingElement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let name = self.name();
|
||||
let comments = f.comments().comments_before(name.span().start);
|
||||
@@ -62,13 +62,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXClosingElement<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXFragment<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXFragment<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
AnyJsxTagWithChildren::Fragment(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXOpeningFragment> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXOpeningFragment> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments().comments_before(self.span.end);
|
||||
|
||||
@@ -97,7 +97,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXOpeningFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXClosingFragment> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXClosingFragment> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments().comments_before(self.span.end);
|
||||
|
||||
@@ -128,19 +128,19 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXClosingFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXNamespacedName<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXNamespacedName<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.namespace(), ":", self.name()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXMemberExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXMemberExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.object(), ".", self.property()]);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXExpressionContainer<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXExpressionContainer<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let has_comment = |f: &mut JsFormatter<'_, '_>| {
|
||||
let expression_span = self.expression.span();
|
||||
@@ -293,11 +293,11 @@ pub fn should_inline_jsx_expression(container: &JSXExpressionContainer<'_>) -> b
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXEmptyExpression> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXEmptyExpression> {
|
||||
fn write(&self, _f: &mut JsFormatter<'_, 'a>) {}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, JSXAttributeItem<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, JSXAttributeItem<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if f.options().attribute_position == AttributePosition::Multiline {
|
||||
f.join_nodes_with_hardline().entries(self.iter());
|
||||
@@ -307,7 +307,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, JSXAttribu
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXAttribute<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXAttribute<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, self.name());
|
||||
|
||||
@@ -334,7 +334,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXAttribute<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXSpreadAttribute<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXSpreadAttribute<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments();
|
||||
let has_comment = comments.has_comment_before(self.argument.span().start)
|
||||
@@ -356,13 +356,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXSpreadAttribute<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXIdentifier<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXIdentifier<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, text_without_whitespace(self.name().as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXSpreadChild<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXSpreadChild<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let comments = f.context().comments();
|
||||
let has_comment = comments.has_comment_before(self.expression.span().start)
|
||||
@@ -384,7 +384,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, JSXSpreadChild<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, JSXText<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, JSXText<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, text(self.value().as_str()));
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct FormatOpeningElement<'a, 'b> {
|
||||
element: &'b AstNode<'a, JSXOpeningElement<'a>>,
|
||||
element: &'b AstNode<'b, 'a, JSXOpeningElement<'a>>,
|
||||
is_self_closing: bool,
|
||||
}
|
||||
|
||||
impl<'a> Deref for FormatOpeningElement<'a, '_> {
|
||||
type Target = AstNode<'a, JSXOpeningElement<'a>>;
|
||||
impl<'a, 'b> Deref for FormatOpeningElement<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, JSXOpeningElement<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.element
|
||||
@@ -23,7 +23,7 @@ impl<'a> Deref for FormatOpeningElement<'a, '_> {
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatOpeningElement<'a, 'b> {
|
||||
pub fn new(element: &'b AstNode<'a, JSXOpeningElement<'a>>, is_self_closing: bool) -> Self {
|
||||
pub fn new(element: &'b AstNode<'b, 'a, JSXOpeningElement<'a>>, is_self_closing: bool) -> Self {
|
||||
Self { element, is_self_closing }
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatOpeningElement<'a, '_> {
|
||||
|
||||
let has_multiline_string_attribute = attributes
|
||||
.iter()
|
||||
.any(|attribute| is_multiline_string_literal_attribute(attribute));
|
||||
.any(|attribute| is_multiline_string_literal_attribute(&attribute));
|
||||
write!(f, [group(&format_inner).should_expand(has_multiline_string_attribute)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSMappedType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSMappedType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if f.comments().is_suppressed(self.key.span.start) {
|
||||
return write!(f, FormatSuppressedNode(self.span));
|
||||
|
||||
@@ -17,14 +17,14 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ComputedMemberExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ComputedMemberExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, self.object());
|
||||
FormatComputedMemberExpressionWithoutObject(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, StaticMemberExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, StaticMemberExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// Before the recording: printing the object consumes its cast comment
|
||||
let object_is_cast_target = is_cast_target(self.object.span(), f);
|
||||
@@ -87,7 +87,7 @@ fn operator_token(optional: bool) -> &'static str {
|
||||
/// `object_is_cast_target`:
|
||||
/// a cast-target object is neither a call nor an identifier to the rules below (see `is_cast_target`).
|
||||
fn layout<'a>(
|
||||
node: &AstNode<'a, StaticMemberExpression<'a>>,
|
||||
node: &AstNode<'_, 'a, StaticMemberExpression<'a>>,
|
||||
is_member_chain: bool,
|
||||
object_is_cast_target: bool,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
@@ -151,7 +151,7 @@ fn layout<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, PrivateFieldExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, PrivateFieldExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, [self.object(), self.optional().then_some("?"), ".", self.field()]);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -17,8 +17,8 @@ use crate::{
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ObjectLike<'a, 'b> {
|
||||
ObjectExpression(&'b AstNode<'a, ObjectExpression<'a>>),
|
||||
TSTypeLiteral(&'b AstNode<'a, TSTypeLiteral<'a>>),
|
||||
ObjectExpression(&'b AstNode<'b, 'a, ObjectExpression<'a>>),
|
||||
TSTypeLiteral(&'b AstNode<'b, 'a, TSTypeLiteral<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> ObjectLike<'a, '_> {
|
||||
@@ -47,7 +47,7 @@ impl<'a> ObjectLike<'a, '_> {
|
||||
}
|
||||
AstNodes::TSThisParameter(param) => {
|
||||
matches!(param.parent(), AstNodes::Function(func) if {
|
||||
should_hug_function_parameters(func.params(), Some(param), false, f)
|
||||
should_hug_function_parameters(&func.params(), Some(**param), false, f)
|
||||
})
|
||||
},
|
||||
_ => false,
|
||||
|
||||
@@ -19,8 +19,8 @@ use super::{
|
||||
};
|
||||
|
||||
pub enum ObjectPatternLike<'a, 'b> {
|
||||
ObjectPattern(&'b AstNode<'a, ObjectPattern<'a>>),
|
||||
ObjectAssignmentTarget(&'b AstNode<'a, ObjectAssignmentTarget<'a>>),
|
||||
ObjectPattern(&'b AstNode<'b, 'a, ObjectPattern<'a>>),
|
||||
ObjectAssignmentTarget(&'b AstNode<'b, 'a, ObjectAssignmentTarget<'a>>),
|
||||
}
|
||||
|
||||
impl GetSpan for ObjectPatternLike<'_, '_> {
|
||||
|
||||
@@ -23,7 +23,9 @@ use super::FormatWrite;
|
||||
/// Span windows assuming source-ordered, non-overlapping fields are inverted here:
|
||||
/// the generated `following_span_start` for `this_param` (= params span start) points before it,
|
||||
/// so the generated trailing-comment pass never captures anything (see [`Parameter::This`]).
|
||||
pub fn get_this_param<'a>(parent: &AstNodes<'a>) -> Option<&'a AstNode<'a, TSThisParameter<'a>>> {
|
||||
pub fn get_this_param<'b, 'a>(
|
||||
parent: &'b AstNodes<'b, 'a>,
|
||||
) -> Option<AstNode<'b, 'a, TSThisParameter<'a>>> {
|
||||
match parent {
|
||||
AstNodes::Function(func) => func.this_param(),
|
||||
AstNodes::TSFunctionType(func) => func.this_param(),
|
||||
@@ -33,7 +35,7 @@ pub fn get_this_param<'a>(parent: &AstNodes<'a>) -> Option<&'a AstNode<'a, TSThi
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, FormalParameters<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, FormalParameters<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// `function foo /**/ () {}`
|
||||
// ^^^ keep comments printed before parameters
|
||||
@@ -104,7 +106,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, FormalParameters<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, FormalParameter<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, FormalParameter<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let content = format_with(|f| {
|
||||
let left = format_with(|f| {
|
||||
@@ -166,7 +168,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, FormalParameter<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSThisParameter<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSThisParameter<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["this", self.type_annotation()]);
|
||||
}
|
||||
@@ -174,11 +176,11 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSThisParameter<'a>> {
|
||||
|
||||
enum Parameter<'a, 'b> {
|
||||
This {
|
||||
param: &'b AstNode<'a, TSThisParameter<'a>>,
|
||||
list: &'b AstNode<'a, FormalParameters<'a>>,
|
||||
param: AstNode<'b, 'a, TSThisParameter<'a>>,
|
||||
list: &'b AstNode<'b, 'a, FormalParameters<'a>>,
|
||||
},
|
||||
Formal(&'b AstNode<'a, FormalParameter<'a>>),
|
||||
Rest(&'b AstNode<'a, FormalParameterRest<'a>>),
|
||||
Formal(AstNode<'b, 'a, FormalParameter<'a>>),
|
||||
Rest(AstNode<'b, 'a, FormalParameterRest<'a>>),
|
||||
}
|
||||
|
||||
impl GetSpan for Parameter<'_, '_> {
|
||||
@@ -215,8 +217,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for Parameter<'a, '_> {
|
||||
|
||||
struct FormalParametersIter<'a, 'b> {
|
||||
this: Option<Parameter<'a, 'b>>,
|
||||
params: AstNodeIterator<'a, FormalParameter<'a>>,
|
||||
rest: Option<&'b AstNode<'a, FormalParameterRest<'a>>>,
|
||||
params: AstNodeIterator<'b, 'a, FormalParameter<'a>>,
|
||||
rest: Option<AstNode<'b, 'a, FormalParameterRest<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b ParameterList<'a, 'b>> for FormalParametersIter<'a, 'b> {
|
||||
@@ -243,8 +245,8 @@ impl<'a, 'b> Iterator for FormalParametersIter<'a, 'b> {
|
||||
}
|
||||
|
||||
pub struct ParameterList<'a, 'b> {
|
||||
list: &'b AstNode<'a, FormalParameters<'a>>,
|
||||
this: Option<&'b AstNode<'a, TSThisParameter<'a>>>,
|
||||
list: &'b AstNode<'b, 'a, FormalParameters<'a>>,
|
||||
this: Option<AstNode<'b, 'a, TSThisParameter<'a>>>,
|
||||
layout: Option<ParameterLayout>,
|
||||
trailing_separator_override: Option<TrailingSeparator>,
|
||||
}
|
||||
@@ -284,8 +286,8 @@ pub enum ParameterLayout {
|
||||
|
||||
impl<'a, 'b> ParameterList<'a, 'b> {
|
||||
pub fn with_layout(
|
||||
list: &'b AstNode<'a, FormalParameters<'a>>,
|
||||
this: Option<&'b AstNode<'a, TSThisParameter<'a>>>,
|
||||
list: &'b AstNode<'b, 'a, FormalParameters<'a>>,
|
||||
this: Option<AstNode<'b, 'a, TSThisParameter<'a>>>,
|
||||
layout: ParameterLayout,
|
||||
) -> Self {
|
||||
Self { list, this, layout: Some(layout), trailing_separator_override: None }
|
||||
@@ -364,8 +366,8 @@ pub fn can_avoid_parentheses(arrow: &ArrowFunctionExpression<'_>, f: &JsFormatte
|
||||
/// (`FormalParameters` before printing, `FormalParameter` after its leading comments, the type literal while printing),
|
||||
/// so every comment check here must be position-based to give all of them the same answer.
|
||||
pub fn should_hug_function_parameters<'a>(
|
||||
parameters: &AstNode<'a, FormalParameters<'a>>,
|
||||
this_param: Option<&AstNode<'a, TSThisParameter<'a>>>,
|
||||
parameters: &AstNode<'_, 'a, FormalParameters<'a>>,
|
||||
this_param: Option<AstNode<'_, 'a, TSThisParameter<'a>>>,
|
||||
parentheses_not_needed: bool,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Program<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Program<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// BOM: JS is the exception to the entries-own-the-strip rule.
|
||||
// `format_program` is AST-in (the formatter never owns pre-parse text)
|
||||
@@ -36,7 +36,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, Program<'a>> {
|
||||
|
||||
/// A program without its envelope (BOM, trailing newline): the embedded root of [`crate::format_to_ir`],
|
||||
/// whose host owns the layout around it.
|
||||
pub struct FormatProgramBody<'a, 'b>(pub &'b AstNode<'a, Program<'a>>);
|
||||
pub struct FormatProgramBody<'a, 'b>(pub &'b AstNode<'b, 'a, Program<'a>>);
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for FormatProgramBody<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
@@ -53,7 +53,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatProgramBody<'a, '_> {
|
||||
[
|
||||
program.hashbang(),
|
||||
program.directives(),
|
||||
FormatStatementsWithImports(program.body()),
|
||||
FormatStatementsWithImports(&program.body()),
|
||||
format_trailing_comments
|
||||
]
|
||||
);
|
||||
@@ -61,11 +61,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatProgramBody<'a, '_> {
|
||||
}
|
||||
|
||||
pub(super) struct FormatStatementsWithImports<'a, 'b>(
|
||||
pub &'b AstNode<'a, ArenaVec<'a, Statement<'a>>>,
|
||||
pub &'b AstNode<'b, 'a, ArenaVec<'a, Statement<'a>>>,
|
||||
);
|
||||
|
||||
impl<'a> Deref for FormatStatementsWithImports<'a, '_> {
|
||||
type Target = AstNode<'a, ArenaVec<'a, Statement<'a>>>;
|
||||
impl<'a, 'b> Deref for FormatStatementsWithImports<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, ArenaVec<'a, Statement<'a>>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
@@ -83,16 +83,16 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatStatementsWithImports<'a, '_>
|
||||
// so they are excluded from the sortable run.
|
||||
if import_sort_enabled
|
||||
&& matches!(stmt.as_ref(), Statement::ImportDeclaration(_))
|
||||
&& !is_import_suppressed(stmt, join.fmt())
|
||||
&& !is_import_suppressed(&stmt, join.fmt())
|
||||
{
|
||||
let next_stmt = format_import_decls_with_sort(stmt, &mut stmts_iter, &mut join);
|
||||
let next_stmt = format_import_decls_with_sort(&stmt, &mut stmts_iter, &mut join);
|
||||
match next_stmt {
|
||||
Some(next_stmt) => stmt = next_stmt,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
join.entry(statement_span(stmt.as_ref()), stmt);
|
||||
join.entry(statement_span(stmt.as_ref()), &stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,10 +112,10 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatStatementsWithImports<'a, '_>
|
||||
// This function consumes the whole run, so is unlikely to be called more than once, even in files with lots of imports.
|
||||
#[cold]
|
||||
fn format_import_decls_with_sort<'a, 'iter>(
|
||||
stmt: &AstNode<'a, Statement<'a>>,
|
||||
stmts_iter: &mut impl Iterator<Item = &'iter AstNode<'a, Statement<'a>>>,
|
||||
stmt: &AstNode<'_, 'a, Statement<'a>>,
|
||||
stmts_iter: &mut impl Iterator<Item = AstNode<'iter, 'a, Statement<'a>>>,
|
||||
join: &mut JoinNodesBuilder<'_, '_, 'a, Line>,
|
||||
) -> Option<&'iter AstNode<'a, Statement<'a>>> {
|
||||
) -> Option<AstNode<'iter, 'a, Statement<'a>>> {
|
||||
// Output inter-statement separator separately, so `chunk_start` points
|
||||
// to start of IR for the `ImportDeclaration` itself
|
||||
join.separator_no_entry(stmt.span());
|
||||
@@ -131,11 +131,11 @@ fn format_import_decls_with_sort<'a, 'iter>(
|
||||
let mut next_stmt = None;
|
||||
for stmt in stmts_iter {
|
||||
if let Statement::ImportDeclaration(decl) = stmt.as_ref() {
|
||||
if is_import_suppressed(stmt, join.fmt()) {
|
||||
if is_import_suppressed(&stmt, join.fmt()) {
|
||||
next_stmt = Some(stmt);
|
||||
break;
|
||||
}
|
||||
join.entry(decl.span, stmt);
|
||||
join.entry(decl.span, &stmt);
|
||||
count += 1;
|
||||
} else {
|
||||
next_stmt = Some(stmt);
|
||||
@@ -153,12 +153,12 @@ fn format_import_decls_with_sort<'a, 'iter>(
|
||||
|
||||
/// An `ImportDeclaration` is suppressed if it has a leading or trailing suppression comment,
|
||||
/// which causes it to be emitted verbatim and act as a partition boundary, excluding it from the sortable run.
|
||||
fn is_import_suppressed(stmt: &AstNode<'_, Statement<'_>>, f: &JsFormatter<'_, '_>) -> bool {
|
||||
fn is_import_suppressed(stmt: &AstNode<'_, '_, Statement<'_>>, f: &JsFormatter<'_, '_>) -> bool {
|
||||
f.comments()
|
||||
.is_node_suppressed(stmt.span(), || suppressed_statement_content_end(stmt.as_ref(), f))
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, Directive<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, Directive<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let Some(last_directive) = self.last() else {
|
||||
// No directives, no extra new line
|
||||
@@ -195,7 +195,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, Directive<
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Directive<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Directive<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(
|
||||
f,
|
||||
@@ -212,7 +212,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, Directive<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, Hashbang<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, Hashbang<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, ["#!", text(self.value().as_str().trim_end())]);
|
||||
|
||||
|
||||
@@ -9,11 +9,9 @@ use crate::{
|
||||
prelude::*,
|
||||
trivia::{DanglingIndentMode, FormatDanglingComments},
|
||||
},
|
||||
print::{
|
||||
ExpressionLeftSide,
|
||||
semicolon::{OptionalSemicolon, assignment_chain_leaf_end},
|
||||
},
|
||||
print::semicolon::{OptionalSemicolon, assignment_chain_leaf_end},
|
||||
utils::{
|
||||
expression::LeftSide,
|
||||
format_node_without_trailing_comments::format_content_without_comments_after,
|
||||
typecast::format_leading_comments_and_open_paren,
|
||||
},
|
||||
@@ -22,13 +20,13 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ReturnStatement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ReturnStatement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
ReturnAndThrowStatement::ReturnStatement(self).fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, ThrowStatement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, ThrowStatement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
ReturnAndThrowStatement::ThrowStatement(self).fmt(f);
|
||||
}
|
||||
@@ -36,8 +34,8 @@ impl<'a> FormatWrite<'a> for AstNode<'a, ThrowStatement<'a>> {
|
||||
|
||||
/// Unified enum for statements that have an optional argument (return/throw)
|
||||
pub enum ReturnAndThrowStatement<'a, 'b> {
|
||||
ReturnStatement(&'b AstNode<'a, ReturnStatement<'a>>),
|
||||
ThrowStatement(&'b AstNode<'a, ThrowStatement<'a>>),
|
||||
ReturnStatement(&'b AstNode<'b, 'a, ReturnStatement<'a>>),
|
||||
ThrowStatement(&'b AstNode<'b, 'a, ThrowStatement<'a>>),
|
||||
}
|
||||
|
||||
impl<'a, 'b> ReturnAndThrowStatement<'a, 'b> {
|
||||
@@ -50,7 +48,7 @@ impl<'a, 'b> ReturnAndThrowStatement<'a, 'b> {
|
||||
}
|
||||
|
||||
/// Get the argument expression if present
|
||||
fn argument(&self) -> Option<&'b AstNode<'a, Expression<'a>>> {
|
||||
fn argument(&self) -> Option<AstNode<'b, 'a, Expression<'a>>> {
|
||||
match self {
|
||||
Self::ReturnStatement(node) => node.argument(),
|
||||
Self::ThrowStatement(node) => Some(node.argument()),
|
||||
@@ -88,12 +86,12 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ReturnAndThrowStatement<'a, '_> {
|
||||
if f.comments().has_trailing_suppression_comment(argument_end) {
|
||||
// Keep a trailing suppression comment visible to the argument,
|
||||
// so it preserves its original text.
|
||||
write!(f, FormatAdjacentArgument(argument));
|
||||
write!(f, FormatAdjacentArgument(&argument));
|
||||
} else {
|
||||
// Hide the argument's dangling comments so they are printed
|
||||
// after the semicolon by the dangling comments logic below.
|
||||
format_content_without_comments_after(
|
||||
&FormatAdjacentArgument(argument),
|
||||
&FormatAdjacentArgument(&argument),
|
||||
argument_end,
|
||||
f,
|
||||
);
|
||||
@@ -101,7 +99,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ReturnAndThrowStatement<'a, '_> {
|
||||
} else {
|
||||
// The common case:
|
||||
// no comment between the argument and the end of the statement, nothing to relocate.
|
||||
write!(f, FormatAdjacentArgument(argument));
|
||||
write!(f, FormatAdjacentArgument(&argument));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +130,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ReturnAndThrowStatement<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FormatAdjacentArgument<'a, 'b>(pub &'b AstNode<'a, Expression<'a>>);
|
||||
pub struct FormatAdjacentArgument<'a, 'b>(pub &'b AstNode<'b, 'a, Expression<'a>>);
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for FormatAdjacentArgument<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
@@ -186,7 +184,7 @@ fn has_argument_leading_comments(argument: &AstNode<Expression>, f: &JsFormatter
|
||||
.get_type_cast_comment_index(argument.span())
|
||||
.map(|idx| comments.unprinted_comments()[idx].span.end);
|
||||
|
||||
for left_side in ExpressionLeftSide::from(argument).iter() {
|
||||
for left_side in LeftSide::Expression(argument.inner()).iter() {
|
||||
let start = left_side.span().start;
|
||||
let leading_comments = comments.comments_before(start);
|
||||
|
||||
@@ -211,8 +209,8 @@ fn has_argument_leading_comments(argument: &AstNode<Expression>, f: &JsFormatter
|
||||
|
||||
// This check is based on
|
||||
// <https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js#L335-L349>
|
||||
if let ExpressionLeftSide::Expression(left_side) = left_side {
|
||||
let has_leading_own_line_comment = match left_side.as_ref() {
|
||||
if let LeftSide::Expression(left_side) = left_side {
|
||||
let has_leading_own_line_comment = match left_side {
|
||||
Expression::ChainExpression(chain) => {
|
||||
if let ChainElement::StaticMemberExpression(member) = &chain.expression {
|
||||
let comments = f
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxc_formatter_core::Format;
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::{
|
||||
JsFormatContext, JsFormatter, JsFormatterExt as _,
|
||||
trivia::{FormatTrailingComments, format_leading_comments},
|
||||
@@ -187,7 +187,7 @@ fn arrow_body_keeps_trailing_comment_inside_parens(body: &Expression<'_>) -> boo
|
||||
/// only a sequence keeps its parens as an assignment's right-hand side.
|
||||
pub fn write_trailing_comments_inside_parens<'a>(
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
parent: &AstNodes<'a>,
|
||||
parent: &AstNodes<'_, 'a>,
|
||||
node_end: u32,
|
||||
is_sequence: bool,
|
||||
) {
|
||||
@@ -271,7 +271,7 @@ where
|
||||
/// own-line ones are left for the next node's leading pass;
|
||||
/// the caller (the generated `Statement` fmt) prints the trailing comments.
|
||||
pub fn write_suppressed_statement<'a>(
|
||||
stmt: &AstNode<'a, Statement<'a>>,
|
||||
stmt: &AstNode<'_, 'a, Statement<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let span = statement_span(stmt.as_ref());
|
||||
@@ -279,10 +279,12 @@ pub fn write_suppressed_statement<'a>(
|
||||
{
|
||||
return false;
|
||||
}
|
||||
match stmt.as_ast_nodes() {
|
||||
match &stmt.as_kind() {
|
||||
// The `semi: false` ASI guard goes before a leading type cast comment, verbatim or not:
|
||||
// Prettier's `printIgnored` puts it after and breaks the cast (DIVERGENCES.md#suppressed-cast-comment-asi-guard)
|
||||
AstNodes::ExpressionStatement(stmt) => write_leading_comments_with_asi_guard(stmt, true, f),
|
||||
AstNodeKind::ExpressionStatement(stmt) => {
|
||||
write_leading_comments_with_asi_guard(stmt, true, f);
|
||||
}
|
||||
_ => format_leading_comments(span).fmt(f),
|
||||
}
|
||||
if write_suppressed_content(span, suppressed_statement_content_end(stmt.as_ref(), f), f) {
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn sequence_leading_comments_start(sequence: &SequenceExpression<'_>) -> u32
|
||||
sequence.expressions.first().map_or(sequence.span.start, |e| e.span().start)
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, SequenceExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, SequenceExpression<'a>> {
|
||||
fn leading_comments_start(&self) -> u32 {
|
||||
sequence_leading_comments_start(self)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, SwitchStatement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, SwitchStatement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let discriminant = self.discriminant();
|
||||
let cases = self.cases();
|
||||
@@ -53,13 +53,13 @@ impl<'a> FormatWrite<'a> for AstNode<'a, SwitchStatement<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, SwitchCase<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, SwitchCase<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
f.join_nodes_with_hardline().entries(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, SwitchCase<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, SwitchCase<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// Whether the first statement in the clause is a `BlockStatement`
|
||||
// and there are no other non-empty statements.
|
||||
@@ -74,7 +74,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, SwitchCase<'a>> {
|
||||
// For non-block consequents the generic pass's `line_suffix` flush lands
|
||||
// before their line break, keeping the comment on the clause line,
|
||||
// so the bound is only needed before a block's `{`.
|
||||
write_node_with_terminator(test, ":", f);
|
||||
write_node_with_terminator(&test, ":", f);
|
||||
} else {
|
||||
write!(f, [test, ":"]);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, SwitchCase<'a>> {
|
||||
let consequent = self.consequent();
|
||||
if is_single_block_statement {
|
||||
// `unwrap` is safe: the empty consequent returned above.
|
||||
write!(f, FormatStatementBody::new(consequent.first().unwrap()));
|
||||
write!(f, FormatStatementBody::new(&consequent.first().unwrap()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ fn write_text_piece<'a>(text: &'a str, indent_width: IndentWidth, f: &mut JsForm
|
||||
|
||||
/// Print `${expr}` like a plain template expression, minus source-indentation preservation.
|
||||
/// Default options (zero indention) are exactly that (same as graphql.rs).
|
||||
fn write_expression<'a>(expr: &AstNode<'a, Expression<'a>>, f: &mut JsFormatter<'_, 'a>) {
|
||||
fn write_expression<'a>(expr: AstNode<'_, 'a, Expression<'a>>, f: &mut JsFormatter<'_, 'a>) {
|
||||
let te = TemplateExpression::Expression(expr);
|
||||
FormatTemplateExpression::new(&te, FormatTemplateExpressionOptions::default()).fmt(f);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ fn write_expression<'a>(expr: &AstNode<'a, Expression<'a>>, f: &mut JsFormatter<
|
||||
/// Joins quasis with special placeholder markers, formats as SCSS,
|
||||
/// then replaces placeholder occurrences in the resulting IR with `${expr}` Docs.
|
||||
pub(super) fn format_css_doc<'a>(
|
||||
quasi: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
quasi: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let quasis = &quasi.quasis;
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
/// - tagged template (gql`...`)
|
||||
/// - and function call (`graphql(schema, `...`)`)
|
||||
pub(super) fn format_graphql_doc<'a>(
|
||||
quasi: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
quasi: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let quasis = &quasi.quasis;
|
||||
@@ -139,7 +139,7 @@ pub(super) fn format_graphql_doc<'a>(
|
||||
if has_prev_part {
|
||||
write!(f, [hard_line_break()]);
|
||||
}
|
||||
let te = TemplateExpression::Expression(expr);
|
||||
let te = TemplateExpression::Expression(*expr);
|
||||
FormatTemplateExpression::new(&te, FormatTemplateExpressionOptions::default())
|
||||
.fmt(f);
|
||||
has_prev_part = true;
|
||||
|
||||
@@ -30,7 +30,7 @@ const COUNTER: &str = "0";
|
||||
///
|
||||
/// Supports both html-in-js and angular-in-js (`@Component({ template })`).
|
||||
pub(super) fn format_html_doc<'a>(
|
||||
quasi: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
quasi: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
is_angular: bool,
|
||||
) -> bool {
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{MarkdownInJsTemplate, ast_nodes::AstNode, format_args, formatter::pr
|
||||
/// Unescapes backticks in `.raw`, strips common indentation, formats as markdown,
|
||||
/// then re-escapes backticks and applies indented or dedent-to-root layout.
|
||||
pub(super) fn try_embed_markdown<'a>(
|
||||
tagged: &AstNode<'a, TaggedTemplateExpression<'a>>,
|
||||
tagged: &AstNode<'_, 'a, TaggedTemplateExpression<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let raw = tagged.quasi.quasis[0].value.raw.as_str();
|
||||
|
||||
@@ -62,7 +62,7 @@ fn template_comment_language(
|
||||
/// Try to format a template literal with the embedded formatter if supported.
|
||||
/// Returns `true` if formatting was performed, `false` if not applicable.
|
||||
pub(super) fn try_format_template_literal<'a>(
|
||||
template: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
template: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
// Without a dispatcher (`embeddedLanguageFormatting: off`), embeds print verbatim
|
||||
@@ -82,16 +82,16 @@ pub(super) fn try_format_template_literal<'a>(
|
||||
/// Try to format a tagged template with the embedded formatter if supported.
|
||||
/// Returns `true` if formatting was performed, `false` if not applicable.
|
||||
pub(super) fn try_format_embedded_template<'a>(
|
||||
tagged: &AstNode<'a, TaggedTemplateExpression<'a>>,
|
||||
tagged: &AstNode<'_, 'a, TaggedTemplateExpression<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
if !f.session().has_dispatcher() {
|
||||
return false;
|
||||
}
|
||||
match tagged_template_language(tagged) {
|
||||
Some(EmbedLanguage::Css) => css::format_css_doc(tagged.quasi(), f),
|
||||
Some(EmbedLanguage::Graphql) => graphql::format_graphql_doc(tagged.quasi(), f),
|
||||
Some(EmbedLanguage::Html) => html::format_html_doc(tagged.quasi(), f, false),
|
||||
Some(EmbedLanguage::Css) => css::format_css_doc(&tagged.quasi(), f),
|
||||
Some(EmbedLanguage::Graphql) => graphql::format_graphql_doc(&tagged.quasi(), f),
|
||||
Some(EmbedLanguage::Html) => html::format_html_doc(&tagged.quasi(), f, false),
|
||||
Some(EmbedLanguage::Markdown) => markdown::try_embed_markdown(tagged, f),
|
||||
None => false,
|
||||
}
|
||||
@@ -170,7 +170,7 @@ pub fn embed_hug(
|
||||
/// Try to format a template literal inside a `graphql()` function call.
|
||||
/// Returns `true` if formatting was performed, `false` if not applicable.
|
||||
fn try_format_graphql_call<'a>(
|
||||
template: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
template: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let AstNodes::CallExpression(call) = template.parent() else { return false };
|
||||
@@ -187,7 +187,7 @@ fn try_format_graphql_call<'a>(
|
||||
/// - HTML
|
||||
/// - GraphQL
|
||||
fn try_format_comment_embedded<'a>(
|
||||
template: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
template: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
match template_comment_language(template, f) {
|
||||
@@ -200,7 +200,7 @@ fn try_format_comment_embedded<'a>(
|
||||
/// Try to format a template literal inside css prop or styled-jsx with the embedded formatter.
|
||||
/// Returns `true` if formatting was attempted, `false` if not applicable.
|
||||
fn try_format_css_template<'a>(
|
||||
template_literal: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
template_literal: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
if !is_in_css_jsx(template_literal) {
|
||||
@@ -210,7 +210,7 @@ fn try_format_css_template<'a>(
|
||||
}
|
||||
|
||||
/// Check if the template literal is inside a `css` prop or `<style jsx>` element.
|
||||
fn is_in_css_jsx<'a>(node: &AstNode<'a, TemplateLiteral<'a>>) -> bool {
|
||||
fn is_in_css_jsx<'a>(node: &AstNode<'_, 'a, TemplateLiteral<'a>>) -> bool {
|
||||
let AstNodes::JSXExpressionContainer(container) = node.parent() else {
|
||||
return false;
|
||||
};
|
||||
@@ -241,7 +241,7 @@ fn is_in_css_jsx<'a>(node: &AstNode<'a, TemplateLiteral<'a>>) -> bool {
|
||||
/// Try to format a template literal inside Angular @Component's template/styles property.
|
||||
/// Returns `true` if formatting was performed, `false` if not applicable.
|
||||
fn try_format_angular_component<'a>(
|
||||
template_literal: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
template_literal: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
match get_angular_component_property(template_literal) {
|
||||
@@ -252,7 +252,9 @@ fn try_format_angular_component<'a>(
|
||||
}
|
||||
|
||||
/// Detect Angular `@Component({ template: \`...\`, styles: \`...\` })`.
|
||||
fn get_angular_component_property<'a>(node: &AstNode<'a, TemplateLiteral<'a>>) -> Option<&'a str> {
|
||||
fn get_angular_component_property<'a>(
|
||||
node: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
) -> Option<&'a str> {
|
||||
let prop = match node.parent() {
|
||||
AstNodes::ObjectProperty(prop) => prop,
|
||||
AstNodes::ArrayExpression(arr) => {
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TemplateLiteral<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TemplateLiteral<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if embed::try_format_template_literal(self, f) {
|
||||
return;
|
||||
@@ -40,7 +40,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TemplateLiteral<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TaggedTemplateExpression<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TaggedTemplateExpression<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// Format the tag and type arguments.
|
||||
// Comments between the tag (or type arguments) and the quasi always belong to the quasi
|
||||
@@ -54,9 +54,9 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TaggedTemplateExpression<'a>> {
|
||||
// printing ``foo /* c */`x`;``.
|
||||
// Unlike the no-newline form which attaches to the quasi, so formatting is not idempotent.
|
||||
if let Some(type_arguments) = self.type_arguments() {
|
||||
write!(f, [self.tag(), FormatNodeWithoutTrailingComments(type_arguments)]);
|
||||
write!(f, [self.tag(), FormatNodeWithoutTrailingComments(&type_arguments)]);
|
||||
} else {
|
||||
write!(f, [FormatNodeWithoutTrailingComments(self.tag())]);
|
||||
write!(f, [FormatNodeWithoutTrailingComments(&self.tag())]);
|
||||
}
|
||||
|
||||
let quasi = self.quasi();
|
||||
@@ -99,12 +99,12 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TaggedTemplateExpression<'a>> {
|
||||
}
|
||||
|
||||
if embed::try_format_embedded_template(self, f) {
|
||||
} else if is_test_each_pattern(&self.tag) && EachTemplateTable::is_table_like(quasi) {
|
||||
let template = &EachTemplateTable::from_template(quasi, f);
|
||||
} else if is_test_each_pattern(&self.tag) && EachTemplateTable::is_table_like(&quasi) {
|
||||
let template = &EachTemplateTable::from_template(&quasi, f);
|
||||
// Use table formatting
|
||||
write!(f, template);
|
||||
} else {
|
||||
let template = TemplateLike::TemplateLiteral(quasi);
|
||||
let template = TemplateLike::TemplateLiteral(&quasi);
|
||||
write!(f, template);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TaggedTemplateExpression<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TemplateElement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TemplateElement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let source = f.source_text().text_for(self);
|
||||
|
||||
@@ -133,7 +133,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TemplateElement<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSTemplateLiteralType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSTemplateLiteralType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let template = TemplateLike::TSTemplateLiteralType(self);
|
||||
write!(f, template);
|
||||
@@ -207,13 +207,13 @@ impl TemplateElementIndention {
|
||||
|
||||
/// Unified enum for handling both JS template literals and TS template literal types
|
||||
pub enum TemplateLike<'a, 'b> {
|
||||
TemplateLiteral(&'b AstNode<'a, TemplateLiteral<'a>>),
|
||||
TSTemplateLiteralType(&'b AstNode<'a, TSTemplateLiteralType<'a>>),
|
||||
TemplateLiteral(&'b AstNode<'b, 'a, TemplateLiteral<'a>>),
|
||||
TSTemplateLiteralType(&'b AstNode<'b, 'a, TSTemplateLiteralType<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> TemplateLike<'a, '_> {
|
||||
impl<'a, 'b> TemplateLike<'a, 'b> {
|
||||
#[inline]
|
||||
pub fn quasis(&self) -> &AstNode<'a, ArenaVec<'a, TemplateElement<'a>>> {
|
||||
pub fn quasis(&self) -> AstNode<'b, 'a, ArenaVec<'a, TemplateElement<'a>>> {
|
||||
match self {
|
||||
Self::TemplateLiteral(t) => t.quasis(),
|
||||
Self::TSTemplateLiteralType(t) => t.quasis(),
|
||||
@@ -222,13 +222,13 @@ impl<'a> TemplateLike<'a, '_> {
|
||||
}
|
||||
|
||||
/// Iterator that yields template expressions without allocation
|
||||
enum TemplateExpressionIterator<'a> {
|
||||
Expression(AstNodeIterator<'a, Expression<'a>>),
|
||||
TSType(AstNodeIterator<'a, TSType<'a>>),
|
||||
enum TemplateExpressionIterator<'a, 'b> {
|
||||
Expression(AstNodeIterator<'b, 'a, Expression<'a>>),
|
||||
TSType(AstNodeIterator<'b, 'a, TSType<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TemplateExpressionIterator<'a> {
|
||||
type Item = TemplateExpression<'a, 'a>;
|
||||
impl<'a, 'b> Iterator for TemplateExpressionIterator<'a, 'b> {
|
||||
type Item = TemplateExpression<'a, 'b>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
@@ -268,7 +268,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for TemplateLike<'a, '_> {
|
||||
f.context_mut().push_tailwind_context(ctx.with_quasi_position(is_first, is_last));
|
||||
}
|
||||
|
||||
write!(f, *quasi);
|
||||
write!(f, quasi);
|
||||
|
||||
// Pop quasi position context
|
||||
if tailwind_ctx.is_some() {
|
||||
@@ -329,12 +329,12 @@ pub struct FormatTemplateExpressionOptions {
|
||||
}
|
||||
|
||||
pub enum TemplateExpression<'a, 'b> {
|
||||
Expression(&'b AstNode<'a, Expression<'a>>),
|
||||
TSType(&'b AstNode<'a, TSType<'a>>),
|
||||
Expression(AstNode<'b, 'a, Expression<'a>>),
|
||||
TSType(AstNode<'b, 'a, TSType<'a>>),
|
||||
}
|
||||
|
||||
impl TemplateExpression<'_, '_> {
|
||||
pub fn as_expression(&self) -> Option<&AstNode<'_, Expression<'_>>> {
|
||||
pub fn as_expression(&self) -> Option<&AstNode<'_, '_, Expression<'_>>> {
|
||||
match self {
|
||||
Self::Expression(e) => Some(e),
|
||||
Self::TSType(_) => None,
|
||||
@@ -677,7 +677,7 @@ impl<'a> EachTemplateTable<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn from_template(
|
||||
quasi: &AstNode<'a, TemplateLiteral<'a>>,
|
||||
quasi: &AstNode<'_, 'a, TemplateLiteral<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> Self {
|
||||
let mut builder = EachTemplateTableBuilder::new();
|
||||
@@ -696,7 +696,7 @@ impl<'a> EachTemplateTable<'a> {
|
||||
|
||||
builder.entry(EachTemplateElement::LineBreak);
|
||||
|
||||
for expr in quasi.expressions() {
|
||||
for expr in &quasi.expressions() {
|
||||
let options = FormatTemplateExpressionOptions {
|
||||
after_new_line: false,
|
||||
indention: TemplateElementIndention::default(),
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::utils::{
|
||||
statement_body::{write_comments_between_blocks, write_head_body_separator},
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TryStatement<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TryStatement<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let block = self.block();
|
||||
let handler = self.handler();
|
||||
@@ -25,11 +25,11 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TryStatement<'a>> {
|
||||
|
||||
write!(f, "try");
|
||||
write_head_body_separator(block.span.start, f);
|
||||
write_block_before_keyword(block, handler.is_some() || finalizer.is_some(), f);
|
||||
write_block_before_keyword(&block, handler.is_some() || finalizer.is_some(), f);
|
||||
|
||||
if let Some(handler) = handler {
|
||||
write!(f, space());
|
||||
write_block_before_keyword(handler, finalizer.is_some(), f);
|
||||
write_block_before_keyword(&handler, finalizer.is_some(), f);
|
||||
}
|
||||
if let Some(finalizer) = finalizer {
|
||||
// Lexical scan for the keyword's first byte: the gap holds only trivia and `finally`.
|
||||
@@ -58,7 +58,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, CatchClause<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, CatchClause<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let leading_comments = f.context().comments().comments_before(self.span.start);
|
||||
if write_comments_between_blocks(leading_comments, f) {
|
||||
@@ -73,7 +73,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, CatchClause<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, CatchParameter<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, CatchParameter<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, "(");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSTupleElement<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, TSTupleElement<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let trailing_separator = FormatTrailingCommas::ES5.trailing_separator(f.options());
|
||||
f.join_nodes_with_soft_line().entries_with_trailing_separator(
|
||||
@@ -21,7 +21,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSTupleEle
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSTupleType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSTupleType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
write!(f, "[");
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
|
||||
use super::FormatWrite;
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSTypeParameter<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSTypeParameter<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if self.r#const() {
|
||||
write!(f, ["const", space()]);
|
||||
@@ -67,7 +67,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSTypeParameter<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSTypeParameter<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, TSTypeParameter<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// A lone type parameter on an arrow function may need a mandatory trailing comma;
|
||||
let trailing_separator = if should_force_trailing_comma_for_arrow_function(self, f) {
|
||||
@@ -95,7 +95,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSTypePara
|
||||
/// This is a deliberate divergence: removal is keyed on the grammar the source is consumed as,
|
||||
/// not on the host file's path, so plain-TS sources behave identically wherever they live.
|
||||
fn should_force_trailing_comma_for_arrow_function(
|
||||
params: &AstNode<'_, ArenaVec<'_, TSTypeParameter<'_>>>,
|
||||
params: &AstNode<'_, '_, ArenaVec<'_, TSTypeParameter<'_>>>,
|
||||
f: &JsFormatter<'_, '_>,
|
||||
) -> bool {
|
||||
if params.len() != 1 {
|
||||
@@ -125,13 +125,13 @@ pub struct FormatTSTypeParametersOptions {
|
||||
}
|
||||
|
||||
pub struct FormatTSTypeParameters<'a, 'b> {
|
||||
decl: &'b AstNode<'a, TSTypeParameterDeclaration<'a>>,
|
||||
decl: &'b AstNode<'b, 'a, TSTypeParameterDeclaration<'a>>,
|
||||
options: FormatTSTypeParametersOptions,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatTSTypeParameters<'a, 'b> {
|
||||
pub fn new(
|
||||
decl: &'b AstNode<'a, TSTypeParameterDeclaration<'a>>,
|
||||
decl: &'b AstNode<'b, 'a, TSTypeParameterDeclaration<'a>>,
|
||||
options: FormatTSTypeParametersOptions,
|
||||
) -> Self {
|
||||
Self { decl, options }
|
||||
@@ -149,7 +149,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatTSTypeParameters<'a, '_> {
|
||||
[group(&format_args!("<", format_with(|f| {
|
||||
if matches!(self.decl.grand_parent(), AstNodes::CallExpression(call) if is_test_call_expression(call, f.comments()))
|
||||
{
|
||||
f.join_nodes_with_space().entries_with_trailing_separator(params, ",", TrailingSeparator::Omit);
|
||||
f.join_nodes_with_space().entries_with_trailing_separator(¶ms, ",", TrailingSeparator::Omit);
|
||||
} else {
|
||||
soft_block_indent(¶ms).fmt(f);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatTSTypeParameters<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSTypeParameterInstantiation<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSTypeParameterInstantiation<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let params = self.params();
|
||||
|
||||
@@ -191,7 +191,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSTypeParameterInstantiation<'a>> {
|
||||
|
||||
let format_params = format_with(|f| {
|
||||
f.join_with(&soft_line_break_or_space()).entries_with_trailing_separator(
|
||||
params,
|
||||
¶ms,
|
||||
",",
|
||||
TrailingSeparator::Disallowed,
|
||||
);
|
||||
@@ -226,7 +226,7 @@ fn should_hug_single_type(ty: &TSType, f: &JsFormatter<'_, '_>) -> bool {
|
||||
/// const foo: SomeThing<{ [P in "x" | "y"]: number }> = () => {};
|
||||
/// ```
|
||||
fn is_arrow_function_variable_type_argument<'a>(
|
||||
node: &AstNode<'a, TSTypeParameterInstantiation<'a>>,
|
||||
node: &AstNode<'_, 'a, TSTypeParameterInstantiation<'a>>,
|
||||
) -> bool {
|
||||
let Some(first) = node.params().first() else { unreachable!() };
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
write,
|
||||
};
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, TSUnionType<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, TSUnionType<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
// A suppression comment before the union covers the whole union,
|
||||
// decided by the generated `fmt` like any other node (AGENTS.md "Suppression", DIVERGENCES.md#union-suppression-whole);
|
||||
@@ -74,7 +74,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSUnionType<'a>> {
|
||||
let has_alias_level_own_line_comments =
|
||||
is_alias_level && (has_comment_before_pipe || has_comment_after_pipe);
|
||||
if !has_alias_level_own_line_comments {
|
||||
return format_union_types(self.types(), Span::default(), true, f);
|
||||
return format_union_types(&self.types(), Span::default(), true, f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSUnionType<'a>> {
|
||||
|
||||
write!(f, [if_group_breaks(&separator)]);
|
||||
|
||||
format_union_types(types, suppressed_node_span, false, f);
|
||||
format_union_types(&types, suppressed_node_span, false, f);
|
||||
});
|
||||
|
||||
let content = format_with(|f| {
|
||||
@@ -361,7 +361,7 @@ pub fn alias_union_breaks_after_operator(
|
||||
}
|
||||
|
||||
fn should_indent_alias_union<'a>(
|
||||
alias: &AstNode<'a, TSTypeAliasDeclaration<'a>>,
|
||||
alias: &AstNode<'_, 'a, TSTypeAliasDeclaration<'a>>,
|
||||
comment_info: LeadingCommentsInfo,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
@@ -374,7 +374,7 @@ fn should_indent_alias_union<'a>(
|
||||
}
|
||||
|
||||
fn format_union_types<'a>(
|
||||
node: &AstNode<'a, ArenaVec<'a, TSType<'a>>>,
|
||||
node: &AstNode<'_, 'a, ArenaVec<'a, TSType<'a>>>,
|
||||
mut suppressed_node_span: Span,
|
||||
should_hug: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
@@ -461,7 +461,7 @@ fn format_union_types<'a>(
|
||||
// ^^^^^^^^^^^ the following logic is to print comment2,
|
||||
// )[]; // comment 3
|
||||
//```
|
||||
// TODO: We may need to tweak `AstNode<'a, ArenaVec<'a, T>>` iterator as some of Vec's last elements should have the following span.
|
||||
// TODO: We may need to tweak `AstNode<'_, 'a, ArenaVec<'a, T>>` iterator as some of Vec's last elements should have the following span.
|
||||
let comments = f.context().comments().end_of_line_comments_after(element_span.end);
|
||||
write!(f, FormatTrailingComments::Comments(comments));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use super::FormatWrite;
|
||||
/// terminated by the head itself (the head-vs-body test is by span: a declaration can also be the body).
|
||||
/// Everywhere else the declaration terminates itself, including `export const ...`,
|
||||
/// whose `ExportDeclaration` prints no semicolon of its own (see its `FormatWrite` implementation).
|
||||
fn is_for_head_declaration(decl: &AstNode<'_, VariableDeclaration<'_>>) -> bool {
|
||||
fn is_for_head_declaration(decl: &AstNode<'_, '_, VariableDeclaration<'_>>) -> bool {
|
||||
match decl.parent() {
|
||||
AstNodes::ForStatement(stmt) => {
|
||||
stmt.init.as_ref().is_some_and(|init| init.span() == decl.span())
|
||||
@@ -32,7 +32,7 @@ fn is_for_head_declaration(decl: &AstNode<'_, VariableDeclaration<'_>>) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, VariableDeclaration<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, VariableDeclaration<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let semicolon = !is_for_head_declaration(self);
|
||||
|
||||
@@ -58,7 +58,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, VariableDeclaration<'a>> {
|
||||
});
|
||||
write!(
|
||||
f,
|
||||
FormatContentWithSemicolon::new(declarations, content_end, self.span().end)
|
||||
FormatContentWithSemicolon::new(&declarations, content_end, self.span().end)
|
||||
);
|
||||
} else {
|
||||
write!(f, declarations);
|
||||
@@ -68,7 +68,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, VariableDeclaration<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, VariableDeclarator<'a>>> {
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'_, 'a, ArenaVec<'a, VariableDeclarator<'a>>> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
let length = self.len();
|
||||
|
||||
@@ -119,7 +119,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, VariableDe
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FormatWrite<'a> for AstNode<'a, VariableDeclarator<'a>> {
|
||||
impl<'a> FormatWrite<'a> for AstNode<'_, 'a, VariableDeclarator<'a>> {
|
||||
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
AssignmentLike::VariableDeclarator(self).fmt(f);
|
||||
}
|
||||
|
||||
@@ -19,14 +19,15 @@ use crate::{
|
||||
/// A hole's `,` is content (it keeps the hole count), so comments around it stay on their source side:
|
||||
/// the hole prints its leading comments, its `,`, then its same-line trailing comments.
|
||||
/// Comments after the last entry print before the `]` ([`write_unclaimed_comments`]), or lead the rest element.
|
||||
pub fn write_array_node<'a, T>(
|
||||
pub fn write_array_node<'b, 'a, T>(
|
||||
array_span: Span,
|
||||
rest_start: Option<u32>,
|
||||
array: impl IntoIterator<Item = Option<&'a AstNode<'a, T>>>,
|
||||
array: impl IntoIterator<Item = Option<AstNode<'b, 'a, T>>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) where
|
||||
'a: 'b,
|
||||
T: 'a,
|
||||
AstNode<'a, T>: Format<'a, JsFormatContext<'a>> + GetSpan,
|
||||
AstNode<'b, 'a, T>: Format<'a, JsFormatContext<'a>> + GetSpan,
|
||||
{
|
||||
// The elements: from the `[` up to the `]` (exclusive) or the rest element
|
||||
let range = Span::new(array_span.start, rest_start.unwrap_or(array_span.end - 1));
|
||||
@@ -83,7 +84,7 @@ pub fn write_array_node<'a, T>(
|
||||
write!(
|
||||
f,
|
||||
group(&format_args!(
|
||||
FormatNodeWithoutTrailingComments(element),
|
||||
FormatNodeWithoutTrailingComments(&element),
|
||||
trailing
|
||||
))
|
||||
);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::ptr;
|
||||
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_formatter_core::{Format, ScratchBuffer};
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::{
|
||||
JsFormatter,
|
||||
prelude::{FormatElements, format_once, line_suffix_boundary, *},
|
||||
@@ -26,14 +28,14 @@ use super::string::{FormatLiteralStringToken, StringLiteralParentKind};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AssignmentLike<'a, 'b> {
|
||||
VariableDeclarator(&'b AstNode<'a, VariableDeclarator<'a>>),
|
||||
AssignmentExpression(&'b AstNode<'a, AssignmentExpression<'a>>),
|
||||
ObjectProperty(&'b AstNode<'a, ObjectProperty<'a>>),
|
||||
BindingProperty(&'b AstNode<'a, BindingProperty<'a>>),
|
||||
AssignmentTargetPropertyProperty(&'b AstNode<'a, AssignmentTargetPropertyProperty<'a>>),
|
||||
PropertyDefinition(&'b AstNode<'a, PropertyDefinition<'a>>),
|
||||
AccessorProperty(&'b AstNode<'a, AccessorProperty<'a>>),
|
||||
TSTypeAliasDeclaration(&'b AstNode<'a, TSTypeAliasDeclaration<'a>>),
|
||||
VariableDeclarator(&'b AstNode<'b, 'a, VariableDeclarator<'a>>),
|
||||
AssignmentExpression(&'b AstNode<'b, 'a, AssignmentExpression<'a>>),
|
||||
ObjectProperty(&'b AstNode<'b, 'a, ObjectProperty<'a>>),
|
||||
BindingProperty(&'b AstNode<'b, 'a, BindingProperty<'a>>),
|
||||
AssignmentTargetPropertyProperty(&'b AstNode<'b, 'a, AssignmentTargetPropertyProperty<'a>>),
|
||||
PropertyDefinition(&'b AstNode<'b, 'a, PropertyDefinition<'a>>),
|
||||
AccessorProperty(&'b AstNode<'b, 'a, AccessorProperty<'a>>),
|
||||
TSTypeAliasDeclaration(&'b AstNode<'b, 'a, TSTypeAliasDeclaration<'a>>),
|
||||
}
|
||||
|
||||
/// Determines how a assignment like be formatted
|
||||
@@ -159,7 +161,7 @@ fn format_left_trailing_comments(start: u32, f: &mut JsFormatter<'_, '_>) {
|
||||
/// The minimum number of overlapping characters between left and right hand side
|
||||
const MIN_OVERLAP_FOR_BREAK: u8 = 3;
|
||||
|
||||
impl<'a> AssignmentLike<'a, '_> {
|
||||
impl<'a, 'b> AssignmentLike<'a, 'b> {
|
||||
fn write_left(&self, f: &mut JsFormatter<'_, 'a>) -> bool {
|
||||
match self {
|
||||
AssignmentLike::VariableDeclarator(declarator) => {
|
||||
@@ -204,7 +206,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
write!(f, text(ident.name.as_str()));
|
||||
false
|
||||
} else {
|
||||
let width = write_member_name(property.key(), f);
|
||||
let width = write_member_name(&property.key(), f);
|
||||
|
||||
width < text_width_for_break
|
||||
}
|
||||
@@ -228,7 +230,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
write!(f, ["[", property.key(), "]"]);
|
||||
f.source_text().span_width(property.key.span()) + 2 < text_width_for_break
|
||||
} else {
|
||||
let width = write_member_name(property.key(), f);
|
||||
let width = write_member_name(&property.key(), f);
|
||||
|
||||
width < text_width_for_break
|
||||
}
|
||||
@@ -241,7 +243,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
write!(f, ["[", property.name(), "]"]);
|
||||
f.source_text().span_width(property.name.span()) + 2 < text_width_for_break
|
||||
} else {
|
||||
let width = write_member_name(property.name(), f);
|
||||
let width = write_member_name(&property.name(), f);
|
||||
|
||||
width < text_width_for_break
|
||||
}
|
||||
@@ -267,7 +269,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
if property.readonly {
|
||||
write!(f, ["readonly", space()]);
|
||||
}
|
||||
format_property_key(property.key(), property.computed, f);
|
||||
format_property_key(&property.key(), property.computed, f);
|
||||
if property.optional {
|
||||
write!(f, "?");
|
||||
}
|
||||
@@ -296,7 +298,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
write!(f, ["override", space()]);
|
||||
}
|
||||
write!(f, ["accessor", space()]);
|
||||
format_property_key(property.key(), property.computed, f);
|
||||
format_property_key(&property.key(), property.computed, f);
|
||||
if property.definite {
|
||||
write!(f, "!");
|
||||
}
|
||||
@@ -315,7 +317,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
[declaration.id(), FormatNodeWithoutTrailingComments(type_parameters)]
|
||||
);
|
||||
} else {
|
||||
write!(f, [FormatNodeWithoutTrailingComments(declaration.id())]);
|
||||
write!(f, [FormatNodeWithoutTrailingComments(&declaration.id())]);
|
||||
}
|
||||
let start = type_alias_left_end(declaration);
|
||||
|
||||
@@ -365,15 +367,15 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
fn write_right(&self, f: &mut JsFormatter<'_, 'a>, layout: AssignmentLikeLayout) {
|
||||
match self {
|
||||
Self::VariableDeclarator(declarator) => {
|
||||
write!(f, [with_assignment_layout(declarator.init().unwrap(), Some(layout))]);
|
||||
write!(f, [with_assignment_layout(&declarator.init().unwrap(), Some(layout))]);
|
||||
}
|
||||
Self::AssignmentExpression(assignment) => {
|
||||
let right = assignment.right();
|
||||
write!(f, [with_assignment_layout(right, Some(layout))]);
|
||||
write!(f, [with_assignment_layout(&right, Some(layout))]);
|
||||
}
|
||||
Self::ObjectProperty(property) => {
|
||||
let value = property.value();
|
||||
write!(f, [with_assignment_layout(value, Some(layout))]);
|
||||
write!(f, [with_assignment_layout(&value, Some(layout))]);
|
||||
}
|
||||
Self::BindingProperty(property) => {
|
||||
write!(f, property.value());
|
||||
@@ -382,10 +384,10 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
write!(f, property.binding());
|
||||
}
|
||||
Self::PropertyDefinition(property) => {
|
||||
write!(f, [with_assignment_layout(property.value().unwrap(), Some(layout))]);
|
||||
write!(f, [with_assignment_layout(&property.value().unwrap(), Some(layout))]);
|
||||
}
|
||||
Self::AccessorProperty(property) => {
|
||||
write!(f, [with_assignment_layout(property.value().unwrap(), Some(layout))]);
|
||||
write!(f, [with_assignment_layout(&property.value().unwrap(), Some(layout))]);
|
||||
}
|
||||
Self::TSTypeAliasDeclaration(declaration) => {
|
||||
write!(f, [declaration.type_annotation()]);
|
||||
@@ -479,7 +481,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
fn right_expression_shape(&self, f: &JsFormatter<'_, 'a>) -> Option<&Expression<'a>> {
|
||||
self.get_right_expression()
|
||||
.filter(|expr| !is_cast_target(expr.span(), f))
|
||||
.map(AsRef::as_ref)
|
||||
.map(|expr| expr.inner())
|
||||
}
|
||||
|
||||
/// A leading multi-line `*`-aligned block comment on the right-hand side
|
||||
@@ -509,7 +511,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
Some(span.start)
|
||||
}
|
||||
|
||||
fn get_right_expression(&self) -> Option<&AstNode<'a, Expression<'a>>> {
|
||||
fn get_right_expression(&self) -> Option<AstNode<'b, 'a, Expression<'a>>> {
|
||||
match self {
|
||||
AssignmentLike::VariableDeclarator(variable_decorator) => variable_decorator.init(),
|
||||
AssignmentLike::AssignmentExpression(assignment) => Some(assignment.right()),
|
||||
@@ -683,14 +685,14 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
/// for nodes that belong to TypeScript too.
|
||||
fn should_break_after_operator(
|
||||
&self,
|
||||
right_expression: Option<&AstNode<'a, Expression<'a>>>,
|
||||
right_expression: Option<AstNode<'_, 'a, Expression<'a>>>,
|
||||
is_left_short: bool,
|
||||
alias_union_prints_itself: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let comments = f.context().comments();
|
||||
if let Some(right_expression) = right_expression {
|
||||
should_break_after_operator(right_expression, is_left_short, f)
|
||||
should_break_after_operator(&right_expression, is_left_short, f)
|
||||
} else if let AssignmentLike::TSTypeAliasDeclaration(decl) = self {
|
||||
let annotation_start = decl.type_annotation.span().start;
|
||||
// For TSTypeAliasDeclaration, check if the type annotation is a union type with comments
|
||||
@@ -804,7 +806,7 @@ impl<'a> AssignmentLike<'a, '_> {
|
||||
///
|
||||
/// Based on <https://github.com/prettier/prettier/blob/0273e33fc691e28e4ab3f3c8ee86918b65cf823d/src/language-js/print/assignment.js#L196-L264>
|
||||
fn should_break_after_operator<'a>(
|
||||
right: &AstNode<'a, Expression<'a>>,
|
||||
right: &AstNode<'_, 'a, Expression<'a>>,
|
||||
is_left_short: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
@@ -857,9 +859,9 @@ fn should_break_after_operator<'a>(
|
||||
Expression::ClassExpression(class) => !class.decorators.is_empty(),
|
||||
// Based on https://github.com/prettier/prettier/blob/0273e33fc691e28e4ab3f3c8ee86918b65cf823d/src/language-js/print/assignment.js#L235-L263
|
||||
_ if is_left_short => false,
|
||||
_ => get_innermost_expression(right, f).is_some_and(|inner| {
|
||||
matches!(inner.as_ref(), Expression::StringLiteral(_))
|
||||
|| is_poorly_breakable_member_or_call_chain(inner, f)
|
||||
_ => get_innermost_expression(right.inner(), f).is_some_and(|inner| {
|
||||
matches!(inner, Expression::StringLiteral(_))
|
||||
|| is_poorly_breakable_member_or_call_chain(right, inner, f)
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -870,16 +872,16 @@ fn should_break_after_operator<'a>(
|
||||
///
|
||||
/// `None` when the walk reaches a cast target (`!/** @type {T} */ ("s")`): a cast target has no shape.
|
||||
/// The caller has already checked the entry node.
|
||||
fn get_innermost_expression<'a, 'b>(
|
||||
mut current: &'b AstNode<'a, Expression<'a>>,
|
||||
fn get_innermost_expression<'a>(
|
||||
mut current: &'a Expression<'a>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> Option<&'b AstNode<'a, Expression<'a>>> {
|
||||
) -> Option<&'a Expression<'a>> {
|
||||
loop {
|
||||
let argument = match current.as_ast_nodes() {
|
||||
AstNodes::UnaryExpression(unary) => unary.argument(),
|
||||
AstNodes::TSNonNullExpression(non_null) => non_null.expression(),
|
||||
AstNodes::AwaitExpression(expr) => expr.argument(),
|
||||
AstNodes::YieldExpression(expr) => match expr.argument() {
|
||||
let argument = match current {
|
||||
Expression::UnaryExpression(unary) => &unary.argument,
|
||||
Expression::TSNonNullExpression(non_null) => &non_null.expression,
|
||||
Expression::AwaitExpression(expr) => &expr.argument,
|
||||
Expression::YieldExpression(expr) => match &expr.argument {
|
||||
Some(argument) => argument,
|
||||
None => break,
|
||||
},
|
||||
@@ -1040,12 +1042,12 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AssignmentLike<'a, '_> {
|
||||
/// Formats an expression and passes the assignment layout to its formatting function if the expressions
|
||||
/// formatting rule takes the layout as an option.
|
||||
pub struct WithAssignmentLayout<'a, 'b> {
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, Expression<'a>>,
|
||||
layout: Option<AssignmentLikeLayout>,
|
||||
}
|
||||
|
||||
pub fn with_assignment_layout<'a, 'b>(
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, Expression<'a>>,
|
||||
layout: Option<AssignmentLikeLayout>,
|
||||
) -> WithAssignmentLayout<'a, 'b> {
|
||||
WithAssignmentLayout { expression, layout }
|
||||
@@ -1057,8 +1059,8 @@ impl<'a> Format<'a, JsFormatContext<'a>> for WithAssignmentLayout<'a, '_> {
|
||||
// which is reached through the shared generated `fmt` (suppression, type casts, parentheses, comments);
|
||||
// the span-keyed context slot hands it across that frame.
|
||||
// A cast target has no shape; the layout never reaches the arrow inside.
|
||||
if let (Some(layout), AstNodes::ArrowFunctionExpression(arrow)) =
|
||||
(self.layout, self.expression.as_ast_nodes())
|
||||
if let (Some(layout), AstNodeKind::ArrowFunctionExpression(arrow)) =
|
||||
(self.layout, &self.expression.as_kind())
|
||||
&& !is_cast_target(arrow.span(), f)
|
||||
{
|
||||
f.context_mut().set_arrow_assignment_layout(arrow.span(), layout);
|
||||
@@ -1073,8 +1075,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for WithAssignmentLayout<'a, '_> {
|
||||
/// A chain that has no calls at all or all of whose calls have no arguments
|
||||
/// or have only one which [is_short_argument], except for member call chains
|
||||
/// [Prettier applies]: <https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/assignment.js#L329>
|
||||
///
|
||||
/// `expression` is the innermost expression of `right`, see [get_innermost_expression].
|
||||
fn is_poorly_breakable_member_or_call_chain<'a>(
|
||||
expression: &AstNode<'a, Expression<'a>>,
|
||||
right: &AstNode<'_, 'a, Expression<'a>>,
|
||||
expression: &'a Expression<'a>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let threshold = f.options().line_width.value() / 4;
|
||||
@@ -1088,10 +1093,11 @@ fn is_poorly_breakable_member_or_call_chain<'a>(
|
||||
// Simple head is `JsIdentifierExpression` or `JsThisExpression`
|
||||
let mut is_chain_head_simple = false;
|
||||
|
||||
// Keeping track of all call expressions in the chain to check them later
|
||||
let mut call_expressions = vec![];
|
||||
// The outermost call and the number of calls, their nodes are created below once these checks pass
|
||||
let mut first_call = None;
|
||||
let mut call_count = 0;
|
||||
|
||||
let mut expression = expression.as_ast_nodes();
|
||||
let mut expression = expression;
|
||||
|
||||
loop {
|
||||
// A cast target ends the chain without a simple head
|
||||
@@ -1099,30 +1105,42 @@ fn is_poorly_breakable_member_or_call_chain<'a>(
|
||||
break;
|
||||
}
|
||||
expression = match expression {
|
||||
AstNodes::TSNonNullExpression(assertion) => assertion.expression().as_ast_nodes(),
|
||||
AstNodes::CallExpression(call_expression) => {
|
||||
Expression::TSNonNullExpression(assertion) => &assertion.expression,
|
||||
Expression::CallExpression(call_expression) => {
|
||||
is_chain = true;
|
||||
let callee = &call_expression.callee();
|
||||
call_expressions.push(call_expression);
|
||||
callee.as_ast_nodes()
|
||||
first_call.get_or_insert(&**call_expression);
|
||||
call_count += 1;
|
||||
&call_expression.callee
|
||||
}
|
||||
AstNodes::StaticMemberExpression(node) => {
|
||||
Expression::StaticMemberExpression(node) => {
|
||||
is_chain = true;
|
||||
node.object().as_ast_nodes()
|
||||
&node.object
|
||||
}
|
||||
AstNodes::ComputedMemberExpression(node) => {
|
||||
Expression::ComputedMemberExpression(node) => {
|
||||
is_chain = true;
|
||||
node.object().as_ast_nodes()
|
||||
&node.object
|
||||
}
|
||||
AstNodes::PrivateFieldExpression(node) => {
|
||||
Expression::PrivateFieldExpression(node) => {
|
||||
is_chain = true;
|
||||
node.object().as_ast_nodes()
|
||||
&node.object
|
||||
}
|
||||
AstNodes::ChainExpression(chain) => {
|
||||
Expression::ChainExpression(chain) => {
|
||||
is_chain = true;
|
||||
chain.expression().as_ast_nodes()
|
||||
let element = &chain.expression;
|
||||
if is_cast_target(element.span(), f) {
|
||||
break;
|
||||
}
|
||||
match element {
|
||||
ChainElement::CallExpression(call_expression) => {
|
||||
first_call.get_or_insert(&**call_expression);
|
||||
call_count += 1;
|
||||
&call_expression.callee
|
||||
}
|
||||
ChainElement::TSNonNullExpression(assertion) => &assertion.expression,
|
||||
element => element.as_member_expression().unwrap().object(),
|
||||
}
|
||||
}
|
||||
AstNodes::IdentifierReference(_) | AstNodes::ThisExpression(_) => {
|
||||
Expression::Identifier(_) | Expression::ThisExpression(_) => {
|
||||
is_chain_head_simple = true;
|
||||
break;
|
||||
}
|
||||
@@ -1136,14 +1154,52 @@ fn is_poorly_breakable_member_or_call_chain<'a>(
|
||||
return false;
|
||||
}
|
||||
|
||||
if call_expressions.is_empty() {
|
||||
let Some(first_call) = first_call else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if f.comments().has_comment_in_span(call_expressions[0].span) {
|
||||
if f.comments().has_comment_in_span(first_call.span) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The nodes of the calls, as the checks below need the parents:
|
||||
// the walk of `get_innermost_expression` and the loop above, with the links in the arena
|
||||
let allocator = f.allocator();
|
||||
let right = right.as_kind();
|
||||
let mut node = &right;
|
||||
let mut call_expressions = Vec::with_capacity(call_count);
|
||||
loop {
|
||||
let child = match node {
|
||||
AstNodeKind::CallExpression(call) => {
|
||||
call_expressions.push(call);
|
||||
if call_expressions.len() == call_count {
|
||||
break;
|
||||
}
|
||||
call.callee()
|
||||
}
|
||||
AstNodeKind::UnaryExpression(expr) => expr.argument(),
|
||||
AstNodeKind::AwaitExpression(expr) => expr.argument(),
|
||||
AstNodeKind::YieldExpression(expr) => match expr.argument() {
|
||||
Some(argument) => argument,
|
||||
None => break,
|
||||
},
|
||||
AstNodeKind::TSNonNullExpression(expr) => expr.expression(),
|
||||
AstNodeKind::StaticMemberExpression(expr) => expr.object(),
|
||||
AstNodeKind::ComputedMemberExpression(expr) => expr.object(),
|
||||
AstNodeKind::PrivateFieldExpression(expr) => expr.object(),
|
||||
AstNodeKind::ChainExpression(chain) => {
|
||||
node = chain.expression().as_kind().in_arena(allocator);
|
||||
continue;
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
node = child.as_kind().in_arena(allocator);
|
||||
}
|
||||
debug_assert!(
|
||||
call_expressions.len() == call_count
|
||||
&& call_expressions.first().is_some_and(|call| ptr::eq(call.inner(), first_call))
|
||||
);
|
||||
|
||||
for call_expression in &call_expressions {
|
||||
let args = &call_expression.arguments;
|
||||
|
||||
@@ -1166,14 +1222,14 @@ fn is_poorly_breakable_member_or_call_chain<'a>(
|
||||
|
||||
let is_breakable_type_arguments = call_expression
|
||||
.type_arguments()
|
||||
.is_some_and(|type_arguments| is_complex_type_arguments(type_arguments, f));
|
||||
.is_some_and(|type_arguments| is_complex_type_arguments(&type_arguments, f));
|
||||
|
||||
if is_breakable_type_arguments {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
!is_member_call_chain(call_expressions[0], f)
|
||||
call_expressions.first().is_some_and(|call| !is_member_call_chain(call, f))
|
||||
}
|
||||
|
||||
/// This function checks if [`Argument`] is short
|
||||
@@ -1224,7 +1280,7 @@ fn is_short_argument(argument: &Expression, threshold: u16, f: &JsFormatter) ->
|
||||
///
|
||||
/// <https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/assignment.js#L432-L459>
|
||||
fn is_complex_type_arguments<'a>(
|
||||
type_arguments: &AstNode<'a, TSTypeParameterInstantiation<'a>>,
|
||||
type_arguments: &AstNode<'_, 'a, TSTypeParameterInstantiation<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let params = &type_arguments.params;
|
||||
|
||||
@@ -5,7 +5,7 @@ use oxc_span::{GetSpan, Span};
|
||||
|
||||
use crate::{
|
||||
Format,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::{
|
||||
JsFormatter,
|
||||
prelude::*,
|
||||
@@ -16,8 +16,8 @@ use crate::{
|
||||
};
|
||||
|
||||
pub enum ConditionalLike<'a, 'b> {
|
||||
ConditionalExpression(&'b AstNode<'a, ConditionalExpression<'a>>),
|
||||
TSConditionalType(&'b AstNode<'a, TSConditionalType<'a>>),
|
||||
ConditionalExpression(&'b AstNode<'b, 'a, ConditionalExpression<'a>>),
|
||||
TSConditionalType(&'b AstNode<'b, 'a, TSConditionalType<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> ConditionalLike<'a, '_> {
|
||||
@@ -30,7 +30,7 @@ impl<'a> ConditionalLike<'a, '_> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parent(&self) -> &AstNodes<'a> {
|
||||
fn parent(&self) -> &AstNodes<'_, 'a> {
|
||||
match self {
|
||||
ConditionalLike::ConditionalExpression(expr) => expr.parent(),
|
||||
ConditionalLike::TSConditionalType(ty) => ty.parent(),
|
||||
@@ -364,7 +364,7 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
||||
let format_inner = format_with(|f| {
|
||||
let (start, end) = match self.conditional {
|
||||
ConditionalLike::ConditionalExpression(conditional) => {
|
||||
write!(f, FormatNodeWithoutTrailingComments(conditional.test()));
|
||||
write!(f, FormatNodeWithoutTrailingComments(&conditional.test()));
|
||||
(conditional.test.span().end, conditional.consequent.span().start)
|
||||
}
|
||||
ConditionalLike::TSConditionalType(conditional) => {
|
||||
@@ -375,7 +375,7 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
||||
space(),
|
||||
"extends",
|
||||
space(),
|
||||
FormatNodeWithoutTrailingComments(conditional.extends_type())
|
||||
FormatNodeWithoutTrailingComments(&conditional.extends_type())
|
||||
]
|
||||
);
|
||||
(conditional.extends_type.span().end, conditional.true_type.span().start)
|
||||
@@ -405,11 +405,11 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
||||
let format_consequent_with_trailing_comments = format_with(|f| {
|
||||
let (start, end) = match self.conditional {
|
||||
ConditionalLike::ConditionalExpression(conditional) => {
|
||||
write!(f, FormatNodeWithoutTrailingComments(conditional.consequent()));
|
||||
write!(f, FormatNodeWithoutTrailingComments(&conditional.consequent()));
|
||||
(conditional.consequent.span().end, conditional.alternate.span().start)
|
||||
}
|
||||
ConditionalLike::TSConditionalType(conditional) => {
|
||||
write!(f, FormatNodeWithoutTrailingComments(conditional.true_type()));
|
||||
write!(f, FormatNodeWithoutTrailingComments(&conditional.true_type()));
|
||||
(conditional.true_type.span().end, conditional.false_type.span().start)
|
||||
}
|
||||
};
|
||||
@@ -452,10 +452,10 @@ impl<'a> FormatConditionalLike<'a, '_> {
|
||||
|
||||
let format_alternative = format_with(|f| match self.conditional {
|
||||
ConditionalLike::ConditionalExpression(conditional) => {
|
||||
write!(f, [FormatNodeWithoutTrailingComments(conditional.alternate())]);
|
||||
write!(f, [FormatNodeWithoutTrailingComments(&conditional.alternate())]);
|
||||
}
|
||||
ConditionalLike::TSConditionalType(conditional) => {
|
||||
write!(f, [FormatNodeWithoutTrailingComments(conditional.false_type())]);
|
||||
write!(f, [FormatNodeWithoutTrailingComments(&conditional.false_type())]);
|
||||
}
|
||||
});
|
||||
let format_alternative = format_with(|f| {
|
||||
@@ -524,11 +524,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatConditionalLike<'a, '_> {
|
||||
space(),
|
||||
"?",
|
||||
space(),
|
||||
format_jsx_chain_consequent(conditional.consequent()),
|
||||
format_jsx_chain_consequent(&conditional.consequent()),
|
||||
space(),
|
||||
":",
|
||||
space(),
|
||||
format_jsx_chain_alternate(conditional.alternate())
|
||||
format_jsx_chain_alternate(&conditional.alternate())
|
||||
]
|
||||
);
|
||||
} else {
|
||||
@@ -595,14 +595,14 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatConditionalLike<'a, '_> {
|
||||
|
||||
/// Formats JSX consequent with conditional wrapping
|
||||
fn format_jsx_chain_consequent<'a, 'b>(
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, Expression<'a>>,
|
||||
) -> impl Format<'a, JsFormatContext<'a>> + 'b {
|
||||
FormatJsxChainExpression { expression, alternate: false }
|
||||
}
|
||||
|
||||
/// Formats JSX alternate with conditional wrapping
|
||||
fn format_jsx_chain_alternate<'a, 'b>(
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, Expression<'a>>,
|
||||
) -> impl Format<'a, JsFormatContext<'a>> + 'b {
|
||||
FormatJsxChainExpression { expression, alternate: true }
|
||||
}
|
||||
@@ -629,7 +629,7 @@ fn format_jsx_chain_alternate<'a, 'b>(
|
||||
/// );
|
||||
/// ```
|
||||
struct FormatJsxChainExpression<'a, 'b> {
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, Expression<'a>>,
|
||||
alternate: bool,
|
||||
}
|
||||
|
||||
@@ -643,7 +643,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatJsxChainExpression<'a, '_> {
|
||||
};
|
||||
|
||||
let format_expression = format_with(|f| {
|
||||
if let AstNodes::ConditionalExpression(conditional) = self.expression.as_ast_nodes() {
|
||||
if let AstNodeKind::ConditionalExpression(conditional) = &self.expression.as_kind() {
|
||||
FormatConditionalLike {
|
||||
conditional: &ConditionalLike::ConditionalExpression(conditional),
|
||||
options: FormatConditionalLikeOptions { jsx_chain: true },
|
||||
|
||||
@@ -1,127 +1,114 @@
|
||||
use std::iter;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
use crate::ast_nodes::{AstNode, AstNodes};
|
||||
use crate::ast_nodes::{AstNode, AstNodeKind, AstNodes};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ExpressionLeftSide<'a, 'b> {
|
||||
Expression(&'b AstNode<'a, Expression<'a>>),
|
||||
AssignmentTarget(&'b AstNode<'a, AssignmentTarget<'a>>),
|
||||
SimpleAssignmentTarget(&'b AstNode<'a, SimpleAssignmentTarget<'a>>),
|
||||
Expression(AstNode<'b, 'a, Expression<'a>>),
|
||||
AssignmentTarget(AstNode<'b, 'a, AssignmentTarget<'a>>),
|
||||
SimpleAssignmentTarget(AstNode<'b, 'a, SimpleAssignmentTarget<'a>>),
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b AstNode<'a, Expression<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: &'b AstNode<'a, Expression<'a>>) -> Self {
|
||||
impl<'a, 'b> From<AstNode<'b, 'a, Expression<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: AstNode<'b, 'a, Expression<'a>>) -> Self {
|
||||
Self::Expression(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b AstNode<'a, AssignmentTarget<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: &'b AstNode<'a, AssignmentTarget<'a>>) -> Self {
|
||||
impl<'a, 'b> From<AstNode<'b, 'a, AssignmentTarget<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: AstNode<'b, 'a, AssignmentTarget<'a>>) -> Self {
|
||||
Self::AssignmentTarget(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'b AstNode<'a, SimpleAssignmentTarget<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: &'b AstNode<'a, SimpleAssignmentTarget<'a>>) -> Self {
|
||||
impl<'a, 'b> From<AstNode<'b, 'a, SimpleAssignmentTarget<'a>>> for ExpressionLeftSide<'a, 'b> {
|
||||
fn from(value: AstNode<'b, 'a, SimpleAssignmentTarget<'a>>) -> Self {
|
||||
Self::SimpleAssignmentTarget(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ExpressionLeftSide<'a, 'b> {
|
||||
pub fn leftmost(
|
||||
expression: &'b AstNode<'a, Expression<'a>>,
|
||||
) -> &'b AstNode<'a, Expression<'a>> {
|
||||
let current: Self = expression.into();
|
||||
|
||||
current.iter_expression().last().unwrap()
|
||||
/// Returns the leftmost expression that starts `expression` (`expression` itself if none).
|
||||
pub fn leftmost<'e>(expression: &'e Expression<'e>) -> &'e Expression<'e> {
|
||||
LeftSide::Expression(expression)
|
||||
.iter()
|
||||
.filter_map(|left| match left {
|
||||
LeftSide::Expression(expression) => Some(expression),
|
||||
_ => None,
|
||||
})
|
||||
.last()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// See [Self::left_of].
|
||||
pub fn left(&self) -> Option<Self> {
|
||||
match self {
|
||||
Self::Expression(expression) => Self::left_of(expression.as_ast_nodes()),
|
||||
Self::AssignmentTarget(target) => {
|
||||
Self::get_left_side_of_assignment(target.as_ast_nodes())
|
||||
}
|
||||
Self::SimpleAssignmentTarget(target) => {
|
||||
Self::get_left_side_of_assignment(target.as_ast_nodes())
|
||||
}
|
||||
}
|
||||
///
|
||||
/// The node of `self` is in `allocator`, as it is the parent of the left side.
|
||||
pub fn left(&self, allocator: &'a Allocator) -> Option<Self> {
|
||||
let (node, assignment) = match self {
|
||||
Self::Expression(expression) => (expression.as_kind(), false),
|
||||
Self::AssignmentTarget(target) => (target.as_kind(), true),
|
||||
Self::SimpleAssignmentTarget(target) => (target.as_kind(), true),
|
||||
};
|
||||
// Allocate only when the walk continues
|
||||
LeftSide::of(&node.as_ast_nodes(), assignment)?;
|
||||
Self::left_of(node.in_arena(allocator), assignment, allocator)
|
||||
}
|
||||
|
||||
/// Returns the child that starts `node` (e.g. `a` of `a.b`, `a + b`, `a ? b : c`),
|
||||
/// or [None] if `node` starts with its own token.
|
||||
fn left_of(node: &'b AstNodes<'a>) -> Option<Self> {
|
||||
/// [LeftSide::of] with [AstNode]s, for a walk that needs the parents.
|
||||
fn left_of(
|
||||
node: &'b AstNodeKind<'b, 'a>,
|
||||
assignment: bool,
|
||||
allocator: &'a Allocator,
|
||||
) -> Option<Self> {
|
||||
let left = match node {
|
||||
AstNodes::SequenceExpression(expr) => expr.expressions().first().map(Into::into),
|
||||
AstNodes::StaticMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodes::ComputedMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodes::PrivateFieldExpression(expr) => Some(expr.object().into()),
|
||||
AstNodes::TaggedTemplateExpression(expr) => Some(expr.tag().into()),
|
||||
AstNodes::CallExpression(expr) => Some(expr.callee().into()),
|
||||
AstNodes::ConditionalExpression(expr) => Some(expr.test().into()),
|
||||
AstNodes::TSAsExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSSatisfiesExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSNonNullExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSInstantiationExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::AssignmentExpression(expr) => Some(Self::AssignmentTarget(expr.left())),
|
||||
AstNodes::UpdateExpression(expr) => {
|
||||
AstNodeKind::SequenceExpression(expr) => expr.expressions().first().map(Into::into),
|
||||
AstNodeKind::StaticMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodeKind::ComputedMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodeKind::PrivateFieldExpression(expr) => Some(expr.object().into()),
|
||||
AstNodeKind::TaggedTemplateExpression(expr) => Some(expr.tag().into()),
|
||||
AstNodeKind::CallExpression(expr) => Some(expr.callee().into()),
|
||||
AstNodeKind::ConditionalExpression(expr) => Some(expr.test().into()),
|
||||
AstNodeKind::TSAsExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodeKind::TSSatisfiesExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodeKind::TSNonNullExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodeKind::TSInstantiationExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodeKind::TSTypeAssertion(expr) if assignment => Some(expr.expression().into()),
|
||||
AstNodeKind::AssignmentExpression(expr) => Some(expr.left().into()),
|
||||
AstNodeKind::UpdateExpression(expr) => {
|
||||
if expr.prefix {
|
||||
None
|
||||
} else {
|
||||
Some(Self::SimpleAssignmentTarget(expr.argument()))
|
||||
Some(expr.argument().into())
|
||||
}
|
||||
}
|
||||
AstNodes::BinaryExpression(binary) => Some(binary.left().into()),
|
||||
AstNodes::LogicalExpression(logical) => Some(logical.left().into()),
|
||||
AstNodes::ChainExpression(chain) => Self::left_of(chain.expression().as_ast_nodes()),
|
||||
AstNodeKind::BinaryExpression(binary) => Some(binary.left().into()),
|
||||
AstNodeKind::LogicalExpression(logical) => Some(logical.left().into()),
|
||||
AstNodeKind::ChainExpression(chain) => {
|
||||
Self::left_of(chain.expression().as_kind().in_arena(allocator), false, allocator)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
// Keep the two tables in sync
|
||||
debug_assert_eq!(left.map(|left| left.span()), Self::left_span_of(node));
|
||||
debug_assert_eq!(
|
||||
left.map(|left| left.span()),
|
||||
LeftSide::of(&node.as_ast_nodes(), assignment).map(|left| left.span())
|
||||
);
|
||||
left
|
||||
}
|
||||
|
||||
/// Same as [Self::left_of], but reads the span from the field directly,
|
||||
/// so no [AstNode] is allocated.
|
||||
pub fn left_span_of(node: &AstNodes<'_>) -> Option<Span> {
|
||||
match node {
|
||||
AstNodes::SequenceExpression(expr) => expr.expressions.first().map(GetSpan::span),
|
||||
AstNodes::StaticMemberExpression(expr) => Some(expr.object.span()),
|
||||
AstNodes::ComputedMemberExpression(expr) => Some(expr.object.span()),
|
||||
AstNodes::PrivateFieldExpression(expr) => Some(expr.object.span()),
|
||||
AstNodes::TaggedTemplateExpression(expr) => Some(expr.tag.span()),
|
||||
AstNodes::CallExpression(expr) => Some(expr.callee.span()),
|
||||
AstNodes::ConditionalExpression(expr) => Some(expr.test.span()),
|
||||
AstNodes::TSAsExpression(expr) => Some(expr.expression.span()),
|
||||
AstNodes::TSSatisfiesExpression(expr) => Some(expr.expression.span()),
|
||||
AstNodes::TSNonNullExpression(expr) => Some(expr.expression.span()),
|
||||
AstNodes::TSInstantiationExpression(expr) => Some(expr.expression.span()),
|
||||
AstNodes::AssignmentExpression(expr) => Some(expr.left.span()),
|
||||
AstNodes::UpdateExpression(expr) => (!expr.prefix).then(|| expr.argument.span()),
|
||||
AstNodes::BinaryExpression(binary) => Some(binary.left.span()),
|
||||
AstNodes::LogicalExpression(logical) => Some(logical.left.span()),
|
||||
AstNodes::ChainExpression(chain) => match &chain.expression {
|
||||
ChainElement::CallExpression(call) => Some(call.callee.span()),
|
||||
ChainElement::TSNonNullExpression(expr) => Some(expr.expression.span()),
|
||||
element => element.as_member_expression().map(|member| member.object().span()),
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
/// Same as [Self::left_of], but only reads the span, so no [AstNode] is created.
|
||||
pub fn left_span_of(node: &AstNodes<'_, '_>) -> Option<Span> {
|
||||
LeftSide::of(node, false).map(|left| left.span())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = ExpressionLeftSide<'a, 'b>> {
|
||||
iter::successors(Some(*self), Self::left)
|
||||
}
|
||||
|
||||
pub fn iter_expression(&self) -> impl Iterator<Item = &'b AstNode<'a, Expression<'a>>> {
|
||||
self.iter().filter_map(|left| match left {
|
||||
ExpressionLeftSide::Expression(expression) => Some(expression),
|
||||
_ => None,
|
||||
})
|
||||
pub fn iter(
|
||||
&self,
|
||||
allocator: &'a Allocator,
|
||||
) -> impl Iterator<Item = ExpressionLeftSide<'a, 'b>> {
|
||||
iter::successors(Some(*self), move |left| left.left(allocator))
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
@@ -131,17 +118,85 @@ impl<'a, 'b> ExpressionLeftSide<'a, 'b> {
|
||||
ExpressionLeftSide::SimpleAssignmentTarget(target) => target.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_left_side_of_assignment(node: &'b AstNodes<'a>) -> Option<ExpressionLeftSide<'a, 'b>> {
|
||||
match node {
|
||||
AstNodes::TSAsExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSSatisfiesExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSNonNullExpression(expr) => Some(expr.expression().into()),
|
||||
AstNodes::TSTypeAssertion(expr) => Some(expr.expression().into()),
|
||||
AstNodes::ComputedMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodes::StaticMemberExpression(expr) => Some(expr.object().into()),
|
||||
AstNodes::PrivateFieldExpression(expr) => Some(expr.object().into()),
|
||||
_ => None,
|
||||
/// [ExpressionLeftSide] over the AST, without [AstNode]s.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LeftSide<'a> {
|
||||
Expression(&'a Expression<'a>),
|
||||
AssignmentTarget(&'a AssignmentTarget<'a>),
|
||||
SimpleAssignmentTarget(&'a SimpleAssignmentTarget<'a>),
|
||||
}
|
||||
|
||||
impl<'a> LeftSide<'a> {
|
||||
/// Returns the child that starts `node` (e.g. `a` of `a.b`, `a + b`, `a ? b : c`),
|
||||
/// or [None] if `node` starts with its own token.
|
||||
///
|
||||
/// With `assignment` (`node` is an assignment target),
|
||||
/// a `TSTypeAssertion` is walked into as well (`(<T>a) = b`),
|
||||
/// as `get_left_side_of_assignment` did.
|
||||
///
|
||||
/// The table of the left sides, [ExpressionLeftSide::left_of] follows it with [AstNode]s.
|
||||
fn of(node: &AstNodes<'_, 'a>, assignment: bool) -> Option<Self> {
|
||||
let left = match node {
|
||||
AstNodes::SequenceExpression(expr) => expr.inner().expressions.first()?,
|
||||
AstNodes::StaticMemberExpression(expr) => &expr.inner().object,
|
||||
AstNodes::ComputedMemberExpression(expr) => &expr.inner().object,
|
||||
AstNodes::PrivateFieldExpression(expr) => &expr.inner().object,
|
||||
AstNodes::TaggedTemplateExpression(expr) => &expr.inner().tag,
|
||||
AstNodes::CallExpression(expr) => &expr.inner().callee,
|
||||
AstNodes::ConditionalExpression(expr) => &expr.inner().test,
|
||||
AstNodes::TSAsExpression(expr) => &expr.inner().expression,
|
||||
AstNodes::TSSatisfiesExpression(expr) => &expr.inner().expression,
|
||||
AstNodes::TSNonNullExpression(expr) => &expr.inner().expression,
|
||||
AstNodes::TSInstantiationExpression(expr) => &expr.inner().expression,
|
||||
AstNodes::TSTypeAssertion(expr) if assignment => &expr.inner().expression,
|
||||
AstNodes::AssignmentExpression(expr) => {
|
||||
return Some(Self::AssignmentTarget(&expr.inner().left));
|
||||
}
|
||||
AstNodes::UpdateExpression(expr) => {
|
||||
let expr = expr.inner();
|
||||
return (!expr.prefix).then_some(Self::SimpleAssignmentTarget(&expr.argument));
|
||||
}
|
||||
AstNodes::BinaryExpression(binary) => &binary.inner().left,
|
||||
AstNodes::LogicalExpression(logical) => &logical.inner().left,
|
||||
AstNodes::ChainExpression(chain) => match &chain.inner().expression {
|
||||
ChainElement::CallExpression(call) => &call.callee,
|
||||
ChainElement::TSNonNullExpression(expr) => &expr.expression,
|
||||
element => element.as_member_expression()?.object(),
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
Some(Self::Expression(left))
|
||||
}
|
||||
|
||||
/// See [Self::of].
|
||||
pub fn left(self) -> Option<Self> {
|
||||
let (node, assignment) = match self {
|
||||
Self::Expression(expression) => {
|
||||
(AstNode::new(expression, AstNodes::Dummy()).as_kind(), false)
|
||||
}
|
||||
Self::AssignmentTarget(target) => {
|
||||
(AstNode::new(target, AstNodes::Dummy()).as_kind(), true)
|
||||
}
|
||||
Self::SimpleAssignmentTarget(target) => {
|
||||
(AstNode::new(target, AstNodes::Dummy()).as_kind(), true)
|
||||
}
|
||||
};
|
||||
Self::of(&node.as_ast_nodes(), assignment)
|
||||
}
|
||||
|
||||
pub fn iter(self) -> impl Iterator<Item = Self> {
|
||||
iter::successors(Some(self), |left| left.left())
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSpan for LeftSide<'_> {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::Expression(expression) => expression.span(),
|
||||
Self::AssignmentTarget(target) => target.span(),
|
||||
Self::SimpleAssignmentTarget(target) => target.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ pub enum JsxChild<'a, 'b> {
|
||||
EmptyLine,
|
||||
|
||||
/// Any other content that isn't a text. Should be formatted as is.
|
||||
NonText(&'b AstNode<'a, JSXChild<'a>>),
|
||||
NonText(AstNode<'b, 'a, JSXChild<'a>>),
|
||||
}
|
||||
|
||||
impl PartialEq for JsxChild<'_, '_> {
|
||||
@@ -279,13 +279,13 @@ impl<'a> Iterator for JsxSplitChunksIterator<'a> {
|
||||
impl FusedIterator for JsxSplitChunksIterator<'_> {}
|
||||
|
||||
pub fn jsx_split_children<'a, 'b>(
|
||||
children: &'b AstNode<'a, ArenaVec<'a, JSXChild<'a>>>,
|
||||
children: &'b AstNode<'b, 'a, ArenaVec<'a, JSXChild<'a>>>,
|
||||
comments: &Comments<'a>,
|
||||
) -> Vec<JsxChild<'a, 'b>> {
|
||||
let mut builder = JsxSplitChildrenBuilder::new();
|
||||
|
||||
for child in children {
|
||||
match child.as_ref() {
|
||||
match child.inner() {
|
||||
JSXChild::Text(text) => {
|
||||
// Split the text into words
|
||||
// Keep track if there's any leading/trailing empty line, new line or whitespace
|
||||
|
||||
@@ -31,22 +31,22 @@ pub enum CallExpressionPosition {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ChainMember<'a, 'b> {
|
||||
/// Holds onto a [oxc_ast::ast::StaticMemberExpression]
|
||||
StaticMember(&'b AstNode<'a, StaticMemberExpression<'a>>),
|
||||
StaticMember(&'b AstNode<'b, 'a, StaticMemberExpression<'a>>),
|
||||
|
||||
/// Holds onto a [oxc_ast::ast::CallExpression]
|
||||
CallExpression {
|
||||
expression: &'b AstNode<'a, CallExpression<'a>>,
|
||||
expression: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
position: CallExpressionPosition,
|
||||
},
|
||||
|
||||
/// Holds onto a [oxc_ast::ast::ComputedMemberExpression]
|
||||
ComputedMember(&'b AstNode<'a, ComputedMemberExpression<'a>>),
|
||||
ComputedMember(&'b AstNode<'b, 'a, ComputedMemberExpression<'a>>),
|
||||
|
||||
TSNonNullExpression(&'b AstNode<'a, TSNonNullExpression<'a>>),
|
||||
TSNonNullExpression(&'b AstNode<'b, 'a, TSNonNullExpression<'a>>),
|
||||
|
||||
/// Any other node that are not [oxc_ast::ast::CallExpression] or [oxc_ast::ast::StaticMemberExpression]
|
||||
/// Are tracked using this variant
|
||||
Node(&'b AstNode<'a, Expression<'a>>),
|
||||
Node(&'b AstNode<'b, 'a, Expression<'a>>),
|
||||
}
|
||||
|
||||
impl ChainMember<'_, '_> {
|
||||
@@ -136,11 +136,11 @@ impl<'a> Format<'a, JsFormatContext<'a>> for ChainMember<'a, '_> {
|
||||
}
|
||||
|
||||
pub struct FormatComputedMemberExpressionWithoutObject<'a, 'b>(
|
||||
pub &'b AstNode<'a, ComputedMemberExpression<'a>>,
|
||||
pub &'b AstNode<'b, 'a, ComputedMemberExpression<'a>>,
|
||||
);
|
||||
|
||||
impl<'a> Deref for FormatComputedMemberExpressionWithoutObject<'a, '_> {
|
||||
type Target = AstNode<'a, ComputedMemberExpression<'a>>;
|
||||
impl<'a, 'b> Deref for FormatComputedMemberExpressionWithoutObject<'a, 'b> {
|
||||
type Target = AstNode<'b, 'a, ComputedMemberExpression<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
@@ -164,7 +164,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatComputedMemberExpressionWitho
|
||||
group(&format_args!(
|
||||
self.optional().then_some("?."),
|
||||
"[",
|
||||
soft_block_indent(self.expression()),
|
||||
soft_block_indent(&self.expression()),
|
||||
"]"
|
||||
))
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
JsLabels,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
best_fitting,
|
||||
formatter::{Comments, JsFormatter, prelude::*},
|
||||
parentheses::NeedsParentheses,
|
||||
@@ -29,14 +29,14 @@ use super::typecast::is_cast_target;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemberChain<'a, 'b> {
|
||||
root: &'b AstNode<'a, CallExpression<'a>>,
|
||||
root: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
head: MemberChainGroup<'a, 'b>,
|
||||
tail: TailChainGroups<'a, 'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> MemberChain<'a, 'b> {
|
||||
pub(crate) fn from_call_expression(
|
||||
call_expression: &'b AstNode<'a, CallExpression<'a>>,
|
||||
call_expression: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> Self {
|
||||
let mut chain_members = chain_members_iter(call_expression, f).collect::<ChainMembers>();
|
||||
@@ -62,7 +62,7 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
||||
|
||||
/// Here we check if the first group can be merged to the head. If so, then
|
||||
/// we move out the first group out of the groups
|
||||
fn maybe_merge_with_first_group(&mut self, parent: &AstNodes<'a>, f: &JsFormatter<'_, 'a>) {
|
||||
fn maybe_merge_with_first_group(&mut self, parent: &AstNodes<'_, 'a>, f: &JsFormatter<'_, 'a>) {
|
||||
if self.should_merge_tail_with_head(parent, f) {
|
||||
let group = self.tail.pop_first().unwrap();
|
||||
self.head.extend_members(group.into_members());
|
||||
@@ -70,7 +70,11 @@ impl<'a, 'b> MemberChain<'a, 'b> {
|
||||
}
|
||||
|
||||
/// This function checks if the current grouping should be merged with the first group.
|
||||
fn should_merge_tail_with_head(&self, parent: &AstNodes<'a>, f: &JsFormatter<'_, 'a>) -> bool {
|
||||
fn should_merge_tail_with_head(
|
||||
&self,
|
||||
parent: &AstNodes<'_, 'a>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
let Some(first_group) = self.tail.first() else {
|
||||
return false;
|
||||
};
|
||||
@@ -359,14 +363,14 @@ fn is_computed_array_member_access(member: &ChainMember<'_, '_>) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
fn has_arrow_or_function_expression_arg(call: &AstNode<'_, CallExpression<'_>>) -> bool {
|
||||
fn has_arrow_or_function_expression_arg(call: &AstNode<'_, '_, CallExpression<'_>>) -> bool {
|
||||
call.as_ref().arguments.iter().any(|argument| {
|
||||
matches!(&argument, Argument::ArrowFunctionExpression(_) | Argument::FunctionExpression(_))
|
||||
})
|
||||
}
|
||||
|
||||
fn has_simple_arguments<'a>(call: &AstNode<'a, CallExpression<'a>>) -> bool {
|
||||
call.arguments().iter().all(|argument| SimpleArgument::new(argument).is_simple())
|
||||
fn has_simple_arguments<'a>(call: &AstNode<'_, 'a, CallExpression<'a>>) -> bool {
|
||||
call.arguments().iter().all(|argument| SimpleArgument::new(&argument).is_simple())
|
||||
}
|
||||
|
||||
/// In order to detect those cases, we use an heuristic: if the first
|
||||
@@ -388,7 +392,7 @@ fn is_factory(token: &str) -> bool {
|
||||
/// This function is the inverse of the prettier function
|
||||
/// [Prettier applies]: <https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/member-chain.js#L342>
|
||||
pub fn is_member_call_chain<'a>(
|
||||
expression: &AstNode<'a, CallExpression<'a>>,
|
||||
expression: &AstNode<'_, 'a, CallExpression<'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> bool {
|
||||
MemberChain::from_call_expression(expression, f).tail.is_member_call_chain()
|
||||
@@ -399,17 +403,19 @@ fn has_short_name(name: &str, tab_width: u8) -> bool {
|
||||
}
|
||||
|
||||
fn chain_members_iter<'a, 'b>(
|
||||
root: &'b AstNode<'a, CallExpression<'a>>,
|
||||
root: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> impl Iterator<Item = ChainMember<'a, 'b>> {
|
||||
// The members are in the arena, as each one is the parent of the next one
|
||||
let allocator = f.allocator();
|
||||
let mut is_root = true;
|
||||
let mut next: Option<&'b AstNode<'a, Expression<'a>>> = None;
|
||||
let mut next: Option<AstNode<'b, 'a, Expression<'a>>> = None;
|
||||
|
||||
iter::from_fn(move || {
|
||||
let handle_call_expression =
|
||||
|position: CallExpressionPosition,
|
||||
expr: &'b AstNode<'a, CallExpression<'a>>,
|
||||
next: &mut Option<&'b AstNode<'a, Expression<'a>>>| {
|
||||
expr: &'b AstNode<'b, 'a, CallExpression<'a>>,
|
||||
next: &mut Option<AstNode<'b, 'a, Expression<'a>>>| {
|
||||
let callee = expr.callee();
|
||||
|
||||
let is_chain = matches!(
|
||||
@@ -434,7 +440,7 @@ fn chain_members_iter<'a, 'b>(
|
||||
let expression = next.take()?;
|
||||
|
||||
if is_cast_target(expression.span(), f) {
|
||||
return ChainMember::Node(expression).into();
|
||||
return ChainMember::Node(expression.in_arena(allocator)).into();
|
||||
}
|
||||
|
||||
// A nested `ChainExpression` whose parentheses do not survive
|
||||
@@ -443,16 +449,16 @@ fn chain_members_iter<'a, 'b>(
|
||||
// The type-cast case is already handled above:
|
||||
// on the first iteration `chain` is `expression` itself,
|
||||
// and a `ChainElement` can never be another `ChainExpression`, so no re-check is needed here.
|
||||
let mut node = expression.as_ast_nodes();
|
||||
while let AstNodes::ChainExpression(chain) = node {
|
||||
let mut node = expression.as_kind();
|
||||
while let AstNodeKind::ChainExpression(chain) = node {
|
||||
if chain.needs_parentheses(f) {
|
||||
break;
|
||||
}
|
||||
node = chain.expression().as_ast_nodes();
|
||||
node = chain.in_arena(allocator).expression().as_kind();
|
||||
}
|
||||
|
||||
let member = match node {
|
||||
AstNodes::CallExpression(expr) => {
|
||||
AstNodeKind::CallExpression(expr) => {
|
||||
let callee = expr.callee();
|
||||
let is_chain = matches!(
|
||||
callee.as_ref(),
|
||||
@@ -465,21 +471,24 @@ fn chain_members_iter<'a, 'b>(
|
||||
} else {
|
||||
CallExpressionPosition::Start
|
||||
};
|
||||
handle_call_expression(position, expr, &mut next)
|
||||
handle_call_expression(position, expr.in_arena(allocator), &mut next)
|
||||
}
|
||||
AstNodes::StaticMemberExpression(expr) => {
|
||||
AstNodeKind::StaticMemberExpression(expr) => {
|
||||
let expr = expr.in_arena(allocator);
|
||||
next = Some(expr.object());
|
||||
ChainMember::StaticMember(expr)
|
||||
}
|
||||
AstNodes::ComputedMemberExpression(expr) => {
|
||||
AstNodeKind::ComputedMemberExpression(expr) => {
|
||||
let expr = expr.in_arena(allocator);
|
||||
next = Some(expr.object());
|
||||
ChainMember::ComputedMember(expr)
|
||||
}
|
||||
AstNodes::TSNonNullExpression(expr) => {
|
||||
AstNodeKind::TSNonNullExpression(expr) => {
|
||||
let expr = expr.in_arena(allocator);
|
||||
next = Some(expr.expression());
|
||||
ChainMember::TSNonNullExpression(expr)
|
||||
}
|
||||
_ => ChainMember::Node(expression),
|
||||
_ => ChainMember::Node(expression.in_arena(allocator)),
|
||||
};
|
||||
|
||||
Some(member)
|
||||
|
||||
@@ -83,7 +83,7 @@ pub fn is_keyword_property_key(key: &PropertyKey<'_>) -> bool {
|
||||
/// ```javascript
|
||||
/// `connect(a, b, c)(d)`
|
||||
/// ```
|
||||
pub fn is_long_curried_call(call: &AstNode<'_, CallExpression<'_>>) -> bool {
|
||||
pub fn is_long_curried_call(call: &AstNode<'_, '_, CallExpression<'_>>) -> bool {
|
||||
if let AstNodes::CallExpression(parent_call) = call.parent()
|
||||
&& parent_call.is_callee_span(call.span)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ use oxc_syntax::identifier::is_identifier_name_patched;
|
||||
|
||||
use crate::{
|
||||
Format,
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::{JsFormatter, JsFormatterExt as _},
|
||||
utils::{
|
||||
string::{FormatLiteralStringToken, StringLiteralParentKind},
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn format_property_key<'a>(
|
||||
key: &AstNode<'a, PropertyKey<'a>>,
|
||||
key: &AstNode<'_, 'a, PropertyKey<'a>>,
|
||||
computed: bool,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -24,7 +24,7 @@ pub fn format_property_key<'a>(
|
||||
}
|
||||
|
||||
// Check if we're in a Tailwind context and the key is a string literal with multiple classes
|
||||
if let AstNodes::StringLiteral(string) = key.as_ast_nodes() {
|
||||
if let AstNodeKind::StringLiteral(string) = &key.as_kind() {
|
||||
if let Some(ctx) = tailwind_context_for_string_literal(string, f) {
|
||||
// Reuse the existing Tailwind string literal writer
|
||||
write_tailwind_string_literal(string, ctx, f);
|
||||
@@ -52,10 +52,10 @@ pub fn format_property_key<'a>(
|
||||
}
|
||||
|
||||
pub fn write_member_name<'a>(
|
||||
key: &AstNode<'a, PropertyKey<'a>>,
|
||||
key: &AstNode<'_, 'a, PropertyKey<'a>>,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) -> usize {
|
||||
if let AstNodes::StringLiteral(string) = key.as_ast_nodes() {
|
||||
if let AstNodeKind::StringLiteral(string) = &key.as_kind() {
|
||||
if let Some(ctx) = tailwind_context_for_string_literal(string, f) {
|
||||
// Reuse the existing Tailwind string literal writer
|
||||
string.format_leading_comments(f);
|
||||
|
||||
@@ -3,7 +3,7 @@ use oxc_formatter_core::Format;
|
||||
use oxc_span::GetSpan;
|
||||
|
||||
use crate::{
|
||||
ast_nodes::{AstNode, AstNodes},
|
||||
ast_nodes::{AstNode, AstNodeKind, AstNodes},
|
||||
formatter::{
|
||||
JsFormatContext, JsFormatter, JsFormatterExt as _,
|
||||
prelude::{empty_line, format_once, hard_line_break, soft_line_indent_or_space, space},
|
||||
@@ -208,12 +208,12 @@ pub fn write_comments_between_blocks<'a>(
|
||||
}
|
||||
|
||||
pub struct FormatStatementBody<'a, 'b> {
|
||||
body: &'b AstNode<'a, Statement<'a>>,
|
||||
body: &'b AstNode<'b, 'a, Statement<'a>>,
|
||||
force_space: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatStatementBody<'a, 'b> {
|
||||
pub fn new(body: &'b AstNode<'a, Statement<'a>>) -> Self {
|
||||
pub fn new(body: &'b AstNode<'b, 'a, Statement<'a>>) -> Self {
|
||||
Self { body, force_space: false }
|
||||
}
|
||||
|
||||
@@ -227,14 +227,15 @@ impl<'a, 'b> FormatStatementBody<'a, 'b> {
|
||||
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for FormatStatementBody<'a, '_> {
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
if let AstNodes::EmptyStatement(empty) = self.body.as_ast_nodes() {
|
||||
let kind = self.body.as_kind();
|
||||
if let AstNodeKind::EmptyStatement(empty) = &kind {
|
||||
// The `;` IS the body (content), so the head-body separator applies before it too;
|
||||
// without comments no separator is written (`while (x);`).
|
||||
if f.context().comments().has_comment_before(empty.span.start) {
|
||||
write_head_body_separator(empty.span.start, f);
|
||||
}
|
||||
write!(f, empty);
|
||||
} else if let AstNodes::BlockStatement(block) = self.body.as_ast_nodes() {
|
||||
} else if let AstNodeKind::BlockStatement(block) = &kind {
|
||||
write_head_body_separator(block.span().start, f);
|
||||
write!(f, [block]);
|
||||
} else if self.force_space {
|
||||
|
||||
@@ -31,7 +31,7 @@ use super::string::{FormatLiteralStringToken, StringLiteralParentKind};
|
||||
/// - The context is not disabled (e.g., not inside a nested non-Tailwind call)
|
||||
/// - The string contains whitespace (indicating multiple classes to sort)
|
||||
pub fn tailwind_context_for_string_literal<'a>(
|
||||
string: &AstNode<'a, StringLiteral<'a>>,
|
||||
string: &AstNode<'_, 'a, StringLiteral<'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> Option<TailwindContextEntry> {
|
||||
f.context().tailwind_context().copied().filter(|ctx| {
|
||||
@@ -167,7 +167,7 @@ impl CollapseWhitespace {
|
||||
/// ```
|
||||
pub fn can_collapse_whitespace<'a, 'b>(
|
||||
span: Span,
|
||||
ancestors: impl Iterator<Item = &'b AstNodes<'a>>,
|
||||
ancestors: impl Iterator<Item = &'b AstNodes<'b, 'a>>,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
) -> CollapseWhitespace
|
||||
where
|
||||
@@ -233,7 +233,7 @@ where
|
||||
/// - Preserves boundary spaces when required by concat/template context
|
||||
/// - With `preserve_whitespace`, outputs content unchanged
|
||||
pub fn write_tailwind_string_literal<'a>(
|
||||
string_literal: &AstNode<'a, StringLiteral<'a>>,
|
||||
string_literal: &AstNode<'_, 'a, StringLiteral<'a>>,
|
||||
ctx: TailwindContextEntry,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -321,7 +321,7 @@ pub fn write_tailwind_string_literal<'a>(
|
||||
///
|
||||
/// Based on [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/blob/28beb4e008b913414562addec4abb8ab261f3828/src/index.ts#L511-L566).
|
||||
pub fn write_tailwind_template_element<'a>(
|
||||
element: &AstNode<'a, TemplateElement<'a>>,
|
||||
element: &AstNode<'_, 'a, TemplateElement<'a>>,
|
||||
ctx: TailwindContextEntry,
|
||||
f: &mut JsFormatter<'_, 'a>,
|
||||
) {
|
||||
@@ -380,7 +380,7 @@ pub fn write_tailwind_template_element<'a>(
|
||||
|
||||
/// Determines whitespace collapse rules for a template element based on binary expression context.
|
||||
fn can_collapse_whitespace_template<'a>(
|
||||
element: &AstNode<'a, TemplateElement<'a>>,
|
||||
element: &AstNode<'_, 'a, TemplateElement<'a>>,
|
||||
is_first: bool,
|
||||
is_last: bool,
|
||||
f: &JsFormatter<'_, 'a>,
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn formatter_output_path(file_name: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn get_node_type(ty: &TokenStream) -> TokenStream {
|
||||
quote! { AstNode<'a, #ty> }
|
||||
quote! { AstNode<'parent, 'a, #ty> }
|
||||
}
|
||||
|
||||
/// AST nodes whose last child should have `following_span_start = 0`.
|
||||
@@ -71,10 +71,24 @@ impl Generator for FormatterAstNodesGenerator {
|
||||
|
||||
let ast_nodes_variants = ast_nodes_names.iter().map(|(name, lifetime)| {
|
||||
quote! {
|
||||
#name(&'a AstNode<'a, #name #lifetime>),
|
||||
#name(&'parent AstNode<'parent, 'a, #name #lifetime>),
|
||||
}
|
||||
});
|
||||
|
||||
let ast_node_kind_variants = ast_nodes_names.iter().map(|(name, lifetime)| {
|
||||
quote! {
|
||||
#name(AstNode<'parent, 'a, #name #lifetime>),
|
||||
}
|
||||
});
|
||||
|
||||
let as_ast_nodes_match_arms = ast_nodes_names.iter().map(|(name, _)| {
|
||||
quote! { Self::#name(n) => AstNodes::#name(n), }
|
||||
});
|
||||
|
||||
let kind_span_match_arms = ast_nodes_names.iter().map(|(name, _)| {
|
||||
quote! { Self::#name(n) => n.span(), }
|
||||
});
|
||||
|
||||
let dummy_variant = quote! {
|
||||
Self::Dummy() => panic!("Should never be called on a dummy node"),
|
||||
};
|
||||
@@ -92,17 +106,8 @@ impl Generator for FormatterAstNodesGenerator {
|
||||
quote! { Self::#name(_) => #debug_name, }
|
||||
});
|
||||
|
||||
let transmute_self = quote! {
|
||||
#[inline]
|
||||
pub(super) fn transmute_self<'a, T>(s: &AstNode<'a, T>) -> &'a AstNode<'a, T> {
|
||||
// SAFETY: `s` is already allocated in Arena, so transmute from `&` to `&'a` is safe.
|
||||
#[expect(clippy::undocumented_unsafe_blocks)]
|
||||
unsafe { transmute(s) }
|
||||
}
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
use std::mem::transmute;
|
||||
#![expect(clippy::match_same_arms, clippy::elidable_lifetime_names)]
|
||||
///@@line_break
|
||||
use oxc_allocator::ArenaVec;
|
||||
use oxc_ast::ast::*;
|
||||
@@ -116,20 +121,47 @@ impl Generator for FormatterAstNodesGenerator {
|
||||
JsFormatter,
|
||||
trivia::{format_leading_comments, format_trailing_comments},
|
||||
};
|
||||
|
||||
|
||||
|
||||
///@@line_break
|
||||
#transmute_self
|
||||
|
||||
///@@line_break
|
||||
/// A reference to the parent node of an [`AstNode`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AstNodes<'a> {
|
||||
pub enum AstNodes<'parent, 'a> {
|
||||
Dummy(),
|
||||
#(#ast_nodes_variants)*
|
||||
}
|
||||
|
||||
impl AstNodes<'_> {
|
||||
///@@line_break
|
||||
/// An [`AstNode`] of an enum (e.g. `Expression`) resolved to its variant, see `as_kind`.
|
||||
///
|
||||
/// Unlike [`AstNodes`], it owns the node, so it can be created on the stack;
|
||||
/// a child of the variant then refers to it as its parent.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AstNodeKind<'parent, 'a> {
|
||||
#(#ast_node_kind_variants)*
|
||||
}
|
||||
|
||||
///@@line_break
|
||||
impl<'a> AstNodeKind<'_, 'a> {
|
||||
/// Returns the node as a parent reference.
|
||||
#[inline]
|
||||
pub fn as_ast_nodes(&self) -> AstNodes<'_, 'a> {
|
||||
match self {
|
||||
#(#as_ast_nodes_match_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///@@line_break
|
||||
impl GetSpan for AstNodeKind<'_, '_> {
|
||||
#[inline]
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
#(#kind_span_match_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///@@line_break
|
||||
impl<'parent, 'a> AstNodes<'parent, 'a> {
|
||||
/// Returns the span of this AST node.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -149,7 +181,7 @@ impl Generator for FormatterAstNodesGenerator {
|
||||
///
|
||||
/// Panics when called on a `Dummy` node, which should never appear in a real AST.
|
||||
#[inline]
|
||||
pub fn parent(&self) -> &Self {
|
||||
pub fn parent(&self) -> &AstNodes<'parent, 'a> {
|
||||
match self {
|
||||
#dummy_variant
|
||||
#(#parent_match_arms)*
|
||||
@@ -257,10 +289,17 @@ fn generate_struct_impls(
|
||||
};
|
||||
|
||||
let parent_expr = if has_kind {
|
||||
quote! { AstNodes::#struct_name(transmute_self(self)) }
|
||||
quote! { AstNodes::#struct_name(self) }
|
||||
} else {
|
||||
quote! { self.parent }
|
||||
};
|
||||
// A child of a node without `AstNodes` variant inherits the parent of `self`,
|
||||
// so it can outlive the borrow of `self`.
|
||||
let child_lifetime = if has_kind {
|
||||
quote! { '_ }
|
||||
} else {
|
||||
quote! { 'parent }
|
||||
};
|
||||
|
||||
let reference_prefix = if is_copyable {
|
||||
quote! {}
|
||||
@@ -355,22 +394,20 @@ fn generate_struct_impls(
|
||||
if is_option {
|
||||
quote! {
|
||||
#following_span_start_expr
|
||||
self.allocator.alloc(self.inner.#field_name.as_ref().map(|inner| AstNode {
|
||||
self.inner.#field_name.as_ref().map(|inner| AstNode {
|
||||
inner: #inner_access,
|
||||
allocator: self.allocator,
|
||||
parent: #parent_expr,
|
||||
following_span_start
|
||||
})).as_ref()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#following_span_start_expr
|
||||
self.allocator.alloc(AstNode {
|
||||
AstNode {
|
||||
inner: #field_access,
|
||||
allocator: self.allocator,
|
||||
parent: #parent_expr,
|
||||
following_span_start
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -378,7 +415,7 @@ fn generate_struct_impls(
|
||||
let return_type_final = if is_not_ast_node {
|
||||
quote! { #reference_prefix #field_inner_ty }
|
||||
} else {
|
||||
quote! { &AstNode<'a, #field_inner_ty> }
|
||||
quote! { AstNode<#child_lifetime, 'a, #field_inner_ty> }
|
||||
};
|
||||
|
||||
let return_type_final = if is_option {
|
||||
@@ -398,7 +435,7 @@ fn generate_struct_impls(
|
||||
|
||||
quote! {
|
||||
///@@line_break
|
||||
impl<'a> AstNode<'a, #type_ty> {
|
||||
impl<'parent, 'a> AstNode<'parent, 'a, #type_ty> {
|
||||
#(#methods)*
|
||||
|
||||
///@@line_break
|
||||
@@ -552,12 +589,11 @@ fn generate_enum_impls(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
|
||||
let implementation = if has_kind(field_type, schema) {
|
||||
quote! {
|
||||
AstNodes::#node_type_ident(self.allocator.alloc(AstNode {
|
||||
AstNodeKind::#node_type_ident(AstNode {
|
||||
inner: #inner_expr,
|
||||
parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}))
|
||||
following_span_start,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// This panic might indicate a need for further refinement or configuration in your schema/generation
|
||||
@@ -565,7 +601,7 @@ fn generate_enum_impls(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
panic!("No kind for current enum variant yet, please see `tasks/ast_tools/src/generators/ast_kind.rs`")
|
||||
}
|
||||
};
|
||||
quote! { #enum_ident::#variant_name(s) => { #implementation }, }
|
||||
quote! { #enum_ident::#variant_name(s) => #implementation, }
|
||||
});
|
||||
|
||||
let inherits_match_arms = enum_def.inherits_enums(schema).map(|inherited_enum_def| {
|
||||
@@ -573,41 +609,28 @@ fn generate_enum_impls(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
let match_ident = format_ident!("match_{inherits_snake_name}");
|
||||
let to_fn_ident = format_ident!("to_{inherits_snake_name}");
|
||||
|
||||
let implementation = quote! {
|
||||
return self.allocator.alloc(AstNode {
|
||||
quote! {
|
||||
it @ #match_ident!(#enum_ident) => AstNode {
|
||||
inner: it.#to_fn_ident(),
|
||||
parent,
|
||||
allocator: self.allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}).as_ast_nodes();
|
||||
};
|
||||
quote! { it @ #match_ident!(#enum_ident) => { #implementation }, }
|
||||
following_span_start,
|
||||
}.as_kind(),
|
||||
}
|
||||
});
|
||||
|
||||
let node_type = get_node_type(&type_ty);
|
||||
let implementation = if variant_match_arms.len() == 0 {
|
||||
quote! {
|
||||
#[expect(clippy::needless_return)]
|
||||
match self.inner {
|
||||
#(#inherits_match_arms)*
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let node = match self.inner {
|
||||
#(#variant_match_arms)*
|
||||
#(#inherits_match_arms)*
|
||||
};
|
||||
self.allocator.alloc(node)
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
///@@line_break
|
||||
impl<'a> #node_type {
|
||||
impl<'parent, 'a> #node_type {
|
||||
/// Resolves this node to its variant, which has the same parent.
|
||||
#[inline]
|
||||
pub fn as_ast_nodes(&self) -> &AstNodes<'a> {
|
||||
pub fn as_kind(&self) -> AstNodeKind<'parent, 'a> {
|
||||
let parent = self.parent;
|
||||
#implementation
|
||||
let following_span_start = self.following_span_start;
|
||||
match self.inner {
|
||||
#(#variant_match_arms)*
|
||||
#(#inherits_match_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use quote::{format_ident, quote};
|
||||
|
||||
use crate::{
|
||||
Codegen, Generator,
|
||||
generators::{define_generator, formatter::ast_nodes::get_node_type},
|
||||
generators::define_generator,
|
||||
output::Output,
|
||||
schema::{Def, EnumDef, Schema, StructDef, StructOrEnum, TypeDef, TypeId},
|
||||
};
|
||||
@@ -113,7 +113,7 @@ fn generate_struct_implementation(
|
||||
) -> TokenStream {
|
||||
let type_ty = struct_def.ty(schema);
|
||||
let type_ty = quote! {
|
||||
AstNode::<'a, #type_ty>
|
||||
AstNode::<'_, 'a, #type_ty>
|
||||
};
|
||||
|
||||
let struct_name = struct_def.name();
|
||||
@@ -334,12 +334,11 @@ fn generate_enum_implementation(enum_def: &EnumDef, schema: &Schema) -> TokenStr
|
||||
|
||||
Some(quote! {
|
||||
#enum_ident::#variant_name(inner) => {
|
||||
allocator.alloc(AstNode::<#node_type> {
|
||||
AstNode::<#node_type> {
|
||||
inner,
|
||||
parent,
|
||||
allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}).fmt(f);
|
||||
}.fmt(f);
|
||||
},
|
||||
})
|
||||
});
|
||||
@@ -353,19 +352,18 @@ fn generate_enum_implementation(enum_def: &EnumDef, schema: &Schema) -> TokenStr
|
||||
let match_arm = quote! {
|
||||
it @ #match_ident!(#enum_ident) => {
|
||||
let inner = it.#to_fn_ident();
|
||||
allocator.alloc(AstNode::<'a, #inherits_ident> {
|
||||
AstNode::<#inherits_ident> {
|
||||
inner,
|
||||
parent,
|
||||
allocator,
|
||||
following_span_start: self.following_span_start,
|
||||
}).fmt(f);
|
||||
}.fmt(f);
|
||||
},
|
||||
};
|
||||
|
||||
match_arm
|
||||
});
|
||||
|
||||
let node_type = get_node_type(&enum_ty);
|
||||
let node_type = quote! { AstNode<'_, 'a, #enum_ty> };
|
||||
|
||||
let inline_trailing_suppression = match enum_def.name() {
|
||||
"Statement" => {
|
||||
@@ -382,13 +380,16 @@ fn generate_enum_implementation(enum_def: &EnumDef, schema: &Schema) -> TokenStr
|
||||
_ => quote! {},
|
||||
};
|
||||
|
||||
// Every variant of `Expression` implements `NeedsParentheses`, see `get_needs_parentheses_type_ids`
|
||||
let needs_parentheses_impl =
|
||||
(enum_def.name() == "Expression").then(|| generate_needs_parentheses(enum_def, schema));
|
||||
|
||||
quote! {
|
||||
///@@line_break
|
||||
impl<'a> Format<'a, JsFormatContext<'a>> for #node_type {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut JsFormatter<'_, 'a>) {
|
||||
#inline_trailing_suppression
|
||||
let allocator = self.allocator;
|
||||
let parent = self.parent;
|
||||
match self.inner {
|
||||
#(#variant_match_arms)*
|
||||
@@ -396,6 +397,37 @@ fn generate_enum_implementation(enum_def: &EnumDef, schema: &Schema) -> TokenStr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#needs_parentheses_impl
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatches `needs_parentheses` to the variant in a single match.
|
||||
fn generate_needs_parentheses(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
let enum_ident = enum_def.ident();
|
||||
let enum_ty = enum_def.ty(schema);
|
||||
|
||||
let match_arms = enum_def.all_variants(schema).map(|variant| {
|
||||
let variant_name = variant.ident();
|
||||
let node_type = variant.field_type(schema).unwrap().innermost_type(schema).ident();
|
||||
quote! {
|
||||
#enum_ident::#variant_name(inner) => AstNode::<#node_type> {
|
||||
inner,
|
||||
parent: self.parent,
|
||||
following_span_start: self.following_span_start,
|
||||
}.needs_parentheses(f),
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
///@@line_break
|
||||
impl<'a> NeedsParentheses<'a> for AstNode<'_, 'a, #enum_ty> {
|
||||
fn needs_parentheses(&self, f: &JsFormatter<'_, 'a>) -> bool {
|
||||
match self.inner {
|
||||
#(#match_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
checker.ts:
|
||||
file size: 2922154 # 2.92 MB
|
||||
sys allocs: 19386
|
||||
sys reallocs: 495
|
||||
sys deallocs: 19385
|
||||
sys alloc bytes: 6677609 # 6.68 MB
|
||||
sys allocs: 18979
|
||||
sys reallocs: 494
|
||||
sys deallocs: 18978
|
||||
sys alloc bytes: 6581369 # 6.58 MB
|
||||
sys peak growth: 2973842 # 2.97 MB
|
||||
arena allocs: 1058560
|
||||
arena allocs: 36968
|
||||
arena reallocs: 927
|
||||
arena size: 89923584 # 89.92 MB
|
||||
arena size: 51409112 # 51.41 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
sys allocs: 5193
|
||||
sys allocs: 5177
|
||||
sys reallocs: 89
|
||||
sys deallocs: 5192
|
||||
sys alloc bytes: 1306838 # 1.31 MB
|
||||
sys deallocs: 5176
|
||||
sys alloc bytes: 1293782 # 1.29 MB
|
||||
sys peak growth: 435478 # 435.48 kB
|
||||
arena allocs: 209717
|
||||
arena allocs: 11512
|
||||
arena reallocs: 364
|
||||
arena size: 16891184 # 16.89 MB
|
||||
arena size: 9480664 # 9.48 MB
|
||||
|
||||
RadixUIAdoptionSection.jsx:
|
||||
file size: 2520 # 2.52 kB
|
||||
sys allocs: 68
|
||||
sys reallocs: 52
|
||||
sys deallocs: 67
|
||||
sys alloc bytes: 34820 # 34.82 kB
|
||||
sys peak growth: 12120 # 12.12 kB
|
||||
arena allocs: 1149
|
||||
sys alloc bytes: 36388 # 36.39 kB
|
||||
sys peak growth: 12728 # 12.73 kB
|
||||
arena allocs: 57
|
||||
arena reallocs: 0
|
||||
arena size: 118600 # 118.60 kB
|
||||
arena size: 76280 # 76.28 kB
|
||||
|
||||
pdf.mjs:
|
||||
file size: 567296 # 567.30 kB
|
||||
sys allocs: 9776
|
||||
sys allocs: 9736
|
||||
sys reallocs: 167
|
||||
sys deallocs: 9775
|
||||
sys alloc bytes: 2664720 # 2.66 MB
|
||||
sys deallocs: 9735
|
||||
sys alloc bytes: 2642168 # 2.64 MB
|
||||
sys peak growth: 1156488 # 1.16 MB
|
||||
arena allocs: 429815
|
||||
arena allocs: 20583
|
||||
arena reallocs: 428
|
||||
arena size: 29487632 # 29.49 MB
|
||||
arena size: 14139600 # 14.14 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
sys allocs: 84591
|
||||
sys reallocs: 353
|
||||
sys deallocs: 84590
|
||||
sys alloc bytes: 25772794 # 25.77 MB
|
||||
sys allocs: 74785
|
||||
sys reallocs: 351
|
||||
sys deallocs: 74784
|
||||
sys alloc bytes: 25342050 # 25.34 MB
|
||||
sys peak growth: 13710144 # 13.71 MB
|
||||
arena allocs: 2535528
|
||||
arena allocs: 120676
|
||||
arena reallocs: 1889
|
||||
arena size: 246384344 # 246.38 MB
|
||||
arena size: 156478528 # 156.48 MB
|
||||
|
||||
binder.ts:
|
||||
file size: 193077 # 193.08 kB
|
||||
sys allocs: 974
|
||||
sys allocs: 968
|
||||
sys reallocs: 35
|
||||
sys deallocs: 973
|
||||
sys alloc bytes: 345301 # 345.30 kB
|
||||
sys deallocs: 967
|
||||
sys alloc bytes: 341493 # 341.49 kB
|
||||
sys peak growth: 195197 # 195.20 kB
|
||||
arena allocs: 62781
|
||||
arena allocs: 2092
|
||||
arena reallocs: 38
|
||||
arena size: 5660960 # 5.66 MB
|
||||
arena size: 3353016 # 3.35 MB
|
||||
|
||||
kitchen-sink.tsx:
|
||||
file size: 732898 # 732.90 kB
|
||||
sys allocs: 12671
|
||||
sys reallocs: 408
|
||||
sys deallocs: 12670
|
||||
sys alloc bytes: 4192018 # 4.19 MB
|
||||
sys allocs: 12610
|
||||
sys reallocs: 398
|
||||
sys deallocs: 12609
|
||||
sys alloc bytes: 4178154 # 4.18 MB
|
||||
sys peak growth: 1491884 # 1.49 MB
|
||||
arena allocs: 557605
|
||||
arena allocs: 30666
|
||||
arena reallocs: 1041
|
||||
arena size: 38433168 # 38.43 MB
|
||||
arena size: 18552208 # 18.55 MB
|
||||
Reference in new issue
Block a user