2018-03-07 00:40:52 -06:00
|
|
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Rewrite a list some items with overflow.
|
|
|
|
|
|
|
|
use config::lists::*;
|
2018-03-25 17:36:44 -05:00
|
|
|
use syntax::parse::token::DelimToken;
|
2018-08-23 16:14:19 -05:00
|
|
|
use syntax::source_map::Span;
|
2018-09-29 10:26:36 -05:00
|
|
|
use syntax::{ast, ptr};
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
use closures;
|
2018-09-29 10:26:36 -05:00
|
|
|
use expr::{
|
|
|
|
can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call,
|
|
|
|
maybe_get_args_offset,
|
|
|
|
};
|
2018-03-07 00:40:52 -06:00
|
|
|
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
|
2018-09-29 10:26:36 -05:00
|
|
|
use macros::MacroArg;
|
|
|
|
use patterns::{can_be_overflowed_pat, TuplePatField};
|
2018-03-07 00:40:52 -06:00
|
|
|
use rewrite::{Rewrite, RewriteContext};
|
|
|
|
use shape::Shape;
|
2018-08-23 16:14:19 -05:00
|
|
|
use source_map::SpanUtils;
|
2018-03-07 00:40:52 -06:00
|
|
|
use spanned::Spanned;
|
2018-09-29 10:26:36 -05:00
|
|
|
use types::{can_be_overflowed_type, SegmentParam};
|
2018-05-17 23:35:09 -05:00
|
|
|
use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
use std::cmp::min;
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
pub enum OverflowableItem<'a> {
|
|
|
|
Expr(&'a ast::Expr),
|
|
|
|
GenericParam(&'a ast::GenericParam),
|
|
|
|
MacroArg(&'a MacroArg),
|
|
|
|
SegmentParam(&'a SegmentParam<'a>),
|
|
|
|
StructField(&'a ast::StructField),
|
|
|
|
TuplePatField(&'a TuplePatField<'a>),
|
|
|
|
Ty(&'a ast::Ty),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Rewrite for OverflowableItem<'a> {
|
|
|
|
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
|
|
|
|
self.map(|item| item.rewrite(context, shape))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Spanned for OverflowableItem<'a> {
|
|
|
|
fn span(&self) -> Span {
|
|
|
|
self.map(|item| item.span())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> OverflowableItem<'a> {
|
|
|
|
pub fn map<F, T>(&self, f: F) -> T
|
|
|
|
where
|
|
|
|
F: Fn(&IntoOverflowableItem<'a>) -> T,
|
|
|
|
{
|
|
|
|
match self {
|
|
|
|
OverflowableItem::Expr(expr) => f(*expr),
|
|
|
|
OverflowableItem::GenericParam(gp) => f(*gp),
|
|
|
|
OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
|
|
|
|
OverflowableItem::SegmentParam(sp) => f(*sp),
|
|
|
|
OverflowableItem::StructField(sf) => f(*sf),
|
|
|
|
OverflowableItem::TuplePatField(pat) => f(*pat),
|
|
|
|
OverflowableItem::Ty(ty) => f(*ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_expr(&self) -> Option<&'a ast::Expr> {
|
|
|
|
match self {
|
|
|
|
OverflowableItem::Expr(expr) => Some(expr),
|
|
|
|
OverflowableItem::MacroArg(macro_arg) => match macro_arg {
|
|
|
|
MacroArg::Expr(ref expr) => Some(expr),
|
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub 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,
|
|
|
|
},
|
|
|
|
OverflowableItem::SegmentParam(seg) => match seg {
|
|
|
|
SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
|
|
|
|
OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait IntoOverflowableItem<'a>: Rewrite + Spanned {
|
|
|
|
fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
|
|
|
|
fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
|
|
|
|
(**self).into_overflowable_item()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro 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 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, StructField, Ty);
|
|
|
|
impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
|
|
|
|
|
|
|
|
pub 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))
|
|
|
|
}
|
|
|
|
|
2018-03-25 17:36:44 -05:00
|
|
|
const SHORT_ITEM_THRESHOLD: usize = 10;
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
pub fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
|
2018-09-29 00:33:00 -05:00
|
|
|
context: &'a RewriteContext,
|
|
|
|
ident: &'a str,
|
|
|
|
items: impl Iterator<Item = &'a T>,
|
2018-03-07 00:40:52 -06:00
|
|
|
shape: Shape,
|
|
|
|
span: Span,
|
|
|
|
item_max_width: usize,
|
2018-03-07 04:29:42 -06:00
|
|
|
force_separator_tactic: Option<SeparatorTactic>,
|
2018-09-29 10:26:36 -05:00
|
|
|
) -> Option<String> {
|
2018-03-07 00:40:52 -06:00
|
|
|
Context::new(
|
|
|
|
context,
|
|
|
|
items,
|
|
|
|
ident,
|
|
|
|
shape,
|
|
|
|
span,
|
|
|
|
"(",
|
|
|
|
")",
|
|
|
|
item_max_width,
|
2018-03-07 04:29:42 -06:00
|
|
|
force_separator_tactic,
|
2018-03-25 17:36:44 -05:00
|
|
|
None,
|
2018-09-19 09:22:26 -05:00
|
|
|
)
|
|
|
|
.rewrite(shape)
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
pub fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
|
2018-09-29 00:33:00 -05:00
|
|
|
context: &'a RewriteContext,
|
|
|
|
ident: &'a str,
|
|
|
|
items: impl Iterator<Item = &'a T>,
|
2018-03-07 00:40:52 -06:00
|
|
|
shape: Shape,
|
|
|
|
span: Span,
|
2018-09-29 10:26:36 -05:00
|
|
|
) -> Option<String> {
|
2018-03-07 00:40:52 -06:00
|
|
|
Context::new(
|
|
|
|
context,
|
|
|
|
items,
|
|
|
|
ident,
|
|
|
|
shape,
|
|
|
|
span,
|
|
|
|
"<",
|
|
|
|
">",
|
|
|
|
context.config.max_width(),
|
2018-03-07 04:29:42 -06:00
|
|
|
None,
|
2018-03-25 17:36:44 -05:00
|
|
|
None,
|
2018-09-19 09:22:26 -05:00
|
|
|
)
|
|
|
|
.rewrite(shape)
|
2018-03-25 17:36:44 -05:00
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
pub fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
|
2018-09-29 00:33:00 -05:00
|
|
|
context: &'a RewriteContext,
|
|
|
|
name: &'a str,
|
|
|
|
items: impl Iterator<Item = &'a T>,
|
2018-03-25 17:36:44 -05:00
|
|
|
shape: Shape,
|
|
|
|
span: Span,
|
|
|
|
force_separator_tactic: Option<SeparatorTactic>,
|
|
|
|
delim_token: Option<DelimToken>,
|
2018-09-29 10:26:36 -05:00
|
|
|
) -> Option<String> {
|
2018-03-25 17:36:44 -05:00
|
|
|
let (lhs, rhs) = match delim_token {
|
|
|
|
Some(DelimToken::Paren) => ("(", ")"),
|
|
|
|
Some(DelimToken::Brace) => ("{", "}"),
|
|
|
|
_ => ("[", "]"),
|
|
|
|
};
|
|
|
|
Context::new(
|
|
|
|
context,
|
|
|
|
items,
|
|
|
|
name,
|
|
|
|
shape,
|
|
|
|
span,
|
|
|
|
lhs,
|
|
|
|
rhs,
|
|
|
|
context.config.width_heuristics().array_width,
|
|
|
|
force_separator_tactic,
|
|
|
|
Some(("[", "]")),
|
2018-09-19 09:22:26 -05:00
|
|
|
)
|
|
|
|
.rewrite(shape)
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
struct Context<'a> {
|
2018-03-07 00:40:52 -06:00
|
|
|
context: &'a RewriteContext<'a>,
|
2018-09-29 10:26:36 -05:00
|
|
|
items: Vec<OverflowableItem<'a>>,
|
2018-03-07 00:40:52 -06:00
|
|
|
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,
|
2018-03-07 04:29:42 -06:00
|
|
|
force_separator_tactic: Option<SeparatorTactic>,
|
2018-03-25 17:36:44 -05:00
|
|
|
custom_delims: Option<(&'a str, &'a str)>,
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
impl<'a> Context<'a> {
|
|
|
|
pub fn new<T: 'a + IntoOverflowableItem<'a>>(
|
2018-03-07 00:40:52 -06:00
|
|
|
context: &'a RewriteContext,
|
2018-09-29 00:33:00 -05:00
|
|
|
items: impl Iterator<Item = &'a T>,
|
2018-03-07 00:40:52 -06:00
|
|
|
ident: &'a str,
|
|
|
|
shape: Shape,
|
|
|
|
span: Span,
|
|
|
|
prefix: &'static str,
|
|
|
|
suffix: &'static str,
|
|
|
|
item_max_width: usize,
|
2018-03-07 04:29:42 -06:00
|
|
|
force_separator_tactic: Option<SeparatorTactic>,
|
2018-03-25 17:36:44 -05:00
|
|
|
custom_delims: Option<(&'a str, &'a str)>,
|
2018-09-29 10:26:36 -05:00
|
|
|
) -> Context<'a> {
|
2018-03-07 00:40:52 -06:00
|
|
|
let used_width = extra_offset(ident, shape);
|
2018-05-17 23:35:09 -05:00
|
|
|
// 1 = `()`
|
|
|
|
let one_line_width = shape.width.saturating_sub(used_width + 2);
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
// 1 = "(" or ")"
|
|
|
|
let one_line_shape = shape
|
|
|
|
.offset_left(last_line_width(ident) + 1)
|
|
|
|
.and_then(|shape| shape.sub_width(1))
|
|
|
|
.unwrap_or(Shape { width: 0, ..shape });
|
2018-05-17 23:35:09 -05:00
|
|
|
let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
|
2018-03-07 00:40:52 -06:00
|
|
|
Context {
|
|
|
|
context,
|
2018-09-29 10:26:36 -05:00
|
|
|
items: into_overflowable_list(items).collect(),
|
2018-03-07 00:40:52 -06:00
|
|
|
ident,
|
|
|
|
one_line_shape,
|
|
|
|
nested_shape,
|
|
|
|
span,
|
|
|
|
prefix,
|
|
|
|
suffix,
|
|
|
|
item_max_width,
|
|
|
|
one_line_width,
|
2018-03-07 04:29:42 -06:00
|
|
|
force_separator_tactic,
|
2018-03-25 17:36:44 -05:00
|
|
|
custom_delims,
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
fn last_item(&self) -> Option<&OverflowableItem> {
|
2018-03-07 00:40:52 -06:00
|
|
|
self.items.last()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn items_span(&self) -> Span {
|
2018-05-07 16:25:48 -05:00
|
|
|
let span_lo = self
|
|
|
|
.context
|
2018-03-07 00:40:52 -06:00
|
|
|
.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 = if let Some(expr) = last_item.to_expr() {
|
|
|
|
match expr.node {
|
|
|
|
// 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.
|
2018-09-29 00:33:00 -05:00
|
|
|
if closures::args_have_many_closure(&self.items) {
|
2018-03-07 00:40:52 -06:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
closures::rewrite_last_closure(self.context, expr, shape)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => expr.rewrite(self.context, shape),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
last_item.rewrite(self.context, shape)
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(rewrite) = rewrite {
|
|
|
|
let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
|
|
|
|
last_list_item.item = rewrite_first_line;
|
|
|
|
Some(rewrite)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-25 17:34:17 -05:00
|
|
|
fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
|
|
|
|
definitive_tactic(
|
|
|
|
list_items,
|
|
|
|
ListTactic::LimitedHorizontalVertical(self.item_max_width),
|
|
|
|
Separator::Comma,
|
|
|
|
self.one_line_width,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-03-07 00:40:52 -06:00
|
|
|
fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
|
|
|
|
// 1 = "("
|
2018-05-06 01:22:29 -05:00
|
|
|
let combine_arg_with_callee = self.items.len() == 1
|
|
|
|
&& self.items[0].to_expr().is_some()
|
2018-08-27 23:33:51 -05:00
|
|
|
&& self.ident.len() < self.context.config.tab_spaces();
|
2018-09-29 00:33:00 -05:00
|
|
|
let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
// 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.borrow();
|
|
|
|
if !combine_arg_with_callee {
|
2018-05-22 17:22:42 -05:00
|
|
|
if let Some(ref expr) = self.last_item().and_then(|item| item.to_expr()) {
|
|
|
|
if is_method_call(expr) {
|
2018-03-07 00:40:52 -06:00
|
|
|
self.context.force_one_line_chain.replace(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let result = last_item_shape(
|
2018-09-29 00:33:00 -05:00
|
|
|
&self.items,
|
2018-03-07 00:40:52 -06:00
|
|
|
list_items,
|
|
|
|
self.one_line_shape,
|
|
|
|
self.item_max_width,
|
2018-09-19 09:22:26 -05:00
|
|
|
)
|
|
|
|
.and_then(|arg_shape| {
|
2018-03-07 00:40:52 -06:00
|
|
|
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
|
2018-04-04 22:47:09 -05:00
|
|
|
// budget for the inner function to avoid them being deeply nested.
|
2018-03-07 00:40:52 -06:00
|
|
|
// 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 explicitlly check those cases.
|
|
|
|
if count_newlines(overflowed) == 1 {
|
2018-05-07 16:25:48 -05:00
|
|
|
let rw = self
|
|
|
|
.items
|
2018-03-07 00:40:52 -06:00
|
|
|
.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;
|
|
|
|
} else {
|
|
|
|
list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
|
|
|
|
list_items[self.items.len() - 1].item = placeholder;
|
|
|
|
}
|
2018-08-28 20:57:08 -05:00
|
|
|
_ if !self.items.is_empty() => {
|
2018-05-07 16:25:48 -05:00
|
|
|
list_items[self.items.len() - 1].item = self
|
|
|
|
.items
|
2018-03-07 00:40:52 -06:00
|
|
|
.last()
|
|
|
|
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
|
|
|
|
|
|
|
|
// Use horizontal layout for a function with a single argument as long as
|
|
|
|
// everything fits in a single line.
|
2018-03-25 17:34:17 -05:00
|
|
|
// `self.one_line_width == 0` means vertical layout is forced.
|
2018-05-06 01:22:29 -05:00
|
|
|
if self.items.len() == 1
|
|
|
|
&& self.one_line_width != 0
|
|
|
|
&& !list_items[0].has_comment()
|
2018-03-07 00:40:52 -06:00
|
|
|
&& !list_items[0].inner_as_ref().contains('\n')
|
|
|
|
&& ::lists::total_item_width(&list_items[0]) <= self.one_line_width
|
|
|
|
{
|
|
|
|
tactic = DefinitiveListTactic::Horizontal;
|
|
|
|
} else {
|
2018-03-25 17:34:17 -05:00
|
|
|
tactic = self.default_tactic(list_items);
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
if tactic == DefinitiveListTactic::Vertical {
|
|
|
|
if let Some((all_simple, num_args_before)) =
|
2018-09-29 00:33:00 -05:00
|
|
|
maybe_get_args_offset(self.ident, &self.items)
|
2018-03-07 00:40:52 -06:00
|
|
|
{
|
|
|
|
let one_line = all_simple
|
|
|
|
&& definitive_tactic(
|
|
|
|
&list_items[..num_args_before],
|
|
|
|
ListTactic::HorizontalVertical,
|
|
|
|
Separator::Comma,
|
|
|
|
self.nested_shape.width,
|
2018-07-05 18:58:22 -05:00
|
|
|
) == DefinitiveListTactic::Horizontal
|
2018-03-07 00:40:52 -06:00
|
|
|
&& definitive_tactic(
|
|
|
|
&list_items[num_args_before + 1..],
|
|
|
|
ListTactic::HorizontalVertical,
|
|
|
|
Separator::Comma,
|
|
|
|
self.nested_shape.width,
|
2018-07-05 18:58:22 -05:00
|
|
|
) == DefinitiveListTactic::Horizontal;
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
if one_line {
|
|
|
|
tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
|
|
|
|
};
|
2018-09-29 00:33:00 -05:00
|
|
|
} else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
|
2018-03-25 17:36:44 -05:00
|
|
|
tactic = DefinitiveListTactic::Mixed;
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
tactic
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rewrite_items(&self) -> Option<(bool, String)> {
|
|
|
|
let span = self.items_span();
|
|
|
|
let items = itemize_list(
|
|
|
|
self.context.snippet_provider,
|
|
|
|
self.items.iter(),
|
|
|
|
self.suffix,
|
|
|
|
",",
|
|
|
|
|item| item.span().lo(),
|
|
|
|
|item| item.span().hi(),
|
|
|
|
|item| item.rewrite(self.context, self.nested_shape),
|
|
|
|
span.lo(),
|
|
|
|
span.hi(),
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
let mut list_items: Vec<_> = items.collect();
|
|
|
|
|
|
|
|
// 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);
|
2018-08-03 08:13:20 -05:00
|
|
|
let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
|
|
|
|
tactic
|
|
|
|
} else if !self.context.use_block_indent() {
|
|
|
|
SeparatorTactic::Never
|
|
|
|
} else if tactic == DefinitiveListTactic::Mixed {
|
|
|
|
// We are using mixed layout because everything did not fit within a single line.
|
|
|
|
SeparatorTactic::Always
|
|
|
|
} else {
|
|
|
|
self.context.config.trailing_comma()
|
2018-03-07 00:40:52 -06:00
|
|
|
};
|
2018-08-03 08:13:20 -05:00
|
|
|
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);
|
2018-03-07 00:40:52 -06:00
|
|
|
|
|
|
|
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 {
|
2018-05-14 07:58:57 -05:00
|
|
|
width: shape.width.saturating_sub(last_line_width(self.ident)),
|
2018-03-07 00:40:52 -06:00
|
|
|
..shape
|
|
|
|
};
|
|
|
|
|
2018-03-25 17:36:44 -05:00
|
|
|
let (prefix, suffix) = match self.custom_delims {
|
|
|
|
Some((lhs, rhs)) => (lhs, rhs),
|
|
|
|
_ => (self.prefix, self.suffix),
|
|
|
|
};
|
2018-05-17 23:35:09 -05:00
|
|
|
|
|
|
|
// 2 = `()`
|
|
|
|
let fits_one_line = items_str.len() + 2 <= shape.width;
|
2018-03-07 00:40:52 -06:00
|
|
|
let extend_width = if items_str.is_empty() {
|
2018-05-17 23:35:09 -05:00
|
|
|
2
|
2018-03-07 00:40:52 -06:00
|
|
|
} else {
|
2018-05-17 23:35:09 -05:00
|
|
|
first_line_width(items_str) + 1
|
2018-03-07 00:40:52 -06:00
|
|
|
};
|
2018-05-07 16:25:48 -05:00
|
|
|
let nested_indent_str = self
|
|
|
|
.nested_shape
|
2018-03-07 00:40:52 -06:00
|
|
|
.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);
|
2018-03-25 17:36:44 -05:00
|
|
|
result.push_str(prefix);
|
2018-03-07 00:40:52 -06:00
|
|
|
if !self.context.use_block_indent()
|
2018-03-25 06:20:50 -05:00
|
|
|
|| (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
|
2018-03-07 00:40:52 -06:00
|
|
|
|| (is_extendable && extend_width <= shape.width)
|
|
|
|
{
|
2018-05-17 23:35:09 -05:00
|
|
|
result.push_str(items_str);
|
2018-03-07 00:40:52 -06:00
|
|
|
} else {
|
|
|
|
if !items_str.is_empty() {
|
|
|
|
result.push_str(&nested_indent_str);
|
|
|
|
result.push_str(items_str);
|
|
|
|
}
|
|
|
|
result.push_str(&indent_str);
|
|
|
|
}
|
2018-03-25 17:36:44 -05:00
|
|
|
result.push_str(suffix);
|
2018-03-07 00:40:52 -06:00
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rewrite(&self, shape: Shape) -> Option<String> {
|
|
|
|
let (extendable, items_str) = self.rewrite_items()?;
|
|
|
|
|
|
|
|
// If we are using visual indent style and failed to format, retry with block indent.
|
2018-05-06 01:22:29 -05:00
|
|
|
if !self.context.use_block_indent()
|
|
|
|
&& need_block_indent(&items_str, self.nested_shape)
|
2018-03-07 00:40:52 -06:00
|
|
|
&& !extendable
|
|
|
|
{
|
|
|
|
self.context.use_block.replace(true);
|
|
|
|
let result = self.rewrite(shape);
|
|
|
|
self.context.use_block.replace(false);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(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())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-09-29 10:26:36 -05:00
|
|
|
fn can_be_overflowed<'a>(context: &RewriteContext, items: &[OverflowableItem]) -> bool {
|
2018-03-07 00:40:52 -06:00
|
|
|
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.
|
2018-09-29 10:26:36 -05:00
|
|
|
fn last_item_shape(
|
|
|
|
lists: &[OverflowableItem],
|
2018-03-07 00:40:52 -06:00
|
|
|
items: &[ListItem],
|
|
|
|
shape: Shape,
|
|
|
|
args_max_width: usize,
|
2018-09-29 10:26:36 -05:00
|
|
|
) -> Option<Shape> {
|
2018-03-07 00:40:52 -06:00
|
|
|
let is_nested_call = lists
|
|
|
|
.iter()
|
|
|
|
.next()
|
|
|
|
.and_then(|item| item.to_expr())
|
|
|
|
.map_or(false, is_nested_call);
|
|
|
|
if items.len() == 1 && !is_nested_call {
|
|
|
|
return Some(shape);
|
|
|
|
}
|
|
|
|
let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
|
|
|
|
// 2 = ", "
|
|
|
|
acc + 2 + i.inner_as_ref().len()
|
|
|
|
});
|
|
|
|
Shape {
|
|
|
|
width: min(args_max_width, shape.width),
|
|
|
|
..shape
|
2018-09-19 09:22:26 -05:00
|
|
|
}
|
|
|
|
.offset_left(offset)
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn shape_from_indent_style(
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
overhead: usize,
|
|
|
|
offset: usize,
|
|
|
|
) -> Shape {
|
2018-06-30 20:46:32 -05:00
|
|
|
let (shape, overhead) = if context.use_block_indent() {
|
|
|
|
let shape = shape
|
2018-03-07 00:40:52 -06:00
|
|
|
.block()
|
|
|
|
.block_indent(context.config.tab_spaces())
|
2018-06-30 20:46:32 -05:00
|
|
|
.with_max_width(context.config);
|
|
|
|
(shape, 1) // 1 = ","
|
2018-03-07 00:40:52 -06:00
|
|
|
} else {
|
2018-06-30 20:46:32 -05:00
|
|
|
(shape.visual_indent(offset), overhead)
|
|
|
|
};
|
|
|
|
Shape {
|
|
|
|
width: shape.width.saturating_sub(overhead),
|
|
|
|
..shape
|
2018-03-07 00:40:52 -06:00
|
|
|
}
|
|
|
|
}
|
2018-03-25 17:36:44 -05:00
|
|
|
|
|
|
|
fn no_long_items(list: &[ListItem]) -> bool {
|
|
|
|
list.iter()
|
2018-06-04 23:41:13 -05:00
|
|
|
.all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
|
2018-03-25 17:36:44 -05:00
|
|
|
}
|