Implement single line if-else formatting

This commit is contained in:
Marcus Klaas 2015-08-25 21:46:58 +02:00
parent eff87a4a66
commit b7a71250f4
8 changed files with 163 additions and 5 deletions

View File

@ -79,4 +79,5 @@ create_config! {
reorder_imports: bool, // Alphabetically, case sensitive.
expr_indent_style: BlockIndentStyle,
closure_indent_style: BlockIndentStyle,
single_line_if_else: bool,
}

View File

@ -15,3 +15,4 @@ report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Tabbed"
closure_indent_style = "Visual"
single_line_if_else = false

View File

@ -20,7 +20,7 @@ use types::rewrite_path;
use items::{span_lo_for_arg, span_hi_for_arg, rewrite_fn_input};
use syntax::{ast, ptr};
use syntax::codemap::{Pos, Span, BytePos, mk_sp};
use syntax::codemap::{CodeMap, Pos, Span, BytePos, mk_sp};
use syntax::visit::Visitor;
impl Rewrite for ast::Expr {
@ -412,7 +412,7 @@ fn rewrite_range(context: &RewriteContext,
fn rewrite_if_else(context: &RewriteContext,
cond: &ast::Expr,
if_block: &ast::Block,
else_block: Option<&ast::Expr>,
else_block_opt: Option<&ast::Expr>,
pat: Option<&ast::Pat>,
width: usize,
offset: usize)
@ -423,13 +423,22 @@ fn rewrite_if_else(context: &RewriteContext,
cond,
"let ",
" =",
width - 3 - 2,
try_opt!(width.checked_sub(3 + 2)),
offset + 3));
// Try to format if-else on single line.
if context.config.single_line_if_else {
let trial = single_line_if_else(context, &pat_expr_string, if_block, else_block_opt, width);
if trial.is_some() {
return trial;
}
}
let if_block_string = try_opt!(if_block.rewrite(context, width, offset));
let mut result = format!("if {} {}", pat_expr_string, if_block_string);
if let Some(else_block) = else_block {
if let Some(else_block) = else_block_opt {
let else_block_string = try_opt!(else_block.rewrite(context, width, offset));
result.push_str(" else ");
@ -439,6 +448,52 @@ fn rewrite_if_else(context: &RewriteContext,
Some(result)
}
fn single_line_if_else(context: &RewriteContext,
pat_expr_str: &str,
if_node: &ast::Block,
else_block_opt: Option<&ast::Expr>,
width: usize)
-> Option<String> {
let else_block = try_opt!(else_block_opt);
let fixed_cost = "if { } else { }".len();
if let ast::ExprBlock(ref else_node) = else_block.node {
if !is_simple_block(if_node, context.codemap) ||
!is_simple_block(else_node, context.codemap) || pat_expr_str.contains('\n') {
return None;
}
let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
let if_expr = if_node.expr.as_ref().unwrap();
let if_str = try_opt!(if_expr.rewrite(context, new_width, 0));
let new_width = try_opt!(new_width.checked_sub(if_str.len()));
let else_expr = else_node.expr.as_ref().unwrap();
let else_str = try_opt!(else_expr.rewrite(context, new_width, 0));
// FIXME: this check shouldn't be necessary. Rewrites should either fail
// or wrap to a newline when the object does not fit the width.
let fits_line = fixed_cost + pat_expr_str.len() + if_str.len() + else_str.len() <= width;
if fits_line && !if_str.contains('\n') && !else_str.contains('\n') {
return Some(format!("if {} {{ {} }} else {{ {} }}", pat_expr_str, if_str, else_str));
}
}
None
}
// Checks that a block contains no statements, an expression and no comments.
fn is_simple_block(block: &ast::Block, codemap: &CodeMap) -> bool {
if !block.stmts.is_empty() || block.expr.is_none() {
return false;
}
let snippet = codemap.span_to_snippet(block.span).unwrap();
!snippet.contains("//") && !snippet.contains("/*")
}
fn rewrite_match(context: &RewriteContext,
cond: &ast::Expr,
arms: &[ast::Arm],
@ -830,7 +885,7 @@ fn rewrite_paren(context: &RewriteContext,
debug!("rewrite_paren, width: {}, offset: {}", width, offset);
// 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
// paren on the same line as the subexpr.
let subexpr_str = subexpr.rewrite(context, width-2, offset+1);
let subexpr_str = subexpr.rewrite(context, try_opt!(width.checked_sub(2)), offset + 1);
debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
subexpr_str.map(|s| format!("({})", s))
}

View File

@ -15,3 +15,4 @@ report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Tabbed"
closure_indent_style = "Visual"
single_line_if_else = false

View File

@ -43,6 +43,8 @@ some_ridiculously_loooooooooooooooooooooong_function(10000 * 30000000000 + 40000
+ 2 + 3 {
}
let test = if true { 5 } else { 3 };
if cond() {
something();
} else if different_cond() {

View File

@ -0,0 +1,46 @@
// rustfmt-single_line_if_else: true
// Format if-else expressions on a single line, when possible.
fn main() {
let a = if 1 > 2 {
unreachable!()
} else {
10
};
let a = if x { 1 } else if y { 2 } else { 3 };
let b = if cond() {
5
} else {
// Brief comment.
10
};
let c = if cond() {
statement();
5
} else {
10
};
if cond() { statement(); } else { other_statement(); }
if true {
do_something()
}
let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaa } else { bbbbbbbbbb };
let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaa } else {
bbbbbbbbbb };
funk(if test() {
1
} else {
2
},
arg2);
}

View File

@ -61,6 +61,12 @@ fn foo() -> bool {
1 + 2 + 3 {
}
let test = if true {
5
} else {
3
};
if cond() {
something();
} else if different_cond() {

View File

@ -0,0 +1,46 @@
// rustfmt-single_line_if_else: true
// Format if-else expressions on a single line, when possible.
fn main() {
let a = if 1 > 2 { unreachable!() } else { 10 };
let a = if x {
1
} else if y { 2 } else { 3 };
let b = if cond() {
5
} else {
// Brief comment.
10
};
let c = if cond() {
statement();
5
} else {
10
};
if cond() {
statement();
} else {
other_statement();
}
if true {
do_something()
}
let x = if veeeeeeeeery_loooooong_condition() {
aaaaaaaaaaaaaaaaaaaaaaaaaaa
} else {
bbbbbbbbbb
};
let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaa } else { bbbbbbbbbb };
funk(if test() { 1 } else { 2 }, arg2);
}