//! Rewrite a list some items with overflow.

use std::cmp::min;

use itertools::Itertools;
use rustc_ast::ast;
use rustc_ast::token::Delimiter;
use rustc_span::Span;
use tracing::debug;

use crate::closures;
use crate::config::StyleEdition;
use crate::config::{Config, lists::*};
use crate::expr::{
    can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
    rewrite_cond,
};
use crate::lists::{
    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
};
use crate::macros::MacroArg;
use crate::patterns::{TuplePatField, can_be_overflowed_pat};
use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::types::{SegmentParam, can_be_overflowed_type};
use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};

/// A list of `format!`-like macros, that take a long format string and a list of arguments to
/// format.
///
/// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
/// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
/// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
const SPECIAL_CASE_MACROS: &[(&str, usize)] = &[
    // format! like macros
    // From the Rust Standard Library.
    ("eprint!", 0),
    ("eprintln!", 0),
    ("format!", 0),
    ("format_args!", 0),
    ("print!", 0),
    ("println!", 0),
    ("panic!", 0),
    ("unreachable!", 0),
    // From the `log` crate.
    ("debug!", 0),
    ("error!", 0),
    ("info!", 0),
    ("warn!", 0),
    // write! like macros
    ("assert!", 1),
    ("debug_assert!", 1),
    ("write!", 1),
    ("writeln!", 1),
    // assert_eq! like macros
    ("assert_eq!", 2),
    ("assert_ne!", 2),
    ("debug_assert_eq!", 2),
    ("debug_assert_ne!", 2),
];

/// Additional special case macros for version 2; these are separated to avoid breaking changes in
/// version 1.
const SPECIAL_CASE_MACROS_V2: &[(&str, usize)] = &[
    // From the `log` crate.
    ("trace!", 0),
];

const SPECIAL_CASE_ATTR: &[(&str, usize)] = &[
    // From the `failure` crate.
    ("fail", 0),
];

#[derive(Debug)]
pub(crate) enum OverflowableItem<'a> {
    Expr(&'a ast::Expr),
    GenericParam(&'a ast::GenericParam),
    MacroArg(&'a MacroArg),
    MetaItemInner(&'a ast::MetaItemInner),
    SegmentParam(&'a SegmentParam<'a>),
    FieldDef(&'a ast::FieldDef),
    TuplePatField(&'a TuplePatField<'a>),
    Ty(&'a ast::Ty),
    Pat(&'a ast::Pat),
    PreciseCapturingArg(&'a ast::PreciseCapturingArg),
}

impl<'a> Rewrite for OverflowableItem<'a> {
    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
        self.map(|item| item.rewrite(context, shape))
    }

    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
        self.map(|item| item.rewrite_result(context, shape))
    }
}

impl<'a> Spanned for OverflowableItem<'a> {
    fn span(&self) -> Span {
        self.map(|item| item.span())
    }
}

impl<'a> OverflowableItem<'a> {
    fn has_attrs(&self) -> bool {
        match self {
            OverflowableItem::Expr(ast::Expr { attrs, .. })
            | OverflowableItem::GenericParam(ast::GenericParam { attrs, .. }) => !attrs.is_empty(),
            OverflowableItem::FieldDef(ast::FieldDef { attrs, .. }) => !attrs.is_empty(),
            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => !expr.attrs.is_empty(),
            OverflowableItem::MacroArg(MacroArg::Item(item)) => !item.attrs.is_empty(),
            _ => false,
        }
    }

    pub(crate) fn map<F, T>(&self, f: F) -> T
    where
        F: Fn(&dyn IntoOverflowableItem<'a>) -> T,
    {
        match self {
            OverflowableItem::Expr(expr) => f(*expr),
            OverflowableItem::GenericParam(gp) => f(*gp),
            OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
            OverflowableItem::MetaItemInner(nmi) => f(*nmi),
            OverflowableItem::SegmentParam(sp) => f(*sp),
            OverflowableItem::FieldDef(sf) => f(*sf),
            OverflowableItem::TuplePatField(pat) => f(*pat),
            OverflowableItem::Ty(ty) => f(*ty),
            OverflowableItem::Pat(pat) => f(*pat),
            OverflowableItem::PreciseCapturingArg(arg) => f(*arg),
        }
    }

    pub(crate) fn is_simple(&self) -> bool {
        match self {
            OverflowableItem::Expr(expr) => is_simple_expr(expr),
            OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true,
            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
            OverflowableItem::MetaItemInner(meta_item_inner) => match meta_item_inner {
                ast::MetaItemInner::Lit(..) => true,
                ast::MetaItemInner::MetaItem(ref meta_item) => {
                    matches!(meta_item.kind, ast::MetaItemKind::Word)
                }
            },
            // FIXME: Why don't we consider `SegmentParam` to be simple?
            // FIXME: If we also fix `SegmentParam`, then we should apply the same
            // heuristic to `PreciseCapturingArg`.
            _ => false,
        }
    }

    pub(crate) fn is_expr(&self) -> bool {
        matches!(
            self,
            OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..))
        )
    }

    pub(crate) fn is_nested_call(&self) -> bool {
        match self {
            OverflowableItem::Expr(expr) => is_nested_call(expr),
            OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
            _ => false,
        }
    }

    pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> {
        match self {
            OverflowableItem::Expr(expr) => Some(expr),
            OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr),
            _ => None,
        }
    }

    pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool {
        match self {
            OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
            OverflowableItem::MacroArg(macro_arg) => match macro_arg {
                MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
                MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
                MacroArg::Pat(..) => false,
                MacroArg::Item(..) => len == 1,
                MacroArg::Keyword(..) => false,
            },
            OverflowableItem::MetaItemInner(meta_item_inner) if len == 1 => match meta_item_inner {
                ast::MetaItemInner::Lit(..) => false,
                ast::MetaItemInner::MetaItem(..) => true,
            },
            OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => {
                can_be_overflowed_type(context, ty, len)
            }
            OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
            OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
            _ => false,
        }
    }

    fn special_cases(&self, config: &Config) -> impl Iterator<Item = &(&'static str, usize)> {
        let base_cases = match self {
            OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS,
            OverflowableItem::MetaItemInner(..) => SPECIAL_CASE_ATTR,
            _ => &[],
        };
        let additional_cases = match self {
            OverflowableItem::MacroArg(..)
                if config.style_edition() >= StyleEdition::Edition2024 =>
            {
                SPECIAL_CASE_MACROS_V2
            }
            _ => &[],
        };
        base_cases.iter().chain(additional_cases)
    }
}

pub(crate) trait IntoOverflowableItem<'a>: Rewrite + Spanned {
    fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
}

impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for Box<T> {
    fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
        (**self).into_overflowable_item()
    }
}

macro_rules! impl_into_overflowable_item_for_ast_node {
    ($($ast_node:ident),*) => {
        $(
            impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
                    OverflowableItem::$ast_node(self)
                }
            }
        )*
    }
}

