use super::super::chain::{
ChainCall, transparent_callee_sealed_chain, transparent_instantiation_callee,
};
use super::super::comments::CommentSpacing;
use super::super::{Printer, ShareTag, is_curried_arrow_chain, is_multiline_template_expression};
use super::arg_comments::{
PartitionedComments, any_arg_empty_line, any_comment_forces_expansion, build_arg_gap_docs,
build_empty_args_parens_doc, emit_last_arg_trailing_comments, first_arg_has_any_comments,
has_inter_argument_comments, has_trailing_comments_on_args, last_arg_has_comments,
};
use super::arg_predicates::{
arrow_body_breaks_after_space, arrow_body_is_call_through_non_null, is_block_function,
is_function_composition_args, is_ternary_arrow_body, last_arg_is_array_or_object,
lone_arg_params_render_flat, mark_boolean_coercion_argument,
};
use super::arg_wrapping::{
ArgOpener, AssumedHugKind, AssumedParamHug, ChainArgKind, arrow_body_expands_internally,
arrow_body_tail_has_comments, arrow_hug_refused_by_comments, build_args_split_last,
build_arrow_gap_break_multi_arg_doc, build_arrow_gap_break_single_arg_doc, build_arrow_sig_doc,
build_break_body_ladder, build_expand_first_arg_doc, build_expand_last_obj_array_doc,
build_flat_params_arg_doc, build_hugged_arrow_sig_doc, build_joined_argument_doc,
build_printed_argument_doc, build_single_arrow_hug_doc, build_ternary_arrow_hug_ladder,
classify_chain_arg, first_arg_signature_refuses_expand_first, last_arg_arrow_gap_break,
last_two_args_same_type, prebuild_expand_last_break_body, prebuild_expand_last_obj_array_body,
prepend_arrow_body_comments, prepend_hugged_arrow_body_comments, should_expand_first_arg,
sole_template_on_its_own_line, try_hook_deps_args_doc,
};
use crate::ast::{self, Expression, ExpressionKind};
use crate::printer::expressions::functions::{
arrow_signature_refuses_hug, callback_signature_refuses_hug, prepend_leading,
};
use tsv_lang::Span;
use tsv_lang::doc::DocBuf;
use tsv_lang::doc::arena::DocArena;
use tsv_lang::doc::arena::DocId;
#[expect(clippy::inline_always)]
#[inline(always)]
pub(super) fn chain_call_head_doc(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
gap: super::CalleeGap,
) -> Option<DocId> {
let d = printer.d();
let optional_doc = super::optional_callee_gap_doc(printer, gap);
if let Some(inst) = transparent_instantiation_callee(call) {
let ta = &inst.type_arguments;
let mut parts = DocBuf::new();
parts.extend(printer.build_name_to_type_params_comments_opt(
instantiation_window_start(printer, call, inst),
ta.span.start,
CommentSpacing::Trailing,
));
parts.push(shared_chain_type_args_doc(printer, ta));
parts.extend(optional_doc);
return Some(d.concat(&parts));
}
let type_args = call.type_arguments.as_ref();
if optional_doc.is_none() && type_args.is_none() {
return None;
}
let mut parts = DocBuf::new();
parts.extend(optional_doc);
if let Some(ta) = type_args {
parts.extend(printer.build_name_to_type_params_comments_opt(
gap.start,
ta.span.start,
CommentSpacing::Trailing,
));
parts.push(shared_chain_type_args_doc(printer, ta));
}
Some(d.concat(&parts))
}
fn shared_chain_type_args_doc(
printer: &Printer<'_>,
ta: &ast::TSTypeParameterInstantiation<'_>,
) -> DocId {
let key = printer.chain_share_key_of(ta, ta.span, ShareTag::ChainTypeArgs);
printer.chain_shared_doc(key, || printer.build_type_parameter_instantiation_doc(ta))
}
fn instantiation_window_start(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
inst: &ast::TSInstantiationExpression<'_>,
) -> u32 {
let end = inst.expression.span().end;
Printer::gap_start_after_owned_pair(
end,
printer.owned_pair_trailing_gap(end, transparent_callee_sealed_chain(call).is_some()),
)
}
fn build_inline_leading_comments(
printer: &Printer<'_>,
paren_open: u32,
arg_start: u32,
) -> Option<DocId> {
let mut parts = DocBuf::new();
PartitionedComments::new(printer, paren_open, arg_start).emit_unpulled_gap(&mut parts, printer);
(!parts.is_empty()).then(|| printer.d().concat(&parts))
}
fn build_inline_trailing_comments(
printer: &Printer<'_>,
arg_end: u32,
next_boundary: u32,
) -> Option<DocId> {
let d = printer.d();
let pc = PartitionedComments::for_closer_gap(printer, arg_end, next_boundary);
if !pc.has_trailing_block() {
return None;
}
let mut parts = DocBuf::new();
for comment in &pc.trailing_block {
parts.push(d.text(" "));
parts.push(printer.build_comment_doc(comment));
}
Some(d.concat(&parts))
}
pub(super) fn build_call_args_doc_for_chain(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
span: Span,
facts: ChainCall,
) -> DocId {
build_call_args_doc_for_chain_impl(printer, call, span, facts, false)
}
fn single_arrow_expression_body<'a>(
call: &'a ast::CallExpression<'a>,
) -> Option<(
&'a ast::ArrowFunctionExpression<'a>,
Span,
&'a Expression<'a>,
)> {
if call.arguments.len() != 1 {
return None;
}
let arg = &call.arguments[0];
let ExpressionKind::ArrowFunctionExpression(arrow) = &arg.kind else {
return None;
};
match &arrow.body {
ast::ArrowFunctionBody::Expression(body) => Some((arrow, arg.span, body)),
ast::ArrowFunctionBody::BlockStatement(_) => None,
}
}
fn build_hugged_arrow_arg_doc(
printer: &Printer<'_>,
head: ChainHead,
ctx: ChainArgsContext,
arrow: &ast::ArrowFunctionExpression<'_>,
span: Span,
body_expr: &Expression<'_>,
body_doc: DocId,
) -> DocId {
let d = printer.d();
let body_doc =
prepend_hugged_arrow_body_comments(printer, arrow, body_expr.span().start, body_doc);
let sig_doc = prepend_leading(
d,
ctx.leading_comment_doc,
build_arrow_sig_doc(printer, arrow, span),
);
with_chain_head(
d,
head,
d.concat(&[ctx.prefix, sig_doc, d.text(" => "), body_doc, d.text(")")]),
)
}
pub(super) fn build_call_args_doc_for_chain_expanded(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
span: Span,
facts: ChainCall,
) -> DocId {
build_call_args_doc_for_chain_impl(printer, call, span, facts, true)
}
#[derive(Clone, Copy)]
#[expect(clippy::struct_excessive_bools)] struct ChainArgsContext {
paren_open: u32,
prefix: DocId,
own_call_layout: bool,
ungroup_args: bool,
has_leading_comments: bool,
has_leading_comment_on_page: bool,
has_any_comments: bool,
has_any_comment_text: bool,
last_arg_commented: bool,
has_trailing_block_comments: bool,
comments_force_expansion: bool,
leading_comment_doc: Option<DocId>,
}
fn build_call_args_doc_for_chain_impl(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
span: Span,
facts: ChainCall,
force_expand: bool,
) -> DocId {
let ChainCall {
own_call_layout,
ungroup_args,
} = facts;
let any_arg_empty_line = any_arg_empty_line(call.arguments, printer);
let gap = super::callee_gap(printer, call, span);
let paren_open = gap.paren_open(call);
let call_has_comments = printer.has_comments_on_page_between(paren_open, span.end);
let has_leading_comments = call_has_comments
&& !call.arguments.is_empty()
&& printer.has_comments_to_emit_between(paren_open, call.arguments[0].span().start);
let has_leading_comment_on_page = call_has_comments
&& !call.arguments.is_empty()
&& printer.has_comments_on_page_between(paren_open, call.arguments[0].span().start);
let has_inter_arg_comments = call_has_comments && has_inter_argument_comments(call, printer);
let has_trailing_comments =
call_has_comments && has_trailing_comments_on_args(call, span, printer);
let has_trailing_block_comments = call_has_comments
&& call
.arguments
.last()
.is_some_and(|last| printer.has_comments_to_emit_between(last.span().end, span.end));
let has_spread_paren_comments =
call_has_comments && printer.any_paren_interior_forces_expansion(call.arguments);
let has_any_comments = has_leading_comments
|| has_inter_arg_comments
|| has_trailing_comments
|| has_trailing_block_comments
|| has_spread_paren_comments;
let last_arg_commented =
call_has_comments && last_arg_has_comments(call.arguments, printer, span.end, paren_open);
let leading_comment_doc = if has_leading_comments && !call.arguments.is_empty() {
build_inline_leading_comments(printer, paren_open, call.arguments[0].span().start)
} else {
None
};
let comments_force_expansion =
has_any_comments && any_comment_forces_expansion(call, span, printer, paren_open);
let force_expand = force_expand
|| any_arg_empty_line
|| comments_force_expansion
|| is_function_composition_args(call.arguments);
let prefix = if gap.optional.fused() {
printer.d().text("?.(")
} else {
printer.d().text("(")
};
let head = chain_call_head_doc(printer, call, gap);
let head = ChainHead {
doc: head,
hang: gap
.paren_split
.map(|(start, paren)| printer.continuation_indent_prefix(start, paren)),
};
let ctx = ChainArgsContext {
paren_open,
prefix,
own_call_layout,
ungroup_args,
has_leading_comments,
has_leading_comment_on_page,
has_any_comments,
has_any_comment_text: call_has_comments,
last_arg_commented,
has_trailing_block_comments,
comments_force_expansion,
leading_comment_doc,
};
mark_boolean_coercion_argument(printer, call);
printer.mark_relational_sibling_authored_pairs(call.arguments.iter().copied().map(Some));
if let Some(doc) = try_hook_deps_args_doc(
printer,
call.arguments,
paren_open,
span.end,
call_has_comments,
prefix,
) {
return with_chain_head(printer.d(), head, doc);
}
if call.arguments.is_empty() {
build_chain_args_empty(printer, span, ctx, head)
} else if force_expand {
build_chain_args_force_expand(printer, call, span, ctx, head)
} else if call.arguments.len() == 1 {
build_chain_args_single(printer, call, span, ctx, head)
} else {
build_chain_args_multi(printer, call, span, ctx, head)
}
}
#[derive(Clone, Copy)]
pub(super) struct ChainHead {
doc: Option<DocId>,
hang: Option<DocId>,
}
fn with_chain_head(d: &DocArena, head: ChainHead, args: DocId) -> DocId {
let args = match head.hang {
None => args,
Some(hang) => d.indent(d.concat(&[hang, args])),
};
match head.doc {
None => args,
Some(doc) => d.concat(&[doc, args]),
}
}
fn build_chain_args_empty(
printer: &Printer<'_>,
span: Span,
ctx: ChainArgsContext,
head: ChainHead,
) -> DocId {
let ChainArgsContext {
paren_open, prefix, ..
} = ctx;
with_chain_head(
printer.d(),
head,
build_empty_args_parens_doc(printer, paren_open, span.end, prefix),
)
}
fn build_chain_args_force_expand(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
span: Span,
ctx: ChainArgsContext,
head: ChainHead,
) -> DocId {
let d = printer.d();
let ChainArgsContext {
paren_open,
prefix,
has_leading_comments,
has_leading_comment_on_page,
last_arg_commented,
has_trailing_block_comments,
comments_force_expansion,
leading_comment_doc,
..
} = ctx;
if call.arguments.len() == 1
&& !has_leading_comment_on_page
&& !has_trailing_block_comments
&& !comments_force_expansion
{
let arg = &call.arguments[0];
if matches!(
arg.kind,
ExpressionKind::ObjectExpression(_) | ExpressionKind::ArrayExpression(_)
) {
let arg_doc = printer.build_arg_expression_doc_expanded(arg);
return with_chain_head(d, head, d.concat(&[prefix, arg_doc, d.text(")")]));
}
}
if !last_arg_commented
&& !comments_force_expansion
&& let Some((arrow, arrow_span, body_expr)) = single_arrow_expression_body(call)
{
if matches!(body_expr.kind, ExpressionKind::ArrayExpression(_))
&& !arrow_body_tail_has_comments(printer, arrow_span, body_expr)
{
let body_doc = printer.build_arg_expression_doc_expanded(body_expr);
return build_hugged_arrow_arg_doc(
printer, head, ctx, arrow, arrow_span, body_expr, body_doc,
);
}
if matches!(body_expr.kind, ExpressionKind::ObjectExpression(_))
&& !arrow_hug_refused_by_comments(printer, arrow, arrow_span, body_expr)
{
let body_doc = d.parens(printer.build_arg_expression_doc_expanded(body_expr));
return build_hugged_arrow_arg_doc(
printer, head, ctx, arrow, arrow_span, body_expr, body_doc,
);
}
if (arrow_body_is_call_through_non_null(body_expr) || is_ternary_arrow_body(body_expr))
&& !arrow_hug_refused_by_comments(printer, arrow, arrow_span, body_expr)
{
let body_doc = printer.build_arrow_arg_body_doc_unclaimed(body_expr);
let body_doc =
prepend_arrow_body_comments(printer, arrow, body_expr.span().start, body_doc);
let sig_doc = build_arrow_sig_doc(printer, arrow, arrow_span);
let sig_doc = prepend_leading(d, leading_comment_doc, sig_doc);
return with_chain_head(
d,
head,
d.concat(&[
prefix,
sig_doc,
d.text(" =>"),
d.indent_hardline(body_doc),
d.hardline(),
d.text(")"),
]),
);
}
}
let mut arg_parts = DocBuf::new();
let mut paren_line_prefix_parts: DocBuf = DocBuf::new();
let mut prev_gap_has_blank = false;
for (i, arg) in call.arguments.iter().enumerate() {
let arg_start = arg.span().start;
if i == 0 && has_leading_comments {
let first_pc = PartitionedComments::new(printer, paren_open, arg_start);
if first_pc.pulls_to_delimiter_line(printer) {
first_pc.emit_pulled_gap(&mut paren_line_prefix_parts, &mut arg_parts, printer);
} else if let Some(doc) = build_inline_leading_comments(printer, paren_open, arg_start)
{
arg_parts.push(doc);
}
}
if i > 0 {
let prev_end = call.arguments[i - 1].span().end;
let has_comments_before = printer.has_comments_to_emit_between(prev_end, arg_start);
if !has_comments_before && prev_gap_has_blank {
arg_parts.push(d.literalline());
arg_parts.push(d.hardline());
}
}
let flat_sig = call.arguments.len() == 1
&& matches!(&arg.kind, ExpressionKind::ArrowFunctionExpression(arrow)
if arrow.body.is_expression() && !is_curried_arrow_chain(arg));
let build_arg = || build_joined_argument_doc(printer, paren_open, call.arguments, i);
arg_parts.push(if flat_sig {
build_flat_params_arg_doc(printer, build_arg)
} else {
build_arg()
});
let arg_end = arg.span().end;
let next_boundary = if i < call.arguments.len() - 1 {
call.arguments[i + 1].span().start
} else {
span.end
};
if i < call.arguments.len() - 1 {
let next_arg_start = call.arguments[i + 1].span().start;
let interior = printer.arg_paren_interior(paren_open, call.arguments, i);
let pc = printer
.open_inter_arg_gap(&mut arg_parts, arg, interior, next_arg_start)
.comments;
let has_comments_before_next =
printer.has_comments_to_emit_between(arg_end, next_arg_start);
let next_has_blank = if has_comments_before_next {
pc.has_blank_line_in_gap(printer)
} else {
printer.is_next_line_empty(arg_end, printer.blank_scan_end(arg_end, next_arg_start))
};
prev_gap_has_blank = next_has_blank && !has_comments_before_next;
if next_has_blank && has_comments_before_next {
arg_parts.push(d.literalline());
arg_parts.push(d.hardline());
} else if !next_has_blank {
arg_parts.push(d.hardline());
}
pc.emit_leading_comments_inline_aware(&mut arg_parts, printer);
} else {
let interior = printer.arg_paren_interior(paren_open, call.arguments, i);
emit_last_arg_trailing_comments(printer, &mut arg_parts, arg, interior, next_boundary);
}
}
with_chain_head(
d,
head,
d.concat(&[
prefix,
d.concat(&paren_line_prefix_parts),
d.indent_hardline(d.concat(&arg_parts)),
d.hardline(),
d.text(")"),
]),
)
}
fn build_chain_args_single(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
span: Span,
ctx: ChainArgsContext,
head: ChainHead,
) -> DocId {
let d = printer.d();
let ChainArgsContext {
paren_open,
prefix,
own_call_layout,
has_leading_comments,
has_leading_comment_on_page,
has_any_comments,
has_any_comment_text,
last_arg_commented,
leading_comment_doc,
..
} = ctx;
let opener = ArgOpener::chain(prefix);
let arg = &call.arguments[0];
if has_any_comment_text
&& !last_arg_commented
&& let Some(gap_break) = last_arg_arrow_gap_break(printer, arg)
{
return with_chain_head(
d,
head,
build_arrow_gap_break_single_arg_doc(
printer,
opener,
arg,
&gap_break,
leading_comment_doc,
|| printer.build_arg_expression_doc(arg),
),
);
}
if let ExpressionKind::ArrowFunctionExpression(arrow) = &arg.kind
&& let ast::ArrowFunctionBody::Expression(body_expr) = &arrow.body
&& arrow_body_is_call_through_non_null(body_expr)
&& !last_arg_commented
&& !(has_any_comment_text && arrow_hug_refused_by_comments(printer, arrow, arg.span, body_expr))
&& let Some(body_reuse) = prebuild_expand_last_break_body(printer, Some(arg))
{
let arrow_doc = printer
.with_arrow_body_inject(Some(body_reuse), || printer.build_arg_expression_doc(arg));
let arrow_doc = prepend_leading(d, leading_comment_doc, arrow_doc);
let (_, body_doc) = body_reuse;
let body_doc =
prepend_arrow_body_comments(printer, arrow, body_expr.span().start, body_doc);
let sig_doc = build_hugged_arrow_sig_doc(printer, arrow, arg.span)
.map(|sig_doc| prepend_leading(d, leading_comment_doc, sig_doc));
return with_chain_head(
d,
head,
build_break_body_ladder(
d,
opener,
&[],
sig_doc,
body_doc,
false,
arrow_doc,
arrow_doc,
),
);
}
if let ExpressionKind::ArrowFunctionExpression(arrow) = &arg.kind
&& let ast::ArrowFunctionBody::Expression(body_expr) = &arrow.body
&& is_ternary_arrow_body(body_expr)
&& !last_arg_commented
&& !(has_any_comment_text && arrow_hug_refused_by_comments(printer, arrow, arg.span, body_expr))
&& !arrow_signature_refuses_hug(printer, arrow, arg.span)
{
let body_doc = printer.build_arrow_arg_body_doc(body_expr);
let sig_doc = build_arrow_sig_doc(printer, arrow, arg.span);
let sig_doc = prepend_leading(d, leading_comment_doc, sig_doc);
return with_chain_head(
d,
head,
build_ternary_arrow_hug_ladder(
printer,
opener,
arrow,
body_expr,
sig_doc,
body_doc,
|| {
prepend_leading(
d,
leading_comment_doc,
printer.build_arg_expression_doc(arg),
)
},
),
);
}
if let ExpressionKind::ArrowFunctionExpression(arrow) = &arg.kind
&& arrow_body_expands_internally(arrow)
&& !last_arg_commented
&& !arrow_signature_refuses_hug(printer, arrow, arg.span)
{
return with_chain_head(
d,
head,
build_single_arrow_hug_doc(printer, opener, arg, arrow, leading_comment_doc, || {
printer.build_arg_expression_doc(arg)
}),
);
}
let assumed = match &arg.kind {
ExpressionKind::ArrowFunctionExpression(arrow)
if matches!(arrow.body, ast::ArrowFunctionBody::BlockStatement(_))
&& !(has_leading_comment_on_page || last_arg_commented) =>
{
AssumedParamHug::new(printer, arg)
}
_ => None,
};
let arg_doc = AssumedParamHug::sharing(assumed.as_ref(), printer, || {
build_printed_argument_doc(printer, arg, || printer.build_arg_expression_doc(arg))
});
let arg_start = arg.span().start;
let arg_end = arg.span().end;
let leading_comments_doc = if has_leading_comments {
build_inline_leading_comments(printer, paren_open, arg_start)
} else {
None
};
let trailing_comments_doc = if has_any_comments {
build_inline_trailing_comments(printer, arg_end, span.end)
} else {
None
};
let arg_with_comments = match (leading_comments_doc, trailing_comments_doc) {
(Some(leading), Some(trailing)) => d.concat(&[leading, arg_doc, trailing]),
(Some(leading), None) => d.concat(&[leading, arg_doc]),
(None, Some(trailing)) => d.concat(&[arg_doc, trailing]),
(None, None) => arg_doc,
};
let callback_signature_refuses = match &arg.kind {
ExpressionKind::ArrowFunctionExpression(arrow) => {
!arrow.body.is_expression() && callback_signature_refuses_hug(printer, arg, 1)
}
ExpressionKind::FunctionExpression(_) => callback_signature_refuses_hug(printer, arg, 1),
_ => false,
};
if callback_signature_refuses {
return with_chain_head(d, head, opener.wrap_soft(d, arg_with_comments));
}
let template_expands = is_multiline_template_expression(arg)
&& (sole_template_on_its_own_line(printer, paren_open, arg_start) || !own_call_layout);
if template_expands {
return with_chain_head(
d,
head,
d.concat(&[
prefix,
d.indent_hardline(arg_with_comments),
d.hardline(),
d.text(")"),
]),
);
}
if is_multiline_template_expression(arg) {
let leading = if has_leading_comments {
printer.build_rhs_comments_glued_opt(paren_open, arg_start)
} else {
None
};
let mut parts = DocBuf::new();
parts.push(prefix);
parts.extend(leading);
parts.push(arg_doc);
parts.extend(trailing_comments_doc);
parts.push(d.text(")"));
return with_chain_head(d, head, d.concat(&parts));
}
if let ExpressionKind::ArrowFunctionExpression(arrow) = &arg.kind
&& matches!(arrow.body, ast::ArrowFunctionBody::BlockStatement(_))
{
let state_expand = d.concat(&[
prefix,
d.indent_hardline(arg_with_comments),
d.hardline(),
d.text(")"),
]);
let doc = if has_leading_comment_on_page || last_arg_commented {
if d.will_break(arg_with_comments) {
state_expand
} else {
opener.wrap_soft(d, arg_with_comments)
}
} else {
let state_hug = d.concat(&[prefix, arg_with_comments, d.text(")")]);
match assumed {
Some(hug) => {
let build_arg = || {
build_printed_argument_doc(printer, arg, || {
printer.build_arg_expression_doc(arg)
})
};
let with_trailing = |assumed_arg| match trailing_comments_doc {
Some(trailing) => d.concat(&[assumed_arg, trailing]),
None => assumed_arg,
};
if let Some(contents) =
hug.take_forced(printer, AssumedHugKind::ParamLiteral, build_arg)
{
let assumed_arg = with_trailing(contents);
return with_chain_head(
d,
head,
d.concat(&[prefix, assumed_arg, d.text(")")]),
);
}
match hug.build(printer, false, build_arg).literal {
Some(assumed) => {
let state_assumed =
d.concat(&[prefix, with_trailing(assumed.doc), d.text(")")]);
d.conditional_group(&[
state_hug,
assumed.state(d, arg_with_comments, state_assumed),
state_expand,
])
}
None => d.conditional_group(&[state_hug, state_expand]),
}
}
None => d.conditional_group(&[state_hug, state_expand]),
}
};
return with_chain_head(d, head, doc);
}
let signature_forces_break = matches!(&arg.kind, ExpressionKind::ArrowFunctionExpression(arrow)
if arrow_signature_refuses_hug(printer, arrow, arg.span));
let kind = if has_leading_comment_on_page || signature_forces_break {
ChainArgKind::NeedsSoftWrap
} else {
classify_chain_arg(arg)
};
let doc = match kind {
ChainArgKind::NeedsSoftWrap => {
opener.wrap_soft(d, arg_with_comments)
}
ChainArgKind::NeedsWrapper if last_arg_commented => {
opener.wrap_soft(d, arg_with_comments)
}
ChainArgKind::NeedsWrapper => {
opener.hug_arg(d, arg_with_comments)
}
ChainArgKind::HugsNaturally if trailing_comments_doc.is_some() => {
opener.wrap_soft(d, arg_with_comments)
}
ChainArgKind::HugsNaturally => {
if is_curried_arrow_chain(arg) {
let state_hug = d.concat(&[prefix, arg_with_comments, d.text(")")]);
d.conditional_group(&[state_hug, opener.wrap_soft(d, arg_with_comments)])
} else {
opener.lone_hug_ladder(d, arg_with_comments, lone_arg_params_render_flat(arg))
}
}
};
with_chain_head(d, head, doc)
}
fn build_chain_args_multi(
printer: &Printer<'_>,
call: &ast::CallExpression<'_>,
call_span: Span,
ctx: ChainArgsContext,
head: ChainHead,
) -> DocId {
let d = printer.d();
let ChainArgsContext {
paren_open,
prefix,
has_any_comments,
has_any_comment_text,
last_arg_commented,
comments_force_expansion,
ungroup_args,
..
} = ctx;
let opener = ArgOpener::chain(prefix);
if call.arguments.len() >= 2
&& has_any_comment_text
&& !comments_force_expansion
&& !last_arg_commented
&& let Some(last) = call.arguments.last()
&& let Some(gap_break) = last_arg_arrow_gap_break(printer, last)
{
let body_reuse = gap_break.inject;
let (head_parts, printed_last_arg_doc, all_args_broken) = printer
.with_arrow_body_inject(body_reuse, || {
build_args_split_last(call.arguments, printer, paren_open, has_any_comments)
});
return with_chain_head(
d,
head,
build_arrow_gap_break_multi_arg_doc(
printer,
opener,
body_reuse,
&head_parts,
printed_last_arg_doc,
all_args_broken,
|| printer.build_arg_expression_doc(last),
),
);
}
if call.arguments.len() >= 2
&& call.arguments.last().copied().is_some_and(is_block_function)
&& !comments_force_expansion
&& !last_arg_commented
{
let assumed = call
.arguments
.last()
.and_then(|last| AssumedParamHug::new(printer, last));
let (head_parts, last_arg_doc, all_args_broken) =
AssumedParamHug::sharing(assumed.as_ref(), printer, || {
build_args_split_last(call.arguments, printer, paren_open, has_any_comments)
});
if let Some(bail) = opener.expand_all_if_head_breaks(d, &head_parts, all_args_broken) {
return with_chain_head(d, head, bail);
}
if call
.arguments
.last()
.is_some_and(|arg| callback_signature_refuses_hug(printer, arg, call.arguments.len()))
{
return with_chain_head(d, head, opener.expand_all(d, all_args_broken));
}
let last_index = call.arguments.len() - 1;
let build_last =
|| build_joined_argument_doc(printer, paren_open, call.arguments, last_index);
if let Some(contents) = assumed.as_ref().and_then(|hug| {
hug.take_forced(printer, AssumedHugKind::ParamLiteral, build_last)
.or_else(|| hug.take_forced(printer, AssumedHugKind::ReturnType, build_last))
}) {
return with_chain_head(d, head, opener.inline(d, &head_parts, contents));
}
let assumed =
assumed.map_or_else(Default::default, |hug| hug.build(printer, true, build_last));
return with_chain_head(
d,
head,
opener.inline_assumed_or_expand_all(
d,
&head_parts,
last_arg_doc,
assumed,
all_args_broken,
),
);
}
if call.arguments.len() >= 2
&& !comments_force_expansion
&& !(has_any_comments && last_arg_commented)
&& let Some(Expression { span: arrow_span, kind: ExpressionKind::ArrowFunctionExpression(arrow) }) = call.arguments.last()
&& let ast::ArrowFunctionBody::Expression(body_expr) = &arrow.body
&& (arrow_body_is_call_through_non_null(body_expr) || is_ternary_arrow_body(body_expr))
&& !arrow_hug_refused_by_comments(printer, arrow, *arrow_span, body_expr)
{
let body_reuse = prebuild_expand_last_break_body(printer, call.arguments.last().copied());
let inject_prev = body_reuse.map(|(span, doc)| printer.inject_arrow_body(span, doc));
let (head_parts, last_arg_doc, all_args_broken) =
build_args_split_last(call.arguments, printer, paren_open, has_any_comments);
if let Some(prev) = inject_prev {
printer.restore_arrow_body_inject(prev);
}
if let Some(bail) = opener.expand_all_if_head_breaks(d, &head_parts, all_args_broken) {
return with_chain_head(d, head, bail);
}
let sig_doc = build_hugged_arrow_sig_doc(printer, arrow, *arrow_span);
let body_doc = body_reuse.map_or_else(
|| printer.build_arrow_arg_body_doc_unclaimed(body_expr),
|(_, doc)| doc,
);
let body_doc =
prepend_arrow_body_comments(printer, arrow, body_expr.span().start, body_doc);
return with_chain_head(
d,
head,
build_break_body_ladder(
d,
opener,
&head_parts,
sig_doc,
body_doc,
arrow_body_breaks_after_space(printer, arrow, body_expr),
last_arg_doc,
all_args_broken,
),
);
}
if call.arguments.len() >= 2
&& !comments_force_expansion
&& !(has_any_comments && last_arg_commented)
&& let Some(last_arg) = call.arguments.last()
&& let ExpressionKind::ArrowFunctionExpression(arrow) = &last_arg.kind
&& arrow_body_expands_internally(arrow)
&& !arrow_signature_refuses_hug(printer, arrow, last_arg.span)
{
let obj_reuse = prebuild_expand_last_obj_array_body(printer, Some(last_arg));
let (head_parts, _printed_last_arg_doc, all_args_broken) = printer
.with_arrow_body_inject(obj_reuse, || {
build_args_split_last(call.arguments, printer, paren_open, has_any_comments)
});
return with_chain_head(
d,
head,
build_expand_last_obj_array_doc(
printer,
opener,
obj_reuse,
&head_parts,
all_args_broken,
|| printer.build_arg_expression_doc(last_arg),
),
);
}
if should_expand_first_arg(printer, call.arguments)
&& !(comments_force_expansion
|| (has_any_comment_text
&& first_arg_has_any_comments(call.arguments, printer, paren_open))
|| first_arg_signature_refuses_expand_first(printer, call.arguments))
{
return with_chain_head(
d,
head,
build_expand_first_arg_doc(printer, opener, call.arguments, paren_open, call_span.end),
);
}
if call.arguments.len() >= 2
&& last_arg_is_array_or_object(call.arguments, printer)
&& !call
.arguments
.last()
.is_some_and(|arg| printer.arg_is_concisely_printed_array(arg))
&& !comments_force_expansion
&& !last_arg_commented
&& !(call.arguments.len() == 2
&& matches!(
call.arguments.first(),
Some(Expression { kind: ExpressionKind::ArrowFunctionExpression(_), .. })
)
&& matches!(
call.arguments.last(),
Some(Expression { kind: ExpressionKind::ArrayExpression(_), .. })
))
&& !last_two_args_same_type(call.arguments)
{
let (head_parts, last_arg_doc, all_args_broken) =
build_args_split_last(call.arguments, printer, paren_open, has_any_comments);
if let Some(bail) = opener.expand_all_if_head_breaks(d, &head_parts, all_args_broken) {
return with_chain_head(d, head, bail);
}
return with_chain_head(
d,
head,
opener.inline_hug_or_expand_all(d, &head_parts, last_arg_doc, all_args_broken),
);
}
let mut arg_parts = DocBuf::new();
for (i, arg) in call.arguments.iter().enumerate() {
let arg_start = arg.span().start;
let arg_end = arg.span().end;
let is_first = i == 0;
let is_last = i == call.arguments.len() - 1;
if is_first
&& has_any_comments
&& let Some(l) = build_inline_leading_comments(printer, paren_open, arg_start)
{
arg_parts.push(l);
}
arg_parts.push(build_printed_argument_doc(printer, arg, || {
printer.build_arg_expression_doc(arg)
}));
if is_last {
if has_any_comments
&& let Some(t) = build_inline_trailing_comments(printer, arg_end, call_span.end)
{
arg_parts.push(t);
}
} else {
let next_arg_start = call.arguments[i + 1].span().start;
if has_any_comments && printer.has_comments_to_emit_between(arg_end, next_arg_start) {
let (through_comma, leading) = build_arg_gap_docs(printer, arg_end, next_arg_start);
arg_parts.extend(through_comma);
arg_parts.push(d.line());
arg_parts.extend(leading);
} else {
arg_parts.push(d.comma_line());
}
}
}
let args = d.concat(&arg_parts);
let list = if ungroup_args {
opener.soft_list(d, args)
} else {
opener.wrap_soft(d, args)
};
with_chain_head(d, head, list)
}