about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/chains.rs45
-rw-r--r--src/config.rs5
-rw-r--r--tests/source/chains-block-indented-base.rs30
-rw-r--r--tests/source/chains.rs46
-rw-r--r--tests/target/chains-block-indented-base.rs31
-rw-r--r--tests/target/chains.rs64
6 files changed, 211 insertions, 10 deletions
diff --git a/src/chains.rs b/src/chains.rs
index 6122aa14614..4b6824a9073 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -23,6 +23,7 @@ use Indent;
 use rewrite::{Rewrite, RewriteContext};
 use utils::first_line_width;
 use expr::rewrite_call;
+use config::BlockIndentStyle;
 
 use syntax::{ast, ptr};
 use syntax::codemap::{mk_sp, Span};
@@ -41,17 +42,24 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
         expr = subexpr;
     }
 
+    let parent_block_indent = match context.config.chain_base_indent {
+        BlockIndentStyle::Visual => offset,
+        BlockIndentStyle::Inherit => context.block_indent,
+        BlockIndentStyle::Tabbed => context.block_indent.block_indent(context.config),
+    };
+    let parent_context = &RewriteContext { block_indent: parent_block_indent, ..*context };
     let parent = subexpr_list.pop().unwrap();
-    let parent_rewrite = try_opt!(expr.rewrite(context, width, offset));
-    let (extra_indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(parent) ||
-                                    parent_rewrite.len() <= context.config.tab_spaces {
-        (Indent::new(0, parent_rewrite.len()), true)
+    let parent_rewrite = try_opt!(expr.rewrite(parent_context, width, offset));
+    let (indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(parent) ||
+                              parent_rewrite.len() <= context.config.tab_spaces {
+        (offset + Indent::new(0, parent_rewrite.len()), true)
+    } else if is_block_expr(parent, &parent_rewrite) {
+        (parent_block_indent, false)
     } else {
-        (Indent::new(context.config.tab_spaces, 0), false)
+        (offset + Indent::new(context.config.tab_spaces, 0), false)
     };
-    let indent = offset + extra_indent;
 
-    let max_width = try_opt!(width.checked_sub(extra_indent.width()));
+    let max_width = try_opt!((width + offset.width()).checked_sub(indent.width()));
     let mut rewrites = try_opt!(subexpr_list.iter()
                                             .rev()
                                             .map(|e| {
@@ -114,7 +122,7 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
         _ => total_width <= width && rewrites.iter().all(|s| !s.contains('\n')),
     };
 
-    let connector = if fits_single_line {
+    let connector = if fits_single_line && !parent_rewrite.contains('\n') {
         String::new()
     } else {
         format!("\n{}", indent.to_string(context.config))
@@ -132,6 +140,27 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
                  rewrites.join(&connector)))
 }
 
+// States whether an expression's last line exclusively consists of closing
+// parens, braces and brackets in its idiomatic formatting.
+fn is_block_expr(expr: &ast::Expr, repr: &str) -> bool {
+    match expr.node {
+        ast::Expr_::ExprStruct(..) |
+        ast::Expr_::ExprWhile(..) |
+        ast::Expr_::ExprWhileLet(..) |
+        ast::Expr_::ExprIf(..) |
+        ast::Expr_::ExprIfLet(..) |
+        ast::Expr_::ExprBlock(..) |
+        ast::Expr_::ExprLoop(..) |
+        ast::Expr_::ExprForLoop(..) |
+        ast::Expr_::ExprMatch(..) => repr.contains('\n'),
+        ast::Expr_::ExprParen(ref expr) |
+        ast::Expr_::ExprBinary(_, _, ref expr) |
+        ast::Expr_::ExprIndex(_, ref expr) |
+        ast::Expr_::ExprUnary(_, ref expr) => is_block_expr(expr, repr),
+        _ => false,
+    }
+}
+
 fn pop_expr_chain<'a>(expr: &'a ast::Expr) -> Option<&'a ast::Expr> {
     match expr.node {
         ast::Expr_::ExprMethodCall(_, _, ref expressions) => {
diff --git a/src/config.rs b/src/config.rs
index 6a7e686dc11..d788b17cfe7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -290,9 +290,10 @@ create_config! {
         "Multiline style on literal structs";
     enum_trailing_comma: bool, true, "Put a trailing comma on enum declarations";
     report_todo: ReportTactic, ReportTactic::Always,
-        "Report all occurrences of TODO in source file comments";
+        "Report all, none or unnumbered occurrences of TODO in source file comments";
     report_fixme: ReportTactic, ReportTactic::Never,
-        "Report all occurrences of FIXME in source file comments";
+        "Report all, none or unnumbered occurrences of FIXME in source file comments";
+    chain_base_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indent on chain base";
     // Alphabetically, case sensitive.
     reorder_imports: bool, false, "Reorder import statements alphabetically";
     single_line_if_else: bool, false, "Put else on same line as closing brace for if statements";
diff --git a/tests/source/chains-block-indented-base.rs b/tests/source/chains-block-indented-base.rs
new file mode 100644
index 00000000000..05459dc6973
--- /dev/null
+++ b/tests/source/chains-block-indented-base.rs
@@ -0,0 +1,30 @@
+// rustfmt-chain_base_indent: Inherit
+// Test chain formatting with block indented base
+
+fn floaters() {
+    let x = Foo {
+                field1: val1,
+                field2: val2,
+            }
+            .method_call().method_call();
+
+    let y = if cond {
+                val1
+            } else {
+                val2
+            }
+                .method_call();
+
+    {
+        match x {
+            PushParam => {
+                // params are 1-indexed
+                stack.push(mparams[match cur.to_digit(10) {
+                                            Some(d) => d as usize - 1,
+                                            None => return Err("bad param number".to_owned()),
+                                        }]
+                               .clone());
+            }
+        }
+    }
+}
diff --git a/tests/source/chains.rs b/tests/source/chains.rs
index 2a400b306d2..a50a9c51253 100644
--- a/tests/source/chains.rs
+++ b/tests/source/chains.rs
@@ -55,3 +55,49 @@ fn main() {
                          x
                      }).filter(some_mod::some_filter)
 }
+
+fn floaters() {
+    let z = Foo {
+        field1: val1,
+        field2: val2,
+    };
+
+    let x = Foo {
+        field1: val1,
+        field2: val2,
+    }.method_call().method_call();
+
+    let y = if cond {
+                val1
+            } else {
+                val2
+            }
+                .method_call();
+
+    {
+        match x {
+            PushParam => {
+                // params are 1-indexed
+                stack.push(mparams[match cur.to_digit(10) {
+                    Some(d) => d as usize - 1,
+                    None => return Err("bad param number".to_owned()),
+                }]
+                               .clone());
+            }
+        }
+    }
+
+    if cond { some(); } else { none(); }
+        .bar()
+        .baz();
+
+    Foo { x: val } .baz(|| { /*force multiline    */    }) .quux(); 
+
+    Foo { y: i_am_multi_line, z: ok }
+        .baz(|| {
+            // force multiline
+        })
+        .quux(); 
+
+    a + match x { true => "yay!", false => "boo!" }.bar()
+}
diff --git a/tests/target/chains-block-indented-base.rs b/tests/target/chains-block-indented-base.rs
new file mode 100644
index 00000000000..5b9863689de
--- /dev/null
+++ b/tests/target/chains-block-indented-base.rs
@@ -0,0 +1,31 @@
+// rustfmt-chain_base_indent: Inherit
+// Test chain formatting with block indented base
+
+fn floaters() {
+    let x = Foo {
+        field1: val1,
+        field2: val2,
+    }
+    .method_call()
+    .method_call();
+
+    let y = if cond {
+        val1
+    } else {
+        val2
+    }
+    .method_call();
+
+    {
+        match x {
+            PushParam => {
+                // params are 1-indexed
+                stack.push(mparams[match cur.to_digit(10) {
+                    Some(d) => d as usize - 1,
+                    None => return Err("bad param number".to_owned()),
+                }]
+                .clone());
+            }
+        }
+    }
+}
diff --git a/tests/target/chains.rs b/tests/target/chains.rs
index ea9b6867efe..9e195b42d6e 100644
--- a/tests/target/chains.rs
+++ b/tests/target/chains.rs
@@ -62,3 +62,67 @@ fn main() {
                     })
                     .filter(some_mod::some_filter)
 }
+
+fn floaters() {
+    let z = Foo {
+        field1: val1,
+        field2: val2,
+    };
+
+    let x = Foo {
+                field1: val1,
+                field2: val2,
+            }
+            .method_call()
+            .method_call();
+
+    let y = if cond {
+                val1
+            } else {
+                val2
+            }
+            .method_call();
+
+    {
+        match x {
+            PushParam => {
+                // params are 1-indexed
+                stack.push(mparams[match cur.to_digit(10) {
+                               Some(d) => d as usize - 1,
+                               None => return Err("bad param number".to_owned()),
+                           }]
+                           .clone());
+            }
+        }
+    }
+
+    if cond {
+        some();
+    } else {
+        none();
+    }
+    .bar()
+    .baz();
+
+    Foo { x: val }
+        .baz(|| {
+            // force multiline
+        })
+        .quux();
+
+    Foo {
+        y: i_am_multi_line,
+        z: ok,
+    }
+    .baz(|| {
+        // force multiline
+    })
+    .quux();
+
+    a +
+    match x {
+        true => "yay!",
+        false => "boo!",
+    }
+    .bar()
+}