- } else {
- other_child_shape
- };
- debug!(
- "child_shapes {:?} {:?}",
- first_child_shape, other_child_shape
- );
-
- let child_shape_iter = Some(first_child_shape)
- .into_iter()
- .chain(iter::repeat(other_child_shape));
- let subexpr_num = subexpr_list.len();
- let last_subexpr = &subexpr_list[suffix_try_num];
- let subexpr_list = &subexpr_list[suffix_try_num..subexpr_num - prefix_try_num];
- let iter = subexpr_list.iter().skip(1).rev().zip(child_shape_iter);
- let mut rewrites = iter.map(|(e, shape)| rewrite_chain_subexpr(e, total_span, context, shape))
- .collect::<Option<Vec<_>>>()?;
-
- // Total of all items excluding the last.
- let extend_last_subexpr = if is_small_parent {
- rewrites.len() == 1 && last_line_extendable(&rewrites[0])
- } else {
- rewrites.is_empty() && last_line_extendable(&parent_rewrite)
- };
- let almost_total = if extend_last_subexpr {
- last_line_width(&parent_rewrite)
- } else {
- rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len()
- } + suffix_try_num;
- let one_line_budget = if rewrites.is_empty() {
- shape.width
- } else {
- min(shape.width, context.config.width_heuristics().chain_width)
- };
- let all_in_one_line = !parent_rewrite_contains_newline
- && rewrites.iter().all(|s| !s.contains('\n'))
- && almost_total < one_line_budget;
- let last_shape = if rewrites.is_empty() {
- first_child_shape
- } else {
- other_child_shape
- }.sub_width(shape.rhs_overhead(context.config) + suffix_try_num)?;
+
+ Chain { parent, children }
+ }
+
+ // Returns a Vec of the prefixes of the chain.
+ // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a']
+ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<ast::Expr> {
+ let mut subexpr_list = vec![expr.clone()];
+
+ while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) {
+ subexpr_list.push(subexpr.clone());
+ }
+
+ subexpr_list
+ }
+
+ // Returns the expression's subexpression, if it exists. When the subexpr
+ // is a try! macro, we'll convert it to shorthand when the option is set.
+ fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option<ast::Expr> {
+ match expr.kind {
+ ast::ExprKind::MethodCall(_, ref expressions) => {
+ Some(Self::convert_try(&expressions[0], context))
+ }
+ ast::ExprKind::Field(ref subexpr, _)
+ | ast::ExprKind::Try(ref subexpr)
+ | ast::ExprKind::Await(ref subexpr) => Some(Self::convert_try(subexpr, context)),
+ _ => None,
+ }
+ }
+
+ fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr {
+ match expr.kind {
+ ast::ExprKind::Mac(ref mac) if context.config.use_try_shorthand() => {
+ if let Some(subexpr) = convert_try_mac(mac, context) {
+ subexpr
+ } else {
+ expr.clone()
+ }
+ }
+ _ => expr.clone(),
+ }
+ }
+}
+
+impl Rewrite for Chain {
+ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+ debug!("rewrite chain {:?} {:?}", self, shape);
+
+ let mut formatter = match context.config.indent_style() {
+ IndentStyle::Block => {
+ Box::new(ChainFormatterBlock::new(self)) as Box<dyn ChainFormatter>
+ }
+ IndentStyle::Visual => {
+ Box::new(ChainFormatterVisual::new(self)) as Box<dyn ChainFormatter>
+ }
+ };
+
+ formatter.format_root(&self.parent, context, shape)?;
+ if let Some(result) = formatter.pure_root() {
+ return wrap_str(result, context.config.max_width(), shape);
+ }
+
+ // Decide how to layout the rest of the chain.
+ let child_shape = formatter.child_shape(context, shape)?;
+
+ formatter.format_children(context, child_shape)?;
+ formatter.format_last_child(context, shape, child_shape)?;
+
+ let result = formatter.join_rewrites(context, child_shape)?;
+ wrap_str(result, context.config.max_width(), shape)
+ }
+}
+
+// There are a few types for formatting chains. This is because there is a lot
+// in common between formatting with block vs visual indent, but they are
+// different enough that branching on the indent all over the place gets ugly.
+// Anything that can format a chain is a ChainFormatter.
+trait ChainFormatter {
+ // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
+ // Root is the parent plus any other chain items placed on the first line to
+ // avoid an orphan. E.g.,
+ // ```text
+ // foo.bar
+ // .baz()
+ // ```
+ // If `bar` were not part of the root, then foo would be orphaned and 'float'.
+ fn format_root(
+ &mut self,
+ parent: &ChainItem,
+ context: &RewriteContext<'_>,
+ shape: Shape,
+ ) -> Option<()>;
+ fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>;
+ fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>;
+ fn format_last_child(
+ &mut self,
+ context: &RewriteContext<'_>,
+ shape: Shape,
+ child_shape: Shape,
+ ) -> Option<()>;
+ fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>;
+ // Returns `Some` if the chain is only a root, None otherwise.
+ fn pure_root(&mut self) -> Option<String>;
+}
+
+// Data and behaviour that is shared by both chain formatters. The concrete
+// formatters can delegate much behaviour to `ChainFormatterShared`.
+struct ChainFormatterShared<'a> {
+ // The current working set of child items.
+ children: &'a [ChainItem],
+ // The current rewrites of items (includes trailing `?`s, but not any way to
+ // connect the rewrites together).
+ rewrites: Vec<String>,
+ // Whether the chain can fit on one line.
+ fits_single_line: bool,
+ // The number of children in the chain. This is not equal to `self.children.len()`
+ // because `self.children` will change size as we process the chain.
+ child_count: usize,
+}
+
+impl<'a> ChainFormatterShared<'a> {
+ fn new(chain: &'a Chain) -> ChainFormatterShared<'a> {
+ ChainFormatterShared {
+ children: &chain.children,
+ rewrites: Vec::with_capacity(chain.children.len() + 1),
+ fits_single_line: false,
+ child_count: chain.children.len(),
+ }
+ }
+
+ fn pure_root(&mut self) -> Option<String> {
+ if self.children.is_empty() {
+ assert_eq!(self.rewrites.len(), 1);
+ Some(self.rewrites.pop().unwrap())
+ } else {
+ None
+ }
+ }