1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use std::cmp::{Ordering, min};
12 use std::borrow::Borrow;
15 use std::iter::ExactSizeIterator;
18 use {Indent, Shape, Spanned};
19 use codemap::SpanUtils;
20 use rewrite::{Rewrite, RewriteContext};
21 use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
22 DefinitiveListTactic, definitive_tactic, ListItem, format_item_list};
23 use string::{StringFormat, rewrite_string};
24 use utils::{extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
25 semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr, stmt_expr};
26 use visitor::FmtVisitor;
27 use config::{Config, StructLitStyle, MultilineStyle, ControlBraceStyle, FnArgLayoutStyle};
28 use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
29 use types::{rewrite_path, PathContext};
30 use items::{span_lo_for_arg, span_hi_for_arg};
31 use chains::rewrite_chain;
32 use macros::{rewrite_macro, MacroPosition};
34 use syntax::{ast, ptr};
35 use syntax::codemap::{CodeMap, Span, BytePos, mk_sp};
36 use syntax::parse::classify;
38 impl Rewrite for ast::Expr {
39 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
40 format_expr(self, ExprType::SubExpression, context, shape)
50 fn format_expr(expr: &ast::Expr,
52 context: &RewriteContext,
55 let result = match expr.node {
56 ast::ExprKind::Array(ref expr_vec) => {
57 rewrite_array(expr_vec.iter().map(|e| &**e),
58 mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi),
62 ast::ExprKind::Lit(ref l) => {
64 ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
65 rewrite_string_lit(context, l.span, shape)
67 _ => wrap_str(context.snippet(expr.span), context.config.max_width, shape),
70 ast::ExprKind::Call(ref callee, ref args) => {
71 let inner_span = mk_sp(callee.span.hi, expr.span.hi);
72 rewrite_call(context, &**callee, args, inner_span, shape)
74 ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape),
75 ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => {
76 // FIXME: format comments between operands and operator
80 &format!(" {} ", context.snippet(op.span)),
85 ast::ExprKind::Unary(ref op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
86 ast::ExprKind::Struct(ref path, ref fields, ref base) => {
87 rewrite_struct_lit(context,
90 base.as_ref().map(|e| &**e),
94 ast::ExprKind::Tup(ref items) => {
95 rewrite_tuple(context, items.iter().map(|x| &**x), expr.span, shape)
97 ast::ExprKind::While(ref cond, ref block, label) => {
98 ControlFlow::new_while(None, cond, block, label, expr.span).rewrite(context, shape)
100 ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
101 ControlFlow::new_while(Some(pat), cond, block, label, expr.span).rewrite(context, shape)
103 ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
104 ControlFlow::new_for(pat, cond, block, label, expr.span).rewrite(context, shape)
106 ast::ExprKind::Loop(ref block, label) => {
107 ControlFlow::new_loop(block, label, expr.span).rewrite(context, shape)
109 ast::ExprKind::Block(ref block) => block.rewrite(context, shape),
110 ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
111 ControlFlow::new_if(cond,
114 else_block.as_ref().map(|e| &**e),
115 expr_type == ExprType::SubExpression,
118 .rewrite(context, shape)
120 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
121 ControlFlow::new_if(cond,
124 else_block.as_ref().map(|e| &**e),
125 expr_type == ExprType::SubExpression,
128 .rewrite(context, shape)
130 ast::ExprKind::Match(ref cond, ref arms) => {
131 rewrite_match(context, cond, arms, shape, expr.span)
133 ast::ExprKind::Path(ref qself, ref path) => {
134 rewrite_path(context, PathContext::Expr, qself.as_ref(), path, shape)
136 ast::ExprKind::Assign(ref lhs, ref rhs) => {
137 rewrite_assignment(context, lhs, rhs, None, shape)
139 ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
140 rewrite_assignment(context, lhs, rhs, Some(op), shape)
142 ast::ExprKind::Continue(ref opt_ident) => {
143 let id_str = match *opt_ident {
144 Some(ident) => format!(" {}", ident.node),
145 None => String::new(),
147 wrap_str(format!("continue{}", id_str),
148 context.config.max_width,
151 ast::ExprKind::Break(ref opt_ident, ref opt_expr) => {
152 let id_str = match *opt_ident {
153 Some(ident) => format!(" {}", ident.node),
154 None => String::new(),
157 if let Some(ref expr) = *opt_expr {
158 rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape)
160 wrap_str(format!("break{}", id_str), context.config.max_width, shape)
163 ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
164 rewrite_closure(capture, fn_decl, body, expr.span, context, shape)
166 ast::ExprKind::Try(..) |
167 ast::ExprKind::Field(..) |
168 ast::ExprKind::TupField(..) |
169 ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, shape),
170 ast::ExprKind::Mac(ref mac) => {
171 // Failure to rewrite a marco should not imply failure to
172 // rewrite the expression.
173 rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| {
174 wrap_str(context.snippet(expr.span), context.config.max_width, shape)
177 ast::ExprKind::Ret(None) => wrap_str("return".to_owned(), context.config.max_width, shape),
178 ast::ExprKind::Ret(Some(ref expr)) => {
179 rewrite_unary_prefix(context, "return ", &**expr, shape)
181 ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
182 ast::ExprKind::AddrOf(mutability, ref expr) => {
183 rewrite_expr_addrof(context, mutability, expr, shape)
185 ast::ExprKind::Cast(ref expr, ref ty) => {
186 rewrite_pair(&**expr, &**ty, "", " as ", "", context, shape)
188 ast::ExprKind::Type(ref expr, ref ty) => {
189 rewrite_pair(&**expr, &**ty, "", ": ", "", context, shape)
191 ast::ExprKind::Index(ref expr, ref index) => {
192 rewrite_index(&**expr, &**index, context, shape)
194 ast::ExprKind::Repeat(ref expr, ref repeats) => {
195 let (lbr, rbr) = if context.config.spaces_within_square_brackets {
200 rewrite_pair(&**expr, &**repeats, lbr, "; ", rbr, context, shape)
202 ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
203 let delim = match limits {
204 ast::RangeLimits::HalfOpen => "..",
205 ast::RangeLimits::Closed => "...",
208 match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
209 (Some(ref lhs), Some(ref rhs)) => {
210 let sp_delim = if context.config.spaces_around_ranges {
211 format!(" {} ", delim)
215 rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, shape)
217 (None, Some(ref rhs)) => {
218 let sp_delim = if context.config.spaces_around_ranges {
219 format!("{} ", delim)
223 rewrite_unary_prefix(context, &sp_delim, &**rhs, shape)
225 (Some(ref lhs), None) => {
226 let sp_delim = if context.config.spaces_around_ranges {
227 format!(" {}", delim)
231 rewrite_unary_suffix(context, &sp_delim, &**lhs, shape)
233 (None, None) => wrap_str(delim.into(), context.config.max_width, shape),
236 // We do not format these expressions yet, but they should still
237 // satisfy our width restrictions.
238 ast::ExprKind::InPlace(..) |
239 ast::ExprKind::InlineAsm(..) => {
240 wrap_str(context.snippet(expr.span), context.config.max_width, shape)
243 result.and_then(|res| recover_comment_removed(res, expr.span, context, shape))
246 pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
251 context: &RewriteContext,
257 // Get "full width" rhs and see if it fits on the current line. This
258 // usually works fairly well since it tends to place operands of
259 // operations with high precendence close together.
260 // Note that this is non-conservative, but its just to see if it's even
261 // worth trying to put everything on one line.
262 let rhs_shape = try_opt!(shape.sub_width(suffix.len()));
263 let rhs_result = rhs.rewrite(context, rhs_shape);
265 if let Some(rhs_result) = rhs_result {
266 // This is needed in case of line break not caused by a
267 // shortage of space, but by end-of-line comments, for example.
268 if !rhs_result.contains('\n') {
269 let lhs_shape = try_opt!(shape.sub_width(prefix.len() + infix.len()));
270 let lhs_result = lhs.rewrite(context, lhs_shape);
271 if let Some(lhs_result) = lhs_result {
272 let mut result = format!("{}{}{}", prefix, lhs_result, infix);
274 let remaining_width =
275 shape.width.checked_sub(last_line_width(&result)).unwrap_or(0);
277 if rhs_result.len() <= remaining_width {
278 result.push_str(&rhs_result);
279 result.push_str(suffix);
283 // Try rewriting the rhs into the remaining space.
284 let rhs_shape = shape.shrink_left(last_line_width(&result) + suffix.len());
285 if let Some(rhs_shape) = rhs_shape {
286 if let Some(rhs_result) = rhs.rewrite(context, rhs_shape) {
287 // FIXME this should always hold.
288 if rhs_result.len() <= remaining_width {
289 result.push_str(&rhs_result);
290 result.push_str(suffix);
299 // We have to use multiple lines.
301 // Re-evaluate the rhs because we have more space now:
302 let infix = infix.trim_right();
303 let lhs_budget = try_opt!(context.config.max_width.checked_sub(shape.used_width() +
306 let rhs_shape = try_opt!(shape.sub_width(suffix.len() + prefix.len()))
307 .visual_indent(prefix.len());
309 let rhs_result = try_opt!(rhs.rewrite(context, rhs_shape));
310 let lhs_result = try_opt!(lhs.rewrite(context, Shape { width: lhs_budget, ..shape }));
311 Some(format!("{}{}{}\n{}{}{}",
315 rhs_shape.indent.to_string(context.config),
320 pub fn rewrite_array<'a, I>(expr_iter: I,
322 context: &RewriteContext,
325 where I: Iterator<Item = &'a ast::Expr>
327 let bracket_size = if context.config.spaces_within_square_brackets {
333 let nested_shape = match context.config.array_layout {
334 FnArgLayoutStyle::Block |
335 FnArgLayoutStyle::BlockAlways => shape.block().block_indent(context.config.tab_spaces),
336 FnArgLayoutStyle::Visual => {
337 try_opt!(shape.visual_indent(bracket_size).sub_width(bracket_size * 2))
341 let items = itemize_list(context.codemap,
346 |item| item.rewrite(context, nested_shape),
349 .collect::<Vec<_>>();
351 if items.is_empty() {
352 if context.config.spaces_within_square_brackets {
353 return Some("[ ]".to_string());
355 return Some("[]".to_string());
359 let has_long_item = try_opt!(items.iter()
360 .map(|li| li.item.as_ref().map(|s| s.len() > 10))
362 |acc, x| acc.and_then(|y| x.map(|x| x || y))));
364 let tactic = match context.config.array_layout {
365 FnArgLayoutStyle::Block => {
366 // TODO wrong shape in one-line case
367 match shape.width.checked_sub(2 * bracket_size) {
368 Some(width) => definitive_tactic(&items, ListTactic::HorizontalVertical, width),
369 None => DefinitiveListTactic::Vertical,
372 FnArgLayoutStyle::BlockAlways => DefinitiveListTactic::Vertical,
373 FnArgLayoutStyle::Visual => {
374 if has_long_item || items.iter().any(ListItem::is_multiline) {
375 definitive_tactic(&items, ListTactic::HorizontalVertical, nested_shape.width)
377 DefinitiveListTactic::Mixed
382 let fmt = ListFormatting {
385 trailing_separator: SeparatorTactic::Never,
387 ends_with_newline: false,
388 config: context.config,
390 let list_str = try_opt!(write_list(&items, &fmt));
392 let result = if context.config.array_layout == FnArgLayoutStyle::Visual ||
393 tactic != DefinitiveListTactic::Vertical {
394 if context.config.spaces_within_square_brackets && list_str.len() > 0 {
395 format!("[ {} ]", list_str)
397 format!("[{}]", list_str)
400 format!("[\n{}{},\n{}]",
401 nested_shape.indent.to_string(context.config),
403 shape.block().indent.to_string(context.config))
409 // This functions is pretty messy because of the rules around closures and blocks:
410 // FIXME - the below is probably no longer true in full.
411 // * if there is a return type, then there must be braces,
412 // * given a closure with braces, whether that is parsed to give an inner block
413 // or not depends on if there is a return type and if there are statements
415 // * if the first expression in the body ends with a block (i.e., is a
416 // statement without needing a semi-colon), then adding or removing braces
417 // can change whether it is treated as an expression or statement.
418 fn rewrite_closure(capture: ast::CaptureBy,
419 fn_decl: &ast::FnDecl,
422 context: &RewriteContext,
425 let mover = if capture == ast::CaptureBy::Value {
430 // 4 = "|| {".len(), which is overconservative when the closure consists of
431 // a single expression.
432 let nested_shape = try_opt!(try_opt!(shape.shrink_left(mover.len())).sub_width(4));
435 let argument_offset = nested_shape.indent + 1;
436 let arg_shape = try_opt!(nested_shape.shrink_left(1)).visual_indent(0);
437 let ret_str = try_opt!(fn_decl.output.rewrite(context, arg_shape));
439 let arg_items = itemize_list(context.codemap,
440 fn_decl.inputs.iter(),
442 |arg| span_lo_for_arg(arg),
443 |arg| span_hi_for_arg(arg),
444 |arg| arg.rewrite(context, arg_shape),
445 context.codemap.span_after(span, "|"),
447 let item_vec = arg_items.collect::<Vec<_>>();
448 // 1 = space between arguments and return type.
449 let horizontal_budget = nested_shape.width.checked_sub(ret_str.len() + 1).unwrap_or(0);
450 let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
451 let arg_shape = match tactic {
452 DefinitiveListTactic::Horizontal => try_opt!(arg_shape.sub_width(ret_str.len() + 1)),
456 let fmt = ListFormatting {
459 trailing_separator: SeparatorTactic::Never,
461 ends_with_newline: false,
462 config: context.config,
464 let list_str = try_opt!(write_list(&item_vec, &fmt));
465 let mut prefix = format!("{}|{}|", mover, list_str);
467 if !ret_str.is_empty() {
468 if prefix.contains('\n') {
470 prefix.push_str(&argument_offset.to_string(context.config));
474 prefix.push_str(&ret_str);
477 // 1 = space between `|...|` and body.
478 let extra_offset = extra_offset(&prefix, shape) + 1;
479 let body_shape = try_opt!(shape.sub_width(extra_offset)).add_offset(extra_offset);
481 if let ast::ExprKind::Block(ref block) = body.node {
482 // The body of the closure is an empty block.
483 if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) {
484 return Some(format!("{} {{}}", prefix));
487 // Figure out if the block is necessary.
488 let needs_block = block.rules != ast::BlockCheckMode::Default || block.stmts.len() > 1 ||
489 block_contains_comment(block, context.codemap) ||
490 prefix.contains('\n');
492 if ret_str.is_empty() && !needs_block {
493 // lock.stmts.len() == 1
494 if let Some(ref expr) = stmt_expr(&block.stmts[0]) {
495 if let Some(rw) = rewrite_closure_expr(expr, &prefix, context, body_shape) {
502 // We need braces, but we might still prefer a one-liner.
503 let stmt = &block.stmts[0];
504 // 4 = braces and spaces.
505 let mut rewrite = stmt.rewrite(context, try_opt!(body_shape.sub_width(4)));
507 // Checks if rewrite succeeded and fits on a single line.
508 rewrite = and_one_line(rewrite);
510 if let Some(rewrite) = rewrite {
511 return Some(format!("{} {{ {} }}", prefix, rewrite));
515 // Either we require a block, or tried without and failed.
516 return rewrite_closure_block(&block, prefix, context, body_shape);
519 if let Some(rw) = rewrite_closure_expr(body, &prefix, context, body_shape) {
523 // The closure originally had a non-block expression, but we can't fit on
524 // one line, so we'll insert a block.
525 let block = ast::Block {
526 stmts: vec![ast::Stmt {
527 id: ast::NodeId::new(0),
528 node: ast::StmtKind::Expr(ptr::P(body.clone())),
531 id: ast::NodeId::new(0),
532 rules: ast::BlockCheckMode::Default,
535 return rewrite_closure_block(&block, prefix, context, body_shape);
537 fn rewrite_closure_expr(expr: &ast::Expr,
539 context: &RewriteContext,
542 let mut rewrite = expr.rewrite(context, shape);
543 if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
544 rewrite = and_one_line(rewrite);
546 rewrite.map(|rw| format!("{} {}", prefix, rw))
549 fn rewrite_closure_block(block: &ast::Block,
551 context: &RewriteContext,
554 // Start with visual indent, then fall back to block indent if the
556 let rewrite = try_opt!(block.rewrite(&context, shape));
558 let block_threshold = context.config.closure_block_indent_threshold;
559 if block_threshold < 0 || rewrite.matches('\n').count() <= block_threshold as usize {
560 return Some(format!("{} {}", prefix, rewrite));
563 // The body of the closure is big enough to be block indented, that
564 // means we must re-format.
565 let block_shape = shape.block();
566 let rewrite = try_opt!(block.rewrite(&context, block_shape));
567 Some(format!("{} {}", prefix, rewrite))
571 fn and_one_line(x: Option<String>) -> Option<String> {
572 x.and_then(|x| if x.contains('\n') { None } else { Some(x) })
575 fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
576 debug!("nop_block_collapse {:?} {}", block_str, budget);
577 block_str.map(|block_str| if block_str.starts_with('{') && budget >= 2 &&
578 (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() ==
579 block_str.len() - 2) {
586 impl Rewrite for ast::Block {
587 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
588 // shape.width is used only for the single line case: either the empty block `{}`,
589 // or an unsafe expression `unsafe { e }`.
591 if self.stmts.is_empty() && !block_contains_comment(self, context.codemap) &&
593 return Some("{}".to_owned());
596 // If a block contains only a single-line comment, then leave it on one line.
597 let user_str = context.snippet(self.span);
598 let user_str = user_str.trim();
599 if user_str.starts_with('{') && user_str.ends_with('}') {
600 let comment_str = user_str[1..user_str.len() - 1].trim();
601 if self.stmts.is_empty() && !comment_str.contains('\n') &&
602 !comment_str.starts_with("//") &&
603 comment_str.len() + 4 <= shape.width {
604 return Some(format!("{{ {} }}", comment_str));
608 let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
609 visitor.block_indent = shape.indent;
611 let prefix = match self.rules {
612 ast::BlockCheckMode::Unsafe(..) => {
613 let snippet = context.snippet(self.span);
614 let open_pos = try_opt!(snippet.find_uncommented("{"));
615 visitor.last_pos = self.span.lo + BytePos(open_pos as u32);
617 // Extract comment between unsafe and block start.
618 let trimmed = &snippet[6..open_pos].trim();
620 let prefix = if !trimmed.is_empty() {
621 // 9 = "unsafe {".len(), 7 = "unsafe ".len()
622 let budget = try_opt!(shape.width.checked_sub(9));
623 format!("unsafe {} ",
624 try_opt!(rewrite_comment(trimmed,
626 Shape::legacy(budget, shape.indent + 7),
632 if is_simple_block(self, context.codemap) && prefix.len() < shape.width {
634 self.stmts[0].rewrite(context,
635 Shape::legacy(shape.width - prefix.len(),
637 let expr_str = try_opt!(expr_str);
638 let result = format!("{}{{ {} }}", prefix, expr_str);
639 if result.len() <= shape.width && !result.contains('\n') {
646 ast::BlockCheckMode::Default => {
647 visitor.last_pos = self.span.lo;
653 visitor.visit_block(self);
655 Some(format!("{}{}", prefix, visitor.buffer))
659 impl Rewrite for ast::Stmt {
660 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
661 let result = match self.node {
662 ast::StmtKind::Local(ref local) => {
663 local.rewrite(context,
664 Shape::legacy(context.config.max_width, shape.indent))
666 ast::StmtKind::Expr(ref ex) |
667 ast::StmtKind::Semi(ref ex) => {
668 let suffix = if semicolon_for_stmt(self) { ";" } else { "" };
672 ast::StmtKind::Expr(_) => ExprType::SubExpression,
673 ast::StmtKind::Semi(_) => ExprType::Statement,
677 try_opt!(shape.sub_width(suffix.len())))
680 ast::StmtKind::Mac(..) |
681 ast::StmtKind::Item(..) => None,
683 result.and_then(|res| recover_comment_removed(res, self.span, context, shape))
687 // Abstraction over control flow expressions
689 struct ControlFlow<'a> {
690 cond: Option<&'a ast::Expr>,
691 block: &'a ast::Block,
692 else_block: Option<&'a ast::Expr>,
693 label: Option<ast::SpannedIdent>,
694 pat: Option<&'a ast::Pat>,
698 allow_single_line: bool,
699 // True if this is an `if` expression in an `else if` :-( hacky
704 impl<'a> ControlFlow<'a> {
705 fn new_if(cond: &'a ast::Expr,
706 pat: Option<&'a ast::Pat>,
707 block: &'a ast::Block,
708 else_block: Option<&'a ast::Expr>,
709 allow_single_line: bool,
716 else_block: else_block,
725 allow_single_line: allow_single_line,
726 nested_if: nested_if,
731 fn new_loop(block: &'a ast::Block,
732 label: Option<ast::SpannedIdent>,
744 allow_single_line: false,
750 fn new_while(pat: Option<&'a ast::Pat>,
752 block: &'a ast::Block,
753 label: Option<ast::SpannedIdent>,
768 allow_single_line: false,
774 fn new_for(pat: &'a ast::Pat,
776 block: &'a ast::Block,
777 label: Option<ast::SpannedIdent>,
789 allow_single_line: false,
795 fn rewrite_single_line(&self,
797 context: &RewriteContext,
800 assert!(self.allow_single_line);
801 let else_block = try_opt!(self.else_block);
802 let fixed_cost = self.keyword.len() + " { } else { }".len();
804 if let ast::ExprKind::Block(ref else_node) = else_block.node {
805 if !is_simple_block(self.block, context.codemap) ||
806 !is_simple_block(else_node, context.codemap) ||
807 pat_expr_str.contains('\n') {
811 let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
812 let expr = &self.block.stmts[0];
813 let if_str = try_opt!(expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
815 let new_width = try_opt!(new_width.checked_sub(if_str.len()));
816 let else_expr = &else_node.stmts[0];
817 let else_str = try_opt!(else_expr.rewrite(context,
818 Shape::legacy(new_width, Indent::empty())));
820 if if_str.contains('\n') || else_str.contains('\n') {
824 let result = format!("{} {} {{ {} }} else {{ {} }}",
830 if result.len() <= width {
839 impl<'a> Rewrite for ControlFlow<'a> {
840 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
841 debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
842 let constr_shape = if self.nested_if {
843 // We are part of an if-elseif-else chain. Our constraints are tightened.
844 // 7 = "} else " .len()
845 try_opt!(shape.shrink_left(7))
850 let label_string = rewrite_label(self.label);
851 // 1 = space after keyword.
852 let add_offset = self.keyword.len() + label_string.len() + 1;
854 let pat_expr_string = match self.cond {
856 let mut cond_shape = try_opt!(constr_shape.shrink_left(add_offset));
857 if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine {
859 cond_shape = try_opt!(cond_shape.sub_width(2));
862 try_opt!(rewrite_pat_expr(context,
869 None => String::new(),
872 // Try to format if-else on single line.
873 if self.allow_single_line && context.config.single_line_if_else_max_width > 0 {
874 let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
876 if trial.is_some() &&
877 trial.as_ref().unwrap().len() <= context.config.single_line_if_else_max_width {
882 let used_width = if pat_expr_string.contains('\n') {
883 last_line_width(&pat_expr_string)
885 // 2 = spaces after keyword and condition.
886 label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
889 let block_width = shape.width.checked_sub(used_width).unwrap_or(0);
890 // This is used only for the empty block case: `{}`. So, we use 1 if we know
891 // we should avoid the single line case.
892 let block_width = if self.else_block.is_some() || self.nested_if {
898 // TODO this .block() - not what we want if we are actually visually indented
899 let block_shape = Shape { width: block_width, ..shape };
900 let block_str = try_opt!(self.block.rewrite(context, block_shape));
902 let cond_span = if let Some(cond) = self.cond {
905 mk_sp(self.block.span.lo, self.block.span.lo)
908 // for event in event
909 let between_kwd_cond =
910 mk_sp(context.codemap.span_after(self.span, self.keyword.trim()),
911 self.pat.map_or(cond_span.lo, |p| if self.matcher.is_empty() {
914 context.codemap.span_before(self.span, self.matcher.trim())
917 let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
919 let after_cond_comment =
920 extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape);
922 let alt_block_sep = String::from("\n") +
923 &shape.indent.block_only().to_string(context.config);
924 let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
926 } else if context.config.control_brace_style ==
927 ControlBraceStyle::AlwaysNextLine {
928 alt_block_sep.as_str()
933 let mut result = format!("{}{}{}{}{}{}",
936 between_kwd_cond_comment.as_ref().map_or(if
937 pat_expr_string.is_empty() || pat_expr_string.starts_with('\n') {
944 after_cond_comment.as_ref().map_or(block_sep, |s| &**s),
947 if let Some(else_block) = self.else_block {
948 // Since this is an else block, we should not indent for the assignment preceding
949 // the original if, so set shape.offset to 0.
950 let shape = Shape { offset: 0, ..shape };
951 let mut last_in_chain = false;
952 let rewrite = match else_block.node {
953 // If the else expression is another if-else expression, prevent it
954 // from being formatted on a single line.
955 // Note how we're passing the original shape, as the
956 // cost of "else" should not cascade.
957 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
958 ControlFlow::new_if(cond,
961 next_else_block.as_ref().map(|e| &**e),
964 mk_sp(else_block.span.lo, self.span.hi))
965 .rewrite(context, shape.visual_indent(0))
967 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
968 ControlFlow::new_if(cond,
971 next_else_block.as_ref().map(|e| &**e),
974 mk_sp(else_block.span.lo, self.span.hi))
975 .rewrite(context, shape.visual_indent(0))
978 last_in_chain = true;
979 // When rewriting a block, the width is only used for single line
980 // blocks, passing 1 lets us avoid that.
981 let else_shape = Shape { width: min(1, shape.width), ..shape };
982 else_block.rewrite(context, else_shape)
986 let between_kwd_else_block =
987 mk_sp(self.block.span.hi,
988 context.codemap.span_before(mk_sp(self.block.span.hi, else_block.span.lo),
990 let between_kwd_else_block_comment =
991 extract_comment(between_kwd_else_block, context, shape);
993 let after_else = mk_sp(context.codemap.span_after(mk_sp(self.block.span.hi,
997 let after_else_comment = extract_comment(after_else, context, shape);
999 let between_sep = match context.config.control_brace_style {
1000 ControlBraceStyle::AlwaysNextLine |
1001 ControlBraceStyle::ClosingNextLine => &*alt_block_sep,
1002 ControlBraceStyle::AlwaysSameLine => " ",
1004 let after_sep = match context.config.control_brace_style {
1005 ControlBraceStyle::AlwaysNextLine if last_in_chain => &*alt_block_sep,
1008 try_opt!(write!(&mut result,
1010 between_kwd_else_block_comment.as_ref().map_or(between_sep, |s| &**s),
1011 after_else_comment.as_ref().map_or(after_sep, |s| &**s))
1013 result.push_str(&try_opt!(rewrite));
1020 fn rewrite_label(label: Option<ast::SpannedIdent>) -> String {
1022 Some(ident) => format!("{}: ", ident.node),
1023 None => "".to_owned(),
1027 fn extract_comment(span: Span, context: &RewriteContext, shape: Shape) -> Option<String> {
1028 let comment_str = context.snippet(span);
1029 if contains_comment(&comment_str) {
1030 let comment = try_opt!(rewrite_comment(comment_str.trim(), false, shape, context.config));
1031 Some(format!("\n{indent}{}\n{indent}",
1033 indent = shape.indent.to_string(context.config)))
1039 fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool {
1040 let snippet = codemap.span_to_snippet(block.span).unwrap();
1041 contains_comment(&snippet)
1044 // Checks that a block contains no statements, an expression and no comments.
1045 // FIXME: incorrectly returns false when comment is contained completely within
1047 pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1048 (block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) &&
1049 !block_contains_comment(block, codemap))
1052 /// Checks whether a block contains at most one statement or expression, and no comments.
1053 pub fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool {
1054 block.stmts.len() <= 1 && !block_contains_comment(block, codemap)
1057 /// Checks whether a block contains no statements, expressions, or comments.
1058 pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1059 block.stmts.is_empty() && !block_contains_comment(block, codemap)
1062 pub fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
1064 ast::StmtKind::Expr(..) => true,
1069 fn is_unsafe_block(block: &ast::Block) -> bool {
1070 if let ast::BlockCheckMode::Unsafe(..) = block.rules {
1077 // inter-match-arm-comment-rules:
1078 // - all comments following a match arm before the start of the next arm
1079 // are about the second arm
1080 fn rewrite_match_arm_comment(context: &RewriteContext,
1083 arm_indent_str: &str)
1085 // The leading "," is not part of the arm-comment
1086 let missed_str = match missed_str.find_uncommented(",") {
1087 Some(n) => &missed_str[n + 1..],
1088 None => &missed_str[..],
1091 let mut result = String::new();
1092 // any text not preceeded by a newline is pushed unmodified to the block
1093 let first_brk = missed_str.find(|c: char| c == '\n').unwrap_or(0);
1094 result.push_str(&missed_str[..first_brk]);
1095 let missed_str = &missed_str[first_brk..]; // If missed_str had one newline, it starts with it
1097 let first = missed_str.find(|c: char| !c.is_whitespace()).unwrap_or(missed_str.len());
1098 if missed_str[..first].chars().filter(|c| c == &'\n').count() >= 2 {
1099 // Excessive vertical whitespace before comment should be preserved
1100 // TODO handle vertical whitespace better
1103 let missed_str = missed_str[first..].trim();
1104 if !missed_str.is_empty() {
1105 let comment = try_opt!(rewrite_comment(&missed_str, false, shape, context.config));
1107 result.push_str(arm_indent_str);
1108 result.push_str(&comment);
1114 fn rewrite_match(context: &RewriteContext,
1120 if arms.is_empty() {
1125 let cond_budget = try_opt!(shape.width.checked_sub(8));
1126 let cond_str = try_opt!(cond.rewrite(context, Shape::legacy(cond_budget, shape.indent + 6)));
1127 let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
1128 let block_sep = match context.config.control_brace_style {
1129 ControlBraceStyle::AlwaysSameLine => " ",
1130 _ => alt_block_sep.as_str(),
1132 let mut result = format!("match {}{}{{", cond_str, block_sep);
1134 let arm_shape = shape.block_indent(context.config.tab_spaces);
1135 let arm_indent_str = arm_shape.indent.to_string(context.config);
1137 let open_brace_pos = context.codemap.span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])),
1140 for (i, arm) in arms.iter().enumerate() {
1141 // Make sure we get the stuff between arms.
1142 let missed_str = if i == 0 {
1143 context.snippet(mk_sp(open_brace_pos, arm_start_pos(arm)))
1145 context.snippet(mk_sp(arm_end_pos(&arms[i - 1]), arm_start_pos(arm)))
1148 try_opt!(rewrite_match_arm_comment(context, &missed_str, arm_shape, &arm_indent_str));
1149 result.push_str(&comment);
1151 result.push_str(&arm_indent_str);
1153 let arm_str = arm.rewrite(&context,
1155 width: context.config.max_width - arm_shape.indent.width(),
1158 if let Some(ref arm_str) = arm_str {
1159 result.push_str(arm_str);
1161 // We couldn't format the arm, just reproduce the source.
1162 let snippet = context.snippet(mk_sp(arm_start_pos(arm), arm_end_pos(arm)));
1163 result.push_str(&snippet);
1164 result.push_str(arm_comma(context.config, &arm.body));
1167 // BytePos(1) = closing match brace.
1168 let last_span = mk_sp(arm_end_pos(&arms[arms.len() - 1]), span.hi - BytePos(1));
1169 let last_comment = context.snippet(last_span);
1171 try_opt!(rewrite_match_arm_comment(context, &last_comment, arm_shape, &arm_indent_str));
1172 result.push_str(&comment);
1174 result.push_str(&shape.indent.to_string(context.config));
1179 fn arm_start_pos(arm: &ast::Arm) -> BytePos {
1180 let &ast::Arm { ref attrs, ref pats, .. } = arm;
1181 if !attrs.is_empty() {
1182 return attrs[0].span.lo;
1188 fn arm_end_pos(arm: &ast::Arm) -> BytePos {
1192 fn arm_comma(config: &Config, body: &ast::Expr) -> &'static str {
1193 if config.match_block_trailing_comma {
1195 } else if let ast::ExprKind::Block(ref block) = body.node {
1196 if let ast::BlockCheckMode::Default = block.rules {
1207 impl Rewrite for ast::Arm {
1208 fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
1209 debug!("Arm::rewrite {:?} {:?}", self, shape);
1210 let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self;
1212 // FIXME this is all a bit grotty, would be nice to abstract out the
1213 // treatment of attributes.
1214 let attr_str = if !attrs.is_empty() {
1215 // We only use this visitor for the attributes, should we use it for
1217 let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
1218 attr_visitor.block_indent = shape.indent.block_only();
1219 attr_visitor.last_pos = attrs[0].span.lo;
1220 if attr_visitor.visit_attrs(attrs) {
1221 // Attributes included a skip instruction.
1224 attr_visitor.format_missing(pats[0].span.lo);
1225 attr_visitor.buffer.to_string()
1232 let pat_shape = try_opt!(shape.sub_width(5));
1234 let pat_strs = try_opt!(pats.iter()
1235 .map(|p| p.rewrite(context, pat_shape))
1236 .collect::<Option<Vec<_>>>());
1238 let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
1239 let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
1240 let fmt = ListFormatting {
1241 tactic: if all_simple {
1242 DefinitiveListTactic::Mixed
1244 DefinitiveListTactic::Vertical
1247 trailing_separator: SeparatorTactic::Never,
1249 ends_with_newline: false,
1250 config: context.config,
1252 let pats_str = try_opt!(write_list(items, &fmt));
1254 let guard_shape = if pats_str.contains('\n') {
1255 Shape { width: context.config.max_width - shape.indent.width(), ..shape }
1260 let guard_str = try_opt!(rewrite_guard(context,
1263 trimmed_last_line_width(&pats_str)));
1265 let pats_str = format!("{}{}", pats_str, guard_str);
1267 let body = match body.node {
1268 ast::ExprKind::Block(ref block) if !is_unsafe_block(block) &&
1269 is_simple_block(block, context.codemap) &&
1270 context.config.wrap_match_arms => {
1271 if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
1280 let comma = arm_comma(&context.config, body);
1281 let alt_block_sep = String::from("\n") +
1282 &shape.indent.block_only().to_string(context.config);
1284 let pat_width = extra_offset(&pats_str, shape);
1285 // Let's try and get the arm body on the same line as the condition.
1287 if shape.width > pat_width + comma.len() + 4 {
1288 let arm_shape = shape.shrink_left(pat_width + 4)
1290 .sub_width(comma.len())
1293 let rewrite = nop_block_collapse(body.rewrite(context, arm_shape), arm_shape.width);
1294 let is_block = if let ast::ExprKind::Block(..) = body.node {
1301 Some(ref body_str) if !body_str.contains('\n') || !context.config.wrap_match_arms ||
1303 let block_sep = match context.config.control_brace_style {
1304 ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(),
1308 return Some(format!("{}{} =>{}{}{}",
1309 attr_str.trim_left(),
1319 // FIXME: we're doing a second rewrite of the expr; This may not be
1321 let body_shape = try_opt!(shape.sub_width(context.config.tab_spaces))
1322 .block_indent(context.config.tab_spaces);
1323 let next_line_body = try_opt!(nop_block_collapse(body.rewrite(context, body_shape),
1325 let indent_str = shape.indent.block_indent(context.config).to_string(context.config);
1326 let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
1327 if context.config.match_block_trailing_comma {
1337 let block_sep = match context.config.control_brace_style {
1338 ControlBraceStyle::AlwaysNextLine => alt_block_sep + body_prefix + "\n",
1339 _ if body_prefix.is_empty() => "\n".to_owned(),
1340 _ => " ".to_owned() + body_prefix + "\n",
1343 if context.config.wrap_match_arms {
1344 Some(format!("{}{} =>{}{}{}\n{}{}",
1345 attr_str.trim_left(),
1350 shape.indent.to_string(context.config),
1353 Some(format!("{}{} =>{}{}{}{}",
1354 attr_str.trim_left(),
1364 // A pattern is simple if it is very short or it is short-ish and just a path.
1365 // E.g. `Foo::Bar` is simple, but `Foo(..)` is not.
1366 fn pat_is_simple(pat_str: &str) -> bool {
1367 pat_str.len() <= 16 ||
1368 (pat_str.len() <= 24 && pat_str.chars().all(|c| c.is_alphabetic() || c == ':'))
1371 // The `if ...` guard on a match arm.
1372 fn rewrite_guard(context: &RewriteContext,
1373 guard: &Option<ptr::P<ast::Expr>>,
1375 // The amount of space used up on this line for the pattern in
1376 // the arm (excludes offset).
1377 pattern_width: usize)
1379 if let Some(ref guard) = *guard {
1380 // First try to fit the guard string on the same line as the pattern.
1381 // 4 = ` if `, 5 = ` => {`
1382 let overhead = pattern_width + 4 + 5;
1383 if overhead < shape.width {
1384 let cond_shape = shape.shrink_left(pattern_width + 4)
1388 let cond_str = guard.rewrite(context, cond_shape);
1389 if let Some(cond_str) = cond_str {
1390 return Some(format!(" if {}", cond_str));
1394 // Not enough space to put the guard after the pattern, try a newline.
1395 let overhead = shape.indent.block_indent(context.config).width() + 4 + 5;
1396 if overhead < shape.width {
1397 let cond_str = guard.rewrite(context,
1398 Shape::legacy(shape.width - overhead,
1400 shape.indent.block_indent(context.config) +
1402 if let Some(cond_str) = cond_str {
1403 return Some(format!("\n{}if {}",
1405 .block_indent(context.config)
1406 .to_string(context.config),
1417 fn rewrite_pat_expr(context: &RewriteContext,
1418 pat: Option<&ast::Pat>,
1421 // Connecting piece between pattern and expression,
1422 // *without* trailing space.
1426 debug!("rewrite_pat_expr {:?} {:?}", shape, pat);
1427 let mut result = match pat {
1429 let matcher = if matcher.is_empty() {
1432 format!("{} ", matcher)
1434 let pat_shape = try_opt!(try_opt!(shape.shrink_left(matcher.len()))
1435 .sub_width(connector.len()));
1436 let pat_string = try_opt!(pat.rewrite(context, pat_shape));
1437 format!("{}{}{}", matcher, pat_string, connector)
1439 None => String::new(),
1442 // Consider only the last line of the pat string.
1443 let extra_offset = extra_offset(&result, shape);
1445 // The expression may (partially) fit on the current line.
1446 if shape.width > extra_offset + 1 {
1447 let spacer = if pat.is_some() { " " } else { "" };
1449 let expr_shape = try_opt!(shape.sub_width(extra_offset + spacer.len()))
1450 .add_offset(extra_offset + spacer.len());
1451 let expr_rewrite = expr.rewrite(context, expr_shape);
1453 if let Some(expr_string) = expr_rewrite {
1454 let pat_simple = pat.and_then(|p| {
1456 Shape::legacy(context.config.max_width,
1459 .map(|s| pat_is_simple(&s));
1461 if pat.is_none() || pat_simple.unwrap_or(false) || !expr_string.contains('\n') {
1462 result.push_str(spacer);
1463 result.push_str(&expr_string);
1464 return Some(result);
1469 let nested_indent = shape.indent.block_only().block_indent(context.config);
1471 // The expression won't fit on the current line, jump to next.
1473 result.push_str(&nested_indent.to_string(context.config));
1475 let expr_rewrite = expr.rewrite(&context,
1476 Shape::legacy(try_opt!(context.config
1478 .checked_sub(nested_indent.width())),
1480 result.push_str(&try_opt!(expr_rewrite));
1485 fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Option<String> {
1486 let string_lit = context.snippet(span);
1488 if !context.config.format_strings && !context.config.force_format_strings {
1489 return Some(string_lit);
1492 if !context.config.force_format_strings &&
1493 !string_requires_rewrite(context, span, &string_lit, shape) {
1494 return Some(string_lit);
1497 let fmt = StringFormat {
1504 config: context.config,
1507 // Remove the quote characters.
1508 let str_lit = &string_lit[1..string_lit.len() - 1];
1510 rewrite_string(str_lit, &fmt)
1513 fn string_requires_rewrite(context: &RewriteContext,
1519 .lookup_char_pos(span.lo)
1521 .0 != shape.indent.width() {
1525 for (i, line) in string.lines().enumerate() {
1527 if line.len() > shape.width {
1531 if line.len() > shape.width + shape.indent.width() {
1540 pub fn rewrite_call<R>(context: &RewriteContext,
1542 args: &[ptr::P<ast::Expr>],
1549 |callee_max_width| rewrite_call_inner(context, callee, callee_max_width, args, span, shape);
1552 let max_width = try_opt!(shape.width.checked_sub(2));
1553 binary_search(1, max_width, closure)
1556 fn rewrite_call_inner<R>(context: &RewriteContext,
1558 max_callee_width: usize,
1559 args: &[ptr::P<ast::Expr>],
1562 -> Result<String, Ordering>
1565 let callee = callee.borrow();
1566 // FIXME using byte lens instead of char lens (and probably all over the
1568 let callee_str = match callee.rewrite(context, Shape { width: max_callee_width, ..shape }) {
1570 if !string.contains('\n') && string.len() > max_callee_width {
1571 panic!("{:?} {}", string, max_callee_width);
1576 None => return Err(Ordering::Greater),
1579 let span_lo = context.codemap.span_after(span, "(");
1580 let span = mk_sp(span_lo, span.hi);
1582 let used_width = extra_offset(&callee_str, shape);
1584 let remaining_width = match shape.width.checked_sub(used_width + 2) {
1586 None => return Err(Ordering::Greater),
1589 let nested_shape = shape.visual_indent(used_width + 1);
1590 let arg_count = args.len();
1592 let items = itemize_list(context.codemap,
1595 |item| item.span.lo,
1596 |item| item.span.hi,
1598 item.rewrite(context,
1599 Shape { width: remaining_width, ..nested_shape })
1603 let mut item_vec: Vec<_> = items.collect();
1605 // Try letting the last argument overflow to the next line with block
1606 // indentation. If its first line fits on one line with the other arguments,
1607 // we format the function arguments horizontally.
1608 let overflow_last = match args.last().map(|x| &x.node) {
1609 Some(&ast::ExprKind::Closure(..)) |
1610 Some(&ast::ExprKind::Block(..)) if arg_count > 1 => true,
1614 let mut orig_last = None;
1615 let mut placeholder = None;
1617 // Replace the last item with its first line to see if it fits with
1620 let nested_shape = Shape {
1621 width: remaining_width,
1622 indent: nested_shape.indent.block_only(),
1625 let rewrite = args.last().unwrap().rewrite(context, nested_shape);
1627 if let Some(rewrite) = rewrite {
1628 let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
1629 placeholder = Some(rewrite);
1631 swap(&mut item_vec[arg_count - 1].item, &mut orig_last);
1632 item_vec[arg_count - 1].item = rewrite_first_line;
1637 definitive_tactic(&item_vec,
1638 ListTactic::LimitedHorizontalVertical(context.config.fn_call_width),
1641 // Replace the stub with the full overflowing last argument if the rewrite
1642 // succeeded and its first line fits with the other arguments.
1643 match (overflow_last, tactic, placeholder) {
1644 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
1645 item_vec[arg_count - 1].item = placeholder;
1648 item_vec[arg_count - 1].item = orig_last;
1653 let fmt = ListFormatting {
1656 trailing_separator: SeparatorTactic::Never,
1657 shape: nested_shape,
1658 ends_with_newline: false,
1659 config: context.config,
1662 let list_str = match write_list(&item_vec, &fmt) {
1664 None => return Err(Ordering::Less),
1667 Ok(if context.config.spaces_within_parens && list_str.len() > 0 {
1668 format!("{}( {} )", callee_str, list_str)
1670 format!("{}({})", callee_str, list_str)
1674 fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
1675 debug!("rewrite_paren, shape: {:?}", shape);
1676 // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
1677 // paren on the same line as the subexpr.
1678 let sub_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
1679 let subexpr_str = subexpr.rewrite(context, sub_shape);
1680 debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
1682 subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
1683 format!("( {} )", s)
1689 fn rewrite_index(expr: &ast::Expr,
1691 context: &RewriteContext,
1694 let expr_str = try_opt!(expr.rewrite(context, shape));
1696 let (lbr, rbr) = if context.config.spaces_within_square_brackets {
1702 let budget = shape.width.checked_sub(expr_str.len() + lbr.len() + rbr.len()).unwrap_or(0);
1703 let index_str = index.rewrite(context, Shape::legacy(budget, shape.indent));
1704 if let Some(index_str) = index_str {
1705 return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr));
1708 let indent = shape.indent.block_indent(&context.config);
1709 let indent = indent.to_string(&context.config);
1710 // FIXME this is not right, since we don't take into account that shape.width
1711 // might be reduced from max_width by something on the right.
1712 let budget = try_opt!(context.config.max_width.checked_sub(indent.len() + lbr.len() +
1714 let index_str = try_opt!(index.rewrite(context, Shape::legacy(budget, shape.indent)));
1715 Some(format!("{}\n{}{}{}{}", expr_str, indent, lbr, index_str, rbr))
1718 fn rewrite_struct_lit<'a>(context: &RewriteContext,
1720 fields: &'a [ast::Field],
1721 base: Option<&'a ast::Expr>,
1725 debug!("rewrite_struct_lit: shape {:?}", shape);
1727 enum StructLitField<'a> {
1728 Regular(&'a ast::Field),
1729 Base(&'a ast::Expr),
1733 let path_shape = try_opt!(shape.sub_width(2));
1734 let path_str = try_opt!(rewrite_path(context, PathContext::Expr, None, path, path_shape));
1736 // Foo { a: Foo } - indent is +3, width is -5.
1737 let h_shape = shape.sub_width(path_str.len() + 5);
1738 let v_shape = match context.config.struct_lit_style {
1739 StructLitStyle::Visual => {
1740 try_opt!(try_opt!(shape.shrink_left(path_str.len() + 3)).sub_width(2))
1742 StructLitStyle::Block => {
1743 let shape = shape.block_indent(context.config.tab_spaces);
1745 width: try_opt!(context.config.max_width.checked_sub(shape.indent.width())),
1751 let field_iter = fields.into_iter()
1752 .map(StructLitField::Regular)
1753 .chain(base.into_iter().map(StructLitField::Base));
1755 let items = itemize_list(context.codemap,
1758 |item| match *item {
1759 StructLitField::Regular(field) => field.span.lo,
1760 StructLitField::Base(expr) => {
1761 let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
1762 let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
1763 let pos = snippet.find_uncommented("..").unwrap();
1764 last_field_hi + BytePos(pos as u32)
1767 |item| match *item {
1768 StructLitField::Regular(field) => field.span.hi,
1769 StructLitField::Base(expr) => expr.span.hi,
1773 StructLitField::Regular(field) => {
1774 // The 1 taken from the v_budget is for the comma.
1775 rewrite_field(context, field, try_opt!(v_shape.sub_width(1)))
1777 StructLitField::Base(expr) => {
1779 expr.rewrite(context, try_opt!(v_shape.shrink_left(2))).map(|s| format!("..{}", s))
1783 context.codemap.span_after(span, "{"),
1785 let item_vec = items.collect::<Vec<_>>();
1787 let tactic = if let Some(h_shape) = h_shape {
1788 let mut prelim_tactic = match (context.config.struct_lit_style, fields.len()) {
1789 (StructLitStyle::Visual, 1) => ListTactic::HorizontalVertical,
1790 _ => context.config.struct_lit_multiline_style.to_list_tactic(),
1793 if prelim_tactic == ListTactic::HorizontalVertical && fields.len() > 1 {
1794 prelim_tactic = ListTactic::LimitedHorizontalVertical(context.config.struct_lit_width);
1797 definitive_tactic(&item_vec, prelim_tactic, h_shape.width)
1799 DefinitiveListTactic::Vertical
1802 let nested_shape = match tactic {
1803 DefinitiveListTactic::Horizontal => h_shape.unwrap(),
1807 let ends_with_newline = context.config.struct_lit_style != StructLitStyle::Visual &&
1808 tactic == DefinitiveListTactic::Vertical;
1810 let fmt = ListFormatting {
1813 trailing_separator: if base.is_some() {
1814 SeparatorTactic::Never
1816 context.config.trailing_comma
1818 shape: nested_shape,
1819 ends_with_newline: ends_with_newline,
1820 config: context.config,
1822 let fields_str = try_opt!(write_list(&item_vec, &fmt));
1825 if fields_str.is_empty() {
1826 return Some(format!("{} {{}}", path_str));
1829 // One liner or visual indent.
1830 if context.config.struct_lit_style == StructLitStyle::Visual ||
1831 (context.config.struct_lit_multiline_style != MultilineStyle::ForceMulti &&
1832 !fields_str.contains('\n') &&
1833 fields_str.len() <= h_shape.map(|s| s.width).unwrap_or(0)) {
1834 return Some(format!("{} {{ {} }}", path_str, fields_str));
1838 let inner_indent = v_shape.indent.to_string(context.config);
1839 let outer_indent = shape.indent.to_string(context.config);
1840 Some(format!("{} {{\n{}{}\n{}}}",
1845 // FIXME if context.config.struct_lit_style == Visual, but we run out
1846 // of space, we should fall back to BlockIndent.
1849 pub fn type_annotation_separator(config: &Config) -> &str {
1850 match (config.space_before_type_annotation, config.space_after_type_annotation_colon) {
1851 (true, true) => " : ",
1852 (true, false) => " :",
1853 (false, true) => ": ",
1854 (false, false) => ":",
1858 fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
1859 let name = &field.ident.node.to_string();
1860 if field.is_shorthand {
1861 Some(name.to_string())
1863 let separator = type_annotation_separator(context.config);
1864 let overhead = name.len() + separator.len();
1865 let mut expr_shape = try_opt!(shape.sub_width(overhead));
1866 expr_shape.offset += overhead;
1867 let expr = field.expr.rewrite(context, expr_shape);
1870 Some(e) => Some(format!("{}{}{}", name, separator, e)),
1872 let expr_offset = shape.indent.block_indent(context.config);
1873 let expr = field.expr.rewrite(context,
1874 Shape::legacy(try_opt!(context.config
1876 .checked_sub(expr_offset.width())),
1878 expr.map(|s| format!("{}:\n{}{}", name, expr_offset.to_string(&context.config), s))
1884 pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
1889 where I: ExactSizeIterator,
1890 <I as Iterator>::Item: Deref,
1891 <I::Item as Deref>::Target: Rewrite + Spanned + 'a
1893 debug!("rewrite_tuple {:?}", shape);
1894 // In case of length 1, need a trailing comma
1895 if items.len() == 1 {
1897 let nested_shape = try_opt!(shape.sub_width(3)).visual_indent(1);
1900 .rewrite(context, nested_shape)
1901 .map(|s| if context.config.spaces_within_parens {
1902 format!("( {}, )", s)
1908 let list_lo = context.codemap.span_after(span, "(");
1909 let nested_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
1910 let items = itemize_list(context.codemap,
1913 |item| item.span().lo,
1914 |item| item.span().hi,
1915 |item| item.rewrite(context, nested_shape),
1917 span.hi - BytePos(1));
1918 let list_str = try_opt!(format_item_list(items, nested_shape, context.config));
1920 if context.config.spaces_within_parens && list_str.len() > 0 {
1921 Some(format!("( {} )", list_str))
1923 Some(format!("({})", list_str))
1927 pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
1932 let shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
1933 rewrite.rewrite(context, shape).map(|r| format!("{}{}", prefix, r))
1936 // FIXME: this is probably not correct for multi-line Rewrites. we should
1937 // subtract suffix.len() from the last line budget, not the first!
1938 pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
1943 rewrite.rewrite(context, try_opt!(shape.sub_width(suffix.len()))).map(|mut r| {
1949 fn rewrite_unary_op(context: &RewriteContext,
1954 // For some reason, an UnOp is not spanned like BinOp!
1955 let operator_str = match *op {
1956 ast::UnOp::Deref => "*",
1957 ast::UnOp::Not => "!",
1958 ast::UnOp::Neg => "-",
1960 rewrite_unary_prefix(context, operator_str, expr, shape)
1963 fn rewrite_assignment(context: &RewriteContext,
1966 op: Option<&ast::BinOp>,
1969 let operator_str = match op {
1970 Some(op) => context.snippet(op.span),
1971 None => "=".to_owned(),
1974 // 1 = space between lhs and operator.
1975 let lhs_shape = try_opt!(shape.sub_width(operator_str.len() + 1));
1976 let lhs_str = format!("{} {}",
1977 try_opt!(lhs.rewrite(context, lhs_shape)),
1980 rewrite_assign_rhs(context, lhs_str, rhs, shape)
1983 // The left hand side must contain everything up to, and including, the
1984 // assignment operator.
1985 pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
1990 let mut result = lhs.into();
1991 let last_line_width = last_line_width(&result) -
1992 if result.contains('\n') {
1993 shape.indent.width()
1997 // 1 = space between operator and rhs.
1998 let max_width = try_opt!(shape.width.checked_sub(last_line_width + 1));
1999 let rhs = ex.rewrite(context,
2000 Shape::offset(max_width,
2001 shape.indent.block_only(),
2002 shape.indent.alignment + last_line_width + 1));
2004 fn count_line_breaks(src: &str) -> usize {
2005 src.chars().filter(|&x| x == '\n').count()
2009 Some(ref new_str) if count_line_breaks(new_str) < 2 => {
2011 result.push_str(new_str);
2014 // Expression did not fit on the same line as the identifier or is
2015 // at least three lines big. Try splitting the line and see
2016 // if that works better.
2017 let new_offset = shape.indent.block_indent(context.config);
2018 let max_width = try_opt!((shape.width + shape.indent.width())
2019 .checked_sub(new_offset.width()));
2020 let new_rhs = ex.rewrite(context, Shape::legacy(max_width, new_offset));
2023 match (rhs, new_rhs) {
2024 (Some(ref orig_rhs), Some(ref replacement_rhs))
2025 if count_line_breaks(orig_rhs) >
2026 count_line_breaks(replacement_rhs) + 1 => {
2027 result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2028 result.push_str(replacement_rhs);
2030 (None, Some(ref final_rhs)) => {
2031 result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
2032 result.push_str(final_rhs);
2034 (None, None) => return None,
2035 (Some(ref orig_rhs), _) => {
2037 result.push_str(orig_rhs);
2046 fn rewrite_expr_addrof(context: &RewriteContext,
2047 mutability: ast::Mutability,
2051 let operator_str = match mutability {
2052 ast::Mutability::Immutable => "&",
2053 ast::Mutability::Mutable => "&mut ",
2055 rewrite_unary_prefix(context, operator_str, expr, shape)