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;
12 use std::borrow::Borrow;
15 use std::iter::ExactSizeIterator;
18 use {Indent, 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, ElseIfBraceStyle, ControlBraceStyle};
28 use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
29 use types::rewrite_path;
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, width: usize, offset: Indent) -> Option<String> {
40 format_expr(self, ExprType::SubExpression, context, width, offset)
50 fn format_expr(expr: &ast::Expr,
52 context: &RewriteContext,
56 let result = match expr.node {
57 ast::ExprKind::Vec(ref expr_vec) => {
58 rewrite_array(expr_vec.iter().map(|e| &**e),
59 mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi),
64 ast::ExprKind::Lit(ref l) => {
66 ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
67 Some(rewrite_string_lit(context, l.span, width, offset))
70 wrap_str(context.snippet(expr.span),
71 context.config.max_width,
77 ast::ExprKind::Call(ref callee, ref args) => {
78 let inner_span = mk_sp(callee.span.hi, expr.span.hi);
79 rewrite_call(context, &**callee, args, inner_span, width, offset)
81 ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, width, offset),
82 ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => {
83 // FIXME: format comments between operands and operator
87 &format!(" {} ", context.snippet(op.span)),
93 ast::ExprKind::Unary(ref op, ref subexpr) => {
94 rewrite_unary_op(context, op, subexpr, width, offset)
96 ast::ExprKind::Struct(ref path, ref fields, ref base) => {
97 rewrite_struct_lit(context,
100 base.as_ref().map(|e| &**e),
105 ast::ExprKind::Tup(ref items) => {
106 rewrite_tuple(context,
107 items.iter().map(|x| &**x),
112 ast::ExprKind::While(ref cond, ref block, label) => {
113 Loop::new_while(None, cond, block, label).rewrite(context, width, offset)
115 ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
116 Loop::new_while(Some(pat), cond, block, label).rewrite(context, width, offset)
118 ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
119 Loop::new_for(pat, cond, block, label).rewrite(context, width, offset)
121 ast::ExprKind::Loop(ref block, label) => {
122 Loop::new_loop(block, label).rewrite(context, width, offset)
124 ast::ExprKind::Block(ref block) => block.rewrite(context, width, offset),
125 ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
126 rewrite_if_else(context,
130 else_block.as_ref().map(|e| &**e),
137 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
138 rewrite_if_else(context,
142 else_block.as_ref().map(|e| &**e),
149 ast::ExprKind::Match(ref cond, ref arms) => {
150 rewrite_match(context, cond, arms, width, offset, expr.span)
152 ast::ExprKind::Path(ref qself, ref path) => {
153 rewrite_path(context, true, qself.as_ref(), path, width, offset)
155 ast::ExprKind::Assign(ref lhs, ref rhs) => {
156 rewrite_assignment(context, lhs, rhs, None, width, offset)
158 ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
159 rewrite_assignment(context, lhs, rhs, Some(op), width, offset)
161 ast::ExprKind::Continue(ref opt_ident) => {
162 let id_str = match *opt_ident {
163 Some(ident) => format!(" {}", ident.node),
164 None => String::new(),
166 wrap_str(format!("continue{}", id_str),
167 context.config.max_width,
171 ast::ExprKind::Break(ref opt_ident, ref opt_expr) => {
172 let id_str = match *opt_ident {
173 Some(ident) => format!(" {}", ident.node),
174 None => String::new(),
177 if let Some(ref expr) = *opt_expr {
178 rewrite_unary_prefix(context,
179 &format!("break{} ", id_str),
184 wrap_str(format!("break{}", id_str),
185 context.config.max_width,
190 ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
191 rewrite_closure(capture, fn_decl, body, expr.span, context, width, offset)
193 ast::ExprKind::Try(..) |
194 ast::ExprKind::Field(..) |
195 ast::ExprKind::TupField(..) |
196 ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, width, offset),
197 ast::ExprKind::Mac(ref mac) => {
198 // Failure to rewrite a marco should not imply failure to
199 // rewrite the expression.
200 rewrite_macro(mac, None, context, width, offset, MacroPosition::Expression)
202 wrap_str(context.snippet(expr.span),
203 context.config.max_width,
208 ast::ExprKind::Ret(None) => {
209 wrap_str("return".to_owned(), context.config.max_width, width, offset)
211 ast::ExprKind::Ret(Some(ref expr)) => {
212 rewrite_unary_prefix(context, "return ", &**expr, width, offset)
214 ast::ExprKind::Box(ref expr) => {
215 rewrite_unary_prefix(context, "box ", &**expr, width, offset)
217 ast::ExprKind::AddrOf(mutability, ref expr) => {
218 rewrite_expr_addrof(context, mutability, expr, width, offset)
220 ast::ExprKind::Cast(ref expr, ref ty) => {
221 rewrite_pair(&**expr, &**ty, "", " as ", "", context, width, offset)
223 ast::ExprKind::Type(ref expr, ref ty) => {
224 rewrite_pair(&**expr, &**ty, "", ": ", "", context, width, offset)
226 ast::ExprKind::Index(ref expr, ref index) => {
227 rewrite_index(&**expr, &**index, context, width, offset)
229 ast::ExprKind::Repeat(ref expr, ref repeats) => {
230 let (lbr, rbr) = if context.config.spaces_within_square_brackets {
235 rewrite_pair(&**expr, &**repeats, lbr, "; ", rbr, context, width, offset)
237 ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
238 let delim = match limits {
239 ast::RangeLimits::HalfOpen => "..",
240 ast::RangeLimits::Closed => "...",
243 match (lhs.as_ref().map(|x| &**x), rhs.as_ref().map(|x| &**x)) {
244 (Some(ref lhs), Some(ref rhs)) => {
245 let sp_delim = if context.config.spaces_around_ranges {
246 format!(" {} ", delim)
250 rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, width, offset)
252 (None, Some(ref rhs)) => {
253 let sp_delim = if context.config.spaces_around_ranges {
254 format!("{} ", delim)
258 rewrite_unary_prefix(context, &sp_delim, &**rhs, width, offset)
260 (Some(ref lhs), None) => {
261 let sp_delim = if context.config.spaces_around_ranges {
262 format!(" {}", delim)
266 rewrite_unary_suffix(context, &sp_delim, &**lhs, width, offset)
268 (None, None) => wrap_str(delim.into(), context.config.max_width, width, offset),
271 // We do not format these expressions yet, but they should still
272 // satisfy our width restrictions.
273 ast::ExprKind::InPlace(..) |
274 ast::ExprKind::InlineAsm(..) => {
275 wrap_str(context.snippet(expr.span),
276 context.config.max_width,
281 result.and_then(|res| recover_comment_removed(res, expr.span, context, width, offset))
284 pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
289 context: &RewriteContext,
296 let lhs_budget = try_opt!(width.checked_sub(prefix.len() + infix.len()));
297 let rhs_budget = try_opt!(width.checked_sub(suffix.len()));
299 // Get "full width" rhs and see if it fits on the current line. This
300 // usually works fairly well since it tends to place operands of
301 // operations with high precendence close together.
302 // Note that this is non-conservative, but its just to see if it's even
303 // worth trying to put everything on one line.
304 let rhs_result = rhs.rewrite(context, rhs_budget, offset);
306 if let Some(rhs_result) = rhs_result {
307 // This is needed in case of line break not caused by a
308 // shortage of space, but by end-of-line comments, for example.
309 if !rhs_result.contains('\n') {
310 let lhs_result = lhs.rewrite(context, lhs_budget, offset);
311 if let Some(lhs_result) = lhs_result {
312 let mut result = format!("{}{}{}", prefix, lhs_result, infix);
314 let remaining_width = width.checked_sub(last_line_width(&result)).unwrap_or(0);
316 if rhs_result.len() <= remaining_width {
317 result.push_str(&rhs_result);
318 result.push_str(suffix);
322 // Try rewriting the rhs into the remaining space.
323 let rhs_budget = try_opt!(remaining_width.checked_sub(suffix.len()));
324 if let Some(rhs_result) = rhs.rewrite(context, rhs_budget, offset + result.len()) {
325 if rhs_result.len() <= remaining_width {
326 result.push_str(&rhs_result);
327 result.push_str(suffix);
335 // We have to use multiple lines.
337 // Re-evaluate the rhs because we have more space now:
338 let infix = infix.trim_right();
340 try_opt!(context.config.max_width.checked_sub(offset.width() + prefix.len() + infix.len()));
341 let rhs_budget = try_opt!(rhs_budget.checked_sub(prefix.len()));
342 let rhs_offset = offset + prefix.len();
344 let rhs_result = try_opt!(rhs.rewrite(context, rhs_budget, rhs_offset));
345 let lhs_result = try_opt!(lhs.rewrite(context, lhs_budget, offset));
346 Some(format!("{}{}{}\n{}{}{}",
350 rhs_offset.to_string(context.config),
355 pub fn rewrite_array<'a, I>(expr_iter: I,
357 context: &RewriteContext,
361 where I: Iterator<Item = &'a ast::Expr>
363 let bracket_size = if context.config.spaces_within_square_brackets {
368 let offset = offset + bracket_size;
369 let inner_context = &RewriteContext { block_indent: offset, ..*context };
370 let max_item_width = try_opt!(width.checked_sub(bracket_size * 2));
371 let items = itemize_list(context.codemap,
376 |item| item.rewrite(inner_context, max_item_width, offset),
379 .collect::<Vec<_>>();
381 let has_long_item = try_opt!(items.iter()
382 .map(|li| li.item.as_ref().map(|s| s.len() > 10))
383 .fold(Some(false), |acc, x| acc.and_then(|y| x.map(|x| x || y))));
385 let tactic = if has_long_item || items.iter().any(ListItem::is_multiline) {
386 definitive_tactic(&items, ListTactic::HorizontalVertical, max_item_width)
388 DefinitiveListTactic::Mixed
391 let fmt = ListFormatting {
394 trailing_separator: SeparatorTactic::Never,
396 width: max_item_width,
397 ends_with_newline: false,
398 config: context.config,
400 let list_str = try_opt!(write_list(&items, &fmt));
402 Some(if context.config.spaces_within_square_brackets && list_str.len() > 0 {
403 format!("[ {} ]", list_str)
405 format!("[{}]", list_str)
409 // This functions is pretty messy because of the rules around closures and blocks:
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,
426 let mover = if capture == ast::CaptureBy::Value {
431 let offset = offset + mover.len();
433 // 4 = "|| {".len(), which is overconservative when the closure consists of
434 // a single expression.
435 let budget = try_opt!(width.checked_sub(4 + mover.len()));
437 let argument_offset = offset + 1;
438 let ret_str = try_opt!(fn_decl.output.rewrite(context, budget, argument_offset));
440 // 1 = space between arguments and return type.
441 let horizontal_budget = budget.checked_sub(ret_str.len() + 1).unwrap_or(0);
443 let arg_items = itemize_list(context.codemap,
444 fn_decl.inputs.iter(),
446 |arg| span_lo_for_arg(arg),
447 |arg| span_hi_for_arg(arg),
448 |arg| arg.rewrite(context, budget, argument_offset),
449 context.codemap.span_after(span, "|"),
451 let item_vec = arg_items.collect::<Vec<_>>();
452 let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
453 let budget = match tactic {
454 DefinitiveListTactic::Horizontal => horizontal_budget,
458 let fmt = ListFormatting {
461 trailing_separator: SeparatorTactic::Never,
462 indent: argument_offset,
464 ends_with_newline: false,
465 config: context.config,
467 let list_str = try_opt!(write_list(&item_vec, &fmt));
468 let mut prefix = format!("{}|{}|", mover, list_str);
470 if !ret_str.is_empty() {
471 if prefix.contains('\n') {
473 prefix.push_str(&argument_offset.to_string(context.config));
477 prefix.push_str(&ret_str);
480 // 1 = space between `|...|` and body.
481 let extra_offset = extra_offset(&prefix, offset) + 1;
482 let budget = try_opt!(width.checked_sub(extra_offset));
483 let total_offset = offset + extra_offset;
485 if let ast::ExprKind::Block(ref block) = body.node {
486 // The body of the closure is a block.
487 if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) {
488 return Some(format!("{} {{}}", prefix));
491 // Figure out if the block is necessary.
492 let needs_block = block.rules != ast::BlockCheckMode::Default || block.stmts.len() > 1 ||
493 block_contains_comment(block, context.codemap) ||
494 prefix.contains('\n');
496 if ret_str.is_empty() && !needs_block {
497 // lock.stmts.len() == 1
498 if let Some(ref expr) = stmt_expr(&block.stmts[0]) {
499 if let Some(rw) = rewrite_closure_expr(expr,
510 // We need braces, but we might still prefer a one-liner.
511 let stmt = &block.stmts[0];
512 // 4 = braces and spaces.
513 let mut rewrite = stmt.rewrite(context, try_opt!(budget.checked_sub(4)), total_offset);
515 // Checks if rewrite succeeded and fits on a single line.
516 rewrite = and_one_line(rewrite);
518 if let Some(rewrite) = rewrite {
519 return Some(format!("{} {{ {} }}", prefix, rewrite));
523 // Either we require a block, or tried without and failed.
524 return rewrite_closure_block(&block, prefix, context, budget);
527 if let Some(rw) = rewrite_closure_expr(body, &prefix, context, budget, total_offset) {
531 // The closure originally had a non-block expression, but we can't fit on
532 // one line, so we'll insert a block.
533 let block = ast::Block {
534 stmts: vec![ast::Stmt {
535 id: ast::NodeId::new(0),
536 node: ast::StmtKind::Expr(ptr::P(body.clone())),
539 id: ast::NodeId::new(0),
540 rules: ast::BlockCheckMode::Default,
543 return rewrite_closure_block(&block, prefix, context, budget);
545 fn rewrite_closure_expr(expr: &ast::Expr,
547 context: &RewriteContext,
551 let mut rewrite = expr.rewrite(context, budget, offset);
552 if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
553 rewrite = and_one_line(rewrite);
555 rewrite.map(|rw| format!("{} {}", prefix, rw))
558 fn rewrite_closure_block(block: &ast::Block,
560 context: &RewriteContext,
563 // Start with visual indent, then fall back to block indent if the
565 let rewrite = try_opt!(block.rewrite(&context, budget, Indent::empty()));
567 let block_threshold = context.config.closure_block_indent_threshold;
568 if block_threshold < 0 || rewrite.matches('\n').count() <= block_threshold as usize {
569 return Some(format!("{} {}", prefix, rewrite));
572 // The body of the closure is big enough to be block indented, that
573 // means we must re-format.
574 let mut context = context.clone();
575 context.block_indent.alignment = 0;
576 let rewrite = try_opt!(block.rewrite(&context, budget, Indent::empty()));
577 Some(format!("{} {}", prefix, rewrite))
581 fn and_one_line(x: Option<String>) -> Option<String> {
582 x.and_then(|x| if x.contains('\n') { None } else { Some(x) })
585 fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
586 block_str.map(|block_str| if block_str.starts_with('{') && budget >= 2 &&
587 (block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() ==
588 block_str.len() - 2) {
595 impl Rewrite for ast::Block {
596 fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
597 // width is used only for the single line case: either the empty block `{}`,
598 // or an unsafe expression `unsafe { e }`.
600 let user_str = context.snippet(self.span);
601 if user_str == "{}" && width >= 2 {
602 return Some(user_str);
605 let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
606 visitor.block_indent = context.block_indent;
608 let prefix = match self.rules {
609 ast::BlockCheckMode::Unsafe(..) => {
610 let snippet = context.snippet(self.span);
611 let open_pos = try_opt!(snippet.find_uncommented("{"));
612 visitor.last_pos = self.span.lo + BytePos(open_pos as u32);
614 // Extract comment between unsafe and block start.
615 let trimmed = &snippet[6..open_pos].trim();
617 let prefix = if !trimmed.is_empty() {
618 // 9 = "unsafe {".len(), 7 = "unsafe ".len()
619 let budget = try_opt!(width.checked_sub(9));
620 format!("unsafe {} ",
621 try_opt!(rewrite_comment(trimmed,
630 if is_simple_block(self, context.codemap) && prefix.len() < width {
631 let expr_str = self.stmts[0].rewrite(context, width - prefix.len(), offset);
632 let expr_str = try_opt!(expr_str);
633 let result = format!("{}{{ {} }}", prefix, expr_str);
634 if result.len() <= width && !result.contains('\n') {
641 ast::BlockCheckMode::Default => {
642 visitor.last_pos = self.span.lo;
648 visitor.visit_block(self);
650 Some(format!("{}{}", prefix, visitor.buffer))
654 impl Rewrite for ast::Stmt {
655 fn rewrite(&self, context: &RewriteContext, _width: usize, offset: Indent) -> Option<String> {
656 let result = match self.node {
657 ast::StmtKind::Local(ref local) => {
658 local.rewrite(context, context.config.max_width, offset)
660 ast::StmtKind::Expr(ref ex) |
661 ast::StmtKind::Semi(ref ex) => {
662 let suffix = if semicolon_for_stmt(self) { ";" } else { "" };
666 ast::StmtKind::Expr(_) => ExprType::SubExpression,
667 ast::StmtKind::Semi(_) => ExprType::Statement,
671 context.config.max_width - offset.width() - suffix.len(),
675 ast::StmtKind::Mac(..) |
676 ast::StmtKind::Item(..) => None,
678 result.and_then(|res| recover_comment_removed(res, self.span, context, _width, offset))
682 // Abstraction over for, while and loop expressions
684 cond: Option<&'a ast::Expr>,
685 block: &'a ast::Block,
686 label: Option<ast::SpannedIdent>,
687 pat: Option<&'a ast::Pat>,
694 fn new_loop(block: &'a ast::Block, label: Option<ast::SpannedIdent>) -> Loop<'a> {
706 fn new_while(pat: Option<&'a ast::Pat>,
708 block: &'a ast::Block,
709 label: Option<ast::SpannedIdent>)
725 fn new_for(pat: &'a ast::Pat,
727 block: &'a ast::Block,
728 label: Option<ast::SpannedIdent>)
742 impl<'a> Rewrite for Loop<'a> {
743 fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
744 let label_string = rewrite_label(self.label);
746 let inner_width = try_opt!(width.checked_sub(self.keyword.len() + 2 + label_string.len()));
747 let inner_offset = offset + self.keyword.len() + label_string.len();
749 let pat_expr_string = match self.cond {
751 try_opt!(rewrite_pat_expr(context,
759 None => String::new(),
762 let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
763 let block_sep = match context.config.control_brace_style {
764 ControlBraceStyle::AlwaysNextLine => alt_block_sep.as_str(),
765 ControlBraceStyle::AlwaysSameLine => " ",
768 // This is used only for the empty block case: `{}`
769 let block_width = try_opt!(width.checked_sub(label_string.len() + self.keyword.len() +
770 extra_offset(&pat_expr_string, inner_offset) +
773 // FIXME: this drops any comment between "loop" and the block.
775 .rewrite(context, block_width, offset)
777 format!("{}{}{}{}{}",
787 fn rewrite_label(label: Option<ast::SpannedIdent>) -> String {
789 Some(ident) => format!("{}: ", ident.node),
790 None => "".to_owned(),
794 fn extract_comment(span: Span,
795 context: &RewriteContext,
799 let comment_str = context.snippet(span);
800 if contains_comment(&comment_str) {
802 try_opt!(rewrite_comment(comment_str.trim(), false, width, offset, context.config));
803 Some(format!("\n{indent}{}\n{indent}",
805 indent = offset.to_string(context.config)))
811 // Rewrites if-else blocks. If let Some(_) = pat, the expression is
812 // treated as an if-let-else expression.
813 fn rewrite_if_else(context: &RewriteContext,
816 if_block: &ast::Block,
817 else_block_opt: Option<&ast::Expr>,
819 pat: Option<&ast::Pat>,
822 allow_single_line: bool)
824 let (budget, indent) = if !allow_single_line {
825 // We are part of an if-elseif-else chain. Our constraints are tightened.
826 // 7 = "} else" .len()
827 (try_opt!(width.checked_sub(7)), offset + 7)
832 // 3 = "if ", 2 = " {"
833 let pat_penalty = match context.config.else_if_brace_style {
834 ElseIfBraceStyle::AlwaysNextLine => 3,
837 let pat_expr_string = try_opt!(rewrite_pat_expr(context,
842 try_opt!(budget.checked_sub(pat_penalty)),
845 // Try to format if-else on single line.
846 if expr_type == ExprType::SubExpression && allow_single_line &&
847 context.config.single_line_if_else_max_width > 0 {
848 let trial = single_line_if_else(context, &pat_expr_string, if_block, else_block_opt, width);
850 if trial.is_some() &&
851 trial.as_ref().unwrap().len() <= context.config.single_line_if_else_max_width {
856 let if_block_string = try_opt!(if_block.rewrite(context, width, offset));
858 let between_if_cond = mk_sp(context.codemap.span_after(span, "if"),
859 pat.map_or(cond.span.lo,
860 |_| context.codemap.span_before(span, "let")));
862 let between_if_cond_comment = extract_comment(between_if_cond, context, offset, width);
864 let after_cond_comment = extract_comment(mk_sp(cond.span.hi, if_block.span.lo),
869 let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
870 let after_sep = match context.config.else_if_brace_style {
871 ElseIfBraceStyle::AlwaysNextLine => alt_block_sep.as_str(),
874 let mut result = format!("if{}{}{}{}",
875 between_if_cond_comment.as_ref().map_or(" ", |str| &**str),
877 after_cond_comment.as_ref().map_or(after_sep, |str| &**str),
880 if let Some(else_block) = else_block_opt {
881 let mut last_in_chain = false;
882 let rewrite = match else_block.node {
883 // If the else expression is another if-else expression, prevent it
884 // from being formatted on a single line.
885 // Note how we're passing the original width and offset, as the
886 // cost of "else" should not cascade.
887 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
888 rewrite_if_else(context,
892 next_else_block.as_ref().map(|e| &**e),
893 mk_sp(else_block.span.lo, span.hi),
899 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
900 rewrite_if_else(context,
904 next_else_block.as_ref().map(|e| &**e),
905 mk_sp(else_block.span.lo, span.hi),
912 last_in_chain = true;
913 else_block.rewrite(context, width, offset)
917 let between_if_else_block =
918 mk_sp(if_block.span.hi,
919 context.codemap.span_before(mk_sp(if_block.span.hi, else_block.span.lo), "else"));
920 let between_if_else_block_comment =
921 extract_comment(between_if_else_block, context, offset, width);
923 let after_else = mk_sp(context.codemap
924 .span_after(mk_sp(if_block.span.hi, else_block.span.lo),
927 let after_else_comment = extract_comment(after_else, context, offset, width);
929 let between_sep = match context.config.else_if_brace_style {
930 ElseIfBraceStyle::AlwaysNextLine |
931 ElseIfBraceStyle::ClosingNextLine => alt_block_sep.as_str(),
932 ElseIfBraceStyle::AlwaysSameLine => " ",
934 let after_sep = match context.config.else_if_brace_style {
935 ElseIfBraceStyle::AlwaysNextLine if last_in_chain => alt_block_sep.as_str(),
938 try_opt!(write!(&mut result,
940 between_if_else_block_comment.as_ref()
941 .map_or(between_sep, |str| &**str),
942 after_else_comment.as_ref().map_or(after_sep, |str| &**str))
944 result.push_str(&try_opt!(rewrite));
950 fn single_line_if_else(context: &RewriteContext,
952 if_node: &ast::Block,
953 else_block_opt: Option<&ast::Expr>,
956 let else_block = try_opt!(else_block_opt);
957 let fixed_cost = "if { } else { }".len();
959 if let ast::ExprKind::Block(ref else_node) = else_block.node {
960 if !is_simple_block(if_node, context.codemap) ||
961 !is_simple_block(else_node, context.codemap) || pat_expr_str.contains('\n') {
965 let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
966 let if_expr = &if_node.stmts[0];
967 let if_str = try_opt!(if_expr.rewrite(context, new_width, Indent::empty()));
969 let new_width = try_opt!(new_width.checked_sub(if_str.len()));
970 let else_expr = &else_node.stmts[0];
971 let else_str = try_opt!(else_expr.rewrite(context, new_width, Indent::empty()));
973 // FIXME: this check shouldn't be necessary. Rewrites should either fail
974 // or wrap to a newline when the object does not fit the width.
975 let fits_line = fixed_cost + pat_expr_str.len() + if_str.len() + else_str.len() <= width;
977 if fits_line && !if_str.contains('\n') && !else_str.contains('\n') {
978 return Some(format!("if {} {{ {} }} else {{ {} }}",
988 fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool {
989 let snippet = codemap.span_to_snippet(block.span).unwrap();
990 contains_comment(&snippet)
993 // Checks that a block contains no statements, an expression and no comments.
994 // FIXME: incorrectly returns false when comment is contained completely within
996 pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
997 block.stmts.len() == 1 && stmt_is_expr(&block.stmts[0]) &&
998 !block_contains_comment(block, codemap)
1001 /// Checks whether a block contains at most one statement or expression, and no comments.
1002 pub fn is_simple_block_stmt(block: &ast::Block, codemap: &CodeMap) -> bool {
1003 block.stmts.len() <= 1 && !block_contains_comment(block, codemap)
1006 /// Checks whether a block contains no statements, expressions, or comments.
1007 pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
1008 block.stmts.is_empty() && !block_contains_comment(block, codemap)
1011 pub fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
1013 ast::StmtKind::Expr(..) => true,
1018 fn is_unsafe_block(block: &ast::Block) -> bool {
1019 if let ast::BlockCheckMode::Unsafe(..) = block.rules {
1026 // inter-match-arm-comment-rules:
1027 // - all comments following a match arm before the start of the next arm
1028 // are about the second arm
1029 fn rewrite_match_arm_comment(context: &RewriteContext,
1033 arm_indent_str: &str)
1035 // The leading "," is not part of the arm-comment
1036 let missed_str = match missed_str.find_uncommented(",") {
1037 Some(n) => &missed_str[n + 1..],
1038 None => &missed_str[..],
1041 let mut result = String::new();
1042 // any text not preceeded by a newline is pushed unmodified to the block
1043 let first_brk = missed_str.find(|c: char| c == '\n').unwrap_or(0);
1044 result.push_str(&missed_str[..first_brk]);
1045 let missed_str = &missed_str[first_brk..]; // If missed_str had one newline, it starts with it
1047 let first = missed_str.find(|c: char| !c.is_whitespace()).unwrap_or(missed_str.len());
1048 if missed_str[..first].chars().filter(|c| c == &'\n').count() >= 2 {
1049 // Excessive vertical whitespace before comment should be preserved
1050 // TODO handle vertical whitespace better
1053 let missed_str = missed_str[first..].trim();
1054 if !missed_str.is_empty() {
1056 try_opt!(rewrite_comment(&missed_str, false, width, arm_indent, context.config));
1058 result.push_str(arm_indent_str);
1059 result.push_str(&comment);
1065 fn rewrite_match(context: &RewriteContext,
1072 if arms.is_empty() {
1077 let cond_budget = try_opt!(width.checked_sub(8));
1078 let cond_str = try_opt!(cond.rewrite(context, cond_budget, offset + 6));
1079 let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
1080 let block_sep = match context.config.control_brace_style {
1081 ControlBraceStyle::AlwaysSameLine => " ",
1082 ControlBraceStyle::AlwaysNextLine => alt_block_sep.as_str(),
1084 let mut result = format!("match {}{}{{", cond_str, block_sep);
1086 let nested_context = context.nested_context();
1087 let arm_indent = nested_context.block_indent;
1088 let arm_indent_str = arm_indent.to_string(context.config);
1090 let open_brace_pos = context.codemap
1091 .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{");
1093 for (i, arm) in arms.iter().enumerate() {
1094 // Make sure we get the stuff between arms.
1095 let missed_str = if i == 0 {
1096 context.snippet(mk_sp(open_brace_pos, arm_start_pos(arm)))
1098 context.snippet(mk_sp(arm_end_pos(&arms[i - 1]), arm_start_pos(arm)))
1100 let comment = try_opt!(rewrite_match_arm_comment(context,
1105 result.push_str(&comment);
1107 result.push_str(&arm_indent_str);
1109 let arm_str = arm.rewrite(&nested_context,
1110 context.config.max_width - arm_indent.width(),
1112 if let Some(ref arm_str) = arm_str {
1113 result.push_str(arm_str);
1115 // We couldn't format the arm, just reproduce the source.
1116 let snippet = context.snippet(mk_sp(arm_start_pos(arm), arm_end_pos(arm)));
1117 result.push_str(&snippet);
1118 result.push_str(arm_comma(context.config, arm, &arm.body));
1121 // BytePos(1) = closing match brace.
1122 let last_span = mk_sp(arm_end_pos(&arms[arms.len() - 1]), span.hi - BytePos(1));
1123 let last_comment = context.snippet(last_span);
1124 let comment = try_opt!(rewrite_match_arm_comment(context,
1129 result.push_str(&comment);
1131 result.push_str(&context.block_indent.to_string(context.config));
1136 fn arm_start_pos(arm: &ast::Arm) -> BytePos {
1137 let &ast::Arm { ref attrs, ref pats, .. } = arm;
1138 if !attrs.is_empty() {
1139 return attrs[0].span.lo;
1145 fn arm_end_pos(arm: &ast::Arm) -> BytePos {
1149 fn arm_comma(config: &Config, arm: &ast::Arm, body: &ast::Expr) -> &'static str {
1150 if !config.match_wildcard_trailing_comma {
1151 if arm.pats.len() == 1 && arm.pats[0].node == ast::PatKind::Wild && arm.guard.is_none() {
1156 if config.match_block_trailing_comma {
1158 } else if let ast::ExprKind::Block(ref block) = body.node {
1159 if let ast::BlockCheckMode::Default = block.rules {
1170 impl Rewrite for ast::Arm {
1171 fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
1172 let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self;
1174 // FIXME this is all a bit grotty, would be nice to abstract out the
1175 // treatment of attributes.
1176 let attr_str = if !attrs.is_empty() {
1177 // We only use this visitor for the attributes, should we use it for
1179 let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
1180 attr_visitor.block_indent = context.block_indent;
1181 attr_visitor.last_pos = attrs[0].span.lo;
1182 if attr_visitor.visit_attrs(attrs) {
1183 // Attributes included a skip instruction.
1186 attr_visitor.format_missing(pats[0].span.lo);
1187 attr_visitor.buffer.to_string()
1194 let pat_budget = try_opt!(width.checked_sub(5));
1195 let pat_strs = try_opt!(pats.iter()
1196 .map(|p| p.rewrite(context, pat_budget, offset))
1197 .collect::<Option<Vec<_>>>());
1199 let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
1200 let items: Vec<_> = pat_strs.into_iter().map(ListItem::from_str).collect();
1201 let fmt = ListFormatting {
1202 tactic: if all_simple {
1203 DefinitiveListTactic::Mixed
1205 DefinitiveListTactic::Vertical
1208 trailing_separator: SeparatorTactic::Never,
1211 ends_with_newline: false,
1212 config: context.config,
1214 let pats_str = try_opt!(write_list(items, &fmt));
1216 let budget = if pats_str.contains('\n') {
1217 context.config.max_width - offset.width()
1222 let guard_str = try_opt!(rewrite_guard(context,
1226 trimmed_last_line_width(&pats_str)));
1228 let pats_str = format!("{}{}", pats_str, guard_str);
1229 // Where the next text can start.
1230 let mut line_start = last_line_width(&pats_str);
1231 if !pats_str.contains('\n') {
1232 line_start += offset.width();
1235 let body = match body.node {
1236 ast::ExprKind::Block(ref block) if !is_unsafe_block(block) &&
1237 is_simple_block(block, context.codemap) &&
1238 context.config.wrap_match_arms => {
1239 if let ast::StmtKind::Expr(ref expr) = block.stmts[0].node {
1248 let comma = arm_comma(&context.config, self, body);
1249 let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
1251 // Let's try and get the arm body on the same line as the condition.
1253 if context.config.max_width > line_start + comma.len() + 4 {
1254 let budget = context.config.max_width - line_start - comma.len() - 4;
1255 let offset = Indent::new(offset.block_indent, line_start + 4 - offset.block_indent);
1256 let rewrite = nop_block_collapse(body.rewrite(context, budget, offset), budget);
1257 let is_block = if let ast::ExprKind::Block(..) = body.node {
1263 let block_sep = match context.config.control_brace_style {
1264 ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(),
1268 Some(ref body_str) if !body_str.contains('\n') || !context.config.wrap_match_arms ||
1270 return Some(format!("{}{} =>{}{}{}",
1271 attr_str.trim_left(),
1281 // FIXME: we're doing a second rewrite of the expr; This may not be
1283 let body_budget = try_opt!(width.checked_sub(context.config.tab_spaces));
1284 let indent = context.block_indent.block_indent(context.config);
1285 let inner_context = &RewriteContext { block_indent: indent, ..*context };
1286 let next_line_body =
1287 try_opt!(nop_block_collapse(body.rewrite(inner_context, body_budget, indent),
1289 let indent_str = offset.block_indent(context.config).to_string(context.config);
1290 let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
1291 if context.config.match_block_trailing_comma {
1300 let block_sep = match context.config.control_brace_style {
1301 ControlBraceStyle::AlwaysNextLine => alt_block_sep + body_prefix + "\n",
1302 ControlBraceStyle::AlwaysSameLine => String::from(" ") + body_prefix + "\n",
1304 Some(format!("{}{} =>{}{}{}\n{}{}",
1305 attr_str.trim_left(),
1310 offset.to_string(context.config),
1315 // A pattern is simple if it is very short or it is short-ish and just a path.
1316 // E.g. `Foo::Bar` is simple, but `Foo(..)` is not.
1317 fn pat_is_simple(pat_str: &str) -> bool {
1318 pat_str.len() <= 16 ||
1319 (pat_str.len() <= 24 && pat_str.chars().all(|c| c.is_alphabetic() || c == ':'))
1322 // The `if ...` guard on a match arm.
1323 fn rewrite_guard(context: &RewriteContext,
1324 guard: &Option<ptr::P<ast::Expr>>,
1327 // The amount of space used up on this line for the pattern in
1328 // the arm (excludes offset).
1329 pattern_width: usize)
1331 if let Some(ref guard) = *guard {
1332 // First try to fit the guard string on the same line as the pattern.
1333 // 4 = ` if `, 5 = ` => {`
1334 let overhead = pattern_width + 4 + 5;
1335 if overhead < width {
1336 let cond_str = guard.rewrite(context, width - overhead, offset + pattern_width + 4);
1337 if let Some(cond_str) = cond_str {
1338 return Some(format!(" if {}", cond_str));
1342 // Not enough space to put the guard after the pattern, try a newline.
1343 let overhead = offset.block_indent(context.config).width() + 4 + 5;
1344 if overhead < width {
1345 let cond_str = guard.rewrite(context,
1348 offset.block_indent(context.config) + 3);
1349 if let Some(cond_str) = cond_str {
1350 return Some(format!("\n{}if {}",
1351 offset.block_indent(context.config).to_string(context.config),
1362 fn rewrite_pat_expr(context: &RewriteContext,
1363 pat: Option<&ast::Pat>,
1366 // Connecting piece between pattern and expression,
1367 // *without* trailing space.
1372 let pat_offset = offset + matcher.len();
1373 let mut result = match pat {
1375 let pat_budget = try_opt!(width.checked_sub(connector.len() + matcher.len()));
1376 let pat_string = try_opt!(pat.rewrite(context, pat_budget, pat_offset));
1377 format!("{}{}{}", matcher, pat_string, connector)
1379 None => String::new(),
1382 // Consider only the last line of the pat string.
1383 let extra_offset = extra_offset(&result, offset);
1385 // The expression may (partionally) fit on the current line.
1386 if width > extra_offset + 1 {
1387 let spacer = if pat.is_some() { " " } else { "" };
1389 let expr_rewrite = expr.rewrite(context,
1390 try_opt!(width.checked_sub(extra_offset + spacer.len())),
1391 offset + extra_offset + spacer.len());
1393 if let Some(expr_string) = expr_rewrite {
1395 pat.and_then(|p| p.rewrite(context, context.config.max_width, Indent::empty()))
1396 .map(|s| pat_is_simple(&s));
1398 if pat.is_none() || pat_simple.unwrap_or(false) || !expr_string.contains('\n') {
1399 result.push_str(spacer);
1400 result.push_str(&expr_string);
1401 return Some(result);
1406 let nested = context.nested_context();
1408 // The expression won't fit on the current line, jump to next.
1410 result.push_str(&nested.block_indent.to_string(context.config));
1413 expr.rewrite(&nested,
1414 try_opt!(context.config.max_width.checked_sub(nested.block_indent.width())),
1415 nested.block_indent);
1416 result.push_str(&try_opt!(expr_rewrite));
1421 fn rewrite_string_lit(context: &RewriteContext,
1426 let string_lit = context.snippet(span);
1428 if !context.config.format_strings && !context.config.force_format_strings {
1432 if !context.config.force_format_strings &&
1433 !string_requires_rewrite(context, span, &string_lit, width, offset) {
1437 let fmt = StringFormat {
1445 config: context.config,
1448 // Remove the quote characters.
1449 let str_lit = &string_lit[1..string_lit.len() - 1];
1451 rewrite_string(str_lit, &fmt).unwrap_or_else(|| string_lit.to_owned())
1454 fn string_requires_rewrite(context: &RewriteContext,
1460 if context.codemap.lookup_char_pos(span.lo).col.0 != offset.width() {
1464 for (i, line) in string.lines().enumerate() {
1466 if line.len() > width {
1470 if line.len() > width + offset.width() {
1479 pub fn rewrite_call<R>(context: &RewriteContext,
1481 args: &[ptr::P<ast::Expr>],
1488 let closure = |callee_max_width| {
1489 rewrite_call_inner(context, callee, callee_max_width, args, span, width, offset)
1493 let max_width = try_opt!(width.checked_sub(2));
1494 binary_search(1, max_width, closure)
1497 fn rewrite_call_inner<R>(context: &RewriteContext,
1499 max_callee_width: usize,
1500 args: &[ptr::P<ast::Expr>],
1504 -> Result<String, Ordering>
1507 let callee = callee.borrow();
1508 // FIXME using byte lens instead of char lens (and probably all over the
1510 let callee_str = match callee.rewrite(context, max_callee_width, offset) {
1512 if !string.contains('\n') && string.len() > max_callee_width {
1513 panic!("{:?} {}", string, max_callee_width);
1518 None => return Err(Ordering::Greater),
1521 let span_lo = context.codemap.span_after(span, "(");
1522 let span = mk_sp(span_lo, span.hi);
1524 let extra_offset = extra_offset(&callee_str, offset);
1526 let remaining_width = match width.checked_sub(extra_offset + 2) {
1528 None => return Err(Ordering::Greater),
1530 let offset = offset + extra_offset + 1;
1531 let arg_count = args.len();
1532 let block_indent = if arg_count == 1 {
1533 context.block_indent
1537 let inner_context = &RewriteContext { block_indent: block_indent, ..*context };
1539 let items = itemize_list(context.codemap,
1542 |item| item.span.lo,
1543 |item| item.span.hi,
1544 |item| item.rewrite(inner_context, remaining_width, offset),
1547 let mut item_vec: Vec<_> = items.collect();
1549 // Try letting the last argument overflow to the next line with block
1550 // indentation. If its first line fits on one line with the other arguments,
1551 // we format the function arguments horizontally.
1552 let overflow_last = match args.last().map(|x| &x.node) {
1553 Some(&ast::ExprKind::Closure(..)) |
1554 Some(&ast::ExprKind::Block(..)) if arg_count > 1 => true,
1556 } && context.config.chains_overflow_last;
1558 let mut orig_last = None;
1559 let mut placeholder = None;
1561 // Replace the last item with its first line to see if it fits with
1564 let inner_context = &RewriteContext { block_indent: context.block_indent, ..*context };
1565 let rewrite = args.last().unwrap().rewrite(inner_context, remaining_width, offset);
1567 if let Some(rewrite) = rewrite {
1568 let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
1569 placeholder = Some(rewrite);
1571 swap(&mut item_vec[arg_count - 1].item, &mut orig_last);
1572 item_vec[arg_count - 1].item = rewrite_first_line;
1576 let tactic = definitive_tactic(&item_vec,
1577 ListTactic::LimitedHorizontalVertical(context.config
1581 // Replace the stub with the full overflowing last argument if the rewrite
1582 // succeeded and its first line fits with the other arguments.
1583 match (overflow_last, tactic, placeholder) {
1584 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
1585 item_vec[arg_count - 1].item = placeholder;
1588 item_vec[arg_count - 1].item = orig_last;
1593 let fmt = ListFormatting {
1596 trailing_separator: SeparatorTactic::Never,
1599 ends_with_newline: false,
1600 config: context.config,
1603 let list_str = match write_list(&item_vec, &fmt) {
1605 None => return Err(Ordering::Less),
1608 Ok(if context.config.spaces_within_parens && list_str.len() > 0 {
1609 format!("{}( {} )", callee_str, list_str)
1611 format!("{}({})", callee_str, list_str)
1615 fn rewrite_paren(context: &RewriteContext,
1616 subexpr: &ast::Expr,
1620 debug!("rewrite_paren, width: {}, offset: {:?}", width, offset);
1621 // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
1622 // paren on the same line as the subexpr.
1623 let subexpr_str = subexpr.rewrite(context, try_opt!(width.checked_sub(2)), offset + 1);
1624 debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
1626 subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
1627 format!("( {} )", s)
1633 fn rewrite_index(expr: &ast::Expr,
1635 context: &RewriteContext,
1639 let expr_str = try_opt!(expr.rewrite(context, width, offset));
1641 let (lbr, rbr) = if context.config.spaces_within_square_brackets {
1647 let budget = width.checked_sub(expr_str.len() + lbr.len() + rbr.len()).unwrap_or(0);
1648 let index_str = index.rewrite(context, budget, offset);
1649 if let Some(index_str) = index_str {
1650 return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr));
1653 let indent = offset.block_indent(&context.config);
1654 let indent = indent.to_string(&context.config);
1655 // FIXME this is not right, since we don't take into account that width
1656 // might be reduced from max_width by something on the right.
1658 try_opt!(context.config.max_width.checked_sub(indent.len() + lbr.len() + rbr.len()));
1659 let index_str = try_opt!(index.rewrite(context, budget, offset));
1660 Some(format!("{}\n{}{}{}{}", expr_str, indent, lbr, index_str, rbr))
1663 fn rewrite_struct_lit<'a>(context: &RewriteContext,
1665 fields: &'a [ast::Field],
1666 base: Option<&'a ast::Expr>,
1671 debug!("rewrite_struct_lit: width {}, offset {:?}", width, offset);
1673 enum StructLitField<'a> {
1674 Regular(&'a ast::Field),
1675 Base(&'a ast::Expr),
1679 let path_budget = try_opt!(width.checked_sub(2));
1680 let path_str = try_opt!(rewrite_path(context, true, None, path, path_budget, offset));
1682 // Foo { a: Foo } - indent is +3, width is -5.
1683 let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0);
1684 // The 1 taken from the v_budget is for the comma.
1685 let (indent, v_budget) = match context.config.struct_lit_style {
1686 StructLitStyle::Visual => (offset + path_str.len() + 3, h_budget),
1687 StructLitStyle::Block => {
1688 // If we are all on one line, then we'll ignore the indent, and we
1689 // have a smaller budget.
1690 let indent = context.block_indent.block_indent(context.config);
1691 let v_budget = context.config.max_width.checked_sub(indent.width()).unwrap_or(0);
1696 let field_iter = fields.into_iter()
1697 .map(StructLitField::Regular)
1698 .chain(base.into_iter().map(StructLitField::Base));
1700 let inner_context = &RewriteContext { block_indent: indent, ..*context };
1702 let items = itemize_list(context.codemap,
1705 |item| match *item {
1706 StructLitField::Regular(field) => field.span.lo,
1707 StructLitField::Base(expr) => {
1708 let last_field_hi = fields.last()
1709 .map_or(span.lo, |field| field.span.hi);
1711 context.snippet(mk_sp(last_field_hi, expr.span.lo));
1712 let pos = snippet.find_uncommented("..").unwrap();
1713 last_field_hi + BytePos(pos as u32)
1716 |item| match *item {
1717 StructLitField::Regular(field) => field.span.hi,
1718 StructLitField::Base(expr) => expr.span.hi,
1722 StructLitField::Regular(field) => {
1723 rewrite_field(inner_context,
1725 v_budget.checked_sub(1).unwrap_or(0),
1728 StructLitField::Base(expr) => {
1730 expr.rewrite(inner_context, try_opt!(v_budget.checked_sub(2)), indent + 2)
1731 .map(|s| format!("..{}", s))
1735 context.codemap.span_after(span, "{"),
1737 let item_vec = items.collect::<Vec<_>>();
1740 let mut prelim_tactic = match (context.config.struct_lit_style, fields.len()) {
1741 (StructLitStyle::Visual, 1) => ListTactic::HorizontalVertical,
1742 _ => context.config.struct_lit_multiline_style.to_list_tactic(),
1745 if prelim_tactic == ListTactic::HorizontalVertical && fields.len() > 1 {
1746 prelim_tactic = ListTactic::LimitedHorizontalVertical(context.config.struct_lit_width);
1749 definitive_tactic(&item_vec, prelim_tactic, h_budget)
1752 let budget = match tactic {
1753 DefinitiveListTactic::Horizontal => h_budget,
1757 let ends_with_newline = context.config.struct_lit_style != StructLitStyle::Visual &&
1758 tactic == DefinitiveListTactic::Vertical;
1760 let fmt = ListFormatting {
1763 trailing_separator: if base.is_some() {
1764 SeparatorTactic::Never
1766 context.config.struct_lit_trailing_comma
1770 ends_with_newline: ends_with_newline,
1771 config: context.config,
1773 let fields_str = try_opt!(write_list(&item_vec, &fmt));
1775 if fields_str.is_empty() {
1776 return Some(format!("{} {{}}", path_str));
1779 let format_on_newline = || {
1780 let inner_indent = context.block_indent
1781 .block_indent(context.config)
1782 .to_string(context.config);
1783 let outer_indent = context.block_indent.to_string(context.config);
1784 Some(format!("{} {{\n{}{}\n{}}}",
1791 match (context.config.struct_lit_style, context.config.struct_lit_multiline_style) {
1792 (StructLitStyle::Block, _) if fields_str.contains('\n') || fields_str.len() > h_budget => {
1795 (StructLitStyle::Block, MultilineStyle::ForceMulti) => format_on_newline(),
1796 _ => Some(format!("{} {{ {} }}", path_str, fields_str)),
1799 // FIXME if context.config.struct_lit_style == Visual, but we run out
1800 // of space, we should fall back to BlockIndent.
1803 pub fn type_annotation_separator(config: &Config) -> &str {
1804 match (config.space_before_type_annotation, config.space_after_type_annotation_colon) {
1805 (true, true) => " : ",
1806 (true, false) => " :",
1807 (false, true) => ": ",
1808 (false, false) => ":",
1812 fn rewrite_field(context: &RewriteContext,
1817 let name = &field.ident.node.to_string();
1818 let separator = type_annotation_separator(context.config);
1819 let overhead = name.len() + separator.len();
1820 let expr = field.expr.rewrite(context,
1821 try_opt!(width.checked_sub(overhead)),
1825 Some(e) => Some(format!("{}{}{}", name, separator, e)),
1827 let expr_offset = offset.block_indent(context.config);
1828 let expr = field.expr.rewrite(context,
1829 try_opt!(context.config
1831 .checked_sub(expr_offset.width())),
1833 expr.map(|s| format!("{}:\n{}{}", name, expr_offset.to_string(&context.config), s))
1838 pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
1844 where I: ExactSizeIterator,
1845 <I as Iterator>::Item: Deref,
1846 <I::Item as Deref>::Target: Rewrite + Spanned + 'a
1848 let indent = offset + 1;
1849 let aligned = RewriteContext { block_indent: indent, ..context.clone() };
1851 // In case of length 1, need a trailing comma
1852 if items.len() == 1 {
1854 let budget = try_opt!(width.checked_sub(3));
1857 .rewrite(&aligned, budget, indent)
1858 .map(|s| if context.config.spaces_within_parens {
1859 format!("( {}, )", s)
1865 let list_lo = context.codemap.span_after(span, "(");
1866 let budget = try_opt!(width.checked_sub(2));
1867 let items = itemize_list(context.codemap,
1870 |item| item.span().lo,
1871 |item| item.span().hi,
1872 |item| item.rewrite(&aligned, budget, indent),
1874 span.hi - BytePos(1));
1875 let list_str = try_opt!(format_item_list(items, budget, indent, context.config));
1877 if context.config.spaces_within_parens && list_str.len() > 0 {
1878 Some(format!("( {} )", list_str))
1880 Some(format!("({})", list_str))
1884 pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
1890 rewrite.rewrite(context,
1891 try_opt!(width.checked_sub(prefix.len())),
1892 offset + prefix.len())
1893 .map(|r| format!("{}{}", prefix, r))
1896 // FIXME: this is probably not correct for multi-line Rewrites. we should
1897 // subtract suffix.len() from the last line budget, not the first!
1898 pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
1904 rewrite.rewrite(context, try_opt!(width.checked_sub(suffix.len())), offset)
1911 fn rewrite_unary_op(context: &RewriteContext,
1917 // For some reason, an UnOp is not spanned like BinOp!
1918 let operator_str = match *op {
1919 ast::UnOp::Deref => "*",
1920 ast::UnOp::Not => "!",
1921 ast::UnOp::Neg => "-",
1923 rewrite_unary_prefix(context, operator_str, expr, width, offset)
1926 fn rewrite_assignment(context: &RewriteContext,
1929 op: Option<&ast::BinOp>,
1933 let operator_str = match op {
1934 Some(op) => context.snippet(op.span),
1935 None => "=".to_owned(),
1938 // 1 = space between lhs and operator.
1939 let max_width = try_opt!(width.checked_sub(operator_str.len() + 1));
1940 let lhs_str = format!("{} {}",
1941 try_opt!(lhs.rewrite(context, max_width, offset)),
1944 rewrite_assign_rhs(context, lhs_str, rhs, width, offset)
1947 // The left hand side must contain everything up to, and including, the
1948 // assignment operator.
1949 pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
1955 let mut result = lhs.into();
1956 let last_line_width = last_line_width(&result) -
1957 if result.contains('\n') {
1962 // 1 = space between operator and rhs.
1963 let max_width = try_opt!(width.checked_sub(last_line_width + 1));
1964 let rhs = ex.rewrite(context, max_width, offset + last_line_width + 1);
1966 fn count_line_breaks(src: &str) -> usize {
1967 src.chars().filter(|&x| x == '\n').count()
1971 Some(ref new_str) if count_line_breaks(new_str) < 2 => {
1973 result.push_str(new_str);
1976 // Expression did not fit on the same line as the identifier or is
1977 // at least three lines big. Try splitting the line and see
1978 // if that works better.
1979 let new_offset = offset.block_indent(context.config);
1980 let max_width = try_opt!((width + offset.width()).checked_sub(new_offset.width()));
1981 let inner_context = context.nested_context();
1982 let new_rhs = ex.rewrite(&inner_context, max_width, new_offset);
1985 match (rhs, new_rhs) {
1986 (Some(ref orig_rhs), Some(ref replacement_rhs))
1987 if count_line_breaks(orig_rhs) >
1988 count_line_breaks(replacement_rhs) + 1 => {
1989 result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
1990 result.push_str(replacement_rhs);
1992 (None, Some(ref final_rhs)) => {
1993 result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
1994 result.push_str(final_rhs);
1996 (None, None) => return None,
1997 (Some(ref orig_rhs), _) => {
1999 result.push_str(orig_rhs);
2008 fn rewrite_expr_addrof(context: &RewriteContext,
2009 mutability: ast::Mutability,
2014 let operator_str = match mutability {
2015 ast::Mutability::Immutable => "&",
2016 ast::Mutability::Mutable => "&mut ",
2018 rewrite_unary_prefix(context, operator_str, expr, width, offset)