macro_rules! impl_into_overflowable_item_for_rustfmt_types {
    ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
        $(
            impl<'a> IntoOverflowableItem<'a> for $ty {
                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
                    OverflowableItem::$ty(self)
                }
            }
        )*
        $(
            impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
                fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
                    OverflowableItem::$ty_with_lifetime(self)
                }
            }
        )*
    }
}

impl_into_overflowable_item_for_ast_node!(
    Expr,
    GenericParam,
    MetaItemInner,
    FieldDef,
    Ty,
    Pat,
    PreciseCapturingArg
);
impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);

pub(crate) fn into_overflowable_list<'a, T>(
    iter: impl Iterator<Item = &'a T>,
) -> impl Iterator<Item = OverflowableItem<'a>>
where
    T: 'a + IntoOverflowableItem<'a>,
{
    iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
}

pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
    context: &'a RewriteContext<'_>,
    ident: &'a str,
    items: impl Iterator<Item = &'a T>,
    shape: Shape,
    span: Span,
    item_max_width: usize,
    force_separator_tactic: Option<SeparatorTactic>,
) -> RewriteResult {
    Context::new(
        context,
        items,
        ident,
        shape,
        span,
        "(",
        ")",
        item_max_width,
        force_separator_tactic,
        None,
    )
    .rewrite(shape)
}

pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
    context: &'a RewriteContext<'_>,
    ident: &'a str,
    items: impl Iterator<Item = &'a T>,
    shape: Shape,
    span: Span,
) -> RewriteResult {
    Context::new(
        context,
        items,
        ident,
        shape,
        span,
        "<",
        ">",
        context.config.max_width(),
        None,
        None,
    )
    .rewrite(shape)
}

pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
    context: &'a RewriteContext<'_>,
    name: &'a str,
    items: impl Iterator<Item = &'a T>,
    shape: Shape,
    span: Span,
    force_separator_tactic: Option<SeparatorTactic>,
    delim_token: Option<Delimiter>,
) -> RewriteResult {
    let (lhs, rhs) = match delim_token {
        Some(Delimiter::Parenthesis) => ("(", ")"),
        Some(Delimiter::Brace) => ("{", "}"),
        _ => ("[", "]"),
    };
    Context::new(
        context,
        items,
        name,
        shape,
        span,
        lhs,
        rhs,
        context.config.array_width(),
        force_separator_tactic,
        Some(("[", "]")),
    )
    .rewrite(shape)
}

struct Context<'a> {
    context: &'a RewriteContext<'a>,
    items: Vec<OverflowableItem<'a>>,
    ident: &'a str,
    prefix: &'static str,
    suffix: &'static str,
    one_line_shape: Shape,
    nested_shape: Shape,
    span: Span,
    item_max_width: usize,
    one_line_width: usize,
    force_separator_tactic: Option<SeparatorTactic>,
    custom_delims: Option<(&'a str, &'a str)>,
}

impl<'a> Context<'a> {
    fn new<T: 'a + IntoOverflowableItem<'a>>(
        context: &'a RewriteContext<'_>,
        items: impl Iterator<Item = &'a T>,
        ident: &'a str,
        shape: Shape,
        span: Span,
        prefix: &'static str,
        suffix: &'static str,
        item_max_width: usize,
        force_separator_tactic: Option<SeparatorTactic>,
        custom_delims: Option<(&'a str, &'a str)>,
    ) -> Context<'a> {
        let used_width = extra_offset(ident, shape);
        // 1 = `()`
        let one_line_width = shape.width.saturating_sub(used_width + 2);

