Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap/unwrap match arms #603

Merged
merged 4 commits into from
Nov 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/bin/rustfmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,7 @@ fn execute() -> i32 {
Operation::Stdin(input, write_mode) => {
// try to read config from local directory
let config = match lookup_and_read_project_file(&Path::new(".")) {
Ok((_, toml)) => {
Config::from_toml(&toml)
}
Ok((_, toml)) => Config::from_toml(&toml),
Err(_) => Default::default(),
};

Expand Down
37 changes: 14 additions & 23 deletions src/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

use Indent;
use rewrite::{Rewrite, RewriteContext};
use utils::first_line_width;
use utils::{wrap_str, first_line_width};
use expr::rewrite_call;
use config::BlockIndentStyle;

Expand Down Expand Up @@ -58,12 +58,8 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
} else {
match context.config.chain_indent {
BlockIndentStyle::Inherit => (context.block_indent, false),
BlockIndentStyle::Tabbed => {
(context.block_indent.block_indent(context.config), false)
}
BlockIndentStyle::Visual => {
(offset + Indent::new(context.config.tab_spaces, 0), false)
}
BlockIndentStyle::Tabbed => (context.block_indent.block_indent(context.config), false),
BlockIndentStyle::Visual => (offset + Indent::new(context.config.tab_spaces, 0), false),
}
};

Expand Down Expand Up @@ -142,10 +138,13 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
&connector[..]
};

Some(format!("{}{}{}",
parent_rewrite,
first_connector,
rewrites.join(&connector)))
wrap_str(format!("{}{}{}",
parent_rewrite,
first_connector,
rewrites.join(&connector)),
context.config.max_width,
width,
offset)
}

// States whether an expression's last line exclusively consists of closing
Expand All @@ -171,13 +170,9 @@ fn is_block_expr(expr: &ast::Expr, repr: &str) -> bool {

fn pop_expr_chain<'a>(expr: &'a ast::Expr) -> Option<&'a ast::Expr> {
match expr.node {
ast::Expr_::ExprMethodCall(_, _, ref expressions) => {
Some(&expressions[0])
}
ast::Expr_::ExprMethodCall(_, _, ref expressions) => Some(&expressions[0]),
ast::Expr_::ExprTupField(ref subexpr, _) |
ast::Expr_::ExprField(ref subexpr, _) => {
Some(subexpr)
}
ast::Expr_::ExprField(ref subexpr, _) => Some(subexpr),
_ => None,
}
}
Expand All @@ -199,12 +194,8 @@ fn rewrite_chain_expr(expr: &ast::Expr,
width,
offset)
}
ast::Expr_::ExprField(_, ref field) => {
Some(format!(".{}", field.node))
}
ast::Expr_::ExprTupField(_, ref field) => {
Some(format!(".{}", field.node))
}
ast::Expr_::ExprField(_, ref field) => Some(format!(".{}", field.node)),
ast::Expr_::ExprTupField(_, ref field) => Some(format!(".{}", field.node)),
_ => unreachable!(),
}
}
Expand Down
68 changes: 39 additions & 29 deletions src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,14 @@ impl FindUncommented for str {
None => {
return Some(i - pat.len());
}
Some(c) => match kind {
CodeCharKind::Normal if b == c => {}
_ => {
needle_iter = pat.chars();
Some(c) => {
match kind {
CodeCharKind::Normal if b == c => {}
_ => {
needle_iter = pat.chars();
}
}
},
}
}
}

