- } 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)?;
+
+ fn is_post_comment(s: &str) -> bool {
+ let comment_start_index = s.chars().position(|c| c == '/');
+ if comment_start_index.is_none() {
+ return false;
+ }
+
+ let newline_index = s.chars().position(|c| c == '\n');
+ if newline_index.is_none() {
+ return true;
+ }
+
+ comment_start_index.unwrap() < newline_index.unwrap()
+ }
+
+ fn handle_post_comment(
+ post_comment_span: Span,
+ post_comment_snippet: &str,
+ prev_span_end: &mut BytePos,
+ children: &mut Vec<ChainItem>,
+ ) {
+ let white_spaces: &[_] = &[' ', '\t'];
+ if post_comment_snippet
+ .trim_matches(white_spaces)
+ .starts_with('\n')
+ {
+ // No post comment.
+ return;
+ }
+ let trimmed_snippet = trim_tries(post_comment_snippet);
+ if is_post_comment(&trimmed_snippet) {
+ children.push(ChainItem::comment(
+ post_comment_span,
+ trimmed_snippet.trim().to_owned(),
+ CommentPosition::Back,
+ ));
+ *prev_span_end = post_comment_span.hi();
+ }
+ }
+
+ let parent = rev_children.pop().unwrap();
+ let mut children = vec![];
+ let mut prev_span_end = parent.span.hi();
+ let mut iter = rev_children.into_iter().rev().peekable();
+ if let Some(first_chain_item) = iter.peek() {
+ let comment_span = mk_sp(prev_span_end, first_chain_item.span.lo());
+ let comment_snippet = context.snippet(comment_span);
+ if !is_tries(comment_snippet.trim()) {
+ handle_post_comment(
+ comment_span,
+ comment_snippet,
+ &mut prev_span_end,
+ &mut children,
+ );
+ }
+ }
+ while let Some(chain_item) = iter.next() {
+ let comment_snippet = context.snippet(chain_item.span);
+ // FIXME: Figure out the way to get a correct span when converting `try!` to `?`.
+ let handle_comment =
+ !(context.config.use_try_shorthand() || is_tries(comment_snippet.trim()));
+
+ // Pre-comment
+ if handle_comment {
+ let pre_comment_span = mk_sp(prev_span_end, chain_item.span.lo());
+ let pre_comment_snippet = trim_tries(context.snippet(pre_comment_span));
+ let (pre_comment, _) = extract_pre_comment(&pre_comment_snippet);
+ match pre_comment {
+ Some(ref comment) if !comment.is_empty() => {
+ children.push(ChainItem::comment(
+ pre_comment_span,
+ comment.to_owned(),
+ CommentPosition::Top,
+ ));
+ }
+ _ => (),
+ }
+ }
+
+ prev_span_end = chain_item.span.hi();
+ children.push(chain_item);
+
+ // Post-comment
+ if !handle_comment || iter.peek().is_none() {
+ continue;
+ }
+
+ let next_lo = iter.peek().unwrap().span.lo();
+ let post_comment_span = mk_sp(prev_span_end, next_lo);
+ let post_comment_snippet = context.snippet(post_comment_span);
+ handle_post_comment(
+ post_comment_span,
+ post_comment_snippet,
+ &mut prev_span_end,
+ &mut children,
+ );
+ }
+
+ 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.node {
+ ast::ExprKind::MethodCall(_, ref expressions) => {
+ Some(Self::convert_try(&expressions[0], context))
+ }
+ ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) => {
+ Some(Self::convert_try(subexpr, context))
+ }
+ _ => None,
+ }
+ }
+
+ fn convert_try(expr: &ast::Expr, context: &RewriteContext) -> ast::Expr {
+ match expr.node {
+ 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<ChainFormatter>,
+ IndentStyle::Visual => Box::new(ChainFormatterVisual::new(self)) as Box<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.,
+ // ```
+ // 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
+ }
+ }