        // 1 = "(" or ")"
        let one_line_shape = shape
            .offset_left_opt(last_line_width(ident) + 1)
            .and_then(|shape| shape.sub_width_opt(1))
            .unwrap_or(Shape { width: 0, ..shape });
        let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
        Context {
            context,
            items: into_overflowable_list(items).collect(),
            ident,
            one_line_shape,
            nested_shape,
            span,
            prefix,
            suffix,
            item_max_width,
            one_line_width,
            force_separator_tactic,
            custom_delims,
        }
    }

    fn last_item(&self) -> Option<&OverflowableItem<'_>> {
        self.items.last()
    }

    fn items_span(&self) -> Span {
        let span_lo = self
            .context
            .snippet_provider
            .span_after(self.span, self.prefix);
        mk_sp(span_lo, self.span.hi())
    }

    fn rewrite_last_item_with_overflow(
        &self,
        last_list_item: &mut ListItem,
        shape: Shape,
    ) -> Option<String> {
        let last_item = self.last_item()?;
        let rewrite = match last_item {
            OverflowableItem::Expr(expr) => {
                match expr.kind {
                    // When overflowing the closure which consists of a single control flow
                    // expression, force to use block if its condition uses multi line.
                    ast::ExprKind::Closure(..) => {
                        // If the argument consists of multiple closures, we do not overflow
                        // the last closure.
                        if closures::args_have_many_closure(&self.items) {
                            None
                        } else {
                            closures::rewrite_last_closure(self.context, expr, shape).ok()
                        }
                    }

                    // When overflowing the expressions which consists of a control flow
                    // expression, avoid condition to use multi line.
                    ast::ExprKind::If(..)
                    | ast::ExprKind::ForLoop { .. }
                    | ast::ExprKind::Loop(..)
                    | ast::ExprKind::While(..)
                    | ast::ExprKind::Match(..) => {
                        let multi_line = rewrite_cond(self.context, expr, shape)
                            .map_or(false, |cond| cond.contains('\n'));

                        if multi_line {
                            None
                        } else {
                            expr.rewrite(self.context, shape)
                        }
                    }

                    _ => expr.rewrite(self.context, shape),
                }
            }
            item => item.rewrite(self.context, shape),
        };

        if let Some(rewrite) = rewrite {
            // splitn(2, *).next().unwrap() is always safe.
            let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned());
            last_list_item.item = rewrite_first_line;
            Some(rewrite)
        } else {
            None
        }
    }

    fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
        definitive_tactic(
            list_items,
            ListTactic::LimitedHorizontalVertical(self.item_max_width),
            Separator::Comma,
            self.one_line_width,
        )
    }

    fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
        // 1 = "("
        let combine_arg_with_callee = self.items.len() == 1
            && self.items[0].is_expr()
            && !self.items[0].has_attrs()
            && self.ident.len() < self.context.config.tab_spaces();
        let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);

        // Replace the last item with its first line to see if it fits with
        // first arguments.
        let placeholder = if overflow_last {
            let old_value = self.context.force_one_line_chain.get();
            match self.last_item() {
                Some(OverflowableItem::Expr(expr))
                    if !combine_arg_with_callee && is_method_call(expr) =>
                {
                    self.context.force_one_line_chain.replace(true);
                }
                Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
                    if !combine_arg_with_callee
                        && is_method_call(expr)
                        && self.context.config.style_edition() >= StyleEdition::Edition2024 =>
                {
                    self.context.force_one_line_chain.replace(true);
                }
                _ => (),
            }
            let result = last_item_shape(
                &self.items,
                list_items,
                self.one_line_shape,
                self.item_max_width,
            )
            .and_then(|arg_shape| {
                self.rewrite_last_item_with_overflow(
                    &mut list_items[self.items.len() - 1],
                    arg_shape,
                )
            });
            self.context.force_one_line_chain.replace(old_value);
            result
        } else {
            None
        };

        let mut tactic = definitive_tactic(
            &*list_items,
            ListTactic::LimitedHorizontalVertical(self.item_max_width),
            Separator::Comma,
            self.one_line_width,
        );

        // Replace the stub with the full overflowing last argument if the rewrite
        // succeeded and its first line fits with the other arguments.
        match (overflow_last, tactic, placeholder) {
            (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
                if self.items.len() == 1 =>
            {
                // When we are rewriting a nested function call, we restrict the
                // budget for the inner function to avoid them being deeply nested.
                // However, when the inner function has a prefix or a suffix
                // (e.g., `foo() as u32`), this budget reduction may produce poorly
                // formatted code, where a prefix or a suffix being left on its own
                // line. Here we explicitly check those cases.
                if count_newlines(overflowed) == 1 {
                    let rw = self
                        .items
                        .last()
                        .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
                    let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
                    if no_newline {
                        list_items[self.items.len() - 1].item = rw.unknown_error();
                    } else {
                        list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
                    }
                } else {
                    list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
                }
            }
            (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
                list_items[self.items.len() - 1].item = placeholder.unknown_error();
            }
            _ if !self.items.is_empty() => {
                list_items[self.items.len() - 1].item = self
                    .items
                    .last()
                    .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape))
                    .unknown_error();

                // Use horizontal layout for a function with a single argument as long as
                // everything fits in a single line.
                // `self.one_line_width == 0` means vertical layout is forced.
                if self.items.len() == 1
                    && self.one_line_width != 0
                    && !list_items[0].has_comment()
                    && !list_items[0].inner_as_ref().contains('\n')
                    && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width
                {
                    tactic = DefinitiveListTactic::Horizontal;
                } else {
                    tactic = self.default_tactic(list_items);

                    if tactic == DefinitiveListTactic::Vertical {
                        if let Some((all_simple, num_args_before)) =
                            maybe_get_args_offset(self.ident, &self.items, &self.context.config)
                        {
                            let one_line = all_simple
                                && definitive_tactic(
                                    &list_items[..num_args_before],
                                    ListTactic::HorizontalVertical,
                                    Separator::Comma,
                                    self.nested_shape.width,
                                ) == DefinitiveListTactic::Horizontal
                                && definitive_tactic(
                                    &list_items[num_args_before + 1..],
                                    ListTactic::HorizontalVertical,
                                    Separator::Comma,
                                    self.nested_shape.width,
                                ) == DefinitiveListTactic::Horizontal;

                            if one_line {
                                tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
                            };
                        } else if is_every_expr_simple(&self.items)
                            && no_long_items(
                                list_items,
                                self.context.config.short_array_element_width_threshold(),
                            )
                        {
                            tactic = DefinitiveListTactic::Mixed;
                        }
                    }
                }
            }
            _ => (),
        }

        tactic
    }

    fn rewrite_items(&self) -> Result<(bool, String), RewriteError> {
        let span = self.items_span();
        debug!("items: {:?}", self.items);

        let items = itemize_list(
            self.context.snippet_provider,
            self.items.iter(),
            self.suffix,
            ",",
            |item| item.span().lo(),
            |item| item.span().hi(),
            |item| item.rewrite_result(self.context, self.nested_shape),
            span.lo(),
            span.hi(),
            true,
        );
        let mut list_items: Vec<_> = items.collect();

        debug!("items: {list_items:?}");

        // Try letting the last argument overflow to the next line with block
        // indentation. If its first line fits on one line with the other arguments,
        // we format the function arguments horizontally.
        let tactic = self.try_overflow_last_item(&mut list_items);
        let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
            tactic
        } else if !self.context.use_block_indent() {
            SeparatorTactic::Never
        } else {
            self.context.config.trailing_comma()
        };
        let ends_with_newline = match tactic {
            DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
                self.context.use_block_indent()
            }
            _ => false,
        };

        let fmt = ListFormatting::new(self.nested_shape, self.context.config)
            .tactic(tactic)
            .trailing_separator(trailing_separator)
            .ends_with_newline(ends_with_newline);

        write_list(&list_items, &fmt)
            .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
    }

    fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
        let shape = Shape {
            width: shape.width.saturating_sub(last_line_width(self.ident)),
            ..shape
        };

        let (prefix, suffix) = match self.custom_delims {
            Some((lhs, rhs)) => (lhs, rhs),
            _ => (self.prefix, self.suffix),
        };

        let extend_width = if items_str.is_empty() {
            2
        } else {
            first_line_width(items_str) + 1
        };
        let nested_indent_str = self
            .nested_shape
            .indent
            .to_string_with_newline(self.context.config);
        let indent_str = shape
            .block()
            .indent
            .to_string_with_newline(self.context.config);
        let mut result = String::with_capacity(
            self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
        );
        result.push_str(self.ident);
        result.push_str(prefix);
        let force_single_line = if self.context.config.style_edition() >= StyleEdition::Edition2024
        {
            !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
        } else {
            // 2 = `()`
            let fits_one_line = items_str.len() + 2 <= shape.width;
            !self.context.use_block_indent()
                || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
                || (is_extendable && extend_width <= shape.width)
        };
        if force_single_line {
            result.push_str(items_str);
        } else {
            if !items_str.is_empty() {
                result.push_str(&nested_indent_str);
                result.push_str(items_str);
            }
            result.push_str(&indent_str);
        }
        result.push_str(suffix);
        result
    }

    fn rewrite(&self, shape: Shape) -> RewriteResult {
        let (extendable, items_str) = self.rewrite_items()?;

        // If we are using visual indent style and failed to format, retry with block indent.
        if !self.context.use_block_indent()
            && need_block_indent(&items_str, self.nested_shape)
            && !extendable
        {
            self.context.use_block.replace(true);
            let result = self.rewrite(shape);
            self.context.use_block.replace(false);
            return result;
        }

        Ok(self.wrap_items(&items_str, shape, extendable))
    }
}

