]> git.lizzy.rs Git - rust.git/blobdiff - src/items.rs
Remove BlockIndentStyle::Inherit
[rust.git] / src / items.rs
index 6a187bad7d5e71cb30eb6be688652637ee39da35..445959f63b0a004f65b1b20b4bae1655e3edb911 100644 (file)
 
 // Formatting top-level items - functions, structs, enums, traits, impls.
 
-use Indent;
+use {Indent, Shape};
 use codemap::SpanUtils;
 use utils::{format_mutability, format_visibility, contains_skip, end_typaram, wrap_str,
-            last_line_width, format_unsafety, trim_newlines, stmt_expr, semicolon_for_expr};
-use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic,
+            last_line_width, format_unsafety, trim_newlines, stmt_expr, semicolon_for_expr,
+            trimmed_last_line_width};
+use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, list_helper,
             DefinitiveListTactic, ListTactic, definitive_tactic, format_item_list};
 use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, type_annotation_separator};
 use comment::{FindUncommented, contains_comment};
 use visitor::FmtVisitor;
 use rewrite::{Rewrite, RewriteContext};
-use config::{Config, BlockIndentStyle, Density, ReturnIndent, BraceStyle, FnArgLayoutStyle};
+use config::{Config, BlockIndentStyle, Density, ReturnIndent, BraceStyle, FnArgLayoutStyle, Style};
 use itertools::Itertools;
 
 use syntax::{ast, abi, codemap, ptr, symbol};
 // Statements of the form
 // let pat: ty = init;
 impl Rewrite for ast::Local {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        debug!("Local::rewrite {:?} {} {:?}",
+               self,
+               shape.width,
+               shape.indent);
         let mut result = "let ".to_owned();
-        let pattern_offset = offset + result.len();
+        let pattern_offset = shape.indent + result.len();
         // 1 = ;
-        let pattern_width = try_opt!(width.checked_sub(pattern_offset.width() + 1));
+        let pattern_width = try_opt!(shape.width.checked_sub(pattern_offset.width() + 1));
 
-        let pat_str = try_opt!(self.pat.rewrite(&context, pattern_width, pattern_offset));
+        let pat_str = try_opt!(self.pat.rewrite(&context,
+                                                Shape::legacy(pattern_width, pattern_offset)));
         result.push_str(&pat_str);
 
         // String that is placed within the assignment pattern and expression.
@@ -45,10 +51,10 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt
 
             if let Some(ref ty) = self.ty {
                 let separator = type_annotation_separator(context.config);
-                let indent = offset + last_line_width(&result) + separator.len();
+                let indent = shape.indent + last_line_width(&result) + separator.len();
                 // 1 = ;
-                let budget = try_opt!(width.checked_sub(indent.width() + 1));
-                let rewrite = try_opt!(ty.rewrite(context, budget, indent));
+                let budget = try_opt!(shape.width.checked_sub(indent.width() + 1));
+                let rewrite = try_opt!(ty.rewrite(context, Shape::legacy(budget, indent)));
 
                 infix.push_str(separator);
                 infix.push_str(&rewrite);
@@ -64,11 +70,13 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt
         result.push_str(&infix);
 
         if let Some(ref ex) = self.init {
-            let budget = try_opt!(width.checked_sub(context.block_indent.width() + 1));
-
             // 1 = trailing semicolon;
-            result =
-                try_opt!(rewrite_assign_rhs(&context, result, ex, budget, context.block_indent));
+            let budget = try_opt!(shape.width.checked_sub(shape.indent.block_only().width() + 1));
+
+            result = try_opt!(rewrite_assign_rhs(&context,
+                                                 result,
+                                                 ex,
+                                                 Shape::legacy(budget, shape.indent.block_only())));
         }
 
         result.push(';');
@@ -76,38 +84,88 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt
     }
 }
 
