// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::cmp::{Ordering, min};
-use std::iter::ExactSizeIterator;
+use std::cmp::{min, Ordering};
use std::fmt::Write;
+use std::iter::{repeat, ExactSizeIterator};
+
+use syntax::{ast, ptr};
+use syntax::codemap::{BytePos, CodeMap, Span};
+use syntax::parse::classify;
use {Indent, Shape, Spanned};
-use codemap::SpanUtils;
-use rewrite::{Rewrite, RewriteContext};
-use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
- DefinitiveListTactic, definitive_tactic, ListItem, struct_lit_shape,
- struct_lit_tactic, shape_for_tactic, struct_lit_formatting};
-use string::{StringFormat, rewrite_string};
-use utils::{extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
- semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr, stmt_expr,
- colon_spaces, contains_skip, mk_sp, last_line_extendable, paren_overhead};
-use visitor::FmtVisitor;
-use config::{Config, IndentStyle, MultilineStyle, ControlBraceStyle, Style};
-use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
-use types::{rewrite_path, PathContext, can_be_overflowed_type};
-use items::{span_lo_for_arg, span_hi_for_arg};
use chains::rewrite_chain;
-use macros::{rewrite_macro, MacroPosition};
-use patterns::{TuplePatField, can_be_overflowed_pat};
+use codemap::{LineRangeUtils, SpanUtils};
+use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
+ rewrite_comment, rewrite_missing_comment, FindUncommented};
+use config::{Config, ControlBraceStyle, IndentStyle, MultilineStyle, Style};
+use items::{span_hi_for_arg, span_lo_for_arg};
+use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting,
+ struct_lit_shape, struct_lit_tactic, write_list, DefinitiveListTactic, ListFormatting,
+ ListItem, ListTactic, Separator, SeparatorPlace, SeparatorTactic};
+use macros::{rewrite_macro, MacroArg, MacroPosition};
+use patterns::{can_be_overflowed_pat, TuplePatField};
+use rewrite::{Rewrite, RewriteContext};
+use string::{rewrite_string, StringFormat};
+use types::{can_be_overflowed_type, rewrite_path, PathContext};
+use utils::{binary_search, colon_spaces, contains_skip, extra_offset, first_line_width,
+ inner_attributes, last_line_extendable, last_line_width, left_most_sub_expr, mk_sp,
+ outer_attributes, paren_overhead, semicolon_for_stmt, stmt_expr,
+ trimmed_last_line_width, wrap_str};
use vertical::rewrite_with_alignment;
-
-use syntax::{ast, ptr};
-use syntax::codemap::{CodeMap, Span, BytePos};
-use syntax::parse::classify;
+use visitor::FmtVisitor;
impl Rewrite for ast::Expr {
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
}
}
-#[derive(PartialEq)]
+#[derive(Copy, Clone, PartialEq)]
pub enum ExprType {
Statement,
SubExpression,
}
-fn combine_attr_and_expr(
- context: &RewriteContext,
- shape: Shape,
- attr_str: &str,
- expr_str: &str,
-) -> String {
- let separator = if attr_str.is_empty() {
- String::new()
- } else {
- if expr_str.contains('\n') || attr_str.contains('\n') ||
- attr_str.len() + expr_str.len() > shape.width
- {
- format!("\n{}", shape.indent.to_string(context.config))
- } else {
- String::from(" ")
- }
- };
- format!("{}{}{}", attr_str, separator, expr_str)
-}
-
pub fn format_expr(
expr: &ast::Expr,
expr_type: ExprType,
context: &RewriteContext,
shape: Shape,
) -> Option<String> {
- let attr_rw = (&*expr.attrs).rewrite(context, shape);
+ skip_out_of_file_lines_range!(context, expr.span);
+
if contains_skip(&*expr.attrs) {
- if let Some(attr_str) = attr_rw {
- return Some(combine_attr_and_expr(
- context,
- shape,
- &attr_str,
- &context.snippet(expr.span),
- ));
- } else {
- return Some(context.snippet(expr.span));
- }
+ return Some(context.snippet(expr.span()));
}
+
let expr_rw = match expr.node {
- ast::ExprKind::Array(ref expr_vec) => {
- rewrite_array(
- expr_vec.iter().map(|e| &**e),
- mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi),
- context,
- shape,
- false,
- )
- }
- ast::ExprKind::Lit(ref l) => {
- match l.node {
- ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
- rewrite_string_lit(context, l.span, shape)
- }
- _ => {
- wrap_str(
- context.snippet(expr.span),
- context.config.max_width(),
- shape,
- )
- }
+ ast::ExprKind::Array(ref expr_vec) => rewrite_array(
+ expr_vec.iter().map(|e| &**e),
+ mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi()),
+ context,
+ shape,
+ false,
+ ),
+ ast::ExprKind::Lit(ref l) => match l.node {
+ ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
+ rewrite_string_lit(context, l.span, shape)
}
- }
+ _ => wrap_str(
+ context.snippet(expr.span),
+ context.config.max_width(),
+ shape,
+ ),
+ },
ast::ExprKind::Call(ref callee, ref args) => {
- let inner_span = mk_sp(callee.span.hi, expr.span.hi);
+ let inner_span = mk_sp(callee.span.hi(), expr.span.hi());
rewrite_call_with_binary_search(
context,
&**callee,
)
}
ast::ExprKind::Unary(ref op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
- ast::ExprKind::Struct(ref path, ref fields, ref base) => {
- rewrite_struct_lit(
- context,
- path,
- fields,
- base.as_ref().map(|e| &**e),
- expr.span,
- shape,
- )
- }
- ast::ExprKind::Tup(ref items) => {
- rewrite_tuple(
- context,
- &items.iter().map(|x| &**x).collect::<Vec<_>>()[..],
- expr.span,
- shape,
- )
- }
+ ast::ExprKind::Struct(ref path, ref fields, ref base) => rewrite_struct_lit(
+ context,
+ path,
+ fields,
+ base.as_ref().map(|e| &**e),
+ expr.span,
+ shape,
+ ),
+ ast::ExprKind::Tup(ref items) => rewrite_tuple(
+ context,
+ &items.iter().map(|x| &**x).collect::<Vec<_>>()[..],
+ expr.span,
+ shape,
+ ),
ast::ExprKind::If(..) |
ast::ExprKind::IfLet(..) |
ast::ExprKind::ForLoop(..) |
ast::ExprKind::Loop(..) |
ast::ExprKind::While(..) |
- ast::ExprKind::WhileLet(..) => {
- to_control_flow(expr, expr_type)
- .and_then(|control_flow| control_flow.rewrite(context, shape))
- }
+ ast::ExprKind::WhileLet(..) => to_control_flow(expr, expr_type)
+ .and_then(|control_flow| control_flow.rewrite(context, shape)),
ast::ExprKind::Block(ref block) => {
match expr_type {
ExprType::Statement => {
if is_unsafe_block(block) {
block.rewrite(context, shape)
- } else {
+ } else if let rw @ Some(_) = rewrite_empty_block(context, block, shape) {
// Rewrite block without trying to put it in a single line.
- if let rw @ Some(_) = rewrite_empty_block(context, block, shape) {
- return rw;
- }
+ rw
+ } else {
let prefix = try_opt!(block_prefix(context, block, shape));
rewrite_block_with_visitor(context, &prefix, block, shape)
}
}
}
ast::ExprKind::Match(ref cond, ref arms) => {
- rewrite_match(context, cond, arms, shape, expr.span)
+ rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs)
}
ast::ExprKind::Path(ref qself, ref path) => {
rewrite_path(context, PathContext::Expr, qself.as_ref(), path, shape)
)
}
}
+ ast::ExprKind::Yield(ref opt_expr) => if let Some(ref expr) = *opt_expr {
+ rewrite_unary_prefix(context, "yield ", &**expr, shape)
+ } else {
+ wrap_str("yield".to_string(), context.config.max_width(), shape)
+ },
ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
rewrite_closure(capture, fn_decl, body, expr.span, context, shape)
}
fn needs_space_before_range(context: &RewriteContext, lhs: &ast::Expr) -> bool {
match lhs.node {
- ast::ExprKind::Lit(ref lit) => {
- match lit.node {
- ast::LitKind::FloatUnsuffixed(..) => {
- context.snippet(lit.span).ends_with('.')
- }
- _ => false,
+ ast::ExprKind::Lit(ref lit) => match lit.node {
+ ast::LitKind::FloatUnsuffixed(..) => {
+ context.snippet(lit.span).ends_with('.')
}
- }
+ _ => false,
+ },
_ => false,
}
}
match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
- (Some(ref lhs), Some(ref rhs)) => {
+ (Some(lhs), Some(rhs)) => {
let sp_delim = if context.config.spaces_around_ranges() {
format!(" {} ", delim)
} else if needs_space_before_range(context, lhs) {
} else {
delim.into()
};
- rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, shape)
+ rewrite_pair(&*lhs, &*rhs, "", &sp_delim, "", context, shape)
}
- (None, Some(ref rhs)) => {
+ (None, Some(rhs)) => {
let sp_delim = if context.config.spaces_around_ranges() {
format!("{} ", delim)
} else {
delim.into()
};
- rewrite_unary_prefix(context, &sp_delim, &**rhs, shape)
+ rewrite_unary_prefix(context, &sp_delim, &*rhs, shape)
}
- (Some(ref lhs), None) => {
+ (Some(lhs), None) => {
let sp_delim = if context.config.spaces_around_ranges() {
format!(" {}", delim)
} else {
delim.into()
};
- rewrite_unary_suffix(context, &sp_delim, &**lhs, shape)
+ rewrite_unary_suffix(context, &sp_delim, &*lhs, shape)
}
(None, None) => wrap_str(delim.into(), context.config.max_width(), shape),
}
}
// We do not format these expressions yet, but they should still
// satisfy our width restrictions.
- ast::ExprKind::InPlace(..) | ast::ExprKind::InlineAsm(..) => {
- wrap_str(
- context.snippet(expr.span),
- context.config.max_width(),
- shape,
- )
- }
+ ast::ExprKind::InPlace(..) | ast::ExprKind::InlineAsm(..) => wrap_str(
+ context.snippet(expr.span),
+ context.config.max_width(),
+ shape,
+ ),
ast::ExprKind::Catch(ref block) => {
- if let rewrite @ Some(_) =
- rewrite_single_line_block(context, "do catch ", block, shape)
- {
- return rewrite;
+ if let rw @ Some(_) = rewrite_single_line_block(context, "do catch ", block, shape) {
+ rw
+ } else {
+ // 9 = `do catch `
+ let budget = shape.width.checked_sub(9).unwrap_or(0);
+ Some(format!(
+ "{}{}",
+ "do catch ",
+ try_opt!(block.rewrite(&context, Shape::legacy(budget, shape.indent)))
+ ))
}
- // 9 = `do catch `
- let budget = shape.width.checked_sub(9).unwrap_or(0);
- Some(format!(
- "{}{}",
- "do catch ",
- try_opt!(block.rewrite(&context, Shape::legacy(budget, shape.indent)))
- ))
}
};
- match (attr_rw, expr_rw) {
- (Some(attr_str), Some(expr_str)) => {
- recover_comment_removed(
- combine_attr_and_expr(context, shape, &attr_str, &expr_str),
- expr.span,
- context,
- shape,
- )
- }
- _ => None,
- }
+
+ expr_rw
+ .and_then(|expr_str| {
+ recover_comment_removed(expr_str, expr.span, context, shape)
+ })
+ .and_then(|expr_str| {
+ let attrs = outer_attributes(&expr.attrs);
+ let attrs_str = try_opt!(attrs.rewrite(context, shape));
+ let span = mk_sp(
+ attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()),
+ expr.span.lo(),
+ );
+ combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false)
+ })
}
pub fn rewrite_pair<LHS, RHS>(
LHS: Rewrite,
RHS: Rewrite,
{
- // Get "full width" rhs and see if it fits on the current line. This
- // usually works fairly well since it tends to place operands of
- // operations with high precendence close together.
- // Note that this is non-conservative, but its just to see if it's even
- // worth trying to put everything on one line.
- let rhs_shape = try_opt!(shape.sub_width(suffix.len()));
- let rhs_result = rhs.rewrite(context, rhs_shape);
-
- if let Some(rhs_result) = rhs_result {
- // This is needed in case of line break not caused by a
- // shortage of space, but by end-of-line comments, for example.
- if !rhs_result.contains('\n') {
- let lhs_shape =
- try_opt!(try_opt!(shape.offset_left(prefix.len())).sub_width(infix.len()));
- let lhs_result = lhs.rewrite(context, lhs_shape);
- if let Some(lhs_result) = lhs_result {
- let mut result = format!("{}{}{}", prefix, lhs_result, infix);
-
- let remaining_width = shape
- .width
- .checked_sub(last_line_width(&result) + suffix.len())
- .unwrap_or(0);
-
- if rhs_result.len() <= remaining_width {
- result.push_str(&rhs_result);
- result.push_str(suffix);
- return Some(result);
- }
+ let sep = if infix.ends_with(' ') { " " } else { "" };
+ let infix = infix.trim_right();
+ let lhs_overhead = shape.used_width() + prefix.len() + infix.len();
+ let lhs_shape = Shape {
+ width: try_opt!(context.config.max_width().checked_sub(lhs_overhead)),
+ ..shape
+ };
+ let lhs_result = try_opt!(
+ lhs.rewrite(context, lhs_shape)
+ .map(|lhs_str| format!("{}{}{}", prefix, lhs_str, infix))
+ );
- // Try rewriting the rhs into the remaining space.
- let rhs_shape = shape.shrink_left(last_line_width(&result) + suffix.len());
- if let Some(rhs_shape) = rhs_shape {
- if let Some(rhs_result) = rhs.rewrite(context, rhs_shape) {
- // FIXME this should always hold.
- if rhs_result.len() <= remaining_width {
- result.push_str(&rhs_result);
- result.push_str(suffix);
- return Some(result);
- }
- }
- }
- }
+ // Try to the both lhs and rhs on the same line.
+ let rhs_orig_result = shape
+ .offset_left(last_line_width(&lhs_result) + suffix.len() + sep.len())
+ .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape));
+ if let Some(ref rhs_result) = rhs_orig_result {
+ // If the rhs looks like block expression, we allow it to stay on the same line
+ // with the lhs even if it is multi-lined.
+ let allow_same_line = rhs_result
+ .lines()
+ .next()
+ .map(|first_line| first_line.ends_with('{'))
+ .unwrap_or(false);
+ if !rhs_result.contains('\n') || allow_same_line {
+ return Some(format!("{}{}{}{}", lhs_result, sep, rhs_result, suffix));
}
}
// We have to use multiple lines.
-
// Re-evaluate the rhs because we have more space now:
- let infix = infix.trim_right();
let rhs_shape = match context.config.control_style() {
Style::Legacy => {
try_opt!(shape.sub_width(suffix.len() + prefix.len())).visual_indent(prefix.len())
}
};
let rhs_result = try_opt!(rhs.rewrite(context, rhs_shape));
- let lhs_overhead = shape.used_width() + prefix.len() + infix.len();
- let lhs_shape = Shape {
- width: try_opt!(context.config.max_width().checked_sub(lhs_overhead)),
- ..shape
- };
- let lhs_result = try_opt!(lhs.rewrite(context, lhs_shape));
Some(format!(
- "{}{}{}\n{}{}{}",
- prefix,
+ "{}\n{}{}{}",
lhs_result,
- infix,
rhs_shape.indent.to_string(context.config),
rhs_result,
suffix
1 // "["
};
- let mut nested_shape = match context.config.array_layout() {
- IndentStyle::Block => {
- try_opt!(
- shape
- .block()
- .block_indent(context.config.tab_spaces())
- .with_max_width(context.config)
- .sub_width(1)
- )
- }
- IndentStyle::Visual => {
- try_opt!(
- shape
- .visual_indent(bracket_size)
- .sub_width(bracket_size * 2)
- )
- }
+ let nested_shape = match context.config.array_layout() {
+ IndentStyle::Block => try_opt!(
+ shape
+ .block()
+ .block_indent(context.config.tab_spaces())
+ .with_max_width(context.config)
+ .sub_width(1)
+ ),
+ IndentStyle::Visual => try_opt!(
+ shape
+ .visual_indent(bracket_size)
+ .sub_width(bracket_size * 2)
+ ),
};
let items = itemize_list(
context.codemap,
expr_iter,
"]",
- |item| item.span.lo,
- |item| item.span.hi,
+ |item| item.span.lo(),
+ |item| item.span.hi(),
|item| item.rewrite(context, nested_shape),
- span.lo,
- span.hi,
+ span.lo(),
+ span.hi(),
+ false,
).collect::<Vec<_>>();
if items.is_empty() {
Some(width) => {
let tactic =
ListTactic::LimitedHorizontalVertical(context.config.array_width());
- definitive_tactic(&items, tactic, width)
+ definitive_tactic(&items, tactic, Separator::Comma, width)
}
None => DefinitiveListTactic::Vertical,
}
}
- IndentStyle::Visual => {
- if has_long_item || items.iter().any(ListItem::is_multiline) {
- definitive_tactic(
- &items,
- ListTactic::LimitedHorizontalVertical(context.config.array_width()),
- nested_shape.width,
- )
- } else {
- DefinitiveListTactic::Mixed
- }
- }
+ IndentStyle::Visual => if has_long_item || items.iter().any(ListItem::is_multiline) {
+ definitive_tactic(
+ &items,
+ ListTactic::LimitedHorizontalVertical(context.config.array_width()),
+ Separator::Comma,
+ nested_shape.width,
+ )
+ } else {
+ DefinitiveListTactic::Mixed
+ },
};
+ let ends_with_newline = tactic.ends_with_newline(context.config.array_layout());
if context.config.array_horizontal_layout_threshold() > 0 &&
items.len() > context.config.array_horizontal_layout_threshold()
{
tactic = DefinitiveListTactic::Mixed;
- if context.config.array_layout() == IndentStyle::Block {
- nested_shape = try_opt!(
- shape
- .visual_indent(bracket_size)
- .sub_width(bracket_size * 2)
- );
- }
}
let fmt = ListFormatting {
} else {
SeparatorTactic::Vertical
},
+ separator_place: SeparatorPlace::Back,
shape: nested_shape,
- ends_with_newline: false,
+ ends_with_newline: ends_with_newline,
+ preserve_newline: false,
config: context.config,
};
let list_str = try_opt!(write_list(&items, &fmt));
let result = if context.config.array_layout() == IndentStyle::Visual ||
- tactic != DefinitiveListTactic::Vertical
+ tactic == DefinitiveListTactic::Horizontal
{
- if context.config.spaces_within_square_brackets() && list_str.len() > 0 {
+ if context.config.spaces_within_square_brackets() && !list_str.is_empty() {
format!("[ {} ]", list_str)
} else {
format!("[{}]", list_str)
// 1 = |
let argument_offset = nested_shape.indent + 1;
- let arg_shape = try_opt!(nested_shape.shrink_left(1)).visual_indent(0);
+ let arg_shape = try_opt!(nested_shape.offset_left(1)).visual_indent(0);
let ret_str = try_opt!(fn_decl.output.rewrite(context, arg_shape));
let arg_items = itemize_list(
|arg| span_hi_for_arg(context, arg),
|arg| arg.rewrite(context, arg_shape),
context.codemap.span_after(span, "|"),
- body.span.lo,
+ body.span.lo(),
+ false,
);
let item_vec = arg_items.collect::<Vec<_>>();
// 1 = space between arguments and return type.
.width
.checked_sub(ret_str.len() + 1)
.unwrap_or(0);
- let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
+ let tactic = definitive_tactic(
+ &item_vec,
+ ListTactic::HorizontalVertical,
+ Separator::Comma,
+ horizontal_budget,
+ );
let arg_shape = match tactic {
DefinitiveListTactic::Horizontal => try_opt!(arg_shape.sub_width(ret_str.len() + 1)),
_ => arg_shape,
tactic: tactic,
separator: ",",
trailing_separator: SeparatorTactic::Never,
+ separator_place: SeparatorPlace::Back,
shape: arg_shape,
ends_with_newline: false,
+ preserve_newline: true,
config: context.config,
};
let list_str = try_opt!(write_list(&item_vec, &fmt));
}
// Figure out if the block is necessary.
- let needs_block = block.rules != ast::BlockCheckMode::Default ||
- block.stmts.len() > 1 || context.inside_macro ||
+ let needs_block = block.rules != ast::BlockCheckMode::Default || block.stmts.len() > 1 ||
+ context.inside_macro ||
block_contains_comment(block, context.codemap) ||
prefix.contains('\n');
};
if no_return_type && !needs_block {
// lock.stmts.len() == 1
- if let Some(ref expr) = stmt_expr(&block.stmts[0]) {
+ if let Some(expr) = stmt_expr(&block.stmts[0]) {
if let Some(rw) = rewrite_closure_expr(expr, &prefix, context, body_shape) {
return Some(rw);
}
}
// Either we require a block, or tried without and failed.
- rewrite_closure_block(&block, &prefix, context, body_shape)
+ rewrite_closure_block(block, &prefix, context, body_shape)
} else {
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| {
// The closure originally had a non-block expression, but we can't fit on
if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
rewrite = and_one_line(rewrite);
}
+ rewrite = rewrite.and_then(|rw| {
+ if context.config.multiline_closure_forces_block() && rw.contains('\n') {
+ None
+ } else {
+ Some(rw)
+ }
+ });
rewrite.map(|rw| format!("{} {}", prefix, rw))
}
// closure is large.
let block_threshold = context.config.closure_block_indent_threshold();
if block_threshold >= 0 {
- if let Some(block_str) = block.rewrite(&context, shape) {
+ if let Some(block_str) = block.rewrite(context, shape) {
if block_str.matches('\n').count() <= block_threshold as usize &&
!need_block_indent(&block_str, shape)
{
// The body of the closure is big enough to be block indented, that
// means we must re-format.
let block_shape = shape.block();
- let block_str = try_opt!(block.rewrite(&context, block_shape));
+ let block_str = try_opt!(block.rewrite(context, block_shape));
Some(format!("{} {}", prefix, block_str))
}
block: &ast::Block,
shape: Shape,
) -> Option<String> {
- if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) &&
- shape.width >= 2
+ if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) && shape.width >= 2
{
return Some("{}".to_owned());
}
ast::BlockCheckMode::Unsafe(..) => {
let snippet = context.snippet(block.span);
let open_pos = try_opt!(snippet.find_uncommented("{"));
- visitor.last_pos = block.span.lo + BytePos(open_pos as u32)
+ visitor.last_pos = block.span.lo() + BytePos(open_pos as u32)
}
- ast::BlockCheckMode::Default => visitor.last_pos = block.span.lo,
+ ast::BlockCheckMode::Default => visitor.last_pos = block.span.lo(),
}
- visitor.visit_block(block);
- if visitor.failed && shape.indent.alignment != 0 {
- block.rewrite(
- context,
- Shape::indented(shape.indent.block_only(), context.config),
- )
- } else {
- Some(format!("{}{}", prefix, visitor.buffer))
- }
+ visitor.visit_block(block, None);
+ Some(format!("{}{}", prefix, visitor.buffer))
}
impl Rewrite for ast::Block {
impl Rewrite for ast::Stmt {
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+ skip_out_of_file_lines_range!(context, self.span());
+
let result = match self.node {
ast::StmtKind::Local(ref local) => local.rewrite(context, shape),
ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
- let suffix = if semicolon_for_stmt(self) { ";" } else { "" };
-
- format_expr(
- ex,
- match self.node {
- ast::StmtKind::Expr(_) => ExprType::SubExpression,
- ast::StmtKind::Semi(_) => ExprType::Statement,
- _ => unreachable!(),
- },
- context,
- try_opt!(shape.sub_width(suffix.len())),
- ).map(|s| s + suffix)
+ let suffix = if semicolon_for_stmt(context, self) {
+ ";"
+ } else {
+ ""
+ };
+
+ let shape = try_opt!(shape.sub_width(suffix.len()));
+ format_expr(ex, ExprType::Statement, context, shape).map(|s| s + suffix)
}
ast::StmtKind::Mac(..) | ast::StmtKind::Item(..) => None,
};
result.and_then(|res| {
- recover_comment_removed(res, self.span, context, shape)
+ recover_comment_removed(res, self.span(), context, shape)
})
}
}
ast::ExprKind::Block(ref block) if block.stmts.len() == 1 => {
stmt_expr(&block.stmts[0]).and_then(|e| rewrite_cond(context, e, shape))
}
- _ => {
- to_control_flow(expr, ExprType::SubExpression).and_then(|control_flow| {
- let alt_block_sep =
- String::from("\n") + &shape.indent.block_only().to_string(context.config);
- control_flow
- .rewrite_cond(context, shape, &alt_block_sep)
- .and_then(|rw| Some(rw.0))
- })
- }
+ _ => to_control_flow(expr, ExprType::SubExpression).and_then(|control_flow| {
+ let alt_block_sep =
+ String::from("\n") + &shape.indent.block_only().to_string(context.config);
+ control_flow
+ .rewrite_cond(context, shape, &alt_block_sep)
+ .and_then(|rw| Some(rw.0))
+ }),
}
}
fn to_control_flow<'a>(expr: &'a ast::Expr, expr_type: ExprType) -> Option<ControlFlow<'a>> {
match expr.node {
- ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
- Some(ControlFlow::new_if(
- cond,
- None,
- if_block,
- else_block.as_ref().map(|e| &**e),
- expr_type == ExprType::SubExpression,
- false,
- expr.span,
- ))
- }
+ ast::ExprKind::If(ref cond, ref if_block, ref else_block) => Some(ControlFlow::new_if(
+ cond,
+ None,
+ if_block,
+ else_block.as_ref().map(|e| &**e),
+ expr_type == ExprType::SubExpression,
+ false,
+ expr.span,
+ )),
ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
Some(ControlFlow::new_if(
cond,
ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
Some(ControlFlow::new_for(pat, cond, block, label, expr.span))
}
- ast::ExprKind::Loop(ref block, label) => Some(
- ControlFlow::new_loop(block, label, expr.span),
- ),
- ast::ExprKind::While(ref cond, ref block, label) => Some(
- ControlFlow::new_while(None, cond, block, label, expr.span),
- ),
- ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
- Some(ControlFlow::new_while(
- Some(pat),
- cond,
- block,
- label,
- expr.span,
- ))
+ ast::ExprKind::Loop(ref block, label) => {
+ Some(ControlFlow::new_loop(block, label, expr.span))
+ }
+ ast::ExprKind::While(ref cond, ref block, label) => {
+ Some(ControlFlow::new_while(None, cond, block, label, expr.span))
}
+ ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => Some(
+ ControlFlow::new_while(Some(pat), cond, block, label, expr.span),
+ ),
_ => None,
}
}
let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
let expr = &self.block.stmts[0];
- let if_str = try_opt!(expr.rewrite(
- context,
- Shape::legacy(new_width, Indent::empty()),
- ));
+ let if_str = try_opt!(expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
let new_width = try_opt!(new_width.checked_sub(if_str.len()));
let else_expr = &else_node.stmts[0];
shape: Shape,
alt_block_sep: &str,
) -> Option<(String, usize)> {
+ // Do not take the rhs overhead from the upper expressions into account
+ // when rewriting pattern.
+ let new_width = context
+ .config
+ .max_width()
+ .checked_sub(shape.used_width())
+ .unwrap_or(0);
+ let fresh_shape = Shape {
+ width: new_width,
+ ..shape
+ };
let constr_shape = if self.nested_if {
// We are part of an if-elseif-else chain. Our constraints are tightened.
// 7 = "} else " .len()
- try_opt!(shape.shrink_left(7))
+ try_opt!(fresh_shape.offset_left(7))
} else {
- shape
+ fresh_shape
};
let label_string = rewrite_label(self.label);
let pat_expr_string = match self.cond {
Some(cond) => {
- let mut cond_shape = match context.config.control_style() {
+ let cond_shape = match context.config.control_style() {
Style::Legacy => try_opt!(constr_shape.shrink_left(offset)),
Style::Rfc => try_opt!(constr_shape.offset_left(offset)),
};
- if context.config.control_brace_style() != ControlBraceStyle::AlwaysNextLine {
- // 2 = " {".len()
- cond_shape = try_opt!(cond_shape.sub_width(2));
- }
-
try_opt!(rewrite_pat_expr(
context,
self.pat,
None => String::new(),
};
+ let brace_overhead =
+ if context.config.control_brace_style() != ControlBraceStyle::AlwaysNextLine {
+ // 2 = ` {`
+ 2
+ } else {
+ 0
+ };
+ let one_line_budget = context
+ .config
+ .max_width()
+ .checked_sub(constr_shape.used_width() + offset + brace_overhead)
+ .unwrap_or(0);
let force_newline_brace = context.config.control_style() == Style::Rfc &&
- pat_expr_string.contains('\n') &&
+ (pat_expr_string.contains('\n') || pat_expr_string.len() > one_line_budget) &&
!last_line_extendable(&pat_expr_string);
// Try to format if-else on single line.
let cond_span = if let Some(cond) = self.cond {
cond.span
} else {
- mk_sp(self.block.span.lo, self.block.span.lo)
+ mk_sp(self.block.span.lo(), self.block.span.lo())
};
- // for event in event
+ // `for event in event`
+ // Do not include label in the span.
+ let lo = self.label.map_or(self.span.lo(), |label| label.span.hi());
let between_kwd_cond = mk_sp(
- context.codemap.span_after(self.span, self.keyword.trim()),
- self.pat.map_or(
- cond_span.lo,
- |p| if self.matcher.is_empty() {
- p.span.lo
+ context
+ .codemap
+ .span_after(mk_sp(lo, self.span.hi()), self.keyword.trim()),
+ self.pat
+ .map_or(cond_span.lo(), |p| if self.matcher.is_empty() {
+ p.span.lo()
} else {
context.codemap.span_before(self.span, self.matcher.trim())
- },
- ),
+ }),
);
let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
let after_cond_comment =
- extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape);
+ extract_comment(mk_sp(cond_span.hi(), self.block.span.lo()), context, shape);
let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
""
} else if context.config.control_brace_style() == ControlBraceStyle::AlwaysNextLine ||
- force_newline_brace
+ force_newline_brace
{
alt_block_sep
} else {
next_else_block.as_ref().map(|e| &**e),
false,
true,
- mk_sp(else_block.span.lo, self.span.hi),
+ mk_sp(else_block.span.lo(), self.span.hi()),
).rewrite(context, shape)
}
ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
next_else_block.as_ref().map(|e| &**e),
false,
true,
- mk_sp(else_block.span.lo, self.span.hi),
+ mk_sp(else_block.span.lo(), self.span.hi()),
).rewrite(context, shape)
}
_ => {
};
let between_kwd_else_block = mk_sp(
- self.block.span.hi,
+ self.block.span.hi(),
context
.codemap
- .span_before(mk_sp(self.block.span.hi, else_block.span.lo), "else"),
+ .span_before(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
);
let between_kwd_else_block_comment =
extract_comment(between_kwd_else_block, context, shape);
let after_else = mk_sp(
context
.codemap
- .span_after(mk_sp(self.block.span.hi, else_block.span.lo), "else"),
- else_block.span.lo,
+ .span_after(mk_sp(self.block.span.hi(), else_block.span.lo()), "else"),
+ else_block.span.lo(),
);
let after_else_comment = extract_comment(after_else, context, shape);
}
fn extract_comment(span: Span, context: &RewriteContext, shape: Shape) -> Option<String> {
- let comment_str = context.snippet(span);
- if contains_comment(&comment_str) {
- let comment = try_opt!(rewrite_comment(
- comment_str.trim(),
- false,
- shape,
- context.config,
- ));
- Some(format!(
+ match rewrite_missing_comment(span, shape, context) {
+ Some(ref comment) if !comment.is_empty() => Some(format!(
"\n{indent}{}\n{indent}",
comment,
indent = shape.indent.to_string(context.config)
- ))
- } else {
- None
+ )),
+ _ => None,
}
}
// the expression.
pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
(block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) &&
- !block_contains_comment(block, codemap))
+ !block_contains_comment(block, codemap))
}
/// Checks whether a block contains at most one statement or expression, and no comments.
}
}
-// inter-match-arm-comment-rules:
-// - all comments following a match arm before the start of the next arm
-// are about the second arm
-fn rewrite_match_arm_comment(
- context: &RewriteContext,
- missed_str: &str,
- shape: Shape,
- arm_indent_str: &str,
-) -> Option<String> {
- // The leading "," is not part of the arm-comment
- let missed_str = match missed_str.find_uncommented(",") {
- Some(n) => &missed_str[n + 1..],
- None => &missed_str[..],
- };
-
- let mut result = String::new();
- // any text not preceeded by a newline is pushed unmodified to the block
- let first_brk = missed_str.find(|c: char| c == '\n').unwrap_or(0);
- result.push_str(&missed_str[..first_brk]);
- let missed_str = &missed_str[first_brk..]; // If missed_str had one newline, it starts with it
+// A simple wrapper type against ast::Arm. Used inside write_list().
+struct ArmWrapper<'a> {
+ pub arm: &'a ast::Arm,
+ // True if the arm is the last one in match expression. Used to decide on whether we should add
+ // trailing comma to the match arm when `config.trailing_comma() == Never`.
+ pub is_last: bool,
+}
- let first = missed_str
- .find(|c: char| !c.is_whitespace())
- .unwrap_or(missed_str.len());
- if missed_str[..first].chars().filter(|c| c == &'\n').count() >= 2 {
- // Excessive vertical whitespace before comment should be preserved
- // FIXME handle vertical whitespace better
- result.push('\n');
- }
- let missed_str = missed_str[first..].trim();
- if !missed_str.is_empty() {
- let comment = try_opt!(rewrite_comment(&missed_str, false, shape, context.config));
- result.push('\n');
- result.push_str(arm_indent_str);
- result.push_str(&comment);
+impl<'a> ArmWrapper<'a> {
+ pub fn new(arm: &'a ast::Arm, is_last: bool) -> ArmWrapper<'a> {
+ ArmWrapper { arm, is_last }
}
+}
- Some(result)
+impl<'a> Rewrite for ArmWrapper<'a> {
+ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+ rewrite_match_arm(context, self.arm, shape, self.is_last)
+ }
}
fn rewrite_match(
arms: &[ast::Arm],
shape: Shape,
span: Span,
+ attrs: &[ast::Attribute],
) -> Option<String> {
if arms.is_empty() {
return None;
}
- // `match `cond` {`
+ // Do not take the rhs overhead from the upper expressions into account
+ // when rewriting match condition.
+ let new_width = try_opt!(context.config.max_width().checked_sub(shape.used_width()));
+ let cond_shape = Shape {
+ width: new_width,
+ ..shape
+ };
+ // 6 = `match `
let cond_shape = match context.config.control_style() {
- Style::Legacy => try_opt!(shape.shrink_left(6).and_then(|s| s.sub_width(2))),
- Style::Rfc => try_opt!(shape.offset_left(8)),
+ Style::Legacy => try_opt!(cond_shape.shrink_left(6)),
+ Style::Rfc => try_opt!(cond_shape.offset_left(6)),
};
let cond_str = try_opt!(cond.rewrite(context, cond_shape));
let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
let block_sep = match context.config.control_brace_style() {
- ControlBraceStyle::AlwaysNextLine => alt_block_sep.as_str(),
+ ControlBraceStyle::AlwaysNextLine => &alt_block_sep,
+ _ if last_line_extendable(&cond_str) => " ",
+ // 2 = ` {`
+ _ if cond_str.contains('\n') || cond_str.len() + 2 > cond_shape.width => &alt_block_sep,
_ => " ",
};
- let mut result = format!("match {}{}{{", cond_str, block_sep);
- let arm_shape = if context.config.indent_match_arms() {
- shape.block_indent(context.config.tab_spaces())
+ let nested_indent_str = shape
+ .indent
+ .block_indent(context.config)
+ .to_string(context.config);
+ // Inner attributes.
+ let inner_attrs = &inner_attributes(attrs);
+ let inner_attrs_str = if inner_attrs.is_empty() {
+ String::new()
} else {
- shape.block_indent(0)
+ try_opt!(
+ inner_attrs
+ .rewrite(context, shape)
+ .map(|s| format!("{}{}\n", nested_indent_str, s))
+ )
};
- let arm_indent_str = arm_shape.indent.to_string(context.config);
+ let open_brace_pos = if inner_attrs.is_empty() {
+ context
+ .codemap
+ .span_after(mk_sp(cond.span.hi(), arms[0].span().lo()), "{")
+ } else {
+ inner_attrs[inner_attrs.len() - 1].span().hi()
+ };
- let open_brace_pos = context
- .codemap
- .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{");
+ let arm_indent_str = if context.config.indent_match_arms() {
+ nested_indent_str
+ } else {
+ shape.indent.to_string(context.config)
+ };
- let arm_num = arms.len();
- for (i, arm) in arms.iter().enumerate() {
- // Make sure we get the stuff between arms.
- let missed_str = if i == 0 {
- context.snippet(mk_sp(open_brace_pos, arm_start_pos(arm)))
- } else {
- context.snippet(mk_sp(arm_end_pos(&arms[i - 1]), arm_start_pos(arm)))
- };
- let comment = try_opt!(rewrite_match_arm_comment(
+ Some(format!(
+ "match {}{}{{\n{}{}{}\n{}}}",
+ cond_str,
+ block_sep,
+ inner_attrs_str,
+ arm_indent_str,
+ try_opt!(rewrite_match_arms(
context,
- &missed_str,
- arm_shape,
- &arm_indent_str,
- ));
- result.push_str(&comment);
- result.push('\n');
- result.push_str(&arm_indent_str);
-
- let arm_str = arm.rewrite(&context, arm_shape.with_max_width(context.config));
- if let Some(ref arm_str) = arm_str {
- // Trim the trailing comma if necessary.
- if i == arm_num - 1 && context.config.trailing_comma() == SeparatorTactic::Never &&
- arm_str.ends_with(',')
- {
- result.push_str(&arm_str[0..arm_str.len() - 1])
- } else {
- result.push_str(arm_str)
- }
- } else {
- // We couldn't format the arm, just reproduce the source.
- let snippet = context.snippet(mk_sp(arm_start_pos(arm), arm_end_pos(arm)));
- result.push_str(&snippet);
- if context.config.trailing_comma() != SeparatorTactic::Never {
- result.push_str(arm_comma(context.config, &arm.body))
- }
- }
- }
- // BytePos(1) = closing match brace.
- let last_span = mk_sp(arm_end_pos(&arms[arms.len() - 1]), span.hi - BytePos(1));
- let last_comment = context.snippet(last_span);
- let comment = try_opt!(rewrite_match_arm_comment(
- context,
- &last_comment,
- arm_shape,
- &arm_indent_str,
- ));
- result.push_str(&comment);
- result.push('\n');
- result.push_str(&shape.indent.to_string(context.config));
- result.push('}');
- Some(result)
-}
-
-fn arm_start_pos(arm: &ast::Arm) -> BytePos {
- let &ast::Arm {
- ref attrs,
- ref pats,
- ..
- } = arm;
- if !attrs.is_empty() {
- return attrs[0].span.lo;
- }
-
- pats[0].span.lo
-}
-
-fn arm_end_pos(arm: &ast::Arm) -> BytePos {
- arm.body.span.hi
+ arms,
+ shape,
+ span,
+ open_brace_pos,
+ )),
+ shape.indent.to_string(context.config),
+ ))
}
-fn arm_comma(config: &Config, body: &ast::Expr) -> &'static str {
- if config.match_block_trailing_comma() {
+fn arm_comma(config: &Config, body: &ast::Expr, is_last: bool) -> &'static str {
+ if is_last && config.trailing_comma() == SeparatorTactic::Never {
+ ""
+ } else if config.match_block_trailing_comma() {
","
} else if let ast::ExprKind::Block(ref block) = body.node {
if let ast::BlockCheckMode::Default = block.rules {
}
}
-// Match arms.
-impl Rewrite for ast::Arm {
- fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
- debug!("Arm::rewrite {:?} {:?}", self, shape);
- let &ast::Arm {
- ref attrs,
- ref pats,
- ref guard,
- ref body,
- } = self;
-
- let attr_str = if !attrs.is_empty() {
- if contains_skip(attrs) {
- return None;
- }
- format!(
- "{}\n{}",
- try_opt!(attrs.rewrite(context, shape)),
- shape.indent.to_string(context.config)
- )
- } else {
- String::new()
- };
+fn rewrite_match_arms(
+ context: &RewriteContext,
+ arms: &[ast::Arm],
+ shape: Shape,
+ span: Span,
+ open_brace_pos: BytePos,
+) -> Option<String> {
+ let arm_shape = if context.config.indent_match_arms() {
+ shape.block_indent(context.config.tab_spaces())
+ } else {
+ shape.block_indent(0)
+ }.with_max_width(context.config);
- // Patterns
- // 5 = ` => {`
- let pat_shape = try_opt!(shape.sub_width(5));
+ let arm_len = arms.len();
+ let is_last_iter = repeat(false)
+ .take(arm_len.checked_sub(1).unwrap_or(0))
+ .chain(repeat(true));
+ let items = itemize_list(
+ context.codemap,
+ arms.iter()
+ .zip(is_last_iter)
+ .map(|(arm, is_last)| ArmWrapper::new(arm, is_last)),
+ "}",
+ |arm| arm.arm.span().lo(),
+ |arm| arm.arm.span().hi(),
+ |arm| arm.rewrite(context, arm_shape),
+ open_brace_pos,
+ span.hi(),
+ false,
+ );
+ let arms_vec: Vec<_> = items.collect();
+ let fmt = ListFormatting {
+ tactic: DefinitiveListTactic::Vertical,
+ // We will add/remove commas inside `arm.rewrite()`, and hence no separator here.
+ separator: "",
+ trailing_separator: SeparatorTactic::Never,
+ separator_place: SeparatorPlace::Back,
+ shape: arm_shape,
+ ends_with_newline: true,
+ preserve_newline: true,
+ config: context.config,
+ };
- let pat_strs = try_opt!(
- pats.iter()
- .map(|p| p.rewrite(context, pat_shape))
- .collect::<Option<Vec<_>>>()
- );
+ write_list(&arms_vec, &fmt)
+}
- let all_simple = pat_strs.iter().all(|p| !p.contains('\n'));
- let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
- let mut tactic = definitive_tactic(&items, ListTactic::HorizontalVertical, pat_shape.width);
- if tactic == DefinitiveListTactic::Horizontal && all_simple {
- tactic = DefinitiveListTactic::Mixed;
- }
- let fmt = ListFormatting {
- tactic: tactic,
- separator: " |",
- trailing_separator: SeparatorTactic::Never,
- shape: pat_shape,
- ends_with_newline: false,
- config: context.config,
- };
- let pats_str = try_opt!(write_list(&items, &fmt));
+fn rewrite_match_arm(
+ context: &RewriteContext,
+ arm: &ast::Arm,
+ shape: Shape,
+ is_last: bool,
+) -> Option<String> {
+ let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
+ if contains_skip(&arm.attrs) {
+ let (_, body) = flatten_arm_body(context, &arm.body);
+ // `arm.span()` does not include trailing comma, add it manually.
+ return Some(format!(
+ "{}{}",
+ context.snippet(arm.span()),
+ arm_comma(context.config, body, is_last),
+ ));
+ }
+ (
+ mk_sp(
+ arm.attrs[arm.attrs.len() - 1].span.hi(),
+ arm.pats[0].span.lo(),
+ ),
+ try_opt!(arm.attrs.rewrite(context, shape)),
+ )
+ } else {
+ (mk_sp(arm.span().lo(), arm.span().lo()), String::new())
+ };
+ let pats_str = try_opt!(
+ rewrite_match_pattern(context, &arm.pats, &arm.guard, shape).and_then(|pats_str| {
+ combine_strs_with_missing_comments(
+ context,
+ &attrs_str,
+ &pats_str,
+ missing_span,
+ shape,
+ false,
+ )
+ })
+ );
+ rewrite_match_body(
+ context,
+ &arm.body,
+ &pats_str,
+ shape,
+ arm.guard.is_some(),
+ is_last,
+ )
+}
- let guard_shape = if pats_str.contains('\n') {
- shape.with_max_width(context.config)
- } else {
- shape
- };
+fn rewrite_match_pattern(
+ context: &RewriteContext,
+ pats: &[ptr::P<ast::Pat>],
+ guard: &Option<ptr::P<ast::Expr>>,
+ shape: Shape,
+) -> Option<String> {
+ // Patterns
+ // 5 = ` => {`
+ let pat_shape = try_opt!(shape.sub_width(5));
+
+ let pat_strs = try_opt!(
+ pats.iter()
+ .map(|p| p.rewrite(context, pat_shape))
+ .collect::<Option<Vec<_>>>()
+ );
- let guard_str = try_opt!(rewrite_guard(
- context,
- guard,
- guard_shape,
- trimmed_last_line_width(&pats_str),
- ));
+ let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
+ let tactic = definitive_tactic(
+ &items,
+ ListTactic::HorizontalVertical,
+ Separator::VerticalBar,
+ pat_shape.width,
+ );
+ let fmt = ListFormatting {
+ tactic: tactic,
+ separator: " |",
+ trailing_separator: SeparatorTactic::Never,
+ separator_place: context.config.match_pattern_separator_break_point(),
+ shape: pat_shape,
+ ends_with_newline: false,
+ preserve_newline: false,
+ config: context.config,
+ };
+ let pats_str = try_opt!(write_list(&items, &fmt));
- let pats_str = format!("{}{}", pats_str, guard_str);
+ // Guard
+ let guard_str = try_opt!(rewrite_guard(
+ context,
+ guard,
+ shape,
+ trimmed_last_line_width(&pats_str),
+ ));
- let (mut extend, body) = match body.node {
- ast::ExprKind::Block(ref block)
- if !is_unsafe_block(block) && is_simple_block(block, context.codemap) &&
- context.config.wrap_match_arms() => {
- if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
- (false, &**expr)
- } else {
- (false, &**body)
- }
+ Some(format!("{}{}", pats_str, guard_str))
+}
+
+// (extend, body)
+// @extend: true if the arm body can be put next to `=>`
+// @body: flattened body, if the body is block with a single expression
+fn flatten_arm_body<'a>(context: &'a RewriteContext, body: &'a ast::Expr) -> (bool, &'a ast::Expr) {
+ match body.node {
+ ast::ExprKind::Block(ref block)
+ if !is_unsafe_block(block) && is_simple_block(block, context.codemap) =>
+ {
+ if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
+ (
+ !context.config.multiline_match_arm_forces_block() &&
+ expr.can_be_overflowed(context, 1),
+ &**expr,
+ )
+ } else {
+ (false, &*body)
}
- ast::ExprKind::Call(_, ref args) => (args.len() == 1, &**body),
- ast::ExprKind::Closure(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Tup(..) => (
- true,
- &**body,
- ),
- _ => (false, &**body),
- };
- extend &= context.use_block_indent();
+ }
+ _ => (
+ !context.config.multiline_match_arm_forces_block() &&
+ body.can_be_overflowed(context, 1),
+ &*body,
+ ),
+ }
+}
- let comma = arm_comma(&context.config, body);
- let alt_block_sep =
- String::from("\n") + &shape.indent.block_only().to_string(context.config);
+fn rewrite_match_body(
+ context: &RewriteContext,
+ body: &ptr::P<ast::Expr>,
+ pats_str: &str,
+ shape: Shape,
+ has_guard: bool,
+ is_last: bool,
+) -> Option<String> {
+ let (extend, body) = flatten_arm_body(context, body);
- let pat_width = extra_offset(&pats_str, shape);
- // Let's try and get the arm body on the same line as the condition.
- // 4 = ` => `.len()
- if shape.width > pat_width + comma.len() + 4 {
- let arm_shape = shape
- .offset_left(pat_width + 4)
- .unwrap()
- .sub_width(comma.len())
- .unwrap();
- let rewrite = nop_block_collapse(
- format_expr(body, ExprType::Statement, context, arm_shape),
- arm_shape.width,
- );
- let is_block = if let ast::ExprKind::Block(..) = body.node {
- true
- } else {
- false
- };
+ let comma = arm_comma(context.config, body, is_last);
+ let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
+ let alt_block_sep = alt_block_sep.as_str();
+ let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block) = body.node {
+ (true, is_empty_block(block, context.codemap))
+ } else {
+ (false, false)
+ };
- match rewrite {
- Some(ref body_str)
- if (!body_str.contains('\n') && body_str.len() <= arm_shape.width) ||
- !context.config.wrap_match_arms() ||
- (extend && first_line_width(body_str) <= arm_shape.width) ||
- is_block => {
- let block_sep = match context.config.control_brace_style() {
- ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(),
- _ => " ",
- };
+ let combine_orig_body = |body_str: &str| {
+ let block_sep = match context.config.control_brace_style() {
+ ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep,
+ _ => " ",
+ };
- return Some(format!(
- "{}{} =>{}{}{}",
- attr_str.trim_left(),
- pats_str,
- block_sep,
- body_str,
- comma
- ));
- }
- _ => {}
- }
+ Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
+ };
+
+ let forbid_same_line = has_guard && pats_str.contains('\n') && !is_empty_block;
+ let next_line_indent = if is_block {
+ shape.indent
+ } else {
+ shape.indent.block_indent(context.config)
+ };
+ let combine_next_line_body = |body_str: &str| {
+ if is_block {
+ return Some(format!(
+ "{} =>\n{}{}",
+ pats_str,
+ next_line_indent.to_string(context.config),
+ body_str
+ ));
}
- // FIXME: we're doing a second rewrite of the expr; This may not be
- // necessary.
- let body_shape = try_opt!(shape.block_left(context.config.tab_spaces()));
- let next_line_body = try_opt!(nop_block_collapse(
- format_expr(body, ExprType::Statement, context, body_shape),
- body_shape.width,
- ));
- let indent_str = shape
- .indent
- .block_indent(context.config)
- .to_string(context.config);
+ let indent_str = shape.indent.to_string(context.config);
+ let nested_indent_str = next_line_indent.to_string(context.config);
let (body_prefix, body_suffix) = if context.config.wrap_match_arms() {
- if context.config.match_block_trailing_comma() {
- ("{", "},")
+ let comma = if context.config.match_block_trailing_comma() {
+ ","
} else {
- ("{", "}")
- }
+ ""
+ };
+ ("{", format!("\n{}}}{}", indent_str, comma))
} else {
- ("", ",")
+ ("", String::from(","))
};
-
let block_sep = match context.config.control_brace_style() {
- ControlBraceStyle::AlwaysNextLine => alt_block_sep + body_prefix + "\n",
+ ControlBraceStyle::AlwaysNextLine => format!("{}{}\n", alt_block_sep, body_prefix),
_ if body_prefix.is_empty() => "\n".to_owned(),
- _ => " ".to_owned() + body_prefix + "\n",
- };
+ _ if forbid_same_line => format!("{}{}\n", alt_block_sep, body_prefix),
+ _ => format!(" {}\n", body_prefix),
+ } + &nested_indent_str;
- if context.config.wrap_match_arms() {
- Some(format!(
- "{}{} =>{}{}{}\n{}{}",
- attr_str.trim_left(),
- pats_str,
- block_sep,
- indent_str,
- next_line_body,
- shape.indent.to_string(context.config),
- body_suffix
- ))
- } else {
- Some(format!(
- "{}{} =>{}{}{}{}",
- attr_str.trim_left(),
- pats_str,
- block_sep,
- indent_str,
- next_line_body,
- body_suffix
- ))
+ Some(format!(
+ "{} =>{}{}{}",
+ pats_str,
+ block_sep,
+ body_str,
+ body_suffix
+ ))
+ };
+
+ // Let's try and get the arm body on the same line as the condition.
+ // 4 = ` => `.len()
+ let orig_body_shape = shape
+ .offset_left(extra_offset(pats_str, shape) + 4)
+ .and_then(|shape| shape.sub_width(comma.len()));
+ let orig_body = if let Some(body_shape) = orig_body_shape {
+ let rewrite = nop_block_collapse(
+ format_expr(body, ExprType::Statement, context, body_shape),
+ body_shape.width,
+ );
+
+ match rewrite {
+ Some(ref body_str)
+ if !forbid_same_line &&
+ (is_block ||
+ (!body_str.contains('\n') && body_str.len() <= body_shape.width)) =>
+ {
+ return combine_orig_body(body_str);
+ }
+ _ => rewrite,
+ }
+ } else {
+ None
+ };
+ let orig_budget = orig_body_shape.map_or(0, |shape| shape.width);
+
+ // Try putting body on the next line and see if it looks better.
+ let next_line_body_shape = Shape::indented(next_line_indent, context.config);
+ let next_line_body = nop_block_collapse(
+ format_expr(body, ExprType::Statement, context, next_line_body_shape),
+ next_line_body_shape.width,
+ );
+ match (orig_body, next_line_body) {
+ (Some(ref orig_str), Some(ref next_line_str))
+ if forbid_same_line || prefer_next_line(orig_str, next_line_str) =>
+ {
+ combine_next_line_body(next_line_str)
}
+ (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
+ combine_orig_body(orig_str)
+ }
+ (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => {
+ combine_next_line_body(next_line_str)
+ }
+ (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str),
+ (None, None) => None,
+ (Some(ref orig_str), _) => combine_orig_body(orig_str),
}
}
if let Some(ref guard) = *guard {
// First try to fit the guard string on the same line as the pattern.
// 4 = ` if `, 5 = ` => {`
- if let Some(cond_shape) = shape
- .shrink_left(pattern_width + 4)
- .and_then(|s| s.sub_width(5))
- {
- if let Some(cond_str) = guard
- .rewrite(context, cond_shape)
- .and_then(|s| s.rewrite(context, cond_shape))
- {
- if !cond_str.contains('\n') {
+ let cond_shape = shape
+ .offset_left(pattern_width + 4)
+ .and_then(|s| s.sub_width(5));
+ if let Some(cond_shape) = cond_shape {
+ if let Some(cond_str) = guard.rewrite(context, cond_shape) {
+ if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() {
return Some(format!(" if {}", cond_str));
}
}
}
// Not enough space to put the guard after the pattern, try a newline.
- // 3 == `if `
- if let Some(cond_shape) = Shape::indented(
- shape.indent.block_indent(context.config) + 3,
- context.config,
- ).sub_width(3)
- {
+ // 3 = `if `, 5 = ` => {`
+ let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config)
+ .offset_left(3)
+ .and_then(|s| s.sub_width(5));
+ if let Some(cond_shape) = cond_shape {
if let Some(cond_str) = guard.rewrite(context, cond_shape) {
return Some(format!(
"\n{}if {}",
- shape
- .indent
- .block_indent(context.config)
- .to_string(context.config),
+ cond_shape.indent.to_string(context.config),
cond_str
));
}
// The expression won't fit on the current line, jump to next.
let nested_shape = shape
- .block()
.block_indent(context.config.tab_spaces())
.with_max_width(context.config);
let nested_indent_str = nested_shape.indent.to_string(context.config);
string: &str,
shape: Shape,
) -> bool {
- if context.codemap.lookup_char_pos(span.lo).col.0 != shape.indent.width() {
+ if context.codemap.lookup_char_pos(span.lo()).col.0 != shape.indent.width() {
return true;
}
if line.len() > shape.width {
return true;
}
- } else {
- if line.len() > shape.width + shape.indent.width() {
- return true;
- }
+ } else if line.len() > shape.width + shape.indent.width() {
+ return true;
}
}
};
rewrite_call_inner(
context,
- &callee,
+ callee,
&args.iter().map(|x| &**x).collect::<Vec<_>>(),
span,
shape,
} else {
1
};
- let used_width = extra_offset(&callee_str, shape);
+ let used_width = extra_offset(callee_str, shape);
let one_line_width = shape
.width
.checked_sub(used_width + 2 * paren_overhead)
).ok_or(Ordering::Greater)?;
let span_lo = context.codemap.span_after(span, "(");
- let args_span = mk_sp(span_lo, span.hi);
+ let args_span = mk_sp(span_lo, span.hi());
let (extendable, list_str) = rewrite_call_args(
context,
one_line_width,
args_max_width,
force_trailing_comma,
- ).or_else(|| if context.use_block_indent() {
- rewrite_call_args(
- context,
- args,
- args_span,
- Shape::indented(
- shape.block().indent.block_indent(context.config),
- context.config,
- ),
- 0,
- 0,
- force_trailing_comma,
- )
- } else {
- None
- })
- .ok_or(Ordering::Less)?;
+ ).ok_or(Ordering::Less)?;
if !context.use_block_indent() && need_block_indent(&list_str, nested_shape) && !extendable {
let mut new_context = context.clone();
}
let args_shape = shape
- .sub_width(last_line_width(&callee_str))
+ .sub_width(last_line_width(callee_str))
.ok_or(Ordering::Less)?;
Ok(format!(
"{}{}",
context.codemap,
args.iter(),
")",
- |item| item.span().lo,
- |item| item.span().hi,
+ |item| item.span().lo(),
+ |item| item.span().hi(),
|item| item.rewrite(context, shape),
- span.lo,
- span.hi,
+ span.lo(),
+ span.hi(),
+ true,
);
let mut item_vec: Vec<_> = items.collect();
} else {
context.config.trailing_comma()
},
+ separator_place: SeparatorPlace::Back,
shape: shape,
ends_with_newline: context.use_block_indent() && tactic == DefinitiveListTactic::Vertical,
+ preserve_newline: false,
config: context.config,
};
where
T: Rewrite + Spanned + ToExpr + 'a,
{
- let overflow_last = can_be_overflowed(&context, args);
+ let overflow_last = can_be_overflowed(context, args);
// Replace the last item with its first line to see if it fits with
// first arguments.
- let (orig_last, placeholder) = if overflow_last {
+ let placeholder = if overflow_last {
let mut context = context.clone();
if let Some(expr) = args[args.len() - 1].to_expr() {
- match expr.node {
- ast::ExprKind::MethodCall(..) => context.force_one_line_chain = true,
- _ => (),
+ if let ast::ExprKind::MethodCall(..) = expr.node {
+ context.force_one_line_chain = true;
}
}
- last_arg_shape(&context, &item_vec, shape, args_max_width)
- .map_or((None, None), |arg_shape| {
- rewrite_last_arg_with_overflow(
- &context,
- args,
- &mut item_vec[args.len() - 1],
- arg_shape,
- )
- })
+ last_arg_shape(&context, item_vec, shape, args_max_width).and_then(|arg_shape| {
+ rewrite_last_arg_with_overflow(&context, args, &mut item_vec[args.len() - 1], arg_shape)
+ })
} else {
- (None, None)
+ None
};
- let tactic = definitive_tactic(
+ let mut tactic = definitive_tactic(
&*item_vec,
ListTactic::LimitedHorizontalVertical(args_max_width),
+ Separator::Comma,
one_line_width,
);
(true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
item_vec[args.len() - 1].item = placeholder;
}
- (true, _, _) => {
- item_vec[args.len() - 1].item = orig_last;
+ _ if args.len() >= 1 => {
+ item_vec[args.len() - 1].item = args.last()
+ .and_then(|last_arg| last_arg.rewrite(context, shape));
+ tactic = definitive_tactic(
+ &*item_vec,
+ ListTactic::LimitedHorizontalVertical(args_max_width),
+ Separator::Comma,
+ one_line_width,
+ );
}
- (false, _, _) => {}
+ _ => (),
}
tactic
fn last_arg_shape(
context: &RewriteContext,
- items: &Vec<ListItem>,
+ items: &[ListItem],
shape: Shape,
args_max_width: usize,
) -> Option<Shape> {
let overhead = items.iter().rev().skip(1).fold(0, |acc, i| {
- acc + i.item.as_ref().map_or(0, |s| first_line_width(&s))
+ acc + i.item.as_ref().map_or(0, |s| first_line_width(s))
});
let max_width = min(args_max_width, shape.width);
let arg_indent = if context.use_block_indent() {
let body_shape = try_opt!(shape.offset_left(extra_offset));
// When overflowing the closure which consists of a single control flow expression,
// force to use block if its condition uses multi line.
- if rewrite_cond(context, body, body_shape)
- .map(|cond| cond.contains('\n'))
- .unwrap_or(false)
- {
+ let is_multi_lined_cond = rewrite_cond(context, body, body_shape)
+ .map(|cond| cond.contains('\n') || cond.len() > body_shape.width)
+ .unwrap_or(false);
+ if is_multi_lined_cond {
return rewrite_closure_with_block(context, body_shape, &prefix, body);
}
args: &[&T],
last_item: &mut ListItem,
shape: Shape,
-) -> (Option<String>, Option<String>)
+) -> Option<String>
where
T: Rewrite + Spanned + ToExpr + 'a,
{
} else {
last_arg.rewrite(context, shape)
};
- let orig_last = last_item.item.clone();
if let Some(rewrite) = rewrite {
let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
last_item.item = rewrite_first_line;
- (orig_last, Some(rewrite))
+ Some(rewrite)
} else {
- (orig_last, None)
+ None
}
}
) -> String {
if !context.use_block_indent() ||
(context.inside_macro && !args_str.contains('\n') &&
- args_str.len() + paren_overhead(context) <= shape.width) || is_extendable
+ args_str.len() + paren_overhead(context) <= shape.width) || is_extendable
{
- if context.config.spaces_within_parens() && args_str.len() > 0 {
+ if context.config.spaces_within_parens() && !args_str.is_empty() {
format!("( {} )", args_str)
} else {
format!("({})", args_str)
fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
debug!("rewrite_paren, shape: {:?}", shape);
- let paren_overhead = paren_overhead(context);
- let sub_shape = try_opt!(shape.sub_width(paren_overhead / 2)).visual_indent(paren_overhead / 2);
+ let total_paren_overhead = paren_overhead(context);
+ let paren_overhead = total_paren_overhead / 2;
+ let sub_shape = try_opt!(
+ shape
+ .offset_left(paren_overhead)
+ .and_then(|s| s.sub_width(paren_overhead))
+ );
- let paren_wrapper = |s: &str| if context.config.spaces_within_parens() && s.len() > 0 {
+ let paren_wrapper = |s: &str| if context.config.spaces_within_parens() && !s.is_empty() {
format!("( {} )", s)
} else {
format!("({})", s)
let subexpr_str = try_opt!(subexpr.rewrite(context, sub_shape));
debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
- if subexpr_str.contains('\n') {
+ if subexpr_str.contains('\n') ||
+ first_line_width(&subexpr_str) + total_paren_overhead <= shape.width
+ {
Some(paren_wrapper(&subexpr_str))
} else {
- if subexpr_str.len() + paren_overhead <= shape.width {
- Some(paren_wrapper(&subexpr_str))
- } else {
- let sub_shape = try_opt!(shape.offset_left(2));
- let subexpr_str = try_opt!(subexpr.rewrite(context, sub_shape));
- Some(paren_wrapper(&subexpr_str))
- }
+ None
}
}
let index_shape = try_opt!(index_shape.sub_width(rbr.len() + rhs_overhead));
let new_index_rw = index.rewrite(context, index_shape);
match (orig_index_rw, new_index_rw) {
- (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => {
- Some(format!(
- "{}\n{}{}{}{}",
- expr_str,
- indent.to_string(&context.config),
- lbr,
- new_index_str,
- rbr
- ))
- }
- (None, Some(ref new_index_str)) => {
- Some(format!(
- "{}\n{}{}{}{}",
- expr_str,
- indent.to_string(&context.config),
- lbr,
- new_index_str,
- rbr
- ))
- }
+ (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!(
+ "{}\n{}{}{}{}",
+ expr_str,
+ indent.to_string(context.config),
+ lbr,
+ new_index_str,
+ rbr
+ )),
+ (None, Some(ref new_index_str)) => Some(format!(
+ "{}\n{}{}{}{}",
+ expr_str,
+ indent.to_string(context.config),
+ lbr,
+ new_index_str,
+ rbr
+ )),
(Some(ref index_str), _) => Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr)),
_ => None,
}
path_shape,
));
- if fields.len() == 0 && base.is_none() {
+ if fields.is_empty() && base.is_none() {
return Some(format!("{} {{}}", path_str));
}
fields,
context,
shape,
- mk_sp(body_lo, span.hi),
+ mk_sp(body_lo, span.hi()),
one_line_width,
))
} else {
.chain(base.into_iter().map(StructLitField::Base));
let span_lo = |item: &StructLitField| match *item {
- StructLitField::Regular(field) => field.span().lo,
+ StructLitField::Regular(field) => field.span().lo(),
StructLitField::Base(expr) => {
- let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
- let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
+ let last_field_hi = fields.last().map_or(span.lo(), |field| field.span.hi());
+ let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo()));
let pos = snippet.find_uncommented("..").unwrap();
last_field_hi + BytePos(pos as u32)
}
};
let span_hi = |item: &StructLitField| match *item {
- StructLitField::Regular(field) => field.span().hi,
- StructLitField::Base(expr) => expr.span.hi,
+ StructLitField::Regular(field) => field.span().hi(),
+ StructLitField::Base(expr) => expr.span.hi(),
};
let rewrite = |item: &StructLitField| match *item {
StructLitField::Regular(field) => {
}
StructLitField::Base(expr) => {
// 2 = ..
- expr.rewrite(context, try_opt!(v_shape.shrink_left(2)))
+ expr.rewrite(context, try_opt!(v_shape.offset_left(2)))
.map(|s| format!("..{}", s))
}
};
span_hi,
rewrite,
body_lo,
- span.hi,
+ span.hi(),
+ false,
);
let item_vec = items.collect::<Vec<_>>();
) -> String {
if context.config.struct_lit_style() == IndentStyle::Block &&
(fields_str.contains('\n') ||
- context.config.struct_lit_multiline_style() == MultilineStyle::ForceMulti ||
- fields_str.len() > one_line_width)
+ context.config.struct_lit_multiline_style() == MultilineStyle::ForceMulti ||
+ fields_str.len() > one_line_width)
{
format!(
"\n{}{}\n{}",
"{}{}:\n{}{}",
attrs_str,
name,
- expr_offset.to_string(&context.config),
+ expr_offset.to_string(context.config),
s
)
})
context.codemap,
items,
")",
- |item| item.span().lo,
- |item| item.span().hi,
+ |item| item.span().lo(),
+ |item| item.span().hi(),
|item| item.rewrite(context, nested_shape),
list_lo,
- span.hi - BytePos(1),
+ span.hi() - BytePos(1),
+ false,
);
let item_vec: Vec<_> = items.collect();
let tactic = definitive_tactic(
&item_vec,
ListTactic::HorizontalVertical,
+ Separator::Comma,
nested_shape.width,
);
let fmt = ListFormatting {
tactic: tactic,
separator: ",",
trailing_separator: SeparatorTactic::Never,
+ separator_place: SeparatorPlace::Back,
shape: shape,
ends_with_newline: false,
+ preserve_newline: false,
config: context.config,
};
let list_str = try_opt!(write_list(&item_vec, &fmt));
- if context.config.spaces_within_parens() && list_str.len() > 0 {
+ if context.config.spaces_within_parens() && !list_str.is_empty() {
Some(format!("( {} )", list_str))
} else {
Some(format!("({})", list_str))
{
debug!("rewrite_tuple {:?}", shape);
if context.use_block_indent() {
- // We use the same rule as funcation call for rewriting tuple.
+ // We use the same rule as function calls for rewriting tuples.
let force_trailing_comma = if context.inside_macro {
span_ends_with_comma(context, span)
} else {
shape: Shape,
) -> Option<String> {
let lhs = lhs.into();
- let last_line_width = last_line_width(&lhs) -
- if lhs.contains('\n') {
- shape.indent.width()
- } else {
- 0
- };
+ let last_line_width = last_line_width(&lhs) - if lhs.contains('\n') {
+ shape.indent.width()
+ } else {
+ 0
+ };
// 1 = space between operator and rhs.
let orig_shape = try_opt!(shape.offset_left(last_line_width + 1));
let rhs = try_opt!(choose_rhs(
}
fn prefer_next_line(orig_rhs: &str, next_line_rhs: &str) -> bool {
-
fn count_line_breaks(src: &str) -> usize {
src.chars().filter(|&x| x == '\n').count()
}
false
}
}
+
+impl<'a> ToExpr for MacroArg {
+ fn to_expr(&self) -> Option<&ast::Expr> {
+ match self {
+ &MacroArg::Expr(ref expr) => Some(expr),
+ _ => None,
+ }
+ }
+
+ fn can_be_overflowed(&self, context: &RewriteContext, len: usize) -> bool {
+ match self {
+ &MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
+ &MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
+ &MacroArg::Pat(..) => false,
+ }
+ }
+}