fn need_block_indent(s: &str, shape: Shape) -> bool {
    s.lines().skip(1).any(|s| {
        s.find(|c| !char::is_whitespace(c))
            .map_or(false, |w| w + 1 < shape.indent.width())
    })
}

fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool {
    items
        .last()
        .map_or(false, |x| x.can_be_overflowed(context, items.len()))
}

/// Returns a shape for the last argument which is going to be overflowed.
fn last_item_shape(
    lists: &[OverflowableItem<'_>],
    items: &[ListItem],
    shape: Shape,
    args_max_width: usize,
) -> Option<Shape> {
    if items.len() == 1 && !lists.get(0)?.is_nested_call() {
        return Some(shape);
    }
    let offset = items
        .iter()
        .dropping_back(1)
        .map(|i| {
            // 2 = ", "
            2 + i.inner_as_ref().len()
        })
        .sum();
    Shape {
        width: min(args_max_width, shape.width),
        ..shape
    }
    .offset_left_opt(offset)
}

fn shape_from_indent_style(
    context: &RewriteContext<'_>,
    shape: Shape,
    overhead: usize,
    offset: usize,
) -> Shape {
    let (shape, overhead) = if context.use_block_indent() {
        let shape = shape
            .block()
            .block_indent(context.config.tab_spaces())
            .with_max_width(context.config);
        (shape, 1) // 1 = ","
    } else {
        (shape.visual_indent(offset), overhead)
    };
    Shape {
        width: shape.width.saturating_sub(overhead),
        ..shape
    }
}

fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool {
    list.iter()
        .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold)
}

/// In case special-case style is required, returns an offset from which we start horizontal layout.
pub(crate) fn maybe_get_args_offset(
    callee_str: &str,
    args: &[OverflowableItem<'_>],
    config: &Config,
) -> Option<(bool, usize)> {
    if let Some(&(_, num_args_before)) = args
        .get(0)?
        .special_cases(config)
        .find(|&&(s, _)| s == callee_str)
    {
        let all_simple = args.len() > num_args_before
            && is_every_expr_simple(&args[0..num_args_before])
            && is_every_expr_simple(&args[num_args_before + 1..]);

        Some((all_simple, num_args_before))
    } else {
        None
    }
}
