X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fchains.rs;h=c7acf19ee0cea6e42bb41891bb371a394b829b23;hb=66c27c9161b2aa70c2902807be12952bd4a0a62b;hp=9d0e8c7c6d282f9d8182d2ae2f1f8ae667727f9e;hpb=49945a14f7a566dd12993910171e9bdd9b6c32fb;p=rust.git diff --git a/src/chains.rs b/src/chains.rs index 9d0e8c7c6d2..c7acf19ee0c 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -1,15 +1,5 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Formatting of chained expressions, i.e. expressions which are chained by -//! dots: struct and enum field access, method calls, and try shorthand (?). +//! Formatting of chained expressions, i.e., expressions that are chained by +//! dots: struct and enum field access, method calls, and try shorthand (`?`). //! //! Instead of walking these subexpressions one-by-one, as is our usual strategy //! for expression formatting, we collect maximal sequences of these expressions @@ -26,7 +16,7 @@ //! following values of `chain_indent`: //! Block: //! -//! ```ignore +//! ```text //! let foo = { //! aaaa; //! bbb; @@ -37,7 +27,7 @@ //! //! Visual: //! -//! ```ignore +//! ```text //! let foo = { //! aaaa; //! bbb; @@ -51,7 +41,7 @@ //! the braces. //! Block: //! -//! ```ignore +//! ```text //! let a = foo.bar //! .baz() //! .qux @@ -59,32 +49,36 @@ //! //! Visual: //! -//! ```ignore +//! ```text //! let a = foo.bar //! .baz() //! .qux //! ``` -use codemap::SpanUtils; -use comment::rewrite_comment; -use config::IndentStyle; -use expr::rewrite_call; -use macros::convert_try_mac; -use rewrite::{Rewrite, RewriteContext}; -use shape::Shape; -use utils::{ - first_line_width, last_line_extendable, last_line_width, mk_sp, trimmed_last_line_width, - wrap_str, -}; - use std::borrow::Cow; use std::cmp::min; -use std::iter; -use syntax::codemap::Span; +use syntax::source_map::{BytePos, Span}; use syntax::{ast, ptr}; -pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option { +use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; +use crate::config::IndentStyle; +use crate::expr::rewrite_call; +use crate::lists::extract_pre_comment; +use crate::macros::convert_try_mac; +use crate::rewrite::{Rewrite, RewriteContext}; +use crate::shape::Shape; +use crate::source_map::SpanUtils; +use crate::utils::{ + self, first_line_width, last_line_extendable, last_line_width, mk_sp, rewrite_ident, + trimmed_last_line_width, wrap_str, +}; + +pub(crate) fn rewrite_chain( + expr: &ast::Expr, + context: &RewriteContext<'_>, + shape: Shape, +) -> Option { let chain = Chain::from_ast(expr, context); debug!("rewrite_chain {:?} {:?}", chain, shape); @@ -97,6 +91,12 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - chain.rewrite(context, shape) } +#[derive(Debug)] +enum CommentPosition { + Back, + Top, +} + // An expression plus trailing `?`s to be formatted together. #[derive(Debug)] struct ChainItem { @@ -118,17 +118,19 @@ enum ChainItemKind { ), StructField(ast::Ident), TupleField(ast::Ident, bool), - Comment, + Await, + Comment(String, CommentPosition), } impl ChainItemKind { - fn is_block_like(&self, context: &RewriteContext, reps: &str) -> bool { + fn is_block_like(&self, context: &RewriteContext<'_>, reps: &str) -> bool { match self { - ChainItemKind::Parent(ref expr) => is_block_expr(context, expr, reps), - ChainItemKind::MethodCall(..) => reps.contains('\n'), - ChainItemKind::StructField(..) + ChainItemKind::Parent(ref expr) => utils::is_block_expr(context, expr, reps), + ChainItemKind::MethodCall(..) + | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) - | ChainItemKind::Comment => false, + | ChainItemKind::Await + | ChainItemKind::Comment(..) => false, } } @@ -141,7 +143,7 @@ fn is_tup_field_access(expr: &ast::Expr) -> bool { } } - fn from_ast(context: &RewriteContext, expr: &ast::Expr) -> (ChainItemKind, Span) { + fn from_ast(context: &RewriteContext<'_>, expr: &ast::Expr) -> (ChainItemKind, Span) { let (kind, span) = match expr.node { ast::ExprKind::MethodCall(ref segment, ref expressions) => { let types = if let Some(ref generic_args) = segment.args { @@ -166,6 +168,10 @@ fn from_ast(context: &RewriteContext, expr: &ast::Expr) -> (ChainItemKind, Span) let span = mk_sp(nested.span.hi(), field.span.hi()); (kind, span) } + ast::ExprKind::Await(ast::AwaitOrigin::FieldLike, ref nested) => { + let span = mk_sp(nested.span.hi(), expr.span.hi()); + (ChainItemKind::Await, span) + } _ => return (ChainItemKind::Parent(expr.clone()), expr.span), }; @@ -176,37 +182,37 @@ fn from_ast(context: &RewriteContext, expr: &ast::Expr) -> (ChainItemKind, Span) } impl Rewrite for ChainItem { - fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { let shape = shape.sub_width(self.tries)?; let rewrite = match self.kind { ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } - ChainItemKind::StructField(ident) => format!(".{}", ident.name), - ChainItemKind::TupleField(ident, nested) => { - format!("{}.{}", if nested { " " } else { "" }, ident.name) + ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)), + ChainItemKind::TupleField(ident, nested) => format!( + "{}.{}", + if nested { " " } else { "" }, + rewrite_ident(context, ident) + ), + ChainItemKind::Await => ".await".to_owned(), + ChainItemKind::Comment(ref comment, _) => { + rewrite_comment(comment, false, shape, context.config)? } - ChainItemKind::Comment => rewrite_comment( - context.snippet(self.span).trim(), - false, - shape, - context.config, - )?, }; Some(format!("{}{}", rewrite, "?".repeat(self.tries))) } } impl ChainItem { - fn new(context: &RewriteContext, expr: &ast::Expr, tries: usize) -> ChainItem { + fn new(context: &RewriteContext<'_>, expr: &ast::Expr, tries: usize) -> ChainItem { let (kind, span) = ChainItemKind::from_ast(context, expr); ChainItem { kind, tries, span } } - fn comment(span: Span) -> ChainItem { + fn comment(span: Span, comment: String, pos: CommentPosition) -> ChainItem { ChainItem { - kind: ChainItemKind::Comment, + kind: ChainItemKind::Comment(comment, pos), tries: 0, span, } @@ -214,7 +220,7 @@ fn comment(span: Span) -> ChainItem { fn is_comment(&self) -> bool { match self.kind { - ChainItemKind::Comment => true, + ChainItemKind::Comment(..) => true, _ => false, } } @@ -224,7 +230,7 @@ fn rewrite_method_call( types: &[ast::GenericArg], args: &[ptr::P], span: Span, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, ) -> Option { let type_str = if types.is_empty() { @@ -237,7 +243,7 @@ fn rewrite_method_call( format!("::<{}>", type_list.join(", ")) }; - let callee_str = format!(".{}{}", method_name, type_str); + let callee_str = format!(".{}{}", rewrite_ident(context, method_name), type_str); rewrite_call(context, &callee_str, &args[1..], span, shape) } } @@ -249,7 +255,7 @@ struct Chain { } impl Chain { - fn from_ast(expr: &ast::Expr, context: &RewriteContext) -> 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 @@ -269,20 +275,101 @@ 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, + ) { + 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_hi = parent.span.hi(); - for chain_item in rev_children.into_iter().rev() { - let comment_span = mk_sp(prev_hi, chain_item.span.lo()); + 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 !(context.config.use_try_shorthand() - || comment_snippet.trim().is_empty() - || is_tries(comment_snippet.trim())) - { - children.push(ChainItem::comment(comment_span)); + if !is_tries(comment_snippet.trim()) { + handle_post_comment( + comment_span, + comment_snippet, + &mut prev_span_end, + &mut children, + ); } - prev_hi = chain_item.span.hi(); + } + 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 } @@ -290,7 +377,7 @@ fn is_tries(s: &str) -> bool { // 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 { + fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec { let mut subexpr_list = vec![expr.clone()]; while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) { @@ -302,19 +389,21 @@ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext) -> Vec Option { + fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option { 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) => { + ast::ExprKind::Field(ref subexpr, _) + | ast::ExprKind::Try(ref subexpr) + | ast::ExprKind::Await(ast::AwaitOrigin::FieldLike, ref subexpr) => { Some(Self::convert_try(subexpr, context)) } _ => None, } } - fn convert_try(expr: &ast::Expr, context: &RewriteContext) -> ast::Expr { + 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) { @@ -329,12 +418,16 @@ fn convert_try(expr: &ast::Expr, context: &RewriteContext) -> ast::Expr { } impl Rewrite for Chain { - fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { debug!("rewrite chain {:?} {:?}", self, shape); let mut formatter = match context.config.indent_style() { - IndentStyle::Block => Box::new(ChainFormatterBlock::new(self)) as Box, - IndentStyle::Visual => Box::new(ChainFormatterVisual::new(self)) as Box, + IndentStyle::Block => { + Box::new(ChainFormatterBlock::new(self)) as Box + } + IndentStyle::Visual => { + Box::new(ChainFormatterVisual::new(self)) as Box + } }; formatter.format_root(&self.parent, context, shape)?; @@ -361,7 +454,7 @@ 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() // ``` @@ -369,18 +462,18 @@ trait ChainFormatter { fn format_root( &mut self, parent: &ChainItem, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, ) -> Option<()>; - fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option; - fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()>; + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option; + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>; fn format_last_child( &mut self, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, ) -> Option<()>; - fn join_rewrites(&self, context: &RewriteContext, child_shape: Shape) -> Option; + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option; // Returns `Some` if the chain is only a root, None otherwise. fn pure_root(&mut self) -> Option; } @@ -423,7 +516,7 @@ fn pure_root(&mut self) -> Option { // know whether 'overflowing' the last child make a better formatting: // // A chain with overflowing the last child: - // ``` + // ```text // parent.child1.child2.last_child( // a, // b, @@ -432,7 +525,7 @@ fn pure_root(&mut self) -> Option { // ``` // // A chain without overflowing the last child (in vertical layout): - // ``` + // ```text // parent // .child1 // .child2 @@ -440,8 +533,8 @@ fn pure_root(&mut self) -> Option { // ``` // // In particular, overflowing is effective when the last child is a method with a multi-lined - // block-like argument (e.g. closure): - // ``` + // block-like argument (e.g., closure): + // ```text // parent.child1.child2.last_child(|a, b, c| { // let x = foo(a, b, c); // let y = bar(a, b, c); @@ -454,7 +547,7 @@ fn pure_root(&mut self) -> Option { fn format_last_child( &mut self, may_extend: bool, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, ) -> Option<()> { @@ -466,13 +559,17 @@ fn format_last_child( let almost_total = if extendable { prev_last_line_width } else { - self.rewrites.iter().fold(0, |a, b| a + b.len()) + self.rewrites + .iter() + .map(|rw| utils::unicode_str_width(&rw)) + .sum() } + last.tries; let one_line_budget = if self.child_count == 1 { shape.width } else { min(shape.width, context.config.width_heuristics().chain_width) - }.saturating_sub(almost_total); + } + .saturating_sub(almost_total); let all_in_one_line = !self.children.iter().any(ChainItem::is_comment) && self.rewrites.iter().all(|s| !s.contains('\n')) @@ -489,7 +586,15 @@ fn format_last_child( if all_in_one_line || extendable { // First we try to 'overflow' the last child and see if it looks better than using // vertical layout. - if let Some(one_line_shape) = last_shape.offset_left(almost_total) { + let one_line_shape = if context.use_block_indent() { + last_shape.offset_left(almost_total) + } else { + last_shape + .visual_indent(almost_total) + .sub_width(almost_total) + }; + + if let Some(one_line_shape) = one_line_shape { if let Some(rw) = last.rewrite(context, one_line_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. @@ -527,17 +632,18 @@ fn format_last_child( } } + let last_shape = if context.use_block_indent() { + last_shape + } else { + child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? + }; + last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape)); self.rewrites.push(last_subexpr_str?); Some(()) } - fn join_rewrites( - &self, - context: &RewriteContext, - child_shape: Shape, - block_like_iter: impl Iterator, - ) -> Option { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { let connector = if self.fits_single_line { // Yay, we can put everything on one line. Cow::from("") @@ -551,13 +657,14 @@ fn join_rewrites( let mut rewrite_iter = self.rewrites.iter(); let mut result = rewrite_iter.next().unwrap().clone(); - - for (rewrite, prev_is_block_like) in rewrite_iter.zip(block_like_iter) { - if !prev_is_block_like { - result.push_str(&connector); - } else if rewrite.starts_with('/') { - // This is comment, add a space before it. - result.push(' '); + let children_iter = self.children.iter(); + let iter = rewrite_iter.zip(children_iter); + + for (rewrite, chain_item) in iter { + match chain_item.kind { + ChainItemKind::Comment(_, CommentPosition::Back) => result.push(' '), + ChainItemKind::Comment(_, CommentPosition::Top) => result.push_str(&connector), + _ => result.push_str(&connector), } result.push_str(&rewrite); } @@ -569,15 +676,14 @@ fn join_rewrites( // Formats a chain using block indent. struct ChainFormatterBlock<'a> { shared: ChainFormatterShared<'a>, - // For each rewrite, whether the corresponding item is block-like. - is_block_like: Vec, + root_ends_with_block: bool, } impl<'a> ChainFormatterBlock<'a> { fn new(chain: &'a Chain) -> ChainFormatterBlock<'a> { ChainFormatterBlock { shared: ChainFormatterShared::new(chain), - is_block_like: Vec::with_capacity(chain.children.len() + 1), + root_ends_with_block: false, } } } @@ -586,7 +692,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { fn format_root( &mut self, parent: &ChainItem, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, ) -> Option<()> { let mut root_rewrite: String = parent.rewrite(context, shape)?; @@ -596,7 +702,7 @@ fn format_root( while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') { let item = &self.shared.children[0]; - if let ChainItemKind::Comment = item.kind { + if let ChainItemKind::Comment(..) = item.kind { break; } let shape = shape.offset_left(root_rewrite.len())?; @@ -605,33 +711,32 @@ fn format_root( None => break, } - root_ends_with_block = item.kind.is_block_like(context, &root_rewrite); + root_ends_with_block = last_line_extendable(&root_rewrite); self.shared.children = &self.shared.children[1..]; if self.shared.children.is_empty() { break; } } - self.is_block_like.push(root_ends_with_block); self.shared.rewrites.push(root_rewrite); + self.root_ends_with_block = root_ends_with_block; Some(()) } - fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option { + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { Some( - if self.is_block_like[0] { + if self.root_ends_with_block { shape.block_indent(0) } else { shape.block_indent(context.config.tab_spaces()) - }.with_max_width(context.config), + } + .with_max_width(context.config), ) } - fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> { + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { for item in &self.shared.children[..self.shared.children.len() - 1] { let rewrite = item.rewrite(context, child_shape)?; - self.is_block_like - .push(item.kind.is_block_like(context, &rewrite)); self.shared.rewrites.push(rewrite); } Some(()) @@ -639,7 +744,7 @@ fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> O fn format_last_child( &mut self, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, ) -> Option<()> { @@ -647,9 +752,8 @@ fn format_last_child( .format_last_child(true, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext, child_shape: Shape) -> Option { - self.shared - .join_rewrites(context, child_shape, self.is_block_like.iter().cloned()) + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + self.shared.join_rewrites(context, child_shape) } fn pure_root(&mut self) -> Option { @@ -677,7 +781,7 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { fn format_root( &mut self, parent: &ChainItem, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, ) -> Option<()> { let parent_shape = shape.visual_indent(0); @@ -691,7 +795,7 @@ fn format_root( if !multiline || parent.kind.is_block_like(context, &root_rewrite) { let item = &self.shared.children[0]; - if let ChainItemKind::Comment = item.kind { + if let ChainItemKind::Comment(..) = item.kind { self.shared.rewrites.push(root_rewrite); return Some(()); } @@ -717,14 +821,14 @@ fn format_root( Some(()) } - fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option { + fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { shape .with_max_width(context.config) .offset_left(self.offset) .map(|s| s.visual_indent(0)) } - fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> { + fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { for item in &self.shared.children[..self.shared.children.len() - 1] { let rewrite = item.rewrite(context, child_shape)?; self.shared.rewrites.push(rewrite); @@ -734,7 +838,7 @@ fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> O fn format_last_child( &mut self, - context: &RewriteContext, + context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, ) -> Option<()> { @@ -742,9 +846,8 @@ fn format_last_child( .format_last_child(false, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext, child_shape: Shape) -> Option { - self.shared - .join_rewrites(context, child_shape, iter::repeat(false)) + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option { + self.shared.join_rewrites(context, child_shape) } fn pure_root(&mut self) -> Option { @@ -752,33 +855,27 @@ fn pure_root(&mut self) -> Option { } } -// States whether an expression's last line exclusively consists of closing -// parens, braces, and brackets in its idiomatic formatting. -fn is_block_expr(context: &RewriteContext, expr: &ast::Expr, repr: &str) -> bool { - match expr.node { - ast::ExprKind::Mac(..) - | ast::ExprKind::Call(..) - | ast::ExprKind::MethodCall(..) - | ast::ExprKind::Struct(..) - | ast::ExprKind::While(..) - | ast::ExprKind::WhileLet(..) - | ast::ExprKind::If(..) - | ast::ExprKind::IfLet(..) - | ast::ExprKind::Block(..) - | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) - | ast::ExprKind::Match(..) => repr.contains('\n'), - ast::ExprKind::Paren(ref expr) - | ast::ExprKind::Binary(_, _, ref expr) - | ast::ExprKind::Index(_, ref expr) - | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Closure(_, _, _, _, ref expr, _) - | ast::ExprKind::Try(ref expr) - | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr), - // This can only be a string lit - ast::ExprKind::Lit(_) => { - repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces() +/// Removes try operators (`?`s) that appear in the given string. If removing +/// them leaves an empty line, remove that line as well unless it is the first +/// line (we need the first newline for detecting pre/post comment). +fn trim_tries(s: &str) -> String { + let mut result = String::with_capacity(s.len()); + let mut line_buffer = String::with_capacity(s.len()); + for (kind, rich_char) in CharClasses::new(s.chars()) { + match rich_char.get_char() { + '\n' => { + if result.is_empty() || !line_buffer.trim().is_empty() { + result.push_str(&line_buffer); + result.push('\n') + } + line_buffer.clear(); + } + '?' if kind == FullCodeCharKind::Normal => continue, + c => line_buffer.push(c), } - _ => false, } + if !line_buffer.trim().is_empty() { + result.push_str(&line_buffer); + } + result }