From c4101de53d8a0707907cb03c67a9224ab9943d37 Mon Sep 17 00:00:00 2001 From: Marcus Klaas Date: Mon, 20 Jul 2015 23:29:25 +0200 Subject: [PATCH] Refactor some things; add extra tests. --- src/config.rs | 13 ++ src/default.toml | 1 + src/expr.rs | 219 +++++++++++++++++---------- src/imports.rs | 8 +- src/lib.rs | 3 +- tests/config/expr_visual_indent.toml | 16 ++ tests/config/reorder_imports.toml | 1 + tests/config/small_tabs.toml | 1 + tests/config/visual_struct_lits.toml | 1 + tests/source/expr-visual-indent.rs | 9 ++ tests/source/expr.rs | 24 +++ tests/source/struct_lits.rs | 11 ++ tests/target/expr-visual-indent.rs | 9 ++ tests/target/expr.rs | 44 ++++++ tests/target/struct_lits.rs | 20 ++- 15 files changed, 292 insertions(+), 88 deletions(-) create mode 100644 tests/config/expr_visual_indent.toml create mode 100644 tests/source/expr-visual-indent.rs create mode 100644 tests/target/expr-visual-indent.rs diff --git a/src/config.rs b/src/config.rs index a01d3e64689..3d41b51661e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,18 @@ use lists::SeparatorTactic; use issues::ReportTactic; +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum BlockIndentStyle { + // Same level as parent. + Inherit, + // One level deeper than parent. + Tabbed, + // Aligned with block open. + Visual, +} + +impl_enum_decodable!(BlockIndentStyle, Inherit, Tabbed, Visual); + #[derive(RustcDecodable, Clone)] pub struct Config { pub max_width: usize, @@ -31,6 +43,7 @@ pub struct Config { pub report_todo: ReportTactic, pub report_fixme: ReportTactic, pub reorder_imports: bool, // Alphabetically, case sensitive. + pub expr_indent_style: BlockIndentStyle, } impl Config { diff --git a/src/default.toml b/src/default.toml index e31a0f257e9..035fa21e917 100644 --- a/src/default.toml +++ b/src/default.toml @@ -13,3 +13,4 @@ enum_trailing_comma = true report_todo = "Always" report_fixme = "Never" reorder_imports = false +expr_indent_style = "Tabbed" diff --git a/src/expr.rs b/src/expr.rs index 35bd90f5f32..e81314eba79 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -14,6 +14,7 @@ use StructLitStyle; use utils::{span_after, make_indent}; use visitor::FmtVisitor; +use config::BlockIndentStyle; use syntax::{ast, ptr}; use syntax::codemap::{Pos, Span, BytePos, mk_sp}; @@ -57,48 +58,16 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti rewrite_tuple_lit(context, items, self.span, width, offset) } ast::Expr_::ExprWhile(ref cond, ref block, label) => { - rewrite_loop(context, - cond, - block, - label, - None, - "while ", - "let ", - " = ", - width, - offset) + Loop::new_while(None, cond, block, label).rewrite(context, width, offset) } ast::Expr_::ExprWhileLet(ref pat, ref cond, ref block, label) => { - rewrite_loop(context, - cond, - block, - label, - Some(pat), - "while ", - "let ", - " = ", - width, - offset) + Loop::new_while(Some(pat), cond, block, label).rewrite(context, width, offset) } ast::Expr_::ExprForLoop(ref pat, ref cond, ref block, label) => { - rewrite_loop(context, - cond, - block, - label, - Some(pat), - "for ", - "", - " in ", - width, - offset) + Loop::new_for(pat, cond, block, label).rewrite(context, width, offset) } ast::Expr_::ExprLoop(ref block, label) => { - // Of all the loops, this is the only one that does not use - // rewrite_loop! - // FIXME: this drops any comment between "loop" and the block. - block.rewrite(context, width, offset).map(|result| { - format!("{}loop {}", rewrite_label(label), result) - }) + Loop::new_loop(block, label).rewrite(context, width, offset) } ast::Expr_::ExprBlock(ref block) => { block.rewrite(context, width, offset) @@ -121,7 +90,8 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti width, offset) } - // We reformat it ourselves because rustc gives us a bad span for ranges + // We reformat it ourselves because rustc gives us a bad span + // for ranges, see rust#27162 ast::Expr_::ExprRange(ref left, ref right) => { rewrite_range(context, left.as_ref().map(|e| &**e), @@ -161,6 +131,91 @@ fn rewrite(&self, context: &RewriteContext, _: usize, _: usize) -> Option { + cond: Option<&'a ast::Expr>, + block: &'a ast::Block, + label: Option, + pat: Option<&'a ast::Pat>, + keyword: &'a str, + matcher: &'a str, + connector: &'a str, +} + +impl<'a> Loop<'a> { + fn new_loop(block: &'a ast::Block, label: Option) -> Loop<'a> { + Loop { + cond: None, + block: block, + label: label, + pat: None, + keyword: "loop", + matcher: "", + connector: "", + } + } + + fn new_while(pat: Option<&'a ast::Pat>, + cond: &'a ast::Expr, + block: &'a ast::Block, + label: Option) + -> Loop<'a> { + Loop { + cond: Some(cond), + block: block, + label: label, + pat: pat, + keyword: "while ", + matcher: match pat { + Some(..) => "let ", + None => "" + }, + connector: " =", + } + } + + fn new_for(pat: &'a ast::Pat, + cond: &'a ast::Expr, + block: &'a ast::Block, + label: Option) + -> Loop<'a> { + Loop { + cond: Some(cond), + block: block, + label: label, + pat: Some(pat), + keyword: "for ", + matcher: "", + connector: " in", + } + } +} + +impl<'a> Rewrite for Loop<'a> { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + let label_string = rewrite_label(self.label); + // 2 = " {".len() + let inner_width = width - self.keyword.len() - 2 - label_string.len(); + 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)), + None => String::new() + }; + + // FIXME: this drops any comment between "loop" and the block. + self.block.rewrite(context, width, offset).map(|result| { + format!("{}{}{} {}", label_string, self.keyword, pat_expr_string, result) + }) + } +} + fn rewrite_label(label: Option) -> String { match label { Some(ident) => format!("{}: ", ident), @@ -193,36 +248,8 @@ fn rewrite_range(context: &RewriteContext, Some(format!("{}..{}", left_string, right_string)) } -fn rewrite_loop(context: &RewriteContext, - cond: &ast::Expr, - block: &ast::Block, - label: Option, - pat: Option<&ast::Pat>, - keyword: &str, - matcher: &str, // FIXME: think of better identifiers - connector: &str, - width: usize, - offset: usize) - -> Option { - let label_string = rewrite_label(label); - // 2 = " {" - let inner_width = width - keyword.len() - 2 - label_string.len(); - let inner_offset = offset + keyword.len() + label_string.len(); - - let pat_expr_string = try_opt!(rewrite_pat_expr(context, - pat, - cond, - matcher, - connector, - inner_width, - inner_offset)); - - // FIXME: this drops any comment between "loop" and the block. - block.rewrite(context, width, offset).map(|result| { - format!("{}{}{} {}", label_string, keyword, pat_expr_string, result) - }) -} - +// Rewrites if-else blocks. If let Some(_) = pat, the expression is +// treated as an if-let-else expression. fn rewrite_if_else(context: &RewriteContext, cond: &ast::Expr, if_block: &ast::Block, @@ -236,7 +263,7 @@ fn rewrite_if_else(context: &RewriteContext, pat, cond, "let ", - " = ", + " =", width - 3 - 2, offset + 3)); @@ -261,28 +288,49 @@ fn rewrite_pat_expr(context: &RewriteContext, width: usize, offset: usize) -> Option { + let pat_offset = offset + matcher.len(); let mut result = match pat { Some(pat) => { let pat_string = try_opt!(pat.rewrite(context, width - connector.len() - matcher.len(), - offset + matcher.len())); + pat_offset)); format!("{}{}{}", matcher, pat_string, connector) } None => String::new() }; - // Consider only the last line of the pat string + // Consider only the last line of the pat string. let extra_offset = match result.rfind('\n') { // 1 for newline character Some(idx) => result.len() - idx - 1 - offset, None => result.len() }; - let expr_string = try_opt!(expr.rewrite(context, - width - extra_offset, - offset + extra_offset)); + // The expression may (partionally) fit on the current line. + if width > extra_offset + 1 { + let mut corrected_offset = extra_offset; - result.push_str(&expr_string); + if pat.is_some() { + result.push(' '); + corrected_offset += 1; + } + + let expr_rewrite = expr.rewrite(context, + width - corrected_offset, + offset + corrected_offset); + + if let Some(expr_string) = expr_rewrite { + result.push_str(&expr_string); + return Some(result); + } + } + + // The expression won't fit on the current line, jump to next. + result.push('\n'); + result.push_str(&make_indent(pat_offset)); + + let expr_rewrite = expr.rewrite(context, context.config.max_width - pat_offset, pat_offset); + result.push_str(&&try_opt!(expr_rewrite)); Some(result) } @@ -333,6 +381,8 @@ fn rewrite_call(context: &RewriteContext, // 2 is for parens. let remaining_width = width - callee_str.len() - 2; let offset = callee_str.len() + 1 + offset; + let block_indent = expr_block_indent(context, offset); + let inner_context = &RewriteContext { block_indent: block_indent, ..*context }; let items = itemize_list(context.codemap, Vec::new(), @@ -342,7 +392,7 @@ fn rewrite_call(context: &RewriteContext, |item| item.span.lo, |item| item.span.hi, // Take old span when rewrite fails. - |item| item.rewrite(context, remaining_width, offset) + |item| item.rewrite(inner_context, remaining_width, offset) .unwrap_or(context.codemap.span_to_snippet(item.span) .unwrap()), callee.span.hi + BytePos(1), @@ -361,6 +411,14 @@ fn rewrite_call(context: &RewriteContext, Some(format!("{}({})", callee_str, write_list(&items, &fmt))) } +fn expr_block_indent(context: &RewriteContext, offset: usize) -> usize { + match context.config.expr_indent_style { + BlockIndentStyle::Inherit => context.block_indent, + BlockIndentStyle::Tabbed => context.block_indent + context.config.tab_spaces, + BlockIndentStyle::Visual => offset, + } +} + fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, @@ -391,17 +449,18 @@ enum StructLitField<'a> { } let path_str = pprust::path_to_string(path); - let (indent, h_budget, v_budget) = match context.config.struct_lit_style { + // 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::VisualIndent => { - // Foo { a: Foo } - indent is +3, width is -5. - let budget = width - (path_str.len() + 5); - (offset + path_str.len() + 3, budget, budget) + (offset + path_str.len() + 3, h_budget) } StructLitStyle::BlockIndent => { // If we are all on one line, then we'll ignore the indent, and we // have a smaller budget. let indent = context.block_indent + context.config.tab_spaces; - (indent, width - (path_str.len() + 5), width - indent) + let v_budget = context.config.max_width.checked_sub(indent).unwrap_or(0); + (indent, v_budget) } }; diff --git a/src/imports.rs b/src/imports.rs index 66bf26b041d..12ce3368eb3 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -123,10 +123,10 @@ pub fn rewrite_use_list(&self, let list = write_list(&items[first_index..], &fmt); Some(if path_str.len() == 0 { - format!("{}use {{{}}};", vis, list) - } else { - format!("{}use {}::{{{}}};", vis, path_str, list) - }) + format!("{}use {{{}}};", vis, list) + } else { + format!("{}use {}::{{{}}};", vis, path_str, list) + }) } } diff --git a/src/lib.rs b/src/lib.rs index 5ea4812e144..7c36229e774 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,10 +49,9 @@ use visitor::FmtVisitor; use config::Config; -#[macro_use] -mod config; #[macro_use] mod utils; +pub mod config; mod changes; mod visitor; mod items; diff --git a/tests/config/expr_visual_indent.toml b/tests/config/expr_visual_indent.toml new file mode 100644 index 00000000000..38a099e3bed --- /dev/null +++ b/tests/config/expr_visual_indent.toml @@ -0,0 +1,16 @@ +max_width = 100 +ideal_width = 80 +leeway = 5 +tab_spaces = 4 +newline_style = "Unix" +fn_brace_style = "SameLineWhere" +fn_return_indent = "WithArgs" +fn_args_paren_newline = true +struct_trailing_comma = "Vertical" +struct_lit_style = "BlockIndent" +struct_lit_trailing_comma = "Vertical" +enum_trailing_comma = true +report_todo = "Always" +report_fixme = "Never" +reorder_imports = false +expr_indent_style = "Visual" diff --git a/tests/config/reorder_imports.toml b/tests/config/reorder_imports.toml index ddab2479f2c..5b1ce49a2f2 100644 --- a/tests/config/reorder_imports.toml +++ b/tests/config/reorder_imports.toml @@ -13,3 +13,4 @@ enum_trailing_comma = true report_todo = "Always" report_fixme = "Never" reorder_imports = true +expr_indent_style = "Tabbed" diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml index 303433dbcc1..b2c7f5fc43f 100644 --- a/tests/config/small_tabs.toml +++ b/tests/config/small_tabs.toml @@ -13,3 +13,4 @@ enum_trailing_comma = true report_todo = "Always" report_fixme = "Never" reorder_imports = false +expr_indent_style = "Tabbed" diff --git a/tests/config/visual_struct_lits.toml b/tests/config/visual_struct_lits.toml index cf601303e9b..61bf4b0aee5 100644 --- a/tests/config/visual_struct_lits.toml +++ b/tests/config/visual_struct_lits.toml @@ -13,3 +13,4 @@ enum_trailing_comma = true report_todo = "Always" report_fixme = "Never" reorder_imports = false +expr_indent_style = "Tabbed" diff --git a/tests/source/expr-visual-indent.rs b/tests/source/expr-visual-indent.rs new file mode 100644 index 00000000000..c173d7bd7c4 --- /dev/null +++ b/tests/source/expr-visual-indent.rs @@ -0,0 +1,9 @@ +// rustfmt-config: expr_visual_indent.toml + +// Visual level block indentation. + +fn matcher() { + Some(while true { + test(); + }) +} \ No newline at end of file diff --git a/tests/source/expr.rs b/tests/source/expr.rs index 19baa90ace2..1c063cf7f9d 100644 --- a/tests/source/expr.rs +++ b/tests/source/expr.rs @@ -14,6 +14,10 @@ fn foo() -> bool { (((((((((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + a + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaa))))))))); + { for _ in 0..10 {} } + + {{{{}}}} + if 1 + 2 > 0 { let result = 5; result } else { 4}; if let Some(x) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { @@ -30,6 +34,10 @@ fn foo() -> bool { if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1111 + 2222 {} + if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1 + + 2 + 3 { + } + if cond() { something(); } else if different_cond() { @@ -39,3 +47,19 @@ fn foo() -> bool { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } } + +fn bar() { + let range = ( 111111111 + 333333333333333333 + 1111 + 400000000000000000) .. (2222 + 2333333333333333); + + let another_range = 5..some_func( a , b /* comment */); + + for _ in 1 ..{ call_forever(); } + + syntactically_correct(loop { sup( '?'); }, if cond { 0 } else { 1 }); + + let third = ..10; + let infi_range = ..; + let foo = 1..; + let bar = 5; + let nonsense = (10 .. 0)..(0..10); +} diff --git a/tests/source/struct_lits.rs b/tests/source/struct_lits.rs index f2726821e55..54f2ea5441b 100644 --- a/tests/source/struct_lits.rs +++ b/tests/source/struct_lits.rs @@ -22,6 +22,8 @@ fn main() { Foo { a:Bar, b:foo() }; + Quux { x: if cond { bar(); }, y: baz() }; + A { // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. first: item(), @@ -37,3 +39,12 @@ fn main() { * o o o o */ graph: G, } } + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" bool { a + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaa))))))))); + { + for _ in 0..10 { + } + } + + { + { + { + { + } + } + } + } + if 1 + 2 > 0 { let result = 5; result @@ -39,6 +53,10 @@ fn foo() -> bool { 2222 { } + if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = + 1 + 2 + 3 { + } + if cond() { something(); } else if different_cond() { @@ -49,3 +67,29 @@ fn foo() -> bool { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } } + +fn bar() { + let range = (111111111 + 333333333333333333 + 1111 + 400000000000000000)..(2222 + + 2333333333333333); + + let another_range = 5..some_func(a, b /* comment */); + + for _ in 1.. { + call_forever(); + } + + syntactically_correct(loop { + sup('?'); + }, + if cond { + 0 + } else { + 1 + }); + + let third = ..10; + let infi_range = ..; + let foo = 1..; + let bar = 5; + let nonsense = (10..0)..(0..10); +} diff --git a/tests/target/struct_lits.rs b/tests/target/struct_lits.rs index 293a2a97b7a..9f7ab5cb428 100644 --- a/tests/target/struct_lits.rs +++ b/tests/target/struct_lits.rs @@ -29,9 +29,16 @@ fn main() { Foo { a: Bar, b: foo() }; + Quux { + x: if cond { + bar(); + }, + y: baz(), + }; + A { - // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed - // sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante + // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit + // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante // hendrerit. Donec et mollis dolor. first: item(), // Praesent et diam eget libero egestas mattis sit amet vitae augue. @@ -48,3 +55,12 @@ fn main() { graph: G, } } + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b"