2015-09-11 00:52:16 +02:00
|
|
|
// 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 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2017-11-28 15:03:02 +13:00
|
|
|
//! Formatting of chained expressions, i.e. expressions which 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
|
|
|
|
//! and handle them simultaneously.
|
|
|
|
//!
|
|
|
|
//! Whenever possible, the entire chain is put on a single line. If that fails,
|
|
|
|
//! we put each subexpression on a separate, much like the (default) function
|
|
|
|
//! argument function argument strategy.
|
|
|
|
//!
|
|
|
|
//! Depends on config options: `chain_indent` is the indent to use for
|
|
|
|
//! blocks in the parent/root/base of the chain (and the rest of the chain's
|
|
|
|
//! alignment).
|
|
|
|
//! E.g., `let foo = { aaaa; bbb; ccc }.bar.baz();`, we would layout for the
|
|
|
|
//! following values of `chain_indent`:
|
|
|
|
//! Block:
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
|
|
|
//! ```ignore
|
2017-11-28 15:03:02 +13:00
|
|
|
//! let foo = {
|
|
|
|
//! aaaa;
|
|
|
|
//! bbb;
|
|
|
|
//! ccc
|
|
|
|
//! }.bar
|
|
|
|
//! .baz();
|
|
|
|
//! ```
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
2017-11-28 15:03:02 +13:00
|
|
|
//! Visual:
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
|
|
|
//! ```ignore
|
2017-11-28 15:03:02 +13:00
|
|
|
//! let foo = {
|
|
|
|
//! aaaa;
|
|
|
|
//! bbb;
|
|
|
|
//! ccc
|
|
|
|
//! }
|
|
|
|
//! .bar
|
|
|
|
//! .baz();
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! If the first item in the chain is a block expression, we align the dots with
|
|
|
|
//! the braces.
|
|
|
|
//! Block:
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
|
|
|
//! ```ignore
|
2017-11-28 15:03:02 +13:00
|
|
|
//! let a = foo.bar
|
|
|
|
//! .baz()
|
|
|
|
//! .qux
|
|
|
|
//! ```
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
2017-11-28 15:03:02 +13:00
|
|
|
//! Visual:
|
2018-03-10 09:57:01 +01:00
|
|
|
//!
|
|
|
|
//! ```ignore
|
2017-11-28 15:03:02 +13:00
|
|
|
//! let a = foo.bar
|
|
|
|
//! .baz()
|
|
|
|
//! .qux
|
|
|
|
//! ```
|
2016-04-22 13:36:27 +12:00
|
|
|
|
2018-08-05 13:35:33 +09:00
|
|
|
use codemap::SpanUtils;
|
2018-08-05 14:27:47 +09:00
|
|
|
use comment::rewrite_comment;
|
2017-03-20 11:42:20 +13:00
|
|
|
use config::IndentStyle;
|
2017-07-13 18:42:14 +09:00
|
|
|
use expr::rewrite_call;
|
2016-05-09 20:11:25 +02:00
|
|
|
use macros::convert_try_mac;
|
2017-07-13 18:42:14 +09:00
|
|
|
use rewrite::{Rewrite, RewriteContext};
|
2017-12-24 00:28:58 +09:00
|
|
|
use shape::Shape;
|
2018-07-11 22:06:30 +12:00
|
|
|
use utils::{
|
|
|
|
first_line_width, last_line_extendable, last_line_width, mk_sp, trimmed_last_line_width,
|
|
|
|
wrap_str,
|
|
|
|
};
|
2015-09-11 00:52:16 +02:00
|
|
|
|
2018-02-19 12:47:54 +09:00
|
|
|
use std::borrow::Cow;
|
2017-05-25 15:47:50 +09:00
|
|
|
use std::cmp::min;
|
2018-07-11 14:50:42 +12:00
|
|
|
use std::iter;
|
2018-02-19 12:47:54 +09:00
|
|
|
|
2017-06-06 06:54:22 +02:00
|
|
|
use syntax::codemap::Span;
|
2018-03-14 20:43:01 +13:00
|
|
|
use syntax::{ast, ptr};
|
2015-09-11 00:52:16 +02:00
|
|
|
|
2017-01-31 08:28:48 +13:00
|
|
|
pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option<String> {
|
2018-07-09 13:10:57 +12:00
|
|
|
let chain = Chain::from_ast(expr, context);
|
2018-07-11 14:56:26 +12:00
|
|
|
debug!("rewrite_chain {:?} {:?}", chain, shape);
|
|
|
|
|
2018-07-09 20:45:30 +12:00
|
|
|
// If this is just an expression with some `?`s, then format it trivially and
|
|
|
|
// return early.
|
2018-07-09 13:10:57 +12:00
|
|
|
if chain.children.is_empty() {
|
2018-07-11 13:39:35 +12:00
|
|
|
return chain.parent.rewrite(context, shape);
|
2018-07-09 13:10:57 +12:00
|
|
|
}
|
2018-07-09 20:45:30 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
chain.rewrite(context, shape)
|
2018-07-09 13:02:14 +12:00
|
|
|
}
|
|
|
|
|
2018-07-09 13:10:57 +12:00
|
|
|
// An expression plus trailing `?`s to be formatted together.
|
2018-07-10 12:24:45 +12:00
|
|
|
#[derive(Debug)]
|
2018-07-09 13:10:57 +12:00
|
|
|
struct ChainItem {
|
2018-08-04 23:58:34 +09:00
|
|
|
kind: ChainItemKind,
|
2018-07-09 13:10:57 +12:00
|
|
|
tries: usize,
|
2018-08-05 13:02:24 +09:00
|
|
|
span: Span,
|
2018-07-09 13:10:57 +12:00
|
|
|
}
|
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
// FIXME: we can't use a reference here because to convert `try!` to `?` we
|
|
|
|
// synthesise the AST node. However, I think we could use `Cow` and that
|
|
|
|
// would remove a lot of cloning.
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum ChainItemKind {
|
|
|
|
Parent(ast::Expr),
|
|
|
|
MethodCall(
|
|
|
|
ast::PathSegment,
|
|
|
|
Vec<ast::GenericArg>,
|
|
|
|
Vec<ptr::P<ast::Expr>>,
|
|
|
|
),
|
|
|
|
StructField(ast::Ident),
|
|
|
|
TupleField(ast::Ident, bool),
|
2018-08-05 14:27:47 +09:00
|
|
|
Comment,
|
2018-07-11 13:39:35 +12:00
|
|
|
}
|
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
impl ChainItemKind {
|
|
|
|
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'),
|
2018-08-05 14:27:47 +09:00
|
|
|
ChainItemKind::StructField(..)
|
|
|
|
| ChainItemKind::TupleField(..)
|
|
|
|
| ChainItemKind::Comment => false,
|
2018-08-04 23:58:34 +09:00
|
|
|
}
|
2018-07-11 13:39:35 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_tup_field_access(expr: &ast::Expr) -> bool {
|
|
|
|
match expr.node {
|
|
|
|
ast::ExprKind::Field(_, ref field) => {
|
|
|
|
field.name.to_string().chars().all(|c| c.is_digit(10))
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-05 13:35:33 +09:00
|
|
|
fn from_ast(context: &RewriteContext, expr: &ast::Expr) -> (ChainItemKind, Span) {
|
|
|
|
let (kind, span) = match expr.node {
|
2018-08-04 23:58:34 +09:00
|
|
|
ast::ExprKind::MethodCall(ref segment, ref expressions) => {
|
|
|
|
let types = if let Some(ref generic_args) = segment.args {
|
|
|
|
if let ast::GenericArgs::AngleBracketed(ref data) = **generic_args {
|
|
|
|
data.args.clone()
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
};
|
2018-08-05 13:35:33 +09:00
|
|
|
let span = mk_sp(expressions[0].span.hi(), expr.span.hi());
|
2018-08-05 13:02:24 +09:00
|
|
|
let kind = ChainItemKind::MethodCall(segment.clone(), types, expressions.clone());
|
2018-08-05 13:35:33 +09:00
|
|
|
(kind, span)
|
2018-08-04 23:58:34 +09:00
|
|
|
}
|
|
|
|
ast::ExprKind::Field(ref nested, field) => {
|
2018-08-05 13:02:24 +09:00
|
|
|
let kind = if Self::is_tup_field_access(expr) {
|
2018-08-04 23:58:34 +09:00
|
|
|
ChainItemKind::TupleField(field, Self::is_tup_field_access(nested))
|
|
|
|
} else {
|
|
|
|
ChainItemKind::StructField(field)
|
2018-08-05 13:02:24 +09:00
|
|
|
};
|
2018-08-05 13:35:33 +09:00
|
|
|
let span = mk_sp(nested.span.hi(), field.span.hi());
|
|
|
|
(kind, span)
|
2018-08-04 23:58:34 +09:00
|
|
|
}
|
2018-08-05 13:35:33 +09:00
|
|
|
_ => return (ChainItemKind::Parent(expr.clone()), expr.span),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Remove comments from the span.
|
|
|
|
let lo = context.snippet_provider.span_before(span, ".");
|
|
|
|
(kind, mk_sp(lo, span.hi()))
|
2018-08-04 23:58:34 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rewrite for ChainItem {
|
|
|
|
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
|
|
|
|
let shape = shape.sub_width(self.tries)?;
|
|
|
|
let rewrite = match self.kind {
|
|
|
|
ChainItemKind::Parent(ref expr) => expr.rewrite(context, shape)?,
|
2018-08-05 13:02:24 +09:00
|
|
|
ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
|
|
|
|
Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
|
2018-08-04 23:58:34 +09:00
|
|
|
}
|
|
|
|
ChainItemKind::StructField(ident) => format!(".{}", ident.name),
|
|
|
|
ChainItemKind::TupleField(ident, nested) => {
|
|
|
|
format!("{}.{}", if nested { " " } else { "" }, ident.name)
|
|
|
|
}
|
2018-08-05 14:27:47 +09:00
|
|
|
ChainItemKind::Comment => {
|
|
|
|
rewrite_comment(context.snippet(self.span).trim(), false, shape, context.config)?
|
|
|
|
}
|
2018-08-04 23:58:34 +09:00
|
|
|
};
|
|
|
|
Some(format!("{}{}", rewrite, "?".repeat(self.tries)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChainItem {
|
2018-08-05 13:35:33 +09:00
|
|
|
fn new(context: &RewriteContext, expr: &ast::Expr, tries: usize) -> ChainItem {
|
|
|
|
let (kind, span) = ChainItemKind::from_ast(context, expr);
|
2018-08-05 13:02:24 +09:00
|
|
|
ChainItem { kind, tries, span }
|
|
|
|
}
|
|
|
|
|
2018-08-05 14:28:10 +09:00
|
|
|
fn comment(span: Span) -> ChainItem {
|
|
|
|
ChainItem {
|
|
|
|
kind: ChainItemKind::Comment,
|
|
|
|
tries: 0,
|
|
|
|
span,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-05 14:34:34 +09:00
|
|
|
fn is_comment(&self) -> bool {
|
|
|
|
match self.kind {
|
|
|
|
ChainItemKind::Comment => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 13:39:35 +12:00
|
|
|
fn rewrite_method_call(
|
|
|
|
method_name: ast::Ident,
|
|
|
|
types: &[ast::GenericArg],
|
|
|
|
args: &[ptr::P<ast::Expr>],
|
|
|
|
span: Span,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
) -> Option<String> {
|
2018-08-05 13:35:33 +09:00
|
|
|
let type_str = if types.is_empty() {
|
|
|
|
String::new()
|
2018-07-11 13:39:35 +12:00
|
|
|
} else {
|
|
|
|
let type_list = types
|
|
|
|
.iter()
|
|
|
|
.map(|ty| ty.rewrite(context, shape))
|
|
|
|
.collect::<Option<Vec<_>>>()?;
|
|
|
|
|
2018-08-05 13:35:33 +09:00
|
|
|
format!("::<{}>", type_list.join(", "))
|
2018-07-11 13:39:35 +12:00
|
|
|
};
|
|
|
|
let callee_str = format!(".{}{}", method_name, type_str);
|
|
|
|
rewrite_call(context, &callee_str, &args[1..], span, shape)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 12:24:45 +12:00
|
|
|
#[derive(Debug)]
|
2018-07-09 13:10:57 +12:00
|
|
|
struct Chain {
|
|
|
|
parent: ChainItem,
|
2018-07-09 20:45:30 +12:00
|
|
|
children: Vec<ChainItem>,
|
2018-07-09 13:10:57 +12:00
|
|
|
}
|
2015-09-11 00:52:16 +02:00
|
|
|
|
2018-07-09 13:10:57 +12:00
|
|
|
impl Chain {
|
|
|
|
fn from_ast(expr: &ast::Expr, context: &RewriteContext) -> Chain {
|
2018-07-11 14:56:26 +12:00
|
|
|
let subexpr_list = Self::make_subexpr_list(expr, context);
|
2018-07-09 20:45:30 +12:00
|
|
|
|
|
|
|
// Un-parse the expression tree into ChainItems
|
2018-08-05 14:28:10 +09:00
|
|
|
let mut rev_children = vec![];
|
2018-07-09 20:45:30 +12:00
|
|
|
let mut sub_tries = 0;
|
2018-08-04 23:58:34 +09:00
|
|
|
for subexpr in &subexpr_list {
|
2018-07-09 20:45:30 +12:00
|
|
|
match subexpr.node {
|
|
|
|
ast::ExprKind::Try(_) => sub_tries += 1,
|
|
|
|
_ => {
|
2018-08-05 14:28:10 +09:00
|
|
|
rev_children.push(ChainItem::new(context, subexpr, sub_tries));
|
2018-07-09 20:45:30 +12:00
|
|
|
sub_tries = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-05 14:28:10 +09:00
|
|
|
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 comment_snippet = context.snippet(comment_span);
|
|
|
|
if !comment_snippet.trim().is_empty() {
|
|
|
|
children.push(ChainItem::comment(comment_span));
|
|
|
|
}
|
|
|
|
prev_hi = chain_item.span.hi();
|
|
|
|
children.push(chain_item);
|
2018-07-09 13:10:57 +12:00
|
|
|
}
|
2018-08-05 14:28:10 +09:00
|
|
|
|
|
|
|
Chain { parent, children }
|
2016-05-28 21:50:25 +01:00
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
|
|
|
// 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(),
|
|
|
|
}
|
|
|
|
}
|
2018-07-09 13:10:57 +12:00
|
|
|
}
|
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
impl Rewrite for Chain {
|
|
|
|
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
|
|
|
|
debug!("rewrite chain {:?} {:?}", self, shape);
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
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)?;
|
2018-07-12 10:44:06 +12:00
|
|
|
if let Some(result) = formatter.pure_root() {
|
|
|
|
return wrap_str(result, context.config.max_width(), shape);
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
2018-07-11 14:50:42 +12:00
|
|
|
|
|
|
|
// Decide how to layout the rest of the chain.
|
2018-07-12 10:44:06 +12:00
|
|
|
let child_shape = formatter.child_shape(context, shape)?;
|
2018-07-11 14:50:42 +12:00
|
|
|
|
|
|
|
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)
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
// 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 {
|
2016-04-22 13:36:27 +12:00
|
|
|
// Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
|
2018-07-10 12:24:45 +12:00
|
|
|
// Root is the parent plus any other chain items placed on the first line to
|
|
|
|
// avoid an orphan. E.g.,
|
|
|
|
// ```
|
|
|
|
// foo.bar
|
|
|
|
// .baz()
|
|
|
|
// ```
|
2018-07-24 15:48:23 +12:00
|
|
|
// If `bar` were not part of the root, then foo would be orphaned and 'float'.
|
2018-07-11 21:35:10 +12:00
|
|
|
fn format_root(
|
|
|
|
&mut self,
|
|
|
|
parent: &ChainItem,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
) -> Option<()>;
|
2018-07-12 10:44:06 +12:00
|
|
|
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape>;
|
2018-07-11 14:50:42 +12:00
|
|
|
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()>;
|
2018-07-11 21:35:10 +12:00
|
|
|
fn format_last_child(
|
|
|
|
&mut self,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
child_shape: Shape,
|
|
|
|
) -> Option<()>;
|
2018-07-11 14:50:42 +12:00
|
|
|
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>;
|
|
|
|
}
|
2018-07-10 12:24:45 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
// 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.
|
2018-07-11 21:35:10 +12:00
|
|
|
children: &'a [ChainItem],
|
2018-07-11 14:50:42 +12:00
|
|
|
// 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,
|
2018-07-11 14:56:26 +12:00
|
|
|
// 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,
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
2018-07-10 12:24:45 +12:00
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
impl<'a> ChainFormatterShared<'a> {
|
2018-07-11 14:50:42 +12:00
|
|
|
fn new(chain: &'a Chain) -> ChainFormatterShared<'a> {
|
|
|
|
ChainFormatterShared {
|
|
|
|
children: &chain.children,
|
|
|
|
rewrites: Vec::with_capacity(chain.children.len() + 1),
|
|
|
|
fits_single_line: false,
|
2018-07-11 14:56:26 +12:00
|
|
|
child_count: chain.children.len(),
|
2018-07-10 12:24:45 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
fn pure_root(&mut self) -> Option<String> {
|
|
|
|
if self.children.is_empty() {
|
|
|
|
assert_eq!(self.rewrites.len(), 1);
|
|
|
|
Some(self.rewrites.pop().unwrap())
|
2018-07-11 12:01:39 +12:00
|
|
|
} else {
|
2018-07-11 14:50:42 +12:00
|
|
|
None
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
|
|
|
}
|
2017-10-18 21:55:45 +09:00
|
|
|
|
|
|
|
// 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):
|
|
|
|
// ```
|
2017-11-01 07:33:55 +01:00
|
|
|
// parent.child1.child2.last_child(|a, b, c| {
|
2017-10-18 21:55:45 +09:00
|
|
|
// let x = foo(a, b, c);
|
|
|
|
// let y = bar(a, b, c);
|
|
|
|
//
|
|
|
|
// // ...
|
|
|
|
//
|
|
|
|
// result
|
|
|
|
// })
|
|
|
|
// ```
|
2018-07-11 21:35:10 +12:00
|
|
|
fn format_last_child(
|
|
|
|
&mut self,
|
|
|
|
may_extend: bool,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
child_shape: Shape,
|
|
|
|
) -> Option<()> {
|
2018-08-05 13:54:28 +09:00
|
|
|
let last = self.children.last()?;
|
|
|
|
let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
|
|
|
|
let prev_last_line_width = last_line_width(&self.rewrites[0]);
|
2018-07-11 14:50:42 +12:00
|
|
|
|
2018-07-11 12:01:39 +12:00
|
|
|
// Total of all items excluding the last.
|
|
|
|
let almost_total = if extendable {
|
2018-07-11 22:06:30 +12:00
|
|
|
prev_last_line_width
|
2018-07-11 12:01:39 +12:00
|
|
|
} else {
|
|
|
|
self.rewrites.iter().fold(0, |a, b| a + b.len())
|
|
|
|
} + last.tries;
|
2018-07-11 14:56:26 +12:00
|
|
|
let one_line_budget = if self.child_count == 1 {
|
2018-07-11 12:01:39 +12:00
|
|
|
shape.width
|
|
|
|
} else {
|
|
|
|
min(shape.width, context.config.width_heuristics().chain_width)
|
|
|
|
}.saturating_sub(almost_total);
|
2017-10-18 21:55:45 +09:00
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
let all_in_one_line =
|
2018-08-05 14:34:34 +09:00
|
|
|
!self.children.iter().any(ChainItem::is_comment)
|
|
|
|
&& self.rewrites.iter().all(|s| !s.contains('\n'))
|
|
|
|
&& one_line_budget > 0;
|
2018-07-12 10:44:06 +12:00
|
|
|
let last_shape = if all_in_one_line {
|
2018-07-11 12:01:39 +12:00
|
|
|
shape.sub_width(last.tries)?
|
2018-07-12 10:44:06 +12:00
|
|
|
} else if extendable {
|
|
|
|
child_shape.sub_width(last.tries)?
|
2018-07-11 12:01:39 +12:00
|
|
|
} else {
|
|
|
|
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut last_subexpr_str = None;
|
|
|
|
if all_in_one_line || extendable {
|
|
|
|
// First we try to 'overflow' the last child and see if it looks better than using
|
|
|
|
// vertical layout.
|
chains: prefer to use the next line for an expression, if using the same line would introduce an open block or similar
This problem came to light due to the chains changes, but effects other code too. It only happens rarely, e.g.,
before this fix:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => rewrite_delimited_inner(
delim_tok,
args,
).map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)),
};
```
after:
```
match foo {
MacroArgKind::Delimited(ref delim_tok, ref args) => {
rewrite_delimited_inner(delim_tok, args)
.map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
}
}
```
2018-07-11 21:01:40 +12:00
|
|
|
if let Some(one_line_shape) = last_shape.offset_left(almost_total) {
|
2018-08-04 23:58:34 +09:00
|
|
|
if let Some(rw) = last.rewrite(context, one_line_shape) {
|
2018-07-11 12:01:39 +12:00
|
|
|
// 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 = first_line_width(&rw) <= one_line_budget;
|
|
|
|
if could_fit_single_line && line_count >= 5 {
|
|
|
|
last_subexpr_str = Some(rw);
|
|
|
|
self.fits_single_line = all_in_one_line;
|
|
|
|
} else {
|
2018-07-11 14:56:26 +12:00
|
|
|
// 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.
|
2018-07-11 21:35:10 +12:00
|
|
|
let last_shape = child_shape
|
|
|
|
.sub_width(shape.rhs_overhead(context.config) + last.tries)?;
|
2018-08-04 23:58:34 +09:00
|
|
|
match last.rewrite(context, last_shape) {
|
2018-07-11 12:01:39 +12:00
|
|
|
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);
|
|
|
|
self.fits_single_line = could_fit_single_line && all_in_one_line;
|
|
|
|
}
|
|
|
|
new_rw @ Some(..) => {
|
|
|
|
last_subexpr_str = new_rw;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
last_subexpr_str = Some(rw);
|
|
|
|
self.fits_single_line = could_fit_single_line && all_in_one_line;
|
|
|
|
}
|
2017-08-07 15:17:35 +09:00
|
|
|
}
|
2017-04-15 16:23:38 +09:00
|
|
|
}
|
2015-09-09 23:17:31 +02:00
|
|
|
}
|
|
|
|
}
|
2018-07-10 12:24:45 +12:00
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape));
|
2018-07-11 13:39:35 +12:00
|
|
|
self.rewrites.push(last_subexpr_str?);
|
2018-07-11 12:01:39 +12:00
|
|
|
Some(())
|
2018-07-10 12:24:45 +12:00
|
|
|
}
|
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
fn join_rewrites(
|
|
|
|
&self,
|
|
|
|
context: &RewriteContext,
|
|
|
|
child_shape: Shape,
|
|
|
|
block_like_iter: impl Iterator<Item = bool>,
|
|
|
|
) -> Option<String> {
|
2018-07-11 12:01:39 +12:00
|
|
|
let connector = if self.fits_single_line {
|
|
|
|
// Yay, we can put everything on one line.
|
|
|
|
Cow::from("")
|
|
|
|
} else {
|
|
|
|
// Use new lines.
|
|
|
|
if *context.force_one_line_chain.borrow() {
|
|
|
|
return None;
|
|
|
|
}
|
2018-07-12 10:44:06 +12:00
|
|
|
child_shape.to_string_with_newline(context.config)
|
2018-07-11 12:01:39 +12:00
|
|
|
};
|
2017-05-25 15:47:50 +09:00
|
|
|
|
2018-07-11 12:01:39 +12:00
|
|
|
let mut rewrite_iter = self.rewrites.iter();
|
|
|
|
let mut result = rewrite_iter.next().unwrap().clone();
|
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
for (rewrite, prev_is_block_like) in rewrite_iter.zip(block_like_iter) {
|
|
|
|
if !prev_is_block_like {
|
2018-07-11 12:01:39 +12:00
|
|
|
result.push_str(&connector);
|
|
|
|
}
|
|
|
|
result.push_str(&rewrite);
|
2017-06-04 15:23:00 +09:00
|
|
|
}
|
2015-09-11 00:52:16 +02:00
|
|
|
|
2018-07-11 12:01:39 +12:00
|
|
|
Some(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
// 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<bool>,
|
|
|
|
}
|
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
impl<'a> ChainFormatterBlock<'a> {
|
2018-07-11 14:50:42 +12:00
|
|
|
fn new(chain: &'a Chain) -> ChainFormatterBlock<'a> {
|
|
|
|
ChainFormatterBlock {
|
|
|
|
shared: ChainFormatterShared::new(chain),
|
|
|
|
is_block_like: Vec::with_capacity(chain.children.len() + 1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
|
|
|
|
fn format_root(
|
|
|
|
&mut self,
|
|
|
|
parent: &ChainItem,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
) -> Option<()> {
|
2018-07-11 14:50:42 +12:00
|
|
|
let mut root_rewrite: String = parent.rewrite(context, shape)?;
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
|
2018-07-11 14:50:42 +12:00
|
|
|
let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
|
|
|
|
|
|
|
|
while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') {
|
2018-08-05 13:54:28 +09:00
|
|
|
let item = &self.shared.children[0];
|
2018-08-05 14:28:20 +09:00
|
|
|
if let ChainItemKind::Comment = item.kind {
|
|
|
|
break;
|
|
|
|
}
|
2018-07-11 14:50:42 +12:00
|
|
|
let shape = shape.offset_left(root_rewrite.len())?;
|
2018-08-04 23:58:34 +09:00
|
|
|
match &item.rewrite(context, shape) {
|
2018-07-11 14:50:42 +12:00
|
|
|
Some(rewrite) => root_rewrite.push_str(rewrite),
|
|
|
|
None => break,
|
|
|
|
}
|
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
root_ends_with_block = item.kind.is_block_like(context, &root_rewrite);
|
2018-07-11 14:50:42 +12:00
|
|
|
|
2018-08-05 13:54:28 +09:00
|
|
|
self.shared.children = &self.shared.children[1..];
|
2018-07-11 14:50:42 +12:00
|
|
|
if self.shared.children.is_empty() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.is_block_like.push(root_ends_with_block);
|
|
|
|
self.shared.rewrites.push(root_rewrite);
|
|
|
|
Some(())
|
|
|
|
}
|
|
|
|
|
2018-07-12 10:44:06 +12:00
|
|
|
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape> {
|
|
|
|
Some(
|
|
|
|
if self.is_block_like[0] {
|
|
|
|
shape.block_indent(0)
|
|
|
|
} else {
|
|
|
|
shape.block_indent(context.config.tab_spaces())
|
|
|
|
}.with_max_width(context.config),
|
|
|
|
)
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> {
|
2018-08-05 13:54:28 +09:00
|
|
|
for item in &self.shared.children[..self.shared.children.len() - 1] {
|
2018-08-04 23:58:34 +09:00
|
|
|
let rewrite = item.rewrite(context, child_shape)?;
|
2018-07-11 21:35:10 +12:00
|
|
|
self.is_block_like
|
2018-08-04 23:58:34 +09:00
|
|
|
.push(item.kind.is_block_like(context, &rewrite));
|
2018-07-11 14:50:42 +12:00
|
|
|
self.shared.rewrites.push(rewrite);
|
|
|
|
}
|
|
|
|
Some(())
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
fn format_last_child(
|
|
|
|
&mut self,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
child_shape: Shape,
|
|
|
|
) -> Option<()> {
|
|
|
|
self.shared
|
|
|
|
.format_last_child(true, context, shape, child_shape)
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
fn join_rewrites(&self, context: &RewriteContext, child_shape: Shape) -> Option<String> {
|
2018-07-11 21:35:10 +12:00
|
|
|
self.shared
|
|
|
|
.join_rewrites(context, child_shape, self.is_block_like.iter().cloned())
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
fn pure_root(&mut self) -> Option<String> {
|
|
|
|
self.shared.pure_root()
|
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
}
|
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
// Format a chain using visual indent.
|
2018-07-11 12:01:39 +12:00
|
|
|
struct ChainFormatterVisual<'a> {
|
2018-07-11 14:50:42 +12:00
|
|
|
shared: ChainFormatterShared<'a>,
|
2018-07-12 10:44:06 +12:00
|
|
|
// The extra offset from the chain's shape to the position of the `.`
|
|
|
|
offset: usize,
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ChainFormatterVisual<'a> {
|
|
|
|
fn new(chain: &'a Chain) -> ChainFormatterVisual<'a> {
|
|
|
|
ChainFormatterVisual {
|
2018-07-11 14:50:42 +12:00
|
|
|
shared: ChainFormatterShared::new(chain),
|
2018-07-12 10:44:06 +12:00
|
|
|
offset: 0,
|
2018-07-10 15:14:44 +12:00
|
|
|
}
|
|
|
|
}
|
2018-07-11 14:50:42 +12:00
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
|
|
|
|
fn format_root(
|
|
|
|
&mut self,
|
|
|
|
parent: &ChainItem,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
) -> Option<()> {
|
2018-07-11 22:06:30 +12:00
|
|
|
let parent_shape = shape.visual_indent(0);
|
2018-07-11 13:39:35 +12:00
|
|
|
let mut root_rewrite = parent.rewrite(context, parent_shape)?;
|
2018-07-12 10:44:06 +12:00
|
|
|
let multiline = root_rewrite.contains('\n');
|
|
|
|
self.offset = if multiline {
|
|
|
|
last_line_width(&root_rewrite).saturating_sub(shape.used_width())
|
|
|
|
} else {
|
|
|
|
trimmed_last_line_width(&root_rewrite)
|
|
|
|
};
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-08-04 23:58:34 +09:00
|
|
|
if !multiline || parent.kind.is_block_like(context, &root_rewrite) {
|
2018-08-05 13:54:28 +09:00
|
|
|
let item = &self.shared.children[0];
|
2018-08-05 14:28:20 +09:00
|
|
|
if let ChainItemKind::Comment = item.kind {
|
|
|
|
self.shared.rewrites.push(root_rewrite);
|
|
|
|
return Some(());
|
|
|
|
}
|
2018-07-12 10:44:06 +12:00
|
|
|
let child_shape = parent_shape
|
|
|
|
.visual_indent(self.offset)
|
|
|
|
.sub_width(self.offset)?;
|
2018-08-04 23:58:34 +09:00
|
|
|
let rewrite = item.rewrite(context, child_shape)?;
|
2018-07-12 10:44:06 +12:00
|
|
|
match wrap_str(rewrite, context.config.max_width(), shape) {
|
|
|
|
Some(rewrite) => root_rewrite.push_str(&rewrite),
|
|
|
|
None => {
|
|
|
|
// We couldn't fit in at the visual indent, try the last
|
|
|
|
// indent.
|
2018-08-04 23:58:34 +09:00
|
|
|
let rewrite = item.rewrite(context, parent_shape)?;
|
2018-07-12 10:44:06 +12:00
|
|
|
root_rewrite.push_str(&rewrite);
|
|
|
|
self.offset = 0;
|
|
|
|
}
|
|
|
|
}
|
2018-07-11 12:01:39 +12:00
|
|
|
|
2018-08-05 13:54:28 +09:00
|
|
|
self.shared.children = &self.shared.children[1..];
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
self.shared.rewrites.push(root_rewrite);
|
2018-07-11 12:01:39 +12:00
|
|
|
Some(())
|
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-12 10:44:06 +12:00
|
|
|
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape> {
|
|
|
|
shape
|
|
|
|
.with_max_width(context.config)
|
|
|
|
.offset_left(self.offset)
|
|
|
|
.map(|s| s.visual_indent(0))
|
2017-10-07 21:51:15 +09:00
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-11 12:01:39 +12:00
|
|
|
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> {
|
2018-08-05 13:54:28 +09:00
|
|
|
for item in &self.shared.children[..self.shared.children.len() - 1] {
|
2018-08-04 23:58:34 +09:00
|
|
|
let rewrite = item.rewrite(context, child_shape)?;
|
2018-07-11 14:50:42 +12:00
|
|
|
self.shared.rewrites.push(rewrite);
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
|
|
|
Some(())
|
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-11 21:35:10 +12:00
|
|
|
fn format_last_child(
|
|
|
|
&mut self,
|
|
|
|
context: &RewriteContext,
|
|
|
|
shape: Shape,
|
|
|
|
child_shape: Shape,
|
|
|
|
) -> Option<()> {
|
|
|
|
self.shared
|
|
|
|
.format_last_child(false, context, shape, child_shape)
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
2018-07-09 13:02:14 +12:00
|
|
|
|
2018-07-11 12:01:39 +12:00
|
|
|
fn join_rewrites(&self, context: &RewriteContext, child_shape: Shape) -> Option<String> {
|
2018-07-11 21:35:10 +12:00
|
|
|
self.shared
|
|
|
|
.join_rewrites(context, child_shape, iter::repeat(false))
|
2018-07-11 12:01:39 +12:00
|
|
|
}
|
2016-05-09 20:07:59 +02:00
|
|
|
|
2018-07-11 14:50:42 +12:00
|
|
|
fn pure_root(&mut self) -> Option<String> {
|
|
|
|
self.shared.pure_root()
|
2018-07-09 14:18:58 +12:00
|
|
|
}
|
|
|
|
}
|
2018-07-12 10:44:06 +12:00
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|