Merge pull request from marcusklaas/match-arm-delining

Improve heuristics for match arm body placement
This commit is contained in:
Marcus Klaas de Vries 2015-09-27 12:00:57 +02:00
commit afee62e476
7 changed files with 418 additions and 278 deletions

@ -15,7 +15,7 @@ use Indent;
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
use string::{StringFormat, rewrite_string};
use utils::{span_after, extra_offset, first_line_width, last_line_width, wrap_str, binary_search};
use utils::{span_after, extra_offset, last_line_width, wrap_str, binary_search};
use visitor::FmtVisitor;
use config::{StructLitStyle, MultilineStyle};
use comment::{FindUncommented, rewrite_comment, contains_comment};
@ -155,11 +155,14 @@ impl Rewrite for ast::Expr {
rewrite_chain(self, context, width, offset)
}
ast::Expr_::ExprMac(ref mac) => {
// Failure to rewrite a marco should not imply failure to rewrite the Expr
rewrite_macro(mac, context, width, offset).or(wrap_str(context.snippet(self.span),
context.config.max_width,
width,
offset))
// Failure to rewrite a marco should not imply failure to
// rewrite the expression.
rewrite_macro(mac, context, width, offset).or_else(|| {
wrap_str(context.snippet(self.span),
context.config.max_width,
width,
offset)
})
}
ast::Expr_::ExprRet(None) => {
wrap_str("return".to_owned(),
@ -168,10 +171,10 @@ impl Rewrite for ast::Expr {
offset)
}
ast::Expr_::ExprRet(Some(ref expr)) => {
rewrite_unary_prefix(context, "return ", &expr, width, offset)
rewrite_unary_prefix(context, "return ", expr, width, offset)
}
ast::Expr_::ExprBox(ref expr) => {
rewrite_unary_prefix(context, "box ", &expr, width, offset)
rewrite_unary_prefix(context, "box ", expr, width, offset)
}
ast::Expr_::ExprAddrOf(mutability, ref expr) => {
rewrite_expr_addrof(context, mutability, &expr, width, offset)
@ -352,9 +355,9 @@ fn rewrite_closure(capture: ast::CaptureClause,
Some(format!("{} {}", prefix, try_opt!(body_rewrite)))
}
fn nop_block_collapse(block_str: Option<String>) -> Option<String> {
fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
block_str.map(|block_str| {
if block_str.starts_with("{") &&
if block_str.starts_with("{") && budget >= 2 &&
(block_str[1..].find(|c: char| !c.is_whitespace()).unwrap() == block_str.len() - 2) {
"{}".to_owned()
} else {
@ -872,15 +875,10 @@ impl Rewrite for ast::Arm {
let pats_str = format!("{}{}", pats_str, guard_str);
// Where the next text can start.
let mut line_start = last_line_width(&pats_str);
if pats_str.find('\n').is_none() {
if !pats_str.contains('\n') {
line_start += offset.width();
}
let mut line_indent = offset + pats_width;
if vertical {
line_indent = line_indent.block_indent(context.config);
}
let comma = if let ast::ExprBlock(_) = body.node {
""
} else {
@ -889,39 +887,66 @@ impl Rewrite for ast::Arm {
// Let's try and get the arm body on the same line as the condition.
// 4 = ` => `.len()
if context.config.max_width > line_start + comma.len() + 4 {
let same_line_body = if context.config.max_width > line_start + comma.len() + 4 {
let budget = context.config.max_width - line_start - comma.len() - 4;
if let Some(ref body_str) = nop_block_collapse(body.rewrite(context,
budget,
line_indent + 4)) {
if first_line_width(body_str) <= budget {
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() =>
return Some(format!("{}{} => {}{}",
attr_str.trim_left(),
pats_str,
body_str,
comma));
}
comma)),
_ => rewrite,
}
}
} else {
None
};
// We have to push the body to the next line.
if comma.is_empty() {
if let ast::ExprBlock(_) = body.node {
// We're trying to fit a block in, but it still failed, give up.
return None;
}
let body_budget = try_opt!(width.checked_sub(context.config.tab_spaces));
let body_str = try_opt!(nop_block_collapse(body.rewrite(context,
body_budget,
context.block_indent)));
Some(format!("{}{} =>\n{}{},",
let next_line_body = nop_block_collapse(body.rewrite(context,
body_budget,
context.block_indent
.block_indent(context.config)),
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)),
};
Some(format!("{}{} =>{}{},",
attr_str.trim_left(),
pats_str,
offset.block_indent(context.config).to_string(context.config),
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,
}
}
// The `if ...` guard on a match arm.
fn rewrite_guard(context: &RewriteContext,
guard: &Option<ptr::P<ast::Expr>>,

@ -969,8 +969,8 @@ impl<'a> FmtVisitor<'a> {
let extra_indent = match self.config.where_indent {
BlockIndentStyle::Inherit => Indent::empty(),
BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => Indent::new(config.tab_spaces,
0),
BlockIndentStyle::Tabbed | BlockIndentStyle::Visual =>
Indent::new(config.tab_spaces, 0),
};
let context = self.get_context();

@ -85,9 +85,9 @@ const SKIP_ANNOTATION: &'static str = "rustfmt_skip";
pub struct Indent {
// Width of the block indent, in characters. Must be a multiple of
// Config::tab_spaces.
block_indent: usize,
pub block_indent: usize,
// Alignment in characters.
alignment: usize,
pub alignment: usize,
}
impl Indent {

@ -130,125 +130,6 @@ fn issue184(source: &str) {
}
}
fn matches() {
match 1 {
1 => 1, // foo
2 => 2,
// bar
3 => 3,
_ => 0 // baz
}
}
fn issue339() {
match a {
b => {}
c => { }
d => {
}
e => {
}
// collapsing here is safe
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => {
}
// collapsing here exceeds line length
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => {
}
h => { // comment above block
}
i => {
} // comment below block
j => {
// comment inside block
}
j2 => {
// comments inside...
} // ... and after
// TODO uncomment when vertical whitespace is handled better
// k => {
//
// // comment with WS above
// }
// l => {
// // comment with ws below
//
// }
m => {
} n => { } o =>
{
}
p => { // Dont collapse me
} q => { } r =>
{
}
s => 0, // s comment
// t comment
t => 1,
u => 2,
// TODO uncomment when block-support exists
// v => {
// } /* funky block
// * comment */
// final comment
}
}
fn issue355() {
match mac {
a => println!("a", b),
b => vec!(1, 2),
c => vec!(3; 4),
d => {
println!("a", b)
}
e => {
vec!(1, 2)
}
f => {
vec!(3; 4)
}
h => println!("a", b), // h comment
i => vec!(1, 2), // i comment
j => vec!(3; 4), // j comment
// k comment
k => println!("a", b),
// l comment
l => vec!(1, 2),
// m comment
m => vec!(3; 4),
// Rewrite splits macro
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => println!("a", b),
// Rewrite splits macro
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo => vec!(1, 2),
// Macro support fails to recognise this macro as splitable
// We push the whole expr to a new line, TODO split this macro as well
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp => vec!(3; 4),
// q, r and s: Rewrite splits match arm
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq => println!("a", b),
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr => vec!(1, 2),
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss => vec!(3; 4),
// Funky bracketing styles
t => println!{"a", b},
u => vec!{1, 2},
v => vec!{3; 4},
w => println!["a", b],
x => vec![1, 2],
y =>vec![3; 4],
// Brackets with comments
tc => println!{"a", b}, // comment
uc => vec!{1, 2}, // comment
vc =>vec!{3; 4}, // comment
wc =>println!["a", b], // comment
xc => vec![1,2], // comment
yc => vec![3; 4], // comment
}
}
fn arrays() {
let x = [0,
1,

223
tests/source/match.rs Normal file

@ -0,0 +1,223 @@
// Match expressions.
fn foo() {
// A match expression.
match x {
// Some comment.
a => foo(),
b if 0 < 42 => foo(),
c => { // Another comment.
// Comment.
an_expression;
foo()
}
// Perhaps this should introduce braces?
Foo(ref bar) =>
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
Pattern1 | Pattern2 | Pattern3 => false,
Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn |
Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => {
blah
}
Patternnnnnnnnnnnnnnnnnnn |
Patternnnnnnnnnnnnnnnnnnn |
Patternnnnnnnnnnnnnnnnnnn |
Patternnnnnnnnnnnnnnnnnnn => meh,
Patternnnnnnnnnnnnnnnnnnn |
Patternnnnnnnnnnnnnnnnnnn if looooooooooooooooooong_guard => meh,
Patternnnnnnnnnnnnnnnnnnnnnnnnn |
Patternnnnnnnnnnnnnnnnnnnnnnnnn if looooooooooooooooooooooooooooooooooooooooong_guard =>
meh,
// Test that earlier patterns can take the guard space
(aaaa, bbbbb, ccccccc, aaaaa, bbbbbbbb, cccccc, aaaa, bbbbbbbb, cccccc, dddddd) |
Patternnnnnnnnnnnnnnnnnnnnnnnnn if loooooooooooooooooooooooooooooooooooooooooong_guard => {}
_ => {}
ast::PathParameters::AngleBracketedParameters(ref data) if data.lifetimes.len() > 0 ||
data.types.len() > 0 ||
data.bindings.len() > 0 => {}
}
let whatever = match something {
/// DOC COMMENT!
Some(_) => 42,
// Comment on an attribute.
#[an_attribute]
// Comment after an attribute.
None => 0,
#[rustfmt_skip]
Blurb => { }
};
}
// Test that a match on an overflow line is laid out properly.
fn main() {
let sub_span =
match xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {
Some(sub_span) => Some(sub_span),
None => sub_span,
};
}
// Test that one-line bodies align.
fn main() {
match r {
Variableeeeeeeeeeeeeeeeee => ( "variable",
vec!("id", "name", "qualname",
"value", "type", "scopeid"),
true,
true),
Enummmmmmmmmmmmmmmmmmmmm => ("enum",
vec!("id","qualname","scopeid","value"),
true,
true),
Variantttttttttttttttttttttttt => ("variant",
vec!("id",
"name",
"qualname",
"type",
"value",
"scopeid"),
true,
true),
}
}
fn matches() {
match 1 {
1 => 1, // foo
2 => 2,
// bar
3 => 3,
_ => 0 // baz
}
}
fn issue339() {
match a {
b => {}
c => { }
d => {
}
e => {
}
// collapsing here is safe
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => {
}
// collapsing here exceeds line length
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => {
}
h => { // comment above block
}
i => {
} // comment below block
j => {
// comment inside block
}
j2 => {
// comments inside...
} // ... and after
// TODO uncomment when vertical whitespace is handled better
// k => {
//
// // comment with WS above
// }
// l => {
// // comment with ws below
//
// }
m => {
} n => { } o =>
{
}
p => { // Dont collapse me
} q => { } r =>
{
}
s => 0, // s comment
// t comment
t => 1,
u => 2,
// TODO uncomment when block-support exists
// v => {
// } /* funky block
// * comment */
// final comment
}
}
fn issue355() {
match mac {
a => println!("a", b),
b => vec!(1, 2),
c => vec!(3; 4),
d => {
println!("a", b)
}
e => {
vec!(1, 2)
}
f => {
vec!(3; 4)
}
h => println!("a", b), // h comment
i => vec!(1, 2), // i comment
j => vec!(3; 4), // j comment
// k comment
k => println!("a", b),
// l comment
l => vec!(1, 2),
// m comment
m => vec!(3; 4),
// Rewrite splits macro
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => println!("a", b),
// Rewrite splits macro
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo => vec!(1, 2),
// Macro support fails to recognise this macro as splitable
// We push the whole expr to a new line, TODO split this macro as well
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp => vec!(3; 4),
// q, r and s: Rewrite splits match arm
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq => println!("a", b),
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr => vec!(1, 2),
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss => vec!(3; 4),
// Funky bracketing styles
t => println!{"a", b},
u => vec!{1, 2},
v => vec!{3; 4},
w => println!["a", b],
x => vec![1, 2],
y =>vec![3; 4],
// Brackets with comments
tc => println!{"a", b}, // comment
uc => vec!{1, 2}, // comment
vc =>vec!{3; 4}, // comment
wc =>println!["a", b], // comment
xc => vec![1,2], // comment
yc => vec![3; 4], // comment
yd =>
looooooooooooooooooooooooooooooooooooooooooooooooooooooooong_func(aaaaaaaaaa,
bbbbbbbbbb,
cccccccccc,
dddddddddd),
}
}
fn issue280() {
{
match x {
CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => ch ==
'\n',
ast::ItemConst(ref typ, ref expr) => self.process_static_or_const_item(item,
&typ,
&expr),
}
}
}

@ -167,121 +167,6 @@ fn issue184(source: &str) {
}
}
fn matches() {
match 1 {
1 => 1, // foo
2 => 2,
// bar
3 => 3,
_ => 0, // baz
}
}
fn issue339() {
match a {
b => {}
c => {}
d => {}
e => {}
// collapsing here is safe
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => {}
// collapsing here exceeds line length
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => {
}
h => { // comment above block
}
i => {} // comment below block
j => {
// comment inside block
}
j2 => {
// comments inside...
} // ... and after
// TODO uncomment when vertical whitespace is handled better
// k => {
//
// // comment with WS above
// }
// l => {
// // comment with ws below
//
// }
m => {}
n => {}
o => {}
p => { // Dont collapse me
}
q => {}
r => {}
s => 0, // s comment
// t comment
t => 1,
u => 2,
// TODO uncomment when block-support exists
// v => {
// } /* funky block
// * comment */
// final comment
}
}
fn issue355() {
match mac {
a => println!("a", b),
b => vec!(1, 2),
c => vec!(3; 4),
d => {
println!("a", b)
}
e => {
vec!(1, 2)
}
f => {
vec!(3; 4)
}
h => println!("a", b), // h comment
i => vec!(1, 2), // i comment
j => vec!(3; 4), // j comment
// k comment
k => println!("a", b),
// l comment
l => vec!(1, 2),
// m comment
m => vec!(3; 4),
// Rewrite splits macro
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => println!("a",
b),
// Rewrite splits macro
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo => vec!(1,
2),
// Macro support fails to recognise this macro as splitable
// We push the whole expr to a new line, TODO split this macro as well
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp =>
vec!(3; 4),
// q, r and s: Rewrite splits match arm
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq =>
println!("a", b),
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr =>
vec!(1, 2),
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss =>
vec!(3; 4),
// Funky bracketing styles
t => println!{"a", b},
u => vec!{1, 2},
v => vec!{3; 4},
w => println!["a", b],
x => vec![1, 2],
y => vec![3; 4],
// Brackets with comments
tc => println!{"a", b}, // comment
uc => vec!{1, 2}, // comment
vc => vec!{3; 4}, // comment
wc => println!["a", b], // comment
xc => vec![1, 2], // comment
yc => vec![3; 4], // comment
}
}
fn arrays() {
let x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 7, 8, 9, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 0];

@ -73,14 +73,140 @@ fn main() {
vec!("id", "qualname", "scopeid", "value"),
true,
true),
Variantttttttttttttttttttttttt => ("variant",
vec!("id",
"name",
"qualname",
"type",
"value",
"scopeid"),
true,
true),
Variantttttttttttttttttttttttt =>
("variant",
vec!("id", "name", "qualname", "type", "value", "scopeid"),
true,
true),
}
}
fn matches() {
match 1 {
1 => 1, // foo
2 => 2,
// bar
3 => 3,
_ => 0, // baz
}
}
fn issue339() {
match a {
b => {}
c => {}
d => {}
e => {}
// collapsing here is safe
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff => {}
// collapsing here exceeds line length
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg => {
}
h => { // comment above block
}
i => {} // comment below block
j => {
// comment inside block
}
j2 => {
// comments inside...
} // ... and after
// TODO uncomment when vertical whitespace is handled better
// k => {
//
// // comment with WS above
// }
// l => {
// // comment with ws below
//
// }
m => {}
n => {}
o => {}
p => { // Dont collapse me
}
q => {}
r => {}
s => 0, // s comment
// t comment
t => 1,
u => 2,
// TODO uncomment when block-support exists
// v => {
// } /* funky block
// * comment */
// final comment
}
}
fn issue355() {
match mac {
a => println!("a", b),
b => vec!(1, 2),
c => vec!(3; 4),
d => {
println!("a", b)
}
e => {
vec!(1, 2)
}
f => {
vec!(3; 4)
}
h => println!("a", b), // h comment
i => vec!(1, 2), // i comment
j => vec!(3; 4), // j comment
// k comment
k => println!("a", b),
// l comment
l => vec!(1, 2),
// m comment
m => vec!(3; 4),
// Rewrite splits macro
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn =>
println!("a", b),
// Rewrite splits macro
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo =>
vec!(1, 2),
// Macro support fails to recognise this macro as splitable
// We push the whole expr to a new line, TODO split this macro as well
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp =>
vec!(3; 4),
// q, r and s: Rewrite splits match arm
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq =>
println!("a", b),
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr =>
vec!(1, 2),
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss =>
vec!(3; 4),
// Funky bracketing styles
t => println!{"a", b},
u => vec!{1, 2},
v => vec!{3; 4},
w => println!["a", b],
x => vec![1, 2],
y => vec![3; 4],
// Brackets with comments
tc => println!{"a", b}, // comment
uc => vec!{1, 2}, // comment
vc => vec!{3; 4}, // comment
wc => println!["a", b], // comment
xc => vec![1, 2], // comment
yc => vec![3; 4], // comment
yd => looooooooooooooooooooooooooooooooooooooooooooooooooooooooong_func(aaaaaaaaaa,
bbbbbbbbbb,
cccccccccc,
dddddddddd),
}
}
fn issue280() {
{
match x {
CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline =>
ch == '\n',
ast::ItemConst(ref typ, ref expr) =>
self.process_static_or_const_item(item, &typ, &expr),
}
}
}