Expand Down Expand Up @@ -233,33 +235,39 @@ impl<T> Iterator for CharClasses<T> where T: Iterator, T::Item: RichChar {
let item = try_opt!(self.base.next());
let chr = item.get_char();
self.status = match self.status {
CharClassesStatus::LitString => match chr {
'"' => CharClassesStatus::Normal,
'\\' => CharClassesStatus::LitStringEscape,
_ => CharClassesStatus::LitString,
},
CharClassesStatus::LitString => {
match chr {
'"' => CharClassesStatus::Normal,
'\\' => CharClassesStatus::LitStringEscape,
_ => CharClassesStatus::LitString,
}
}
CharClassesStatus::LitStringEscape => CharClassesStatus::LitString,
CharClassesStatus::LitChar => match chr {
'\\' => CharClassesStatus::LitCharEscape,
'\'' => CharClassesStatus::Normal,
_ => CharClassesStatus::LitChar,
},
CharClassesStatus::LitChar => {
match chr {
'\\' => CharClassesStatus::LitCharEscape,
'\'' => CharClassesStatus::Normal,
_ => CharClassesStatus::LitChar,
}
}
CharClassesStatus::LitCharEscape => CharClassesStatus::LitChar,
CharClassesStatus::Normal => {
match chr {
'"' => CharClassesStatus::LitString,
'\'' => CharClassesStatus::LitChar,
'/' => match self.base.peek() {
Some(next) if next.get_char() == '*' => {
self.status = CharClassesStatus::BlockCommentOpening(1);
return Some((CodeCharKind::Comment, item));
}
Some(next) if next.get_char() == '/' => {
self.status = CharClassesStatus::LineComment;
return Some((CodeCharKind::Comment, item));
'/' => {
match self.base.peek() {
Some(next) if next.get_char() == '*' => {
self.status = CharClassesStatus::BlockCommentOpening(1);
return Some((CodeCharKind::Comment, item));
}
Some(next) if next.get_char() == '/' => {
self.status = CharClassesStatus::LineComment;
return Some((CodeCharKind::Comment, item));
}
_ => CharClassesStatus::Normal,
}
_ => CharClassesStatus::Normal,
},
}
_ => CharClassesStatus::Normal,
}
}
Expand All @@ -271,10 +279,12 @@ impl<T> Iterator for CharClasses<T> where T: Iterator, T::Item: RichChar {
return Some((CodeCharKind::Comment, item));
}
self.status = match self.base.peek() {
Some(next) if next.get_char() == '/' && chr == '*' =>
CharClassesStatus::BlockCommentClosing(deepness - 1),
Some(next) if next.get_char() == '*' && chr == '/' =>
CharClassesStatus::BlockCommentOpening(deepness + 1),
Some(next) if next.get_char() == '/' && chr == '*' => {
CharClassesStatus::BlockCommentClosing(deepness - 1)
}
Some(next) if next.get_char() == '*' && chr == '/' => {
CharClassesStatus::BlockCommentOpening(deepness + 1)
}
_ => CharClassesStatus::BlockComment(deepness),
};
return Some((CodeCharKind::Comment, item));
Expand Down
1 change: 1 addition & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,5 @@ create_config! {
take_source_hints: bool, true, "Retain some formatting characteristics from the source code";
hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
wrap_comments: bool, false, "Break comments to fit on the line";
wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
}
118 changes: 60 additions & 58 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ impl Rewrite for ast::Expr {
let inner_span = mk_sp(callee.span.hi, self.span.hi);
rewrite_call(context, &**callee, args, inner_span, width, offset)
}
ast::Expr_::ExprParen(ref subexpr) => {
rewrite_paren(context, subexpr, width, offset)
}
ast::Expr_::ExprParen(ref subexpr) => rewrite_paren(context, subexpr, width, offset),
ast::Expr_::ExprBinary(ref op, ref lhs, ref rhs) => {
rewrite_binary_op(context, op, lhs, rhs, width, offset)
}
Expand Down Expand Up @@ -91,9 +89,7 @@ impl Rewrite for ast::Expr {
ast::Expr_::ExprLoop(ref block, label) => {
Loop::new_loop(block, label).rewrite(context, width, offset)
}
ast::Expr_::ExprBlock(ref block) => {
block.rewrite(context, width, offset)
}
ast::Expr_::ExprBlock(ref block) => block.rewrite(context, width, offset),
ast::Expr_::ExprIf(ref cond, ref if_block, ref else_block) => {
rewrite_if_else(context,
cond,
Expand Down Expand Up @@ -145,9 +141,7 @@ impl Rewrite for ast::Expr {
}
ast::Expr_::ExprField(..) |
ast::Expr_::ExprTupField(..) |
ast::Expr_::ExprMethodCall(..) => {
rewrite_chain(self, context, width, offset)
}
ast::Expr_::ExprMethodCall(..) => rewrite_chain(self, context, width, offset),
ast::Expr_::ExprMac(ref mac) => {
// Failure to rewrite a marco should not imply failure to
// rewrite the expression.
Expand Down Expand Up @@ -571,13 +565,15 @@ impl<'a> Rewrite for Loop<'a> {
let inner_offset = offset + self.keyword.len() + label_string.len();

let pat_expr_string = match self.cond {
Some(cond) => try_opt!(rewrite_pat_expr(context,
self.pat,
cond,
self.matcher,
self.connector,
inner_width,
inner_offset)),
Some(cond) => {
try_opt!(rewrite_pat_expr(context,
self.pat,
cond,
self.matcher,
self.connector,
inner_width,
inner_offset))
}
None => String::new(),
};

Expand Down Expand Up @@ -711,6 +707,8 @@ fn block_contains_comment(block: &ast::Block, codemap: &CodeMap) -> bool {
}

// Checks that a block contains no statements, an expression and no comments.
// FIXME: incorrectly returns false when comment is contained completely within
// the expression.
pub fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
block.stmts.is_empty() && block.expr.is_some() && !block_contains_comment(block, codemap)
}
Expand All @@ -726,6 +724,14 @@ pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
block.stmts.is_empty() && block.expr.is_none() && !block_contains_comment(block, codemap)
}

fn is_unsafe_block(block: &ast::Block) -> bool {
if let ast::BlockCheckMode::UnsafeBlock(..) = block.rules {
true
} else {
false
}
}

// inter-match-arm-comment-rules:
// - all comments following a match arm before the start of the next arm
// are about the second arm
Expand Down Expand Up @@ -907,7 +913,7 @@ impl Rewrite for ast::Arm {
}

let pats_width = if vertical {
pat_strs[pat_strs.len() - 1].len()
pat_strs.last().unwrap().len()
} else {
total_width
};
Expand All @@ -934,65 +940,62 @@ impl Rewrite for ast::Arm {
line_start += offset.width();
}

let body = match **body {
ast::Expr { node: ast::ExprBlock(ref block), .. } if !is_unsafe_block(block) &&
is_simple_block(block,
context.codemap) &&
context.config.wrap_match_arms => {
block.expr.as_ref().map(|e| &**e).unwrap()
}
ref x => x,
};

let comma = arm_comma(body);

// Let's try and get the arm body on the same line as the condition.
// 4 = ` => `.len()
let same_line_body = if context.config.max_width > line_start + comma.len() + 4 {
if context.config.max_width > line_start + comma.len() + 4 {
let budget = context.config.max_width - line_start - comma.len() - 4;
let offset = Indent::new(offset.block_indent, line_start + 4 - offset.block_indent);
let rewrite = nop_block_collapse(body.rewrite(context, budget, offset), budget);

match rewrite {
Some(ref body_str) if body_str.len() <= budget || comma.is_empty() =>
Some(ref body_str) if !body_str.contains('\n') || !context.config.wrap_match_arms ||
comma.is_empty() => {
return Some(format!("{}{} => {}{}",
attr_str.trim_left(),
pats_str,
body_str,
comma)),
_ => rewrite,
comma));
}
_ => {}
}
} else {
None
};

if let ast::ExprBlock(_) = body.node {
// We're trying to fit a block in, but it still failed, give up.
return None;
}

// FIXME: we're doing a second rewrite of the expr; This may not be
// necessary.
let body_budget = try_opt!(width.checked_sub(context.config.tab_spaces));
let indent = context.block_indent.block_indent(context.config);
let inner_context = &RewriteContext { block_indent: indent, ..*context };
let next_line_body = nop_block_collapse(body.rewrite(inner_context, body_budget, indent),
body_budget);

let body_str = try_opt!(match_arm_heuristic(same_line_body.as_ref().map(|x| &x[..]),
next_line_body.as_ref().map(|x| &x[..])));

let spacer = match same_line_body {
Some(ref body) if body == body_str => " ".to_owned(),
_ => format!("\n{}",
offset.block_indent(context.config).to_string(context.config)),
let next_line_body = try_opt!(nop_block_collapse(body.rewrite(inner_context,
body_budget,
indent),
body_budget));
let indent_str = offset.block_indent(context.config).to_string(context.config);
let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
(" {", "}")
} else {
("", "")
};

Some(format!("{}{} =>{}{},",
Some(format!("{}{} =>{}\n{}{}\n{}{}",
attr_str.trim_left(),
pats_str,
spacer,
body_str))
}
}

// Takes two possible rewrites for the match arm body and chooses the "nicest".
fn match_arm_heuristic<'a>(former: Option<&'a str>, latter: Option<&'a str>) -> Option<&'a str> {
match (former, latter) {
(f @ Some(..), None) => f,
(Some(f), Some(l)) if f.chars().filter(|&c| c == '\n').count() <=
l.chars().filter(|&c| c == '\n').count() => {
Some(f)
}
(_, l) => l,
body_prefix,
indent_str,
next_line_body,
offset.to_string(context.config),
body_suffix))
}
}

Expand Down Expand Up @@ -1285,9 +1288,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
// Foo { a: Foo } - indent is +3, width is -5.
let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0);
let (indent, v_budget) = match context.config.struct_lit_style {
StructLitStyle::Visual => {
(offset + path_str.len() + 3, h_budget)
}
StructLitStyle::Visual => (offset + path_str.len() + 3, h_budget),
StructLitStyle::Block => {
// If we are all on one line, then we'll ignore the indent, and we
// have a smaller budget.
Expand Down Expand Up @@ -1396,8 +1397,9 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,

match (context.config.struct_lit_style,
context.config.struct_lit_multiline_style) {
(StructLitStyle::Block, _) if fields_str.contains('\n') || fields_str.len() > h_budget =>
format_on_newline(),
(StructLitStyle::Block, _) if fields_str.contains('\n') || fields_str.len() > h_budget => {
format_on_newline()
}
(StructLitStyle::Block, MultilineStyle::ForceMulti) => format_on_newline(),
_ => Some(format!("{} {{ {} }}", path_str, fields_str)),
}
Expand Down
Loading