+ // Root is the parent plus any other chain items placed on the first line to
+ // avoid an orphan. E.g.,
+ // ```
+ // foo.bar
+ // .baz()
+ // ```
+ // If `bar` were not part of the root, then baz would be orphaned and 'float'.
+ let mut root_rewrite = chain.parent.expr
+ .rewrite(context, shape)
+ .map(|parent_rw| parent_rw + &"?".repeat(chain.parent.tries))?;
+
+ let mut children: &[_] = &chain.children;
+ let mut root_ends_with_block = is_block_expr(context, &chain.parent.expr, &root_rewrite);
+ let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
+
+ while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') {
+ let item = &children[0];
+ let shape = shape.offset_left(root_rewrite.len())?;
+ match rewrite_chain_subexpr(&item.expr, context, shape) {
+ Some(rewrite) => {
+ root_rewrite.push_str(&rewrite);
+ root_rewrite.push_str(&"?".repeat(item.tries));
+ }
+ None => break,
+ }
+
+ root_ends_with_block = is_block_expr(context, &item.expr, &root_rewrite);
+
+ children = &children[1..];
+ if children.is_empty() {
+ return Some(root_rewrite);
+ }
+ }
+
+ // Separate out the last item in the chain for special treatment below.
+ let last = &children[children.len() - 1];
+ children = &children[..children.len() - 1];
+
+ // Decide how to layout the rest of the chain.
+ let child_shape = if root_ends_with_block {
+ shape
+ } else {
+ shape.block_indent(context.config.tab_spaces())
+ }.with_max_width(context.config);
+
+ let mut rewrites: Vec<String> = Vec::with_capacity(children.len());
+ rewrites.push(root_rewrite);
+ let mut is_block_like = Vec::with_capacity(children.len());
+ is_block_like.push(root_ends_with_block);
+ for item in children {
+ let rewrite = rewrite_chain_subexpr(&item.expr, context, child_shape)?;
+ is_block_like.push(is_block_expr(context, &item.expr, &rewrite));
+ rewrites.push(format!("{}{}", rewrite, "?".repeat(item.tries)));
+ }
+
+ // Total of all items excluding the last.
+ let extend_last_subexpr = last_line_extendable(&rewrites[rewrites.len() - 1]);
+ let almost_total = if extend_last_subexpr {
+ last_line_width(&rewrites[rewrites.len() - 1])
+ } else {
+ rewrites.iter().fold(0, |a, b| a + b.len())
+ } + last.tries;
+ let one_line_budget = if rewrites.len() == 1 {
+ shape.width
+ } else {
+ min(shape.width, context.config.width_heuristics().chain_width)
+ };
+ let all_in_one_line = rewrites.iter().all(|s| !s.contains('\n'))
+ && almost_total < one_line_budget;
+ let last_shape = if all_in_one_line {
+ shape.sub_width(last.tries)?
+ } else {
+ child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
+ };
+
+ // Rewrite the last child. The last child of a chain requires special treatment. We need to
+ // know whether 'overflowing' the last child make a better formatting:
+ //
+ // A chain with overflowing the last child:
+ // ```
+ // parent.child1.child2.last_child(
+ // a,
+ // b,
+ // c,
+ // )
+ // ```
+ //
+ // A chain without overflowing the last child (in vertical layout):
+ // ```
+ // parent
+ // .child1
+ // .child2
+ // .last_child(a, b, c)
+ // ```
+ //
+ // In particular, overflowing is effective when the last child is a method with a multi-lined
+ // block-like argument (e.g. closure):
+ // ```
+ // parent.child1.child2.last_child(|a, b, c| {
+ // let x = foo(a, b, c);
+ // let y = bar(a, b, c);
+ //
+ // // ...
+ //
+ // result
+ // })
+ // ```
+
+ let mut last_subexpr_str = None;
+ let mut fits_single_line = false;
+ if all_in_one_line || extend_last_subexpr {
+ // First we try to 'overflow' the last child and see if it looks better than using
+ // vertical layout.
+ if let Some(shape) = last_shape.offset_left(almost_total) {
+ if let Some(rw) = rewrite_chain_subexpr(&last.expr, context, shape) {
+ // We allow overflowing here only if both of the following conditions match:
+ // 1. The entire chain fits in a single line except the last child.
+ // 2. `last_child_str.lines().count() >= 5`.
+ let line_count = rw.lines().count();
+ let could_fit_single_line = almost_total + first_line_width(&rw) <= one_line_budget;
+ if fits_single_line && line_count >= 5 {
+ last_subexpr_str = Some(rw);
+ fits_single_line = true;
+ } else {
+ // We could not know whether overflowing is better than using vertical layout,
+ // just by looking at the overflowed rewrite. Now we rewrite the last child
+ // on its own line, and compare two rewrites to choose which is better.
+ match rewrite_chain_subexpr(&last.expr, context, last_shape) {
+ Some(ref new_rw) if !could_fit_single_line => {
+ last_subexpr_str = Some(new_rw.clone());
+ }
+ Some(ref new_rw) if new_rw.lines().count() >= line_count => {
+ last_subexpr_str = Some(rw);
+ fits_single_line = could_fit_single_line;
+ }
+ new_rw @ Some(..) => {
+ last_subexpr_str = new_rw;
+ }
+ _ => {
+ last_subexpr_str = Some(rw);
+ fits_single_line = could_fit_single_line;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ last_subexpr_str = last_subexpr_str.or_else(|| rewrite_chain_subexpr(&last.expr, context, last_shape));
+ rewrites.push(format!("{}{}", last_subexpr_str?, "?".repeat(last.tries)));
+
+ // We should never look at this, since we only look at the block-ness of the
+ // previous item in the chain.
+ is_block_like.push(false);
+
+ let connector = if fits_single_line && all_in_one_line {
+ // Yay, we can put everything on one line.
+ Cow::from("")
+ } else {
+ // Use new lines.
+ if *context.force_one_line_chain.borrow() {
+ return None;