1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Rewrite a list some items with overflow.
14 use syntax::parse::token::DelimToken;
15 use syntax::source_map::Span;
16 use syntax::{ast, ptr};
20 can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
21 maybe_get_args_offset,
23 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
25 use patterns::{can_be_overflowed_pat, TuplePatField};
26 use rewrite::{Rewrite, RewriteContext};
28 use source_map::SpanUtils;
30 use types::{can_be_overflowed_type, SegmentParam};
31 use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
35 pub enum OverflowableItem<'a> {
37 GenericParam(&'a ast::GenericParam),
38 MacroArg(&'a MacroArg),
39 SegmentParam(&'a SegmentParam<'a>),
40 StructField(&'a ast::StructField),
41 TuplePatField(&'a TuplePatField<'a>),
45 impl<'a> Rewrite for OverflowableItem<'a> {
46 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
47 self.map(|item| item.rewrite(context, shape))
51 impl<'a> Spanned for OverflowableItem<'a> {
52 fn span(&self) -> Span {
53 self.map(|item| item.span())
57 impl<'a> OverflowableItem<'a> {
58 pub fn map<F, T>(&self, f: F) -> T
60 F: Fn(&IntoOverflowableItem<'a>) -> T,
63 OverflowableItem::Expr(expr) => f(*expr),
64 OverflowableItem::GenericParam(gp) => f(*gp),
65 OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
66 OverflowableItem::SegmentParam(sp) => f(*sp),
67 OverflowableItem::StructField(sf) => f(*sf),
68 OverflowableItem::TuplePatField(pat) => f(*pat),
69 OverflowableItem::Ty(ty) => f(*ty),
73 pub fn is_simple(&self) -> bool {
75 OverflowableItem::Expr(expr) => is_simple_expr(expr),
76 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
81 pub fn is_expr(&self) -> bool {
83 OverflowableItem::Expr(..) => true,
84 OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
89 pub fn is_nested_call(&self) -> bool {
91 OverflowableItem::Expr(expr) => is_nested_call(expr),
92 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
97 pub fn to_expr(&self) -> Option<&'a ast::Expr> {
99 OverflowableItem::Expr(expr) => Some(expr),
100 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
101 MacroArg::Expr(ref expr) => Some(expr),
108 pub fn can_be_overflowed(&self, context: &RewriteContext, len: usize) -> bool {
110 OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
111 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
112 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
113 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
114 MacroArg::Pat(..) => false,
115 MacroArg::Item(..) => len == 1,
117 OverflowableItem::SegmentParam(seg) => match seg {
118 SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
121 OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
122 OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
128 pub trait IntoOverflowableItem<'a>: Rewrite + Spanned {
129 fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
132 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
133 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
134 (**self).into_overflowable_item()
138 macro impl_into_overflowable_item_for_ast_node {
139 ($($ast_node:ident),*) => {
141 impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
142 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
143 OverflowableItem::$ast_node(self)
150 macro impl_into_overflowable_item_for_rustfmt_types {
151 ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
153 impl<'a> IntoOverflowableItem<'a> for $ty {
154 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
155 OverflowableItem::$ty(self)
160 impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
161 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
162 OverflowableItem::$ty_with_lifetime(self)
169 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, StructField, Ty);
170 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
172 pub fn into_overflowable_list<'a, T>(
173 iter: impl Iterator<Item = &'a T>,
174 ) -> impl Iterator<Item = OverflowableItem<'a>>
176 T: 'a + IntoOverflowableItem<'a>,
178 iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
181 const SHORT_ITEM_THRESHOLD: usize = 10;
183 pub fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
184 context: &'a RewriteContext,
186 items: impl Iterator<Item = &'a T>,
189 item_max_width: usize,
190 force_separator_tactic: Option<SeparatorTactic>,
191 ) -> Option<String> {
201 force_separator_tactic,
207 pub fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
208 context: &'a RewriteContext,
210 items: impl Iterator<Item = &'a T>,
213 ) -> Option<String> {
222 context.config.max_width(),
229 pub fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
230 context: &'a RewriteContext,
232 items: impl Iterator<Item = &'a T>,
235 force_separator_tactic: Option<SeparatorTactic>,
236 delim_token: Option<DelimToken>,
237 ) -> Option<String> {
238 let (lhs, rhs) = match delim_token {
239 Some(DelimToken::Paren) => ("(", ")"),
240 Some(DelimToken::Brace) => ("{", "}"),
251 context.config.width_heuristics().array_width,
252 force_separator_tactic,
259 context: &'a RewriteContext<'a>,
260 items: Vec<OverflowableItem<'a>>,
262 prefix: &'static str,
263 suffix: &'static str,
264 one_line_shape: Shape,
267 item_max_width: usize,
268 one_line_width: usize,
269 force_separator_tactic: Option<SeparatorTactic>,
270 custom_delims: Option<(&'a str, &'a str)>,
273 impl<'a> Context<'a> {
274 pub fn new<T: 'a + IntoOverflowableItem<'a>>(
275 context: &'a RewriteContext,
276 items: impl Iterator<Item = &'a T>,
280 prefix: &'static str,
281 suffix: &'static str,
282 item_max_width: usize,
283 force_separator_tactic: Option<SeparatorTactic>,
284 custom_delims: Option<(&'a str, &'a str)>,
286 let used_width = extra_offset(ident, shape);
288 let one_line_width = shape.width.saturating_sub(used_width + 2);
291 let one_line_shape = shape
292 .offset_left(last_line_width(ident) + 1)
293 .and_then(|shape| shape.sub_width(1))
294 .unwrap_or(Shape { width: 0, ..shape });
295 let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
298 items: into_overflowable_list(items).collect(),
307 force_separator_tactic,
312 fn last_item(&self) -> Option<&OverflowableItem> {
316 fn items_span(&self) -> Span {
320 .span_after(self.span, self.prefix);
321 mk_sp(span_lo, self.span.hi())
324 fn rewrite_last_item_with_overflow(
326 last_list_item: &mut ListItem,
328 ) -> Option<String> {
329 let last_item = self.last_item()?;
330 let rewrite = match last_item {
331 OverflowableItem::Expr(ref expr) => {
333 // When overflowing the closure which consists of a single control flow
334 // expression, force to use block if its condition uses multi line.
335 ast::ExprKind::Closure(..) => {
336 // If the argument consists of multiple closures, we do not overflow
338 if closures::args_have_many_closure(&self.items) {
341 closures::rewrite_last_closure(self.context, expr, shape)
344 _ => expr.rewrite(self.context, shape),
347 item @ _ => item.rewrite(self.context, shape),
350 if let Some(rewrite) = rewrite {
351 let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
352 last_list_item.item = rewrite_first_line;
359 fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
362 ListTactic::LimitedHorizontalVertical(self.item_max_width),
368 fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
370 let combine_arg_with_callee = self.items.len() == 1
371 && self.items[0].is_expr()
372 && self.ident.len() < self.context.config.tab_spaces();
373 let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
375 // Replace the last item with its first line to see if it fits with
377 let placeholder = if overflow_last {
378 let old_value = *self.context.force_one_line_chain.borrow();
379 match self.last_item() {
380 Some(OverflowableItem::Expr(expr))
381 if !combine_arg_with_callee && is_method_call(expr) =>
383 self.context.force_one_line_chain.replace(true);
387 let result = last_item_shape(
393 .and_then(|arg_shape| {
394 self.rewrite_last_item_with_overflow(
395 &mut list_items[self.items.len() - 1],
399 self.context.force_one_line_chain.replace(old_value);
405 let mut tactic = definitive_tactic(
407 ListTactic::LimitedHorizontalVertical(self.item_max_width),
412 // Replace the stub with the full overflowing last argument if the rewrite
413 // succeeded and its first line fits with the other arguments.
414 match (overflow_last, tactic, placeholder) {
415 (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
416 if self.items.len() == 1 =>
418 // When we are rewriting a nested function call, we restrict the
419 // budget for the inner function to avoid them being deeply nested.
420 // However, when the inner function has a prefix or a suffix
421 // (e.g. `foo() as u32`), this budget reduction may produce poorly
422 // formatted code, where a prefix or a suffix being left on its own
423 // line. Here we explicitlly check those cases.
424 if count_newlines(overflowed) == 1 {
428 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
429 let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
431 list_items[self.items.len() - 1].item = rw;
433 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
436 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
439 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
440 list_items[self.items.len() - 1].item = placeholder;
442 _ if !self.items.is_empty() => {
443 list_items[self.items.len() - 1].item = self
446 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
448 // Use horizontal layout for a function with a single argument as long as
449 // everything fits in a single line.
450 // `self.one_line_width == 0` means vertical layout is forced.
451 if self.items.len() == 1
452 && self.one_line_width != 0
453 && !list_items[0].has_comment()
454 && !list_items[0].inner_as_ref().contains('\n')
455 && ::lists::total_item_width(&list_items[0]) <= self.one_line_width
457 tactic = DefinitiveListTactic::Horizontal;
459 tactic = self.default_tactic(list_items);
461 if tactic == DefinitiveListTactic::Vertical {
462 if let Some((all_simple, num_args_before)) =
463 maybe_get_args_offset(self.ident, &self.items)
465 let one_line = all_simple
466 && definitive_tactic(
467 &list_items[..num_args_before],
468 ListTactic::HorizontalVertical,
470 self.nested_shape.width,
471 ) == DefinitiveListTactic::Horizontal
472 && definitive_tactic(
473 &list_items[num_args_before + 1..],
474 ListTactic::HorizontalVertical,
476 self.nested_shape.width,
477 ) == DefinitiveListTactic::Horizontal;
480 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
482 } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
483 tactic = DefinitiveListTactic::Mixed;
494 fn rewrite_items(&self) -> Option<(bool, String)> {
495 let span = self.items_span();
496 let items = itemize_list(
497 self.context.snippet_provider,
501 |item| item.span().lo(),
502 |item| item.span().hi(),
503 |item| item.rewrite(self.context, self.nested_shape),
508 let mut list_items: Vec<_> = items.collect();
510 // Try letting the last argument overflow to the next line with block
511 // indentation. If its first line fits on one line with the other arguments,
512 // we format the function arguments horizontally.
513 let tactic = self.try_overflow_last_item(&mut list_items);
514 let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
516 } else if !self.context.use_block_indent() {
517 SeparatorTactic::Never
518 } else if tactic == DefinitiveListTactic::Mixed {
519 // We are using mixed layout because everything did not fit within a single line.
520 SeparatorTactic::Always
522 self.context.config.trailing_comma()
524 let ends_with_newline = match tactic {
525 DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
526 self.context.use_block_indent()
531 let fmt = ListFormatting::new(self.nested_shape, self.context.config)
533 .trailing_separator(trailing_separator)
534 .ends_with_newline(ends_with_newline);
536 write_list(&list_items, &fmt)
537 .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
540 fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
542 width: shape.width.saturating_sub(last_line_width(self.ident)),
546 let (prefix, suffix) = match self.custom_delims {
547 Some((lhs, rhs)) => (lhs, rhs),
548 _ => (self.prefix, self.suffix),
552 let fits_one_line = items_str.len() + 2 <= shape.width;
553 let extend_width = if items_str.is_empty() {
556 first_line_width(items_str) + 1
558 let nested_indent_str = self
561 .to_string_with_newline(self.context.config);
562 let indent_str = shape
565 .to_string_with_newline(self.context.config);
566 let mut result = String::with_capacity(
567 self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
569 result.push_str(self.ident);
570 result.push_str(prefix);
571 if !self.context.use_block_indent()
572 || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
573 || (is_extendable && extend_width <= shape.width)
575 result.push_str(items_str);
577 if !items_str.is_empty() {
578 result.push_str(&nested_indent_str);
579 result.push_str(items_str);
581 result.push_str(&indent_str);
583 result.push_str(suffix);
587 fn rewrite(&self, shape: Shape) -> Option<String> {
588 let (extendable, items_str) = self.rewrite_items()?;
590 // If we are using visual indent style and failed to format, retry with block indent.
591 if !self.context.use_block_indent()
592 && need_block_indent(&items_str, self.nested_shape)
595 self.context.use_block.replace(true);
596 let result = self.rewrite(shape);
597 self.context.use_block.replace(false);
601 Some(self.wrap_items(&items_str, shape, extendable))
605 fn need_block_indent(s: &str, shape: Shape) -> bool {
606 s.lines().skip(1).any(|s| {
607 s.find(|c| !char::is_whitespace(c))
608 .map_or(false, |w| w + 1 < shape.indent.width())
612 fn can_be_overflowed<'a>(context: &RewriteContext, items: &[OverflowableItem]) -> bool {
615 .map_or(false, |x| x.can_be_overflowed(context, items.len()))
618 /// Returns a shape for the last argument which is going to be overflowed.
620 lists: &[OverflowableItem],
623 args_max_width: usize,
625 if items.len() == 1 && !lists.iter().next()?.is_nested_call() {
628 let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
630 acc + 2 + i.inner_as_ref().len()
633 width: min(args_max_width, shape.width),
639 fn shape_from_indent_style(
640 context: &RewriteContext,
645 let (shape, overhead) = if context.use_block_indent() {
648 .block_indent(context.config.tab_spaces())
649 .with_max_width(context.config);
650 (shape, 1) // 1 = ","
652 (shape.visual_indent(offset), overhead)
655 width: shape.width.saturating_sub(overhead),
660 fn no_long_items(list: &[ListItem]) -> bool {
662 .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)