+// TODO convert to using rewrite style rather than visitor
+// TODO format modules in this style
+#[allow(dead_code)]
+struct Item<'a> {
+    keyword: &'static str,
+    abi: String,
+    vis: Option<&'a ast::Visibility>,
+    body: Vec<BodyElement<'a>>,
+    span: Span,
+}
+
+impl<'a> Item<'a> {
+    fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> {
+        let abi = if fm.abi == abi::Abi::C && !config.force_explicit_abi {
+            "extern".into()
+        } else {
+            format!("extern {}", fm.abi)
+        };
+        Item {
+            keyword: "",
+            abi: abi,
+            vis: None,
+            body: fm.items
+                .iter()
+                .map(|i| BodyElement::ForeignItem(i))
+                .collect(),
+            span: span,
+        }
+    }
+}
+
+enum BodyElement<'a> {
+    // Stmt(&'a ast::Stmt),
+    // Field(&'a ast::Field),
+    // Variant(&'a ast::Variant),
+    // Item(&'a ast::Item),
+    ForeignItem(&'a ast::ForeignItem),
+}
+
 impl<'a> FmtVisitor<'a> {
-    pub fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) {
-        let abi_str = ::utils::format_abi(fm.abi, self.config.force_explicit_abi);
-        self.buffer.push_str(&abi_str);
+    fn format_item(&mut self, item: Item) {
+        self.buffer.push_str(&item.abi);
+        self.buffer.push_str(" ");
 
-        let snippet = self.snippet(span);
+        let snippet = self.snippet(item.span);
         let brace_pos = snippet.find_uncommented("{").unwrap();
 
         self.buffer.push_str("{");
-        if !fm.items.is_empty() || contains_comment(&snippet[brace_pos..]) {
+        if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) {
             // FIXME: this skips comments between the extern keyword and the opening
             // brace.
-            self.last_pos = span.lo + BytePos(brace_pos as u32 + 1);
+            self.last_pos = item.span.lo + BytePos(brace_pos as u32 + 1);
             self.block_indent = self.block_indent.block_indent(self.config);
 
-            if fm.items.is_empty() {
-                self.format_missing_no_indent(span.hi - BytePos(1));
+            if item.body.is_empty() {
+                self.format_missing_no_indent(item.span.hi - BytePos(1));
                 self.block_indent = self.block_indent.block_unindent(self.config);
 
                 self.buffer.push_str(&self.block_indent.to_string(self.config));
             } else {
-                for item in &fm.items {
-                    self.format_foreign_item(&*item);
+                for item in &item.body {
+                    self.format_body_element(item);
                 }
 
                 self.block_indent = self.block_indent.block_unindent(self.config);
-                self.format_missing_with_indent(span.hi - BytePos(1));
+                self.format_missing_with_indent(item.span.hi - BytePos(1));
             }
         }
 
         self.buffer.push_str("}");
-        self.last_pos = span.hi;
+        self.last_pos = item.span.hi;
+    }
+
+    fn format_body_element(&mut self, element: &BodyElement) {
+        match *element {
+            BodyElement::ForeignItem(ref item) => self.format_foreign_item(item),
+        }
+    }
+
+    pub fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) {
+        let item = Item::from_foreign_mod(fm, span, self.config);
+        self.format_item(item);
     }
 
     fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
@@ -133,6 +191,7 @@ fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
                                               &item.vis,
                                               span,
                                               false,
+                                              false,
                                               false);
 
                 match rewrite {
@@ -152,7 +211,7 @@ fn format_foreign_item(&mut self, item: &ast::ForeignItem) {
                 let offset = self.block_indent + prefix.len();
                 // 1 = ;
                 let width = self.config.max_width - offset.width() - 1;
-                let rewrite = ty.rewrite(&self.get_context(), width, offset);
+                let rewrite = ty.rewrite(&self.get_context(), Shape::legacy(width, offset));
 
                 match rewrite {
                     Some(result) => {
@@ -200,7 +259,8 @@ pub fn rewrite_fn(&mut self,
                                                                          vis,
                                                                          span,
                                                                          newline_brace,
-                                                                         has_body));
+                                                                         has_body,
+                                                                         true));
 
         if self.config.fn_brace_style != BraceStyle::AlwaysNextLine && !result.contains('\n') {
             newline_brace = false;
@@ -245,6 +305,7 @@ pub fn rewrite_required_fn(&mut self,
                                                        &ast::Visibility::Inherited,
                                                        span,
                                                        false,
+                                                       false,
                                                        false));
 
         // Re-attach semicolon
@@ -273,15 +334,17 @@ fn single_line_fn(&self, fn_str: &str, block: &ast::Block) -> Option<String> {
                             let suffix = if semicolon_for_expr(e) { ";" } else { "" };
 
                             e.rewrite(&self.get_context(),
-                                         self.config.max_width - self.block_indent.width(),
-                                         self.block_indent)
+                                         Shape::legacy(self.config.max_width -
+                                                       self.block_indent.width(),
+                                                       self.block_indent))
                                 .map(|s| s + suffix)
                                 .or_else(|| Some(self.snippet(e.span)))
                         }
                         None => {
                             stmt.rewrite(&self.get_context(),
-                                         self.config.max_width - self.block_indent.width(),
-                                         self.block_indent)
+                                         Shape::legacy(self.config.max_width -
+                                                       self.block_indent.width(),
+                                                       self.block_indent))
                         }
                     }
                 } else {
@@ -320,7 +383,7 @@ pub fn visit_enum(&mut self,
                                            self.block_indent,
                                            self.block_indent.block_indent(self.config),
                                            mk_sp(span.lo, body_start))
-            .unwrap();
+                .unwrap();
         self.buffer.push_str(&generics_str);
 
         self.last_pos = body_start;
@@ -377,9 +440,8 @@ fn format_variant_list(&self,
         let fmt = ListFormatting {
             tactic: DefinitiveListTactic::Vertical,
             separator: ",",
-            trailing_separator: SeparatorTactic::from_bool(self.config.enum_trailing_comma),
-            indent: self.block_indent,
-            width: budget,
+            trailing_separator: self.config.trailing_comma,
+            shape: Shape::legacy(budget, self.block_indent),
             ends_with_newline: true,
             config: self.config,
         };
@@ -399,11 +461,10 @@ fn format_variant(&self, field: &ast::Variant) -> Option<String> {
         }
 
         let indent = self.block_indent;
-        let mut result = try_opt!(field.node
-            .attrs
-            .rewrite(&self.get_context(),
-                     self.config.max_width - indent.width(),
-                     indent));
+        let mut result = try_opt!(field.node.attrs.rewrite(&self.get_context(),
+                                                           Shape::legacy(self.config.max_width -
+                                                                         indent.width(),
+                                                                         indent)));
         if !result.is_empty() {
             result.push('\n');
             result.push_str(&indent.to_string(self.config));
@@ -433,8 +494,7 @@ fn format_variant(&self, field: &ast::Variant) -> Option<String> {
 
                 wrap_str(tag,
                          self.config.max_width,
-                         self.config.max_width - indent.width(),
-                         indent)
+                         Shape::legacy(self.config.max_width - indent.width(), indent))
             }
         };
 
@@ -450,30 +510,27 @@ fn format_variant(&self, field: &ast::Variant) -> Option<String> {
 pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -> Option<String> {
     if let ast::ItemKind::Impl(_, _, ref generics, ref trait_ref, _, ref items) = item.node {
         let mut result = String::new();
-
         // First try to format the ref and type without a split at the 'for'.
         let mut ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset, false));
 
         // If there is a line break present in the first result format it again
         // with a split at the 'for'. Skip this if there is no trait ref and
         // therefore no 'for'.
-        if let Some(_) = *trait_ref {
-            if ref_and_type.contains('\n') {
-                ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset, true));
-            }
+        if ref_and_type.contains('\n') && trait_ref.is_some() {
+            ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset, true));
         }
         result.push_str(&ref_and_type);
 
         let where_budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
         let where_clause_str = try_opt!(rewrite_where_clause(context,
                                                              &generics.where_clause,
-                                                             context.config,
                                                              context.config.item_brace_style,
-                                                             context.block_indent,
-                                                             where_budget,
+                                                             Shape::legacy(where_budget,
+                                                                           offset.block_only()),
                                                              context.config.where_density,
                                                              "{",
-                                                             true,
+                                                             false,
+                                                             last_line_width(&ref_and_type) == 1,
                                                              None));
 
         if try_opt!(is_impl_single_line(context, &items, &result, &where_clause_str, &item)) {
@@ -489,7 +546,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
 
         if !where_clause_str.is_empty() && !where_clause_str.contains('\n') {
             result.push('\n');
-            let width = context.block_indent.width() + context.config.tab_spaces - 1;
+            let width = offset.block_indent + context.config.tab_spaces - 1;
             let where_indent = Indent::new(0, width);
             result.push_str(&where_indent.to_string(context.config));
         }
@@ -518,7 +575,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
 
         if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-            visitor.block_indent = context.block_indent.block_indent(context.config);
+            visitor.block_indent = offset.block_only().block_indent(context.config);
             visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
 
             for item in items {
@@ -528,7 +585,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
             visitor.format_missing(item.span.hi - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
-            let outer_indent_str = context.block_indent.to_string(context.config);
+            let outer_indent_str = offset.block_only().to_string(context.config);
 
             result.push('\n');
             result.push_str(&inner_indent_str);
@@ -572,7 +629,7 @@ fn format_impl_ref_and_type(context: &RewriteContext,
         item.node {
         let mut result = String::new();
 
-        result.push_str(&*format_visibility(&item.vis));
+        result.push_str(&format_visibility(&item.vis));
         result.push_str(format_unsafety(unsafety));
         result.push_str("impl");
 
@@ -583,32 +640,35 @@ fn format_impl_ref_and_type(context: &RewriteContext,
         };
         let generics_str = try_opt!(rewrite_generics(context,
                                                      generics,
-                                                     offset,
-                                                     context.config.max_width,
+                                                     Shape::legacy(context.config.max_width,
+                                                                   offset),
                                                      offset + result.len(),
                                                      mk_sp(lo, hi)));
         result.push_str(&generics_str);
 
-        result.push(' ');
         if polarity == ast::ImplPolarity::Negative {
-            result.push('!');
+            result.push_str(" !");
         }
         if let Some(ref trait_ref) = *trait_ref {
-            let budget = try_opt!(context.config.max_width.checked_sub(result.len()));
-            let indent = offset + result.len();
-            result.push_str(&*try_opt!(trait_ref.rewrite(context, budget, indent)));
+            if polarity != ast::ImplPolarity::Negative {
+                result.push_str(" ");
+            }
+            let used_space = last_line_width(&result);
+            let budget = try_opt!(context.config.max_width.checked_sub(used_space));
+            let indent = offset + used_space;
+            result.push_str(&*try_opt!(trait_ref.rewrite(context, Shape::legacy(budget, indent))));
 
             if split_at_for {
                 result.push('\n');
 
                 // Add indentation of one additional tab.
-                let width = context.block_indent.width() + context.config.tab_spaces;
+                let width = offset.block_indent + context.config.tab_spaces;
                 let for_indent = Indent::new(0, width);
                 result.push_str(&for_indent.to_string(context.config));
 
-                result.push_str("for ");
+                result.push_str("for");
             } else {
-                result.push_str(" for ");
+                result.push_str(" for");
             }
         }
 
@@ -623,10 +683,21 @@ fn format_impl_ref_and_type(context: &RewriteContext,
             }
         }
 
-        let budget = try_opt!(context.config.max_width.checked_sub(used_space));
-        let indent = offset + result.len();
-        result.push_str(&*try_opt!(self_ty.rewrite(context, budget, indent)));
+        // 1 = space before the type.
+        let budget = try_opt!(context.config.max_width.checked_sub(used_space + 1));
+        let indent = offset + result.len() + 1;
+        let self_ty_str = self_ty.rewrite(context, Shape::legacy(budget, indent));
+        if let Some(self_ty_str) = self_ty_str {
+            result.push_str(" ");
+            result.push_str(&self_ty_str);
+            return Some(result);
+        }
 
+        // Can't fit the self type on what's left of the line, so start a new one.
+        let indent = offset.block_indent(context.config);
+        result.push_str(&format!("\n{}", indent.to_string(context.config)));
+        let budget = try_opt!(context.config.max_width.checked_sub(indent.width()));
+        result.push_str(&*try_opt!(self_ty.rewrite(context, Shape::legacy(budget, indent))));
         Some(result)
     } else {
         unreachable!();
@@ -684,22 +755,22 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         let generics_str = try_opt!(rewrite_generics(context,
                                                      generics,
-                                                     offset,
-                                                     context.config.max_width,
+                                                     Shape::legacy(context.config.max_width,
+                                                                   offset),
                                                      offset + result.len(),
                                                      mk_sp(item.span.lo, body_lo)));
         result.push_str(&generics_str);
 
-        let trait_bound_str = try_opt!(rewrite_trait_bounds(context,
-                                                            type_param_bounds,
-                                                            offset,
-                                                            context.config.max_width));
+        let trait_bound_str =
+            try_opt!(rewrite_trait_bounds(context,
+                                          type_param_bounds,
+                                          Shape::legacy(context.config.max_width, offset)));
         // If the trait, generics, and trait bound cannot fit on the same line,
         // put the trait bounds on an indented new line
         if offset.width() + last_line_width(&result) + trait_bound_str.len() >
-           context.config.ideal_width {
+           context.config.comment_width {
             result.push('\n');
-            let trait_indent = context.block_indent.block_indent(context.config);
+            let trait_indent = offset.block_only().block_indent(context.config);
             result.push_str(&trait_indent.to_string(context.config));
         }
         result.push_str(&trait_bound_str);
@@ -718,26 +789,25 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
                 Density::Tall
             };
 
-        let where_budget = try_opt!(context.config
-            .max_width
-            .checked_sub(last_line_width(&result)));
+        let where_budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
         let where_clause_str = try_opt!(rewrite_where_clause(context,
                                                              &generics.where_clause,
-                                                             context.config,
                                                              context.config.item_brace_style,
-                                                             context.block_indent,
-                                                             where_budget,
+                                                             Shape::legacy(where_budget,
+                                                                           offset.block_only()),
                                                              where_density,
                                                              "{",
-                                                             has_body,
+                                                             !has_body,
+                                                             trait_bound_str.is_empty() &&
+                                                             last_line_width(&generics_str) == 1,
                                                              None));
         // If the where clause cannot fit on the same line,
         // put the where clause on a new line
         if !where_clause_str.contains('\n') &&
            last_line_width(&result) + where_clause_str.len() + offset.width() >
-           context.config.ideal_width {
+           context.config.comment_width {
             result.push('\n');
-            let width = context.block_indent.width() + context.config.tab_spaces - 1;
+            let width = offset.block_indent + context.config.tab_spaces - 1;
             let where_indent = Indent::new(0, width);
             result.push_str(&where_indent.to_string(context.config));
         }
@@ -766,7 +836,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-            visitor.block_indent = context.block_indent.block_indent(context.config);
+            visitor.block_indent = offset.block_only().block_indent(context.config);
             visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
 
             for item in trait_items {
@@ -776,7 +846,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
             visitor.format_missing(item.span.hi - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
-            let outer_indent_str = context.block_indent.to_string(context.config);
+            let outer_indent_str = offset.block_only().to_string(context.config);
 
             result.push('\n');
             result.push_str(&inner_indent_str);
@@ -829,7 +899,7 @@ fn format_struct_struct(context: &RewriteContext,
         }
         None => {
             if context.config.item_brace_style == BraceStyle::AlwaysNextLine && !fields.is_empty() {
-                format!("\n{}{{", context.block_indent.to_string(context.config))
+                format!("\n{}{{", offset.block_only().to_string(context.config))
             } else {
                 " {".to_owned()
             }
@@ -858,22 +928,23 @@ fn format_struct_struct(context: &RewriteContext,
     // 1 = ","
     let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 1));
 
-    let items = itemize_list(context.codemap,
-                             fields.iter(),
-                             "}",
-                             |field| {
-        // Include attributes and doc comments, if present
-        if !field.attrs.is_empty() {
-            field.attrs[0].span.lo
-        } else {
-            field.span.lo
-        }
-    },
-                             |field| field.ty.span.hi,
-                             |field| field.rewrite(context, item_budget, item_indent),
-                             context.codemap.span_after(span, "{"),
-                             span.hi)
-        .collect::<Vec<_>>();
+    let items =
+        itemize_list(context.codemap,
+                     fields.iter(),
+                     "}",
+                     |field| {
+            // Include attributes and doc comments, if present
+            if !field.attrs.is_empty() {
+                field.attrs[0].span.lo
+            } else {
+                field.span.lo
+            }
+        },
+                     |field| field.ty.span.hi,
+                     |field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
+                     context.codemap.span_after(span, "{"),
+                     span.hi)
+                .collect::<Vec<_>>();
     // 1 = ,
     let budget = context.config.max_width - offset.width() + context.config.tab_spaces - 1;
 
@@ -885,9 +956,8 @@ fn format_struct_struct(context: &RewriteContext,
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
-        trailing_separator: context.config.struct_trailing_comma,
-        indent: item_indent,
-        width: budget,
+        trailing_separator: context.config.trailing_comma,
+        shape: Shape::legacy(budget, item_indent),
         ends_with_newline: true,
         config: context.config,
     };
@@ -928,72 +998,95 @@ fn format_tuple_struct(context: &RewriteContext,
         Some(generics) => {
             let generics_str = try_opt!(rewrite_generics(context,
                                                          generics,
-                                                         offset,
-                                                         context.config.max_width,
+                                                         Shape::legacy(context.config.max_width,
+                                                                       offset),
                                                          offset + header_str.len(),
                                                          mk_sp(span.lo, body_lo)));
             result.push_str(&generics_str);
 
-            let where_budget = try_opt!(context.config
-                .max_width
-                .checked_sub(last_line_width(&result)));
+            let where_budget =
+                try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
             try_opt!(rewrite_where_clause(context,
                                           &generics.where_clause,
-                                          context.config,
                                           context.config.item_brace_style,
-                                          context.block_indent,
-                                          where_budget,
+                                          Shape::legacy(where_budget, offset.block_only()),
                                           Density::Compressed,
                                           ";",
+                                          true,
                                           false,
                                           None))
         }
         None => "".to_owned(),
     };
-    result.push('(');
-
-    let item_indent = context.block_indent + result.len();
-    // 2 = ");"
-    let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 2));
 
-    let items = itemize_list(context.codemap,
-                             fields.iter(),
-                             ")",
-                             |field| {
-        // Include attributes and doc comments, if present
-        if !field.attrs.is_empty() {
-            field.attrs[0].span.lo
-        } else {
-            field.span.lo
+    let (tactic, item_indent) = match context.config.fn_args_layout {
+        FnArgLayoutStyle::Visual => {
+            // 1 = `(`
+            (ListTactic::HorizontalVertical, offset.block_only() + result.len() + 1)
+        }
+        FnArgLayoutStyle::Block |
+        FnArgLayoutStyle::BlockAlways => {
+            (ListTactic::HorizontalVertical, offset.block_only().block_indent(&context.config))
+        }
+    };
+    // 3 = `();`
+    let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 3));
+
+    let items =
+        itemize_list(context.codemap,
+                     fields.iter(),
+                     ")",
+                     |field| {
+            // Include attributes and doc comments, if present
+            if !field.attrs.is_empty() {
+                field.attrs[0].span.lo
+            } else {
+                field.span.lo
+            }
+        },
+                     |field| field.ty.span.hi,
+                     |field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
+                     context.codemap.span_after(span, "("),
+                     span.hi);
+    let body_budget = try_opt!(context.config.max_width.checked_sub(offset.block_only().width() +
+                                                                    result.len() +
+                                                                    3));
+    let body = try_opt!(list_helper(items,
+                                    // TODO budget is wrong in block case
+                                    Shape::legacy(body_budget, item_indent),
+                                    context.config,
+                                    tactic));
+
+    if context.config.fn_args_layout == FnArgLayoutStyle::Visual || !body.contains('\n') {
+        result.push('(');
+        if context.config.spaces_within_parens && body.len() > 0 {
+            result.push(' ');
         }
-    },
-                             |field| field.ty.span.hi,
-                             |field| field.rewrite(context, item_budget, item_indent),
-                             context.codemap.span_after(span, "("),
-                             span.hi);
-    let body = try_opt!(format_item_list(items, item_budget, item_indent, context.config));
-
-    if context.config.spaces_within_parens && body.len() > 0 {
-        result.push(' ');
-    }
 
-    result.push_str(&body);
+        result.push_str(&body);
 
-    if context.config.spaces_within_parens && body.len() > 0 {
-        result.push(' ');
+        if context.config.spaces_within_parens && body.len() > 0 {
+            result.push(' ');
+        }
+        result.push(')');
+    } else {
+        result.push_str("(\n");
+        result.push_str(&item_indent.to_string(&context.config));
+        result.push_str(&body);
+        result.push('\n');
+        result.push_str(&offset.block_only().to_string(&context.config));
+        result.push(')');
     }
 
-    result.push(')');
-
     if !where_clause_str.is_empty() && !where_clause_str.contains('\n') &&
        (result.contains('\n') ||
-        context.block_indent.width() + result.len() + where_clause_str.len() + 1 >
+        offset.block_indent + result.len() + where_clause_str.len() + 1 >
         context.config.max_width) {
-        // We need to put the where clause on a new line, but we didn'to_string
+        // We need to put the where clause on a new line, but we didn't
         // know that earlier, so the where clause will not be indented properly.
         result.push('\n');
-        result.push_str(&(context.block_indent + (context.config.tab_spaces - 1))
-            .to_string(context.config));
+        result.push_str(&(offset.block_only() + (context.config.tab_spaces - 1))
+                             .to_string(context.config));
     }
     result.push_str(&where_clause_str);
 
@@ -1019,25 +1112,21 @@ pub fn rewrite_type_alias(context: &RewriteContext,
     let generics_width = context.config.max_width - " =".len();
     let generics_str = try_opt!(rewrite_generics(context,
                                                  generics,
-                                                 indent,
-                                                 generics_width,
+                                                 Shape::legacy(generics_width, indent),
                                                  generics_indent,
                                                  generics_span));
 
     result.push_str(&generics_str);
 
-    let where_budget = try_opt!(context.config
-        .max_width
-        .checked_sub(last_line_width(&result)));
+    let where_budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
     let where_clause_str = try_opt!(rewrite_where_clause(context,
                                                          &generics.where_clause,
-                                                         context.config,
                                                          context.config.item_brace_style,
-                                                         indent,
-                                                         where_budget,
+                                                         Shape::legacy(where_budget, indent),
                                                          context.config.where_density,
                                                          "=",
-                                                         false,
+                                                         true,
+                                                         true,
                                                          Some(span.hi)));
     result.push_str(&where_clause_str);
     result.push_str(" = ");
@@ -1051,20 +1140,18 @@ pub fn rewrite_type_alias(context: &RewriteContext,
         .unwrap_or(0);
     let type_indent = indent + line_width;
     // Try to fit the type on the same line
-    let ty_str = try_opt!(ty.rewrite(context, budget, type_indent)
-        .or_else(|| {
-            // The line was too short, try to put the type on the next line
+    let ty_str = try_opt!(ty.rewrite(context, Shape::legacy(budget, type_indent)).or_else(|| {
+        // The line was too short, try to put the type on the next line
 
-            // Remove the space after '='
-            result.pop();
-            let type_indent = indent.block_indent(context.config);
-            result.push('\n');
-            result.push_str(&type_indent.to_string(context.config));
-            let budget = try_opt!(context.config
-                .max_width
-                .checked_sub(type_indent.width() + ";".len()));
-            ty.rewrite(context, budget, type_indent)
-        }));
+        // Remove the space after '='
+        result.pop();
+        let type_indent = indent.block_indent(context.config);
+        result.push('\n');
+        result.push_str(&type_indent.to_string(context.config));
+        let budget = try_opt!(context.config.max_width.checked_sub(type_indent.width() +
+                                                                   ";".len()));
+        ty.rewrite(context, Shape::legacy(budget, type_indent))
+    }));
     result.push_str(&ty_str);
     result.push_str(";");
     Some(result)
@@ -1084,19 +1171,21 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) {
 }
 
 impl Rewrite for ast::StructField {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         if contains_skip(&self.attrs) {
             let span = context.snippet(mk_sp(self.attrs[0].span.lo, self.span.hi));
-            return wrap_str(span, context.config.max_width, width, offset);
+            return wrap_str(span, context.config.max_width, shape);
         }
 
         let name = self.ident;
         let vis = format_visibility(&self.vis);
-        let mut attr_str = try_opt!(self.attrs
-            .rewrite(context, context.config.max_width - offset.width(), offset));
+        let mut attr_str = try_opt!(self.attrs.rewrite(context,
+                                                       Shape::legacy(context.config.max_width -
+                                                                     shape.indent.width(),
+                                                                     shape.indent)));
         if !attr_str.is_empty() {
             attr_str.push('\n');
-            attr_str.push_str(&offset.to_string(context.config));
+            attr_str.push_str(&shape.indent.to_string(context.config));
         }
 
         let type_annotation_spacing = type_annotation_spacing(context.config);
@@ -1113,8 +1202,10 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt
         };
 
         let last_line_width = last_line_width(&result);
-        let budget = try_opt!(width.checked_sub(last_line_width));
-        let rewrite = try_opt!(self.ty.rewrite(context, budget, offset + last_line_width));
+        let budget = try_opt!(shape.width.checked_sub(last_line_width));
+        let rewrite = try_opt!(self.ty.rewrite(context,
+                                               Shape::legacy(budget,
+                                                             shape.indent + last_line_width)));
         Some(result + &rewrite)
     }
 }
@@ -1125,6 +1216,7 @@ pub fn rewrite_static(prefix: &str,
                       ty: &ast::Ty,
                       mutability: ast::Mutability,
                       expr_opt: Option<&ptr::P<ast::Expr>>,
+                      offset: Indent,
                       context: &RewriteContext)
                       -> Option<String> {
     let type_annotation_spacing = type_annotation_spacing(context.config);
@@ -1137,16 +1229,20 @@ pub fn rewrite_static(prefix: &str,
                          type_annotation_spacing.1);
     // 2 = " =".len()
     let ty_str = try_opt!(ty.rewrite(context,
-                                     context.config.max_width - context.block_indent.width() -
-                                     prefix.len() - 2,
-                                     context.block_indent));
+                                     Shape::legacy(context.config.max_width - offset.block_indent -
+                                                   prefix.len() -
+                                                   2,
+                                                   offset.block_only())));
 
     if let Some(expr) = expr_opt {
         let lhs = format!("{}{} =", prefix, ty_str);
         // 1 = ;
-        let remaining_width = context.config.max_width - context.block_indent.width() - 1;
-        rewrite_assign_rhs(context, lhs, expr, remaining_width, context.block_indent)
-            .map(|s| s + ";")
+        let remaining_width = context.config.max_width - offset.block_indent - 1;
+        rewrite_assign_rhs(context,
+                           lhs,
+                           expr,
+                           Shape::legacy(remaining_width, offset.block_only()))
+                .map(|s| s + ";")
     } else {
         let lhs = format!("{}{};", prefix, ty_str);
         Some(lhs)
@@ -1164,7 +1260,9 @@ pub fn rewrite_associated_type(ident: ast::Ident,
     let type_bounds_str = if let Some(ty_param_bounds) = ty_param_bounds_opt {
         let bounds: &[_] = ty_param_bounds;
         let bound_str = try_opt!(bounds.iter()
-            .map(|ty_bound| ty_bound.rewrite(context, context.config.max_width, indent))
+            .map(|ty_bound| {
+                ty_bound.rewrite(context, Shape::legacy(context.config.max_width, indent))
+            })
             .intersperse(Some(" + ".to_string()))
             .collect::<Option<String>>());
         if bounds.len() > 0 {
@@ -1178,32 +1276,52 @@ pub fn rewrite_associated_type(ident: ast::Ident,
 
     if let Some(ty) = ty_opt {
         let ty_str = try_opt!(ty.rewrite(context,
-                                         context.config.max_width - context.block_indent.width() -
-                                         prefix.len() -
-                                         2,
-                                         context.block_indent));
+                                         Shape::legacy(context.config.max_width -
+                                                       indent.block_indent -
+                                                       prefix.len() -
+                                                       2,
+                                                       indent.block_only())));
         Some(format!("{} = {};", prefix, ty_str))
     } else {
         Some(format!("{}{};", prefix, type_bounds_str))
     }
 }
 
+pub fn rewrite_associated_impl_type(ident: ast::Ident,
+                                    defaultness: ast::Defaultness,
+                                    ty_opt: Option<&ptr::P<ast::Ty>>,
+                                    ty_param_bounds_opt: Option<&ast::TyParamBounds>,
+                                    context: &RewriteContext,
+                                    indent: Indent)
+                                    -> Option<String> {
+    let result =
+        try_opt!(rewrite_associated_type(ident, ty_opt, ty_param_bounds_opt, context, indent));
+
+    match defaultness {
+        ast::Defaultness::Default => Some(format!("default {}", result)),
+        _ => Some(result),
+    }
+}
+
 impl Rewrite for ast::FunctionRetTy {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match *self {
             ast::FunctionRetTy::Default(_) => Some(String::new()),
             ast::FunctionRetTy::Ty(ref ty) => {
-                let inner_width = try_opt!(width.checked_sub(3));
-                ty.rewrite(context, inner_width, offset + 3).map(|r| format!("-> {}", r))
+                let inner_width = try_opt!(shape.width.checked_sub(3));
+                ty.rewrite(context, Shape::legacy(inner_width, shape.indent + 3)).map(|r| {
+                    format!("-> {}", r)
+                })
             }
         }
     }
 }
 
 impl Rewrite for ast::Arg {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         if is_named_arg(self) {
-            let mut result = try_opt!(self.pat.rewrite(context, width, offset));
+            let mut result = try_opt!(self.pat.rewrite(context,
+                                                       Shape::legacy(shape.width, shape.indent)));
 
             if self.ty.node != ast::TyKind::Infer {
                 if context.config.space_before_type_annotation {
@@ -1213,14 +1331,16 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt
                 if context.config.space_after_type_annotation_colon {
                     result.push_str(" ");
                 }
-                let max_width = try_opt!(width.checked_sub(result.len()));
-                let ty_str = try_opt!(self.ty.rewrite(context, max_width, offset + result.len()));
+                let max_width = try_opt!(shape.width.checked_sub(result.len()));
+                let ty_str = try_opt!(self.ty.rewrite(context,
+                                                      Shape::legacy(max_width,
+                                                                    shape.indent + result.len())));
                 result.push_str(&ty_str);
             }
 
             Some(result)
         } else {
-            self.ty.rewrite(context, width, offset)
+            self.ty.rewrite(context, shape)
         }
     }
 }
@@ -1234,8 +1354,9 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
             let mut_str = format_mutability(m);
             match lt {
                 Some(ref l) => {
-                    let lifetime_str =
-                        try_opt!(l.rewrite(context, usize::max_value(), Indent::empty()));
+                    let lifetime_str = try_opt!(l.rewrite(context,
+                                                          Shape::legacy(usize::max_value(),
+                                                                        Indent::empty())));
                     Some(format!("&{} {}self", lifetime_str, mut_str))
                 }
                 None => Some(format!("&{}self", mut_str)),
@@ -1245,7 +1366,8 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
             assert!(!args.is_empty(), "&[ast::Arg] shouldn't be empty.");
 
             let mutability = explicit_self_mutability(&args[0]);
-            let type_str = try_opt!(ty.rewrite(context, usize::max_value(), Indent::empty()));
+            let type_str = try_opt!(ty.rewrite(context,
+                                               Shape::legacy(usize::max_value(), Indent::empty())));
 
             Some(format!("{}self: {}", format_mutability(mutability), type_str))
         }
@@ -1336,11 +1458,10 @@ fn rewrite_fn_base(context: &RewriteContext,
                    vis: &ast::Visibility,
                    span: Span,
                    newline_brace: bool,
-                   has_body: bool)
+                   has_body: bool,
+                   has_braces: bool)
                    -> Option<(String, bool)> {
     let mut force_new_line_for_brace = false;
-    // FIXME we'll lose any comments in between parts of the function decl, but
-    // anyone who comments there probably deserves what they get.
 
     let where_clause = &generics.where_clause;
 
@@ -1371,16 +1492,19 @@ fn rewrite_fn_base(context: &RewriteContext,
     let generics_span = mk_sp(span.lo, span_for_return(&fd.output).lo);
     let generics_str = try_opt!(rewrite_generics(context,
                                                  generics,
-                                                 indent,
-                                                 context.config.max_width,
+                                                 Shape::legacy(context.config.max_width, indent),
                                                  generics_indent,
                                                  generics_span));
     result.push_str(&generics_str);
 
-    // Note that if the width and indent really matter, we'll re-layout the
+    let snuggle_angle_bracket = last_line_width(&generics_str) == 1;
+
+    // Note that the width and indent don't really matter, we'll re-layout the
     // return type later anyway.
-    let ret_str = try_opt!(fd.output
-        .rewrite(&context, context.config.max_width - indent.width(), indent));
+    let ret_str = try_opt!(fd.output.rewrite(&context,
+                                             Shape::legacy(context.config.max_width -
+                                                           indent.width(),
+                                                           indent)));
 
     let multi_line_ret_str = ret_str.contains('\n');
     let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
@@ -1395,14 +1519,16 @@ fn rewrite_fn_base(context: &RewriteContext,
         multi_line_budget = context.config.max_width - arg_indent.width();
     }
 
-    debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
+    debug!("rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
            one_line_budget,
            multi_line_budget,
            arg_indent);
 
-    // Check if vertical layout was forced by compute_budget_for_args.
+    // Check if vertical layout was forced.
     if one_line_budget == 0 {
-        if context.config.fn_args_paren_newline {
+        if snuggle_angle_bracket {
+            result.push_str("(");
+        } else if context.config.fn_args_paren_newline {
             result.push('\n');
             result.push_str(&arg_indent.to_string(context.config));
             arg_indent = arg_indent + 1; // extra space for `(`
@@ -1426,9 +1552,7 @@ fn rewrite_fn_base(context: &RewriteContext,
     }
 
     // A conservative estimation, to goal is to be over all parens in generics
-    let args_start = generics.ty_params
-        .last()
-        .map_or(span.lo, |tp| end_typaram(tp));
+    let args_start = generics.ty_params.last().map_or(span.lo, |tp| end_typaram(tp));
     let args_span = mk_sp(context.codemap.span_after(mk_sp(args_start, span.hi), "("),
                           span_for_return(&fd.output).lo);
     let arg_str = try_opt!(rewrite_args(context,
@@ -1444,7 +1568,7 @@ fn rewrite_fn_base(context: &RewriteContext,
     let multi_line_arg_str = arg_str.contains('\n');
 
     let put_args_in_block = match context.config.fn_args_layout {
-        FnArgLayoutStyle::Block => multi_line_arg_str,
+        FnArgLayoutStyle::Block => multi_line_arg_str || generics_str.contains('\n'),
         FnArgLayoutStyle::BlockAlways => true,
         _ => false,
     } && !fd.inputs.is_empty();
@@ -1511,11 +1635,11 @@ fn rewrite_fn_base(context: &RewriteContext,
             Indent::new(indent.width(), result.len())
         };
 
-        if multi_line_ret_str {
+        if multi_line_ret_str || ret_should_indent {
             // Now that we know the proper indent and width, we need to
             // re-layout the return type.
             let budget = try_opt!(context.config.max_width.checked_sub(ret_indent.width()));
-            let ret_str = try_opt!(fd.output.rewrite(context, budget, ret_indent));
+            let ret_str = try_opt!(fd.output.rewrite(context, Shape::legacy(budget, ret_indent)));
             result.push_str(&ret_str);
         } else {
             result.push_str(&ret_str);
@@ -1544,30 +1668,41 @@ fn rewrite_fn_base(context: &RewriteContext,
         _ => false,
     } || (put_args_in_block && ret_str.is_empty());
 
-    let where_density = if should_compress_where {
-        Density::Compressed
-    } else {
-        Density::Tall
-    };
+    if where_clause.predicates.len() == 1 && should_compress_where {
+        let budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
+        if let Some(where_clause_str) =
+            rewrite_where_clause(context,
+                                 where_clause,
+                                 context.config.fn_brace_style,
+                                 Shape::legacy(budget, indent),
+                                 Density::Compressed,
+                                 "{",
+                                 !has_braces,
+                                 put_args_in_block && ret_str.is_empty(),
+                                 Some(span.hi)) {
+            if !where_clause_str.contains('\n') {
+                if last_line_width(&result) + where_clause_str.len() > context.config.max_width {
+                    result.push('\n');
+                }
 
-    // Where clause.
-    let where_budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
+                result.push_str(&where_clause_str);
+
+                return Some((result, force_new_line_for_brace));
+            }
+        }
+    }
+
+    let budget = try_opt!(context.config.max_width.checked_sub(indent.block_indent));
     let where_clause_str = try_opt!(rewrite_where_clause(context,
                                                          where_clause,
-                                                         context.config,
                                                          context.config.fn_brace_style,
-                                                         indent,
-                                                         where_budget,
-                                                         where_density,
+                                                         Shape::legacy(budget, indent),
+                                                         Density::Tall,
                                                          "{",
-                                                         has_body,
+                                                         !has_braces,
+                                                         put_args_in_block && ret_str.is_empty(),
                                                          Some(span.hi)));
 
-    if last_line_width(&result) + where_clause_str.len() > context.config.max_width &&
-       !where_clause_str.contains('\n') {
-        result.push('\n');
-    }
-
     result.push_str(&where_clause_str);
 
     Some((result, force_new_line_for_brace))
@@ -1583,9 +1718,12 @@ fn rewrite_args(context: &RewriteContext,
                 span: Span,
                 variadic: bool)
                 -> Option<String> {
-    let mut arg_item_strs = try_opt!(args.iter()
-        .map(|arg| arg.rewrite(&context, multi_line_budget, arg_indent))
-        .collect::<Option<Vec<_>>>());
+    let mut arg_item_strs =
+        try_opt!(args.iter()
+                     .map(|arg| {
+                              arg.rewrite(&context, Shape::legacy(multi_line_budget, arg_indent))
+                          })
+                     .collect::<Option<Vec<_>>>());
 
     // Account for sugary self.
     // FIXME: the comment for the self argument is dropped. This is blocked
@@ -1627,7 +1765,12 @@ enum ArgumentKind<'a> {
         }
 
         let variadic_arg = if variadic {
-            let variadic_span = mk_sp(args.last().unwrap().ty.span.hi, span.hi);
+            let variadic_span = mk_sp(args.last()
+                                          .unwrap()
+                                          .ty
+                                          .span
+                                          .hi,
+                                      span.hi);
             let variadic_start = context.codemap.span_after(variadic_span, "...") - BytePos(3);
             Some(ArgumentKind::Variadic(variadic_start))
         } else {
@@ -1663,7 +1806,6 @@ enum ArgumentKind<'a> {
     }
 
     let indent = match context.config.fn_arg_indent {
-        BlockIndentStyle::Inherit => indent,
         BlockIndentStyle::Tabbed => indent.block_indent(context.config),
         BlockIndentStyle::Visual => arg_indent,
     };
@@ -1678,18 +1820,17 @@ enum ArgumentKind<'a> {
 
     debug!("rewrite_args: budget: {}, tactic: {:?}", budget, tactic);
 
-    let end_with_newline = match context.config.fn_args_layout {
-        FnArgLayoutStyle::Block |
-        FnArgLayoutStyle::BlockAlways => true,
-        _ => false,
+    let (trailing_comma, end_with_newline) = match context.config.fn_args_layout {
+        FnArgLayoutStyle::Block => (SeparatorTactic::Vertical, true),
+        FnArgLayoutStyle::BlockAlways => (SeparatorTactic::Always, true),
+        _ => (SeparatorTactic::Never, false),
     };
 
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
-        trailing_separator: SeparatorTactic::Never,
-        indent: indent,
-        width: budget,
+        trailing_separator: trailing_comma,
+        shape: Shape::legacy(budget, indent),
         ends_with_newline: end_with_newline,
         config: context.config,
     };
@@ -1711,6 +1852,11 @@ fn compute_budgets_for_args(context: &RewriteContext,
                             ret_str_len: usize,
                             newline_brace: bool)
                             -> Option<((usize, usize, Indent))> {
+    debug!("compute_budgets_for_args {} {:?}, {}, {}",
+           result.len(),
+           indent,
+           ret_str_len,
+           newline_brace);
     // Try keeping everything on the same line.
     if !result.contains('\n') {
         // 3 = `() `, space is before ret_string.
@@ -1718,7 +1864,10 @@ fn compute_budgets_for_args(context: &RewriteContext,
         if !newline_brace {
             used_space += 2;
         }
-        let one_line_budget = context.config.max_width.checked_sub(used_space).unwrap_or(0);
+        let one_line_budget = context.config
+            .max_width
+            .checked_sub(used_space)
+            .unwrap_or(0);
 
         if one_line_budget > 0 {
             // 4 = "() {".len()
@@ -1751,8 +1900,8 @@ fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> bool {
 
 fn rewrite_generics(context: &RewriteContext,
                     generics: &ast::Generics,
-                    offset: Indent,
-                    width: usize,
+                    shape: Shape,
+                    // TODO shouldn't need this
                     generics_offset: Indent,
                     span: Span)
                     -> Option<String> {
@@ -1765,18 +1914,18 @@ fn rewrite_generics(context: &RewriteContext,
     }
 
     let offset = match context.config.generics_indent {
-        BlockIndentStyle::Inherit => offset,
-        BlockIndentStyle::Tabbed => offset.block_indent(context.config),
+        BlockIndentStyle::Tabbed => shape.indent.block_indent(context.config),
         // 1 = <
         BlockIndentStyle::Visual => generics_offset + 1,
     };
 
-    let h_budget = try_opt!(width.checked_sub(generics_offset.width() + 2));
+    let h_budget = try_opt!(shape.width.checked_sub(generics_offset.width() + 2));
     // FIXME: might need to insert a newline if the generics are really long.
 
     // Strings for the generics.
-    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, h_budget, offset));
-    let ty_strs = tys.iter().map(|ty_param| ty_param.rewrite(context, h_budget, offset));
+    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, Shape::legacy(h_budget, offset)));
+    let ty_strs =
+        tys.iter().map(|ty_param| ty_param.rewrite(context, Shape::legacy(h_budget, offset)));
 
     // Extract comments between generics.
     let lt_spans = lifetimes.iter().map(|l| {
@@ -1798,19 +1947,26 @@ fn rewrite_generics(context: &RewriteContext,
                              |&(_, ref str)| str.clone(),
                              context.codemap.span_after(span, "<"),
                              span.hi);
-    let list_str = try_opt!(format_item_list(items, h_budget, offset, context.config));
-
-    Some(if context.config.spaces_within_angle_brackets {
+    let list_str =
+        try_opt!(format_item_list(items, Shape::legacy(h_budget, offset), context.config));
+
+    let result = if context.config.generics_indent != BlockIndentStyle::Visual &&
+                    list_str.contains('\n') {
+        format!("<\n{}{}\n{}>",
+                offset.to_string(context.config),
+                list_str,
+                shape.indent.to_string(context.config))
+    } else if context.config.spaces_within_angle_brackets {
         format!("< {} >", list_str)
     } else {
         format!("<{}>", list_str)
-    })
+    };
+    Some(result)
 }
 
 fn rewrite_trait_bounds(context: &RewriteContext,
                         type_param_bounds: &ast::TyParamBounds,
-                        indent: Indent,
-                        width: usize)
+                        shape: Shape)
                         -> Option<String> {
     let bounds: &[_] = type_param_bounds;
 
@@ -1819,9 +1975,9 @@ fn rewrite_trait_bounds(context: &RewriteContext,
     }
 
     let bound_str = try_opt!(bounds.iter()
-        .map(|ty_bound| ty_bound.rewrite(&context, width, indent))
-        .intersperse(Some(" + ".to_string()))
-        .collect::<Option<String>>());
+                                 .map(|ty_bound| ty_bound.rewrite(&context, shape))
+                                 .intersperse(Some(" + ".to_string()))
+                                 .collect::<Option<String>>());
 
     let mut result = String::new();
     result.push_str(": ");
@@ -1829,31 +1985,103 @@ fn rewrite_trait_bounds(context: &RewriteContext,
     Some(result)
 }
 
+//   fn reflow_list_node_with_rule(
+//        &self,
+//        node: &CompoundNode,
+//        rule: &Rule,
+//        args: &[Arg],
+//        shape: &Shape
+//    ) -> Result<String, ()>
+//    where
+//        T: Foo,
+//    {
+
+
+fn rewrite_where_clause_rfc_style(context: &RewriteContext,
+                                  where_clause: &ast::WhereClause,
+                                  shape: Shape,
+                                  terminator: &str,
+                                  suppress_comma: bool,
+                                  // where clause can be kept on the current line.
+                                  snuggle: bool,
+                                  span_end: Option<BytePos>)
+                                  -> Option<String> {
+    let block_shape = shape.block();
+
+    let starting_newline = if snuggle {
+        " ".to_owned()
+    } else {
+        "\n".to_owned() + &block_shape.indent.to_string(context.config)
+    };
+
+    let clause_shape = block_shape.block_indent(context.config.tab_spaces);
+    // each clause on one line, trailing comma (except if suppress_comma)
+    let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
+    // If we don't have the start of the next span, then use the end of the
+    // predicates, but that means we miss comments.
+    let len = where_clause.predicates.len();
+    let end_of_preds = span_for_where_pred(&where_clause.predicates[len - 1]).hi;
+    let span_end = span_end.unwrap_or(end_of_preds);
+    let items = itemize_list(context.codemap,
+                             where_clause.predicates.iter(),
+                             terminator,
+                             |pred| span_for_where_pred(pred).lo,
+                             |pred| span_for_where_pred(pred).hi,
+                             |pred| pred.rewrite(context, clause_shape),
+                             span_start,
+                             span_end);
+    let comma_tactic = if suppress_comma {
+        SeparatorTactic::Never
+    } else {
+        SeparatorTactic::Always
+    };
+
+    let fmt = ListFormatting {
+        tactic: DefinitiveListTactic::Vertical,
+        separator: ",",
+        trailing_separator: comma_tactic,
+        shape: clause_shape,
+        ends_with_newline: true,
+        config: context.config,
+    };
+    let preds_str = try_opt!(write_list(items, &fmt));
+
+    Some(format!("{}where\n{}{}",
+                 starting_newline,
+                 clause_shape.indent.to_string(context.config),
+                 preds_str))
+}
+
 fn rewrite_where_clause(context: &RewriteContext,
                         where_clause: &ast::WhereClause,
-                        config: &Config,
                         brace_style: BraceStyle,
-                        indent: Indent,
-                        width: usize,
+                        shape: Shape,
                         density: Density,
                         terminator: &str,
-                        allow_trailing_comma: bool,
+                        suppress_comma: bool,
+                        snuggle: bool,
                         span_end: Option<BytePos>)
                         -> Option<String> {
     if where_clause.predicates.is_empty() {
         return Some(String::new());
     }
 
-    let extra_indent = match context.config.where_indent {
-        BlockIndentStyle::Inherit => Indent::empty(),
-        BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => Indent::new(config.tab_spaces, 0),
-    };
+    if context.config.where_style == Style::Rfc {
+        return rewrite_where_clause_rfc_style(context,
+                                              where_clause,
+                                              shape,
+                                              terminator,
+                                              suppress_comma,
+                                              snuggle,
+                                              span_end);
+    }
+
+    let extra_indent = Indent::new(context.config.tab_spaces, 0);
 
     let offset = match context.config.where_pred_indent {
-        BlockIndentStyle::Inherit => indent + extra_indent,
-        BlockIndentStyle::Tabbed => indent + extra_indent.block_indent(config),
+        BlockIndentStyle::Tabbed => shape.indent + extra_indent.block_indent(context.config),
         // 6 = "where ".len()
-        BlockIndentStyle::Visual => indent + extra_indent + 6,
+        BlockIndentStyle::Visual => shape.indent + extra_indent + 6,
     };
     // FIXME: if where_pred_indent != Visual, then the budgets below might
     // be out by a char or two.
@@ -1870,21 +2098,25 @@ fn rewrite_where_clause(context: &RewriteContext,
                              terminator,
                              |pred| span_for_where_pred(pred).lo,
                              |pred| span_for_where_pred(pred).hi,
-                             |pred| pred.rewrite(context, budget, offset),
+                             |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
                              span_start,
                              span_end);
     let item_vec = items.collect::<Vec<_>>();
     // FIXME: we don't need to collect here if the where_layout isn't
     // HorizontalVertical.
     let tactic = definitive_tactic(&item_vec, context.config.where_layout, budget);
-    let use_trailing_comma = allow_trailing_comma && context.config.where_trailing_comma;
+
+    let mut comma_tactic = context.config.trailing_comma;
+    // Kind of a hack because we don't usually have trailing commas in where clauses.
+    if comma_tactic == SeparatorTactic::Vertical || suppress_comma {
+        comma_tactic = SeparatorTactic::Never;
+    }
 
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
-        trailing_separator: SeparatorTactic::from_bool(use_trailing_comma),
-        indent: offset,
-        width: budget,
+        trailing_separator: comma_tactic,
+        shape: Shape::legacy(budget, offset),
         ends_with_newline: true,
         config: context.config,
     };
@@ -1904,9 +2136,9 @@ fn rewrite_where_clause(context: &RewriteContext,
         terminator.len()
     };
     if density == Density::Tall || preds_str.contains('\n') ||
-       indent.width() + " where ".len() + preds_str.len() + end_length > width {
+       shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width {
         Some(format!("\n{}where {}",
-                     (indent + extra_indent).to_string(context.config),
+                     (shape.indent + extra_indent).to_string(context.config),
                      preds_str))
     } else {
         Some(format!(" where {}", preds_str))
@@ -1929,38 +2161,41 @@ fn format_generics(context: &RewriteContext,
                    -> Option<String> {
     let mut result = try_opt!(rewrite_generics(context,
                                                generics,
-                                               offset,
-                                               context.config.max_width,
+                                               Shape::legacy(context.config.max_width, offset),
                                                generics_offset,
                                                span));
 
     if !generics.where_clause.predicates.is_empty() || result.contains('\n') {
         let budget = try_opt!(context.config.max_width.checked_sub(last_line_width(&result)));
-        let where_clause_str = try_opt!(rewrite_where_clause(context,
-                                                             &generics.where_clause,
-                                                             context.config,
-                                                             brace_style,
-                                                             context.block_indent,
-                                                             budget,
-                                                             Density::Tall,
-                                                             terminator,
-                                                             true,
-                                                             Some(span.hi)));
+        let where_clause_str =
+            try_opt!(rewrite_where_clause(context,
+                                          &generics.where_clause,
+                                          brace_style,
+                                          Shape::legacy(budget, offset.block_only()),
+                                          Density::Tall,
+                                          terminator,
+                                          false,
+                                          trimmed_last_line_width(&result) == 1,
+                                          Some(span.hi)));
         result.push_str(&where_clause_str);
-        if !force_same_line_brace &&
+        let same_line_brace = force_same_line_brace ||
+                              (generics.where_clause.predicates.is_empty() &&
+                               trimmed_last_line_width(&result) == 1);
+        if !same_line_brace &&
            (brace_style == BraceStyle::SameLineWhere || brace_style == BraceStyle::AlwaysNextLine) {
             result.push('\n');
-            result.push_str(&context.block_indent.to_string(context.config));
+            result.push_str(&offset.block_only().to_string(context.config));
         } else {
             result.push(' ');
         }
         result.push_str(opener);
     } else {
-        if !force_same_line_brace && brace_style == BraceStyle::AlwaysNextLine {
-            result.push('\n');
-            result.push_str(&context.block_indent.to_string(context.config));
-        } else {
+        if force_same_line_brace || trimmed_last_line_width(&result) == 1 ||
+           brace_style != BraceStyle::AlwaysNextLine {
             result.push(' ');
+        } else {
+            result.push('\n');
+            result.push_str(&offset.block_only().to_string(context.config));
         }
         result.push_str(opener);
     }