diff --git a/src/config.rs b/src/config.rs index 3316984b861..82715c2164a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,6 +38,22 @@ configuration_option_enum! { BraceStyle: SameLineWhere, } +configuration_option_enum! { ControlBraceStyle: + // K&R/Stroustrup style, Rust community default + AlwaysSameLine, + // Allman style + AlwaysNextLine, +} + +configuration_option_enum! { ElseIfBraceStyle: + // K&R style, Rust community default + AlwaysSameLine, + // Stroustrup style + ClosingNextLine, + // Allman style + AlwaysNextLine, +} + // How to indent a function's return type. configuration_option_enum! { ReturnIndent: // Aligned with the arguments @@ -315,6 +331,10 @@ create_config! { newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings"; fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions"; item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums"; + else_if_brace_style: ElseIfBraceStyle, ElseIfBraceStyle::AlwaysSameLine, + "Brace style for if, else if, and else constructs"; + control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, + "Brace style for match, loop, for, and while constructs"; impl_empty_single_line: bool, true, "Put empty-body implementations on a single line"; fn_empty_single_line: bool, true, "Put empty-body functions on a single line"; fn_single_line: bool, false, "Put single-expression functions on a single line"; diff --git a/src/expr.rs b/src/expr.rs index 90ccb87e2d4..248cc7e090a 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -23,7 +23,7 @@ use string::{StringFormat, rewrite_string}; use utils::{CodeMapSpanUtils, extra_offset, last_line_width, wrap_str, binary_search, first_line_width, semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr}; use visitor::FmtVisitor; -use config::{Config, StructLitStyle, MultilineStyle}; +use config::{Config, StructLitStyle, MultilineStyle, ElseIfBraceStyle, ControlBraceStyle}; use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed}; use types::rewrite_path; use items::{span_lo_for_arg, span_hi_for_arg}; @@ -648,14 +648,20 @@ impl<'a> Rewrite for Loop<'a> { None => String::new(), }; + let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let block_sep = match context.config.control_brace_style { + ControlBraceStyle::AlwaysNextLine => alt_block_sep.as_str(), + ControlBraceStyle::AlwaysSameLine => " ", + }; // FIXME: this drops any comment between "loop" and the block. self.block .rewrite(context, width, offset) .map(|result| { - format!("{}{}{} {}", + format!("{}{}{}{}{}", label_string, self.keyword, pat_expr_string, + block_sep, result) }) } @@ -701,12 +707,16 @@ fn rewrite_if_else(context: &RewriteContext, allow_single_line: bool) -> Option { // 3 = "if ", 2 = " {" + let pat_penalty = match context.config.else_if_brace_style { + ElseIfBraceStyle::AlwaysNextLine => 3, + _ => 3 + 2, + }; let pat_expr_string = try_opt!(rewrite_pat_expr(context, pat, cond, "let ", " =", - try_opt!(width.checked_sub(3 + 2)), + try_opt!(width.checked_sub(pat_penalty)), offset + 3)); // Try to format if-else on single line. @@ -731,13 +741,19 @@ fn rewrite_if_else(context: &RewriteContext, offset, width); + let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let after_sep = match context.config.else_if_brace_style { + ElseIfBraceStyle::AlwaysNextLine => alt_block_sep.as_str(), + _ => " ", + }; let mut result = format!("if{}{}{}{}", between_if_cond_comment.as_ref().map_or(" ", |str| &**str), pat_expr_string, - after_cond_comment.as_ref().map_or(" ", |str| &**str), + after_cond_comment.as_ref().map_or(after_sep, |str| &**str), if_block_string); if let Some(else_block) = else_block_opt { + let mut last_in_chain = false; let rewrite = match else_block.node { // If the else expression is another if-else expression, prevent it // from being formatted on a single line. @@ -763,7 +779,10 @@ fn rewrite_if_else(context: &RewriteContext, offset, false) } - _ => else_block.rewrite(context, width, offset), + _ => { + last_in_chain = true; + else_block.rewrite(context, width, offset) + } }; let between_if_else_block = mk_sp(if_block.span.hi, @@ -781,10 +800,20 @@ fn rewrite_if_else(context: &RewriteContext, else_block.span.lo); let after_else_comment = extract_comment(after_else, &context, offset, width); + let between_sep = match context.config.else_if_brace_style { + ElseIfBraceStyle::AlwaysNextLine | + ElseIfBraceStyle::ClosingNextLine => alt_block_sep.as_str(), + ElseIfBraceStyle::AlwaysSameLine => " ", + }; + let after_sep = match context.config.else_if_brace_style { + ElseIfBraceStyle::AlwaysNextLine if last_in_chain => alt_block_sep.as_str(), + _ => " ", + }; try_opt!(write!(&mut result, "{}else{}", - between_if_else_block_comment.as_ref().map_or(" ", |str| &**str), - after_else_comment.as_ref().map_or(" ", |str| &**str)) + between_if_else_block_comment.as_ref() + .map_or(between_sep, |str| &**str), + after_else_comment.as_ref().map_or(after_sep, |str| &**str)) .ok()); result.push_str(&&try_opt!(rewrite)); } @@ -917,7 +946,12 @@ fn rewrite_match(context: &RewriteContext, // `match `cond` {` let cond_budget = try_opt!(width.checked_sub(8)); let cond_str = try_opt!(cond.rewrite(context, cond_budget, offset + 6)); - let mut result = format!("match {} {{", cond_str); + let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let block_sep = match context.config.control_brace_style { + ControlBraceStyle::AlwaysSameLine => " ", + ControlBraceStyle::AlwaysNextLine => alt_block_sep.as_str(), + }; + let mut result = format!("match {}{}{{", cond_str, block_sep); let nested_context = context.nested_context(); let arm_indent = nested_context.block_indent; @@ -1076,6 +1110,7 @@ impl Rewrite for ast::Arm { }; let comma = arm_comma(&context.config, self, body); + let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); // Let's try and get the arm body on the same line as the condition. // 4 = ` => `.len() @@ -1089,12 +1124,17 @@ impl Rewrite for ast::Arm { false }; + let block_sep = match context.config.control_brace_style { + ControlBraceStyle::AlwaysNextLine if is_block => alt_block_sep.as_str(), + _ => " ", + }; match rewrite { Some(ref body_str) if !body_str.contains('\n') || !context.config.wrap_match_arms || is_block => { - return Some(format!("{}{} => {}{}", + return Some(format!("{}{} =>{}{}{}", attr_str.trim_left(), pats_str, + block_sep, body_str, comma)); } @@ -1122,10 +1162,14 @@ impl Rewrite for ast::Arm { ("", "") }; - Some(format!("{}{} =>{}\n{}{}\n{}{}", + let block_sep = match context.config.control_brace_style { + ControlBraceStyle::AlwaysNextLine => alt_block_sep, + ControlBraceStyle::AlwaysSameLine => String::from(body_prefix) + "\n", + }; + Some(format!("{}{} =>{}{}{}\n{}{}", attr_str.trim_left(), pats_str, - body_prefix, + block_sep, indent_str, next_line_body, offset.to_string(context.config), diff --git a/tests/source/control-brace-style-always-next-line.rs b/tests/source/control-brace-style-always-next-line.rs new file mode 100644 index 00000000000..9a2ec04e300 --- /dev/null +++ b/tests/source/control-brace-style-always-next-line.rs @@ -0,0 +1,44 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + loop { + (); + (); + } + + + 'loop_label: loop // loop comment + { + (); + } + + + cond = true; + while cond { + (); + } + + + 'while_label: while cond { // while comment + (); + } + + + for obj in iter { + for sub_obj in obj + { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + }, + }; +} diff --git a/tests/source/control-brace-style-always-same-line.rs b/tests/source/control-brace-style-always-same-line.rs new file mode 100644 index 00000000000..52b0c8fdc46 --- /dev/null +++ b/tests/source/control-brace-style-always-same-line.rs @@ -0,0 +1,44 @@ +// rustfmt-control_brace_style: AlwaysSameLine + +fn main() { + loop { + (); + (); + } + + + 'loop_label: loop // loop comment + { + (); + } + + + cond = true; + while cond { + (); + } + + + 'while_label: while cond { // while comment + (); + } + + + for obj in iter { + for sub_obj in obj + { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + }, + }; +} diff --git a/tests/source/else-if-brace-style-always-next-line.rs b/tests/source/else-if-brace-style-always-next-line.rs new file mode 100644 index 00000000000..18a8ace1f14 --- /dev/null +++ b/tests/source/else-if-brace-style-always-next-line.rs @@ -0,0 +1,54 @@ +// rustfmt-else_if_brace_style: AlwaysNextLine + +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/source/else-if-brace-style-always-same-line.rs b/tests/source/else-if-brace-style-always-same-line.rs new file mode 100644 index 00000000000..090b1e9a9db --- /dev/null +++ b/tests/source/else-if-brace-style-always-same-line.rs @@ -0,0 +1,54 @@ +// rustfmt-else_if_brace_style: AlwaysSameLine + +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/source/else-if-brace-style-closing-next-line.rs b/tests/source/else-if-brace-style-closing-next-line.rs new file mode 100644 index 00000000000..755b95bd541 --- /dev/null +++ b/tests/source/else-if-brace-style-closing-next-line.rs @@ -0,0 +1,54 @@ +// rustfmt-else_if_brace_style: ClosingNextLine + +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/control-brace-style-always-next-line.rs b/tests/target/control-brace-style-always-next-line.rs new file mode 100644 index 00000000000..534dfb89969 --- /dev/null +++ b/tests/target/control-brace-style-always-next-line.rs @@ -0,0 +1,52 @@ +// rustfmt-control_brace_style: AlwaysNextLine + +fn main() { + loop + { + (); + (); + } + + + 'loop_label: loop + { + (); + } + + + cond = true; + while cond + { + (); + } + + + 'while_label: while cond + { + // while comment + (); + } + + + for obj in iter + { + for sub_obj in obj + { + 'nested_while_label: while cond + { + (); + } + } + } + + match some_var + { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => + { + do_stuff(); + val2 + } + }; +} diff --git a/tests/target/control-brace-style-always-same-line.rs b/tests/target/control-brace-style-always-same-line.rs new file mode 100644 index 00000000000..60a07e46986 --- /dev/null +++ b/tests/target/control-brace-style-always-same-line.rs @@ -0,0 +1,43 @@ +// rustfmt-control_brace_style: AlwaysSameLine + +fn main() { + loop { + (); + (); + } + + + 'loop_label: loop { + (); + } + + + cond = true; + while cond { + (); + } + + + 'while_label: while cond { + // while comment + (); + } + + + for obj in iter { + for sub_obj in obj { + 'nested_while_label: while cond { + (); + } + } + } + + match some_var { // match comment + pattern0 => val0, + pattern1 => val1, + pattern2 | pattern3 => { + do_stuff(); + val2 + } + }; +} diff --git a/tests/target/else-if-brace-style-always-next-line.rs b/tests/target/else-if-brace-style-always-next-line.rs new file mode 100644 index 00000000000..3fbd8b7af25 --- /dev/null +++ b/tests/target/else-if-brace-style-always-next-line.rs @@ -0,0 +1,62 @@ +// rustfmt-else_if_brace_style: AlwaysNextLine + +fn main() { + if false + { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + + let a = if 0 > 1 + { + unreachable!() + } + else + { + 0x0 + }; + + + if true + { + (); + } + else if false + { + (); + (); + } + else + { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } + else if false + // else-if-chain else-if comment + { + (); + (); + } + else + // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/else-if-brace-style-always-same-line.rs b/tests/target/else-if-brace-style-always-same-line.rs new file mode 100644 index 00000000000..393944133f7 --- /dev/null +++ b/tests/target/else-if-brace-style-always-same-line.rs @@ -0,0 +1,51 @@ +// rustfmt-else_if_brace_style: AlwaysSameLine + +fn main() { + if false { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + + let a = if 0 > 1 { + unreachable!() + } else { + 0x0 + }; + + + if true { + (); + } else if false { + (); + (); + } else { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } else if false + // else-if-chain else-if comment + { + (); + (); + } else + // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tests/target/else-if-brace-style-closing-next-line.rs b/tests/target/else-if-brace-style-closing-next-line.rs new file mode 100644 index 00000000000..e50f29d2921 --- /dev/null +++ b/tests/target/else-if-brace-style-closing-next-line.rs @@ -0,0 +1,56 @@ +// rustfmt-else_if_brace_style: ClosingNextLine + +fn main() { + if false { + (); + (); + } + + if false + // lone if comment + { + (); + (); + } + + + let a = if 0 > 1 { + unreachable!() + } + else { + 0x0 + }; + + + if true { + (); + } + else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true + // else-if-chain if comment + { + (); + } + else if false + // else-if-chain else-if comment + { + (); + (); + } + else + // else-if-chain else comment + { + (); + (); + (); + } +}