- // `rewrite_last` rewrites the last child on its own line. We use a closure here instead of
- // directly calling `rewrite_chain_subexpr()` to avoid exponential blowup.
- let rewrite_last = || rewrite_chain_subexpr(last_subexpr, total_span, context, last_shape);
- let (last_subexpr_str, fits_single_line) = 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.
- parent_shape.offset_left(almost_total).map(|shape| {
- if let Some(rw) = rewrite_chain_subexpr(last_subexpr, total_span, 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 fits_single_line = almost_total + first_line_width(&rw) <= one_line_budget;
- if fits_single_line && line_count >= 5 {
- (Some(rw), 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_last() {
- Some(ref new_rw) if !fits_single_line => (Some(new_rw.clone()), false),
- Some(ref new_rw) if new_rw.lines().count() >= line_count => {
- (Some(rw), fits_single_line)
- }
- new_rw @ Some(..) => (new_rw, false),
- _ => (Some(rw), fits_single_line),
+impl Chain {
+ fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain {
+ let subexpr_list = Self::make_subexpr_list(expr, context);
+
+ // Un-parse the expression tree into ChainItems
+ let mut rev_children = vec![];
+ let mut sub_tries = 0;
+ for subexpr in &subexpr_list {
+ match subexpr.node {
+ ast::ExprKind::Try(_) => sub_tries += 1,
+ _ => {
+ rev_children.push(ChainItem::new(context, subexpr, sub_tries));
+ sub_tries = 0;
+ }
+ }
+ }
+
+ fn is_tries(s: &str) -> bool {
+ s.chars().all(|c| c == '?')
+ }
+
+ 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,
+ ));