]> git.lizzy.rs Git - rust.git/blobdiff - src/items.rs
Merge pull request #128 from marcusklaas/subexpr
[rust.git] / src / items.rs
index 29c950f1464174594f43f1e2f120745ee6bff756..0f7f44f8d99a068dcb48926d85d3cb45c8903800 100644 (file)
 // Formatting top-level items - functions, structs, enums, traits, impls.
 
 use {ReturnIndent, BraceStyle};
-use utils::{format_visibility, make_indent, FindUncommented};
-use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
+use utils::{format_visibility, make_indent, contains_skip, span_after, end_typaram};
+use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
+use comment::FindUncommented;
 use visitor::FmtVisitor;
+
 use syntax::{ast, abi};
 use syntax::codemap::{self, Span, BytePos};
 use syntax::print::pprust;
@@ -30,9 +32,8 @@ pub fn rewrite_fn(&mut self,
                       constness: &ast::Constness,
                       abi: &abi::Abi,
                       vis: ast::Visibility,
-                      span_end: BytePos)
-        -> String
-    {
+                      span: Span)
+                      -> String {
         let newline_brace = self.newline_for_brace(&generics.where_clause);
 
         let mut result = self.rewrite_fn_base(indent,
@@ -44,7 +45,7 @@ pub fn rewrite_fn(&mut self,
                                               constness,
                                               abi,
                                               vis,
-                                              span_end,
+                                              span,
                                               newline_brace);
 
         // Prepare for the function body by possibly adding a newline and indent.
@@ -65,10 +66,9 @@ pub fn rewrite_required_fn(&mut self,
                                ident: ast::Ident,
                                sig: &ast::MethodSig,
                                span: Span)
-        -> String
-    {
+                               -> String {
         // Drop semicolon or it will be interpreted as comment
-        let span_end = span.hi - BytePos(1);
+        let span = codemap::mk_sp(span.lo, span.hi - BytePos(1));
 
         let mut result = self.rewrite_fn_base(indent,
                                               ident,
@@ -79,7 +79,7 @@ pub fn rewrite_required_fn(&mut self,
                                               &sig.constness,
                                               &sig.abi,
                                               ast::Visibility::Inherited,
-                                              span_end,
+                                              span,
                                               false);
 
         // Re-attach semicolon
@@ -98,10 +98,9 @@ fn rewrite_fn_base(&mut self,
                        constness: &ast::Constness,
                        abi: &abi::Abi,
                        vis: ast::Visibility,
-                       span_end: BytePos,
+                       span: Span,
                        newline_brace: bool)
-        -> String
-    {
+                       -> String {
         // FIXME we'll lose any comments in between parts of the function decl, but anyone
         // who comments there probably deserves what they get.
 
@@ -131,7 +130,8 @@ fn rewrite_fn_base(&mut self,
         let generics_indent = indent + result.len();
         result.push_str(&self.rewrite_generics(generics,
                                                generics_indent,
-                                               span_for_return(&fd.output).lo));
+                                               codemap::mk_sp(span.lo,
+                                                              span_for_return(&fd.output).lo)));
 
         let ret_str = self.rewrite_return(&fd.output);
 
@@ -144,7 +144,7 @@ fn rewrite_fn_base(&mut self,
 
         // Check if vertical layout was forced by compute_budget_for_args.
         if one_line_budget <= 0 {
-            if config!(fn_args_paren_newline) {
+            if self.config.fn_args_paren_newline {
                 result.push('\n');
                 result.push_str(&make_indent(arg_indent));
                 arg_indent = arg_indent + 1; // extra space for `(`
@@ -157,12 +157,21 @@ fn rewrite_fn_base(&mut self,
             result.push('(');
         }
 
+        // A conservative estimation, to goal is to be over all parens in generics
+        let args_start = generics.ty_params
+                                 .last()
+                                 .map(|tp| end_typaram(tp))
+                                 .unwrap_or(span.lo);
+        let args_span = codemap::mk_sp(span_after(codemap::mk_sp(args_start, span.hi),
+                                                  "(",
+                                                  self.codemap),
+                                       span_for_return(&fd.output).lo);
         result.push_str(&self.rewrite_args(&fd.inputs,
                                            explicit_self,
                                            one_line_budget,
                                            multi_line_budget,
                                            arg_indent,
-                                           span_for_return(&fd.output)));
+                                           args_span));
         result.push(')');
 
         // Return type.
@@ -170,8 +179,8 @@ fn rewrite_fn_base(&mut self,
             // If we've already gone multi-line, or the return type would push
             // over the max width, then put the return type on a new line.
             if result.contains("\n") ||
-               result.len() + indent + ret_str.len() > config!(max_width) {
-                let indent = match config!(fn_return_indent) {
+               result.len() + indent + ret_str.len() > self.config.max_width {
+                let indent = match self.config.fn_return_indent {
                     ReturnIndent::WithWhereClause => indent + 4,
                     // TODO we might want to check that using the arg indent doesn't
                     // blow our budget, and if it does, then fallback to the where
@@ -189,7 +198,7 @@ fn rewrite_fn_base(&mut self,
             // Comment between return type and the end of the decl.
             let snippet_lo = fd.output.span().hi;
             if where_clause.predicates.len() == 0 {
-                let snippet_hi = span_end;
+                let snippet_hi = span.hi;
                 let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
                 let snippet = snippet.trim();
                 if snippet.len() > 0 {
@@ -204,7 +213,9 @@ fn rewrite_fn_base(&mut self,
         }
 
         // Where clause.
-        result.push_str(&self.rewrite_where_clause(where_clause, indent, span_end));
+        result.push_str(&self.rewrite_where_clause(where_clause,
+                                                   indent,
+                                                   span.hi));
 
         result
     }
@@ -215,9 +226,8 @@ fn rewrite_args(&self,
                     one_line_budget: usize,
                     multi_line_budget: usize,
                     arg_indent: usize,
-                    ret_span: Span)
-        -> String
-    {
+                    span: Span)
+                    -> String {
         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
         // Account for sugary self.
         let mut min_args = 1;
@@ -262,89 +272,48 @@ fn rewrite_args(&self,
         }
 
         // Comments between args
-        let mut arg_comments = Vec::new();
+        let mut arg_items = Vec::new();
         if min_args == 2 {
-            arg_comments.push("".to_owned());
+            arg_items.push(ListItem::from_str(""));
         }
+
         // TODO if there are no args, there might still be a comment, but without
         // spans for the comment or parens, there is no chance of getting it right.
         // You also don't get to put a comment on self, unless it is explicit.
         if args.len() >= min_args {
-            arg_comments = self.make_comments_for_list(arg_comments,
-                                                       args[min_args-1..].iter(),
-                                                       ",",
-                                                       ")",
-                                                       |arg| arg.pat.span.lo,
-                                                       |arg| arg.ty.span.hi,
-                                                       ret_span.lo);
-        }
-
-        debug!("comments: {:?}", arg_comments);
+            let comment_span_start = if min_args == 2 {
+                span_after(span, ",", self.codemap)
+            } else {
+                span.lo
+            };
 
-        // If there are // comments, keep them multi-line.
-        let mut list_tactic = ListTactic::HorizontalVertical;
-        if arg_comments.iter().any(|c| c.contains("//")) {
-            list_tactic = ListTactic::Vertical;
+            arg_items = itemize_list(self.codemap,
+                                     arg_items,
+                                     args[min_args-1..].iter(),
+                                     ",",
+                                     ")",
+                                     |arg| arg.pat.span.lo,
+                                     |arg| arg.ty.span.hi,
+                                     |_| String::new(),
+                                     comment_span_start,
+                                     span.hi);
         }
 
-        assert_eq!(arg_item_strs.len(), arg_comments.len());
-        let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
-
-        let fmt = ListFormatting {
-            tactic: list_tactic,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: arg_indent,
-            h_width: one_line_budget,
-            v_width: multi_line_budget,
-        };
-
-        write_list(&arg_strs, &fmt)
-    }
+        assert_eq!(arg_item_strs.len(), arg_items.len());
 
-    // Gets comments in between items of a list.
-    fn make_comments_for_list<T, I, F1, F2>(&self,
-                                            prefix: Vec<String>,
-                                            mut it: I,
-                                            separator: &str,
-                                            terminator: &str,
-                                            get_lo: F1,
-                                            get_hi: F2,
-                                            next_span_start: BytePos)
-        -> Vec<String>
-        where I: Iterator<Item=T>,
-              F1: Fn(&T) -> BytePos,
-              F2: Fn(&T) -> BytePos
-    {
-        let mut result = prefix;
-
-        let mut prev_end = get_hi(&it.next().unwrap());
-        for item in it {
-            let cur_start = get_lo(&item);
-            let snippet = self.snippet(codemap::mk_sp(prev_end, cur_start));
-            let mut snippet = snippet.trim();
-            let white_space: &[_] = &[' ', '\t'];
-            if snippet.starts_with(separator) {
-                snippet = snippet[separator.len()..].trim_matches(white_space);
-            } else if snippet.ends_with(separator) {
-                snippet = snippet[..snippet.len()-separator.len()].trim_matches(white_space);
-            }
-            result.push(snippet.to_owned());
-            prev_end = get_hi(&item);
+        for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
+            item.item = arg;
         }
-        // Get the last commment.
-        // FIXME If you thought the crap with the commas was ugly, just wait.
-        // This is awful. We're going to look from the last item span to the
-        // start of the return type span, then we drop everything after the
-        // first closing paren.
-        // The fix is comments in the AST or a span for the closing paren.
-        let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start));
-        let snippet = snippet.trim();
-        let snippet = &snippet[..snippet.find_uncommented(terminator).unwrap_or(snippet.len())];
-        let snippet = snippet.trim();
-        result.push(snippet.to_owned());
 
-        result
+        let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
+                                   separator: ",",
+                                   trailing_separator: SeparatorTactic::Never,
+                                   indent: arg_indent,
+                                   h_width: one_line_budget,
+                                   v_width: multi_line_budget,
+                                   ends_with_newline: true, };
+
+        write_list(&arg_items, &fmt)
     }
 
     fn compute_budgets_for_args(&self,
@@ -352,8 +321,7 @@ fn compute_budgets_for_args(&self,
                                 indent: usize,
                                 ret_str_len: usize,
                                 newline_brace: bool)
-        -> (usize, usize, usize)
-    {
+                                -> (usize, usize, usize) {
         let mut budgets = None;
 
         // Try keeping everything on the same line
@@ -363,15 +331,15 @@ fn compute_budgets_for_args(&self,
             if !newline_brace {
                 used_space += 2;
             }
-            let one_line_budget = if used_space > config!(max_width) {
+            let one_line_budget = if used_space > self.config.max_width {
                 0
             } else {
-                config!(max_width) - used_space
+                self.config.max_width - used_space
             };
 
             // 2 = `()`
             let used_space = indent + result.len() + 2;
-            let max_space = config!(ideal_width) + config!(leeway);
+            let max_space = self.config.ideal_width + self.config.leeway;
             debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
                    used_space, max_space);
             if used_space < max_space {
@@ -383,9 +351,9 @@ fn compute_budgets_for_args(&self,
 
         // Didn't work. we must force vertical layout and put args on a newline.
         if let None = budgets {
-            let new_indent = indent + config!(tab_spaces);
+            let new_indent = indent + self.config.tab_spaces;
             let used_space = new_indent + 2; // account for `(` and `)`
-            let max_space = config!(ideal_width) + config!(leeway);
+            let max_space = self.config.ideal_width + self.config.leeway;
             if used_space > max_space {
                 // Whoops! bankrupt.
                 // TODO take evasive action, perhaps kill the indent or something.
@@ -398,7 +366,7 @@ fn compute_budgets_for_args(&self,
     }
 
     fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
-        match config!(fn_brace_style) {
+        match self.config.fn_brace_style {
             BraceStyle::AlwaysNextLine => true,
             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
             _ => false,
@@ -410,18 +378,21 @@ pub fn visit_enum(&mut self,
                       vis: ast::Visibility,
                       enum_def: &ast::EnumDef,
                       generics: &ast::Generics,
-                      span: Span)
-    {
-        let header_str = self.format_header("enum", ident, vis);
+                      span: Span) {
+        let header_str = self.format_header("enum ", ident, vis);
         self.changes.push_str_span(span, &header_str);
 
         let enum_snippet = self.snippet(span);
         let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1);
-        let generics_str = self.format_generics(generics, body_start);
+        let generics_str = self.format_generics(generics,
+                                                " {",
+                                                self.block_indent + self.config.tab_spaces,
+                                                codemap::mk_sp(span.lo,
+                                                               body_start));
         self.changes.push_str_span(span, &generics_str);
 
         self.last_pos = body_start;
-        self.block_indent += config!(tab_spaces);
+        self.block_indent += self.config.tab_spaces;
         for (i, f) in enum_def.variants.iter().enumerate() {
             let next_span_start: BytePos = if i == enum_def.variants.len() - 1 {
                 span.hi
@@ -431,170 +402,243 @@ pub fn visit_enum(&mut self,
 
             self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
         }
-        self.block_indent -= config!(tab_spaces);
+        self.block_indent -= self.config.tab_spaces;
 
         self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32));
         self.changes.push_str_span(span, "}");
     }
 
     // Variant of an enum
-    fn visit_variant(&mut self,
-                     field: &ast::Variant,
-                     last_field: bool,
-                     next_span_start: BytePos)
-    {
+    fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_start: BytePos) {
         if self.visit_attrs(&field.node.attrs) {
             return;
         }
 
-        if let ast::VariantKind::TupleVariantKind(ref types) = field.node.kind {
-            self.format_missing_with_indent(field.span.lo);
-
-            let vis = format_visibility(field.node.vis);
-            self.changes.push_str_span(field.span, vis);
-            let name = field.node.name.to_string();
-            self.changes.push_str_span(field.span, &name);
-
-            let mut result = String::new();
-
-            if types.len() > 0 {
-                let comments = self.make_comments_for_list(Vec::new(),
-                                                           types.iter().map(|arg| arg.ty.span),
-                                                           ",",
-                                                           ")",
-                                                           |span| span.lo,
-                                                           |span| span.hi,
-                                                           next_span_start);
-
-                let type_strings: Vec<_> = types.iter()
-                                                .map(|arg| pprust::ty_to_string(&arg.ty))
-                                                .zip(comments.into_iter())
-                                                .collect();
+        self.format_missing_with_indent(field.span.lo);
 
-                result.push('(');
+        let result = match field.node.kind {
+            ast::VariantKind::TupleVariantKind(ref types) => {
+                let vis = format_visibility(field.node.vis);
+                self.changes.push_str_span(field.span, vis);
+                let name = field.node.name.to_string();
+                self.changes.push_str_span(field.span, &name);
+
+                let mut result = String::new();
+
+                if types.len() > 0 {
+                    let items = itemize_list(self.codemap,
+                                             Vec::new(),
+                                             types.iter(),
+                                             ",",
+                                             ")",
+                                             |arg| arg.ty.span.lo,
+                                             |arg| arg.ty.span.hi,
+                                             |arg| pprust::ty_to_string(&arg.ty),
+                                             span_after(field.span, "(", self.codemap),
+                                             next_span_start);
+
+                    result.push('(');
+
+                    let indent = self.block_indent
+                                 + vis.len()
+                                 + field.node.name.to_string().len()
+                                 + 1; // Open paren
+
+                    let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
+                    let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
+
+                    let fmt = ListFormatting {
+                        tactic: ListTactic::HorizontalVertical,
+                        separator: ",",
+                        trailing_separator: SeparatorTactic::Never,
+                        indent: indent,
+                        h_width: budget,
+                        v_width: budget,
+                        ends_with_newline: false,
+                    };
+                    result.push_str(&write_list(&items, &fmt));
+                    result.push(')');
+                }
 
-                let indent = self.block_indent
-                             + vis.len()
-                             + field.node.name.to_string().len()
-                             + 1; // 1 = (
-
-                let comma_cost = if config!(enum_trailing_comma) { 1 } else { 0 };
-                let budget = config!(ideal_width) - indent - comma_cost - 1; // 1 = )
-
-                let fmt = ListFormatting {
-                    tactic: ListTactic::HorizontalVertical,
-                    separator: ",",
-                    trailing_separator: SeparatorTactic::Never,
-                    indent: indent,
-                    h_width: budget,
-                    v_width: budget,
-                };
-                result.push_str(&write_list(&type_strings, &fmt));
-                result.push(')');
-            }
+                if let Some(ref expr) = field.node.disr_expr {
+                    result.push_str(" = ");
+                    let expr_snippet = self.snippet(expr.span);
+                    result.push_str(&expr_snippet);
 
-            if let Some(ref expr) = field.node.disr_expr {
-                result.push_str(" = ");
-                let expr_snippet = self.snippet(expr.span);
-                result.push_str(&expr_snippet);
+                    // Make sure we do not exceed column limit
+                    // 4 = " = ,"
+                    assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
+                            "Enum variant exceeded column limit");
+                }
 
-                // Make sure we do not exceed column limit
-                // 4 = " = ,"
-                assert!(config!(max_width) >= vis.len() + name.len() + expr_snippet.len() + 4,
-                        "Enum variant exceeded column limit");
+                result
+            },
+            ast::VariantKind::StructVariantKind(ref struct_def) => {
+                // TODO Should limit the width, as we have a trailing comma
+                self.format_struct("",
+                                   field.node.name,
+                                   field.node.vis,
+                                   struct_def,
+                                   None,
+                                   field.span,
+                                   self.block_indent)
             }
+        };
+        self.changes.push_str_span(field.span, &result);
 
-            self.changes.push_str_span(field.span, &result);
-
-            if !last_field || config!(enum_trailing_comma) {
-                self.changes.push_str_span(field.span, ",");
-            }
+        if !last_field || self.config.enum_trailing_comma {
+            self.changes.push_str_span(field.span, ",");
         }
 
-        // TODO: deal with struct-like variants
-
         self.last_pos = field.span.hi + BytePos(1);
     }
 
-    pub fn visit_struct(&mut self,
-                        ident: ast::Ident,
-                        vis: ast::Visibility,
-                        struct_def: &ast::StructDef,
-                        generics: &ast::Generics,
-                        span: Span)
-    {
-        let header_str = self.format_header("struct", ident, vis);
-        self.changes.push_str_span(span, &header_str);
+    fn format_struct(&self,
+                     item_name: &str,
+                     ident: ast::Ident,
+                     vis: ast::Visibility,
+                     struct_def: &ast::StructDef,
+                     generics: Option<&ast::Generics>,
+                     span: Span,
+                     offset: usize)
+                     -> String {
+        let mut result = String::with_capacity(1024);
+
+        let header_str = self.format_header(item_name, ident, vis);
+        result.push_str(&header_str);
 
         if struct_def.fields.len() == 0 {
-            assert!(generics.where_clause.predicates.len() == 0,
-                    "No-field struct with where clause?");
-            assert!(generics.lifetimes.len() == 0, "No-field struct with generics?");
-            assert!(generics.ty_params.len() == 0, "No-field struct with generics?");
+            result.push(';');
+            return result;
+        }
 
-            self.changes.push_str_span(span, ";");
-            return;
+        let is_tuple = match struct_def.fields[0].node.kind {
+            ast::StructFieldKind::NamedField(..) => false,
+            ast::StructFieldKind::UnnamedField(..) => true
+        };
+
+        let (opener, terminator) = if is_tuple { ("(", ")") } else { (" {", "}") };
+
+        let generics_str = match generics {
+            Some(g) => self.format_generics(g,
+                                            opener,
+                                            offset + header_str.len(),
+                                            codemap::mk_sp(span.lo,
+                                                           struct_def.fields[0].span.lo)),
+            None => opener.to_owned()
+        };
+        result.push_str(&generics_str);
+
+        let items = itemize_list(self.codemap,
+                                 Vec::new(),
+                                 struct_def.fields.iter(),
+                                 ",",
+                                 terminator,
+                                 |field| {
+                                      // Include attributes and doc comments,
+                                      // if present
+                                      if field.node.attrs.len() > 0 {
+                                          field.node.attrs[0].span.lo
+                                      } else {
+                                          field.span.lo
+                                      }
+                                 },
+                                 |field| field.node.ty.span.hi,
+                                 |field| self.format_field(field),
+                                 span_after(span, opener.trim(), self.codemap),
+                                 span.hi);
+
+        // 2 terminators and a semicolon
+        let used_budget = offset + header_str.len() + generics_str.len() + 3;
+
+        // Conservative approximation
+        let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0;
+        let break_line = !is_tuple || generics_str.contains('\n') ||
+                         single_line_cost as usize + used_budget > self.config.max_width;
+
+        if break_line {
+            let indentation = make_indent(offset + self.config.tab_spaces);
+            result.push('\n');
+            result.push_str(&indentation);
         }
 
-        let generics_str = self.format_generics(generics, struct_def.fields[0].span.lo);
-        self.changes.push_str_span(span, &generics_str);
+        let tactic = if break_line { ListTactic::Vertical } else { ListTactic::Horizontal };
+
+        // 1 = ,
+        let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1;
+        let fmt = ListFormatting { tactic: tactic,
+                                   separator: ",",
+                                   trailing_separator: self.config.struct_trailing_comma,
+                                   indent: offset + self.config.tab_spaces,
+                                   h_width: self.config.max_width,
+                                   v_width: budget,
+                                   ends_with_newline: false, };
 
-        let struct_snippet = self.snippet(span);
-        // This will drop the comment in between the header and body.
-        self.last_pos = span.lo + BytePos(struct_snippet.find_uncommented("{").unwrap() as u32 + 1);
+        result.push_str(&write_list(&items, &fmt));
 
-        self.block_indent += config!(tab_spaces);
-        for (i, f) in struct_def.fields.iter().enumerate() {
-            self.visit_field(f, i == struct_def.fields.len() - 1, span.lo, &struct_snippet);
+        if break_line {
+            result.push('\n');
+            result.push_str(&make_indent(offset));
         }
-        self.block_indent -= config!(tab_spaces);
 
-        self.format_missing_with_indent(span.lo + BytePos(struct_snippet.rfind('}').unwrap() as u32));
-        self.changes.push_str_span(span, "}");
+        result.push_str(terminator);
+
+        if is_tuple {
+            result.push(';');
+        }
+
+        result
     }
 
-    fn format_header(&self,
-                     item_name: &str,
-                     ident: ast::Ident,
-                     vis: ast::Visibility)
-        -> String
-    {
-        format!("{}{} {}", format_visibility(vis), item_name, &token::get_ident(ident))
+    pub fn visit_struct(&mut self,
+                        ident: ast::Ident,
+                        vis: ast::Visibility,
+                        struct_def: &ast::StructDef,
+                        generics: &ast::Generics,
+                        span: Span) {
+        let indent = self.block_indent;
+        let result = self.format_struct("struct ",
+                                        ident,
+                                        vis,
+                                        struct_def,
+                                        Some(generics),
+                                        span,
+                                        indent);
+        self.changes.push_str_span(span, &result);
+        self.last_pos = span.hi;
+    }
+
+    fn format_header(&self, item_name: &str, ident: ast::Ident, vis: ast::Visibility) -> String {
+        format!("{}{}{}", format_visibility(vis), item_name, &token::get_ident(ident))
     }
 
     fn format_generics(&self,
                        generics: &ast::Generics,
-                       span_end: BytePos)
-        -> String
-    {
-        let mut result = self.rewrite_generics(generics, self.block_indent, span_end);
+                       opener: &str,
+                       offset: usize,
+                       span: Span)
+                       -> String {
+        let mut result = self.rewrite_generics(generics, offset, span);
 
-        if generics.where_clause.predicates.len() > 0 {
+        if generics.where_clause.predicates.len() > 0 || result.contains('\n') {
             result.push_str(&self.rewrite_where_clause(&generics.where_clause,
-                                                             self.block_indent,
-                                                             span_end));
+                                                       self.block_indent,
+                                                       span.hi));
             result.push_str(&make_indent(self.block_indent));
-            result.push_str("\n{");
-
+            result.push('\n');
+            result.push_str(opener.trim());
         } else {
-            result.push_str(" {");
+            result.push_str(opener);
         }
 
         result
     }
 
     // Field of a struct
-    fn visit_field(&mut self,
-                   field: &ast::StructField,
-                   last_field: bool,
-                   // These two args are for missing spans hacks.
-                   struct_start: BytePos,
-                   struct_snippet: &str)
-    {
-        if self.visit_attrs(&field.node.attrs) {
-            return;
+    fn format_field(&self, field: &ast::StructField) -> String {
+        if contains_skip(&field.node.attrs) {
+            return self.snippet(codemap::mk_sp(field.node.attrs[0].span.lo, field.span.hi));
         }
-        self.format_missing_with_indent(field.span.lo);
 
         let name = match field.node.kind {
             ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
@@ -606,38 +650,20 @@ fn visit_field(&mut self,
         };
         let typ = pprust::ty_to_string(&field.node.ty);
 
-        let mut field_str = match name {
-            Some(name) => {
-                let budget = config!(ideal_width) - self.block_indent;
-                // 3 is being conservative and assuming that there will be a trailing comma.
-                if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
-                    format!("{}{}:\n{}{}",
-                            vis,
-                            name,
-                            &make_indent(self.block_indent + config!(tab_spaces)),
-                            typ)
-                } else {
-                    format!("{}{}: {}", vis, name, typ)
-                }
-            }
-            None => format!("{}{}", vis, typ),
-        };
-        if !last_field || config!(struct_trailing_comma) {
-            field_str.push(',');
+        let indent = self.block_indent + self.config.tab_spaces;
+        let mut attr_str = self.rewrite_attrs(&field.node.attrs, indent);
+        if attr_str.len() > 0 {
+            attr_str.push('\n');
+            attr_str.push_str(&make_indent(indent));
+        }
+
+        match name {
+            Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
+            None => format!("{}{}{}", attr_str, vis, typ)
         }
-        self.changes.push_str_span(field.span, &field_str);
-
-        // This hack makes sure we only add comments etc. after the comma, and
-        // makes sure we don't repeat any commas.
-        let hi = field.span.hi;
-        let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find_uncommented(",") {
-            Some(i) => i,
-            None => 0,
-        };
-        self.last_pos = hi + BytePos(comma_pos as u32 + 1);
     }
 
-    fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: BytePos) -> String {
+    fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) -> String {
         // FIXME convert bounds to where clauses where they get too big or if
         // there is a where clause at all.
         let mut result = String::new();
@@ -647,7 +673,7 @@ fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: By
             return result;
         }
 
-        let budget = config!(max_width) - indent - 2;
+        let budget = self.config.max_width - offset - 2;
         // TODO might need to insert a newline if the generics are really long
         result.push('<');
 
@@ -665,30 +691,30 @@ fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: By
             codemap::mk_sp(l.lifetime.span.lo, hi)
         });
         let ty_spans = tys.iter().map(span_for_ty_param);
-        let comments = self.make_comments_for_list(Vec::new(),
-                                                   lt_spans.chain(ty_spans),
-                                                   ",",
-                                                   ">",
-                                                   |sp| sp.lo,
-                                                   |sp| sp.hi,
-                                                   span_end);
-
-        // If there are // comments, keep them multi-line.
-        let mut list_tactic = ListTactic::HorizontalVertical;
-        if comments.iter().any(|c| c.contains("//")) {
-            list_tactic = ListTactic::Vertical;
+
+        let mut items = itemize_list(self.codemap,
+                                     Vec::new(),
+                                     lt_spans.chain(ty_spans),
+                                     ",",
+                                     ">",
+                                     |sp| sp.lo,
+                                     |sp| sp.hi,
+                                     |_| String::new(),
+                                     span_after(span, "<", self.codemap),
+                                     span.hi);
+
+        for (item, ty) in items.iter_mut().zip(lt_strs.chain(ty_strs)) {
+            item.item = ty;
         }
 
-        let generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
-        let fmt = ListFormatting {
-            tactic: list_tactic,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent + 1,
-            h_width: budget,
-            v_width: budget,
-        };
-        result.push_str(&write_list(&generics_strs, &fmt));
+        let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
+                                   separator: ",",
+                                   trailing_separator: SeparatorTactic::Never,
+                                   indent: offset + 1,
+                                   h_width: budget,
+                                   v_width: budget,
+                                   ends_with_newline: true, };
+        result.push_str(&write_list(&items, &fmt));
 
         result.push('>');
 
@@ -699,8 +725,7 @@ fn rewrite_where_clause(&self,
                             where_clause: &ast::WhereClause,
                             indent: usize,
                             span_end: BytePos)
-        -> String
-    {
+                            -> String {
         let mut result = String::new();
         if where_clause.predicates.len() == 0 {
             return result;
@@ -710,29 +735,27 @@ fn rewrite_where_clause(&self,
         result.push_str(&make_indent(indent + 4));
         result.push_str("where ");
 
-        let comments = self.make_comments_for_list(Vec::new(),
-                                                   where_clause.predicates.iter(),
-                                                   ",",
-                                                   "{",
-                                                   |pred| span_for_where_pred(pred).lo,
-                                                   |pred| span_for_where_pred(pred).hi,
-                                                   span_end);
-
-        let where_strs: Vec<_> = where_clause.predicates.iter()
-                                                        .map(|p| (self.rewrite_pred(p)))
-                                                        .zip(comments.into_iter())
-                                                        .collect();
-
-        let budget = config!(ideal_width) + config!(leeway) - indent - 10;
-        let fmt = ListFormatting {
-            tactic: ListTactic::Vertical,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent + 10,
-            h_width: budget,
-            v_width: budget,
-        };
-        result.push_str(&write_list(&where_strs, &fmt));
+        let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
+        let items = itemize_list(self.codemap,
+                                 Vec::new(),
+                                 where_clause.predicates.iter(),
+                                 ",",
+                                 "{",
+                                 |pred| span_for_where_pred(pred).lo,
+                                 |pred| span_for_where_pred(pred).hi,
+                                 |pred| self.rewrite_pred(pred),
+                                 span_start,
+                                 span_end);
+
+        let budget = self.config.ideal_width + self.config.leeway - indent - 10;
+        let fmt = ListFormatting { tactic: ListTactic::Vertical,
+                                   separator: ",",
+                                   trailing_separator: SeparatorTactic::Never,
+                                   indent: indent + 10,
+                                   h_width: budget,
+                                   v_width: budget,
+                                   ends_with_newline: true, };
+        result.push_str(&write_list(&items, &fmt));
 
         result
     }