diff --git a/src/comment.rs b/src/comment.rs
new file mode 100644
index 00000000000..a1338d5b369
--- /dev/null
+++ b/src/comment.rs
@@ -0,0 +1,202 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Format comments.
+
+use string::{StringFormat, rewrite_string};
+use utils::make_indent;
+
+pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usize) -> String {
+    let s = orig.trim();
+
+    // Edge case: block comments. Let's not trim their lines (for now).
+    let opener = if block_style { "/* " } else { "// " };
+    let closer = if block_style { " */" } else { "" };
+    let line_start = if block_style { " * " } else { "// " };
+
+    let max_chars = width.checked_sub(closer.len()).unwrap_or(1)
+                         .checked_sub(opener.len()).unwrap_or(1);
+
+    let fmt = StringFormat {
+        opener: "",
+        closer: "",
+        line_start: line_start,
+        line_end: "",
+        width: max_chars,
+        offset: offset + opener.len() - line_start.len(),
+        trim_end: true
+    };
+
+    let indent_str = make_indent(offset);
+    let line_breaks = s.chars().filter(|&c| c == '\n').count();
+
+    let (_, mut s) = s.lines().enumerate()
+        .map(|(i, mut line)| {
+            line = line.trim();
+
+            // Drop old closer.
+            if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
+                line = &line[..(line.len() - 2)];
+            }
+
+            line.trim_right_matches(' ')
+        })
+        .map(left_trim_comment_line)
+        .fold((true, opener.to_owned()), |(first, mut acc), line| {
+            if !first {
+                acc.push('\n');
+                acc.push_str(&indent_str);
+                acc.push_str(line_start);
+            }
+
+            if line.len() > max_chars {
+                acc.push_str(&rewrite_string(line, &fmt));
+            } else {
+                acc.push_str(line);
+            }
+
+            (false, acc)
+        });
+
+    s.push_str(closer);
+
+    s
+}
+
+fn left_trim_comment_line<'a>(line: &'a str) -> &'a str {
+    if line.starts_with("/* ") || line.starts_with("// ") {
+        &line[3..]
+    } else if line.starts_with("/*") || line.starts_with("* ") || line.starts_with("//") {
+        &line[2..]
+    } else if line.starts_with("*") {
+        &line[1..]
+    } else {
+        line
+    }
+}
+
+#[test]
+fn format_comments() {
+    assert_eq!("/* test */", rewrite_comment(" //test", true, 100, 100));
+    assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, 0));
+
+    assert_eq!("//  A multi line comment\n            // between args.",
+               rewrite_comment("//  A multi line comment\n             // between args.",
+                               false,
+                               60,
+                               12));
+
+    let input = "// comment";
+    let expected_output = "/* com\n                                                                      \
+                            * men\n                                                                      \
+                            * t */";
+    assert_eq!(expected_output, rewrite_comment(input, true, 9, 69));
+}
+
+
+pub trait FindUncommented {
+    fn find_uncommented(&self, pat: &str) -> Option<usize>;
+}
+
+impl FindUncommented for str {
+    fn find_uncommented(&self, pat: &str) -> Option<usize> {
+        let mut needle_iter = pat.chars();
+        let mut possible_comment = false;
+
+        for (i, b) in self.char_indices() {
+            match needle_iter.next() {
+                Some(c) => {
+                    if b != c {
+                        needle_iter = pat.chars();
+                    }
+                },
+                None => return Some(i - pat.len())
+            }
+
+            if possible_comment && (b == '/' || b == '*') {
+                return find_comment_end(&self[(i-1)..])
+                    .and_then(|end| {
+                        self[(end + i - 1)..].find_uncommented(pat)
+                                             .map(|idx| idx + end + i - 1)
+                    });
+            }
+
+            possible_comment = b == '/';
+        }
+
+        // Handle case where the pattern is a suffix of the search string
+        match needle_iter.next() {
+            Some(_) => None,
+            None => Some(self.len() - pat.len())
+        }
+    }
+}
+
+#[test]
+fn test_find_uncommented() {
+    fn check(haystack: &str, needle: &str, expected: Option<usize>) {
+        println!("haystack {:?}, needle: {:?}", haystack, needle);
+        assert_eq!(expected, haystack.find_uncommented(needle));
+    }
+
+    check("/*/ */test", "test", Some(6));
+    check("//test\ntest", "test", Some(7));
+    check("/* comment only */", "whatever", None);
+    check("/* comment */ some text /* more commentary */ result", "result", Some(46));
+    check("sup // sup", "p", Some(2));
+    check("sup", "x", None);
+    check("π? /**/ π is nice!", "π is nice", Some(9));
+    check("/*sup yo? \n sup*/ sup", "p", Some(20));
+    check("hel/*lohello*/lo", "hello", None);
+    check("acb", "ab", None);
+}
+
+// Returns the first byte position after the first comment. The given string
+// is expected to be prefixed by a comment, including delimiters.
+// Good: "/* /* inner */ outer */ code();"
+// Bad:  "code(); // hello\n world!"
+pub fn find_comment_end(s: &str) -> Option<usize> {
+    if s.starts_with("//") {
+        s.find('\n').map(|idx| idx + 1)
+    } else {
+        // Block comment
+        let mut levels = 0;
+        let mut prev_char = 'a';
+
+        for (i, mut c) in s.char_indices() {
+            if c == '*' && prev_char == '/' {
+                levels += 1;
+                c = 'a'; // Invalidate prev_char
+            } else if c == '/' && prev_char == '*' {
+                levels -= 1;
+
+                if levels == 0 {
+                    return Some(i + 1);
+                }
+                c = 'a';
+            }
+
+            prev_char = c;
+        }
+
+        None
+    }
+}
+
+#[test]
+fn comment_end() {
+    assert_eq!(Some(6), find_comment_end("// hi\n"));
+    assert_eq!(Some(9), find_comment_end("/* sup */ "));
+    assert_eq!(Some(9), find_comment_end("/*/**/ */ "));
+    assert_eq!(Some(6), find_comment_end("/*/ */ weird!"));
+    assert_eq!(None, find_comment_end("/* hi /* test */"));
+    assert_eq!(None, find_comment_end("// hi /* test */"));
+    assert_eq!(Some(9), find_comment_end("// hi /*\n."));
+}
diff --git a/src/config.rs b/src/config.rs
index 23285167689..8bcf12854a8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -24,7 +24,7 @@ pub struct Config {
     pub fn_brace_style: BraceStyle,
     pub fn_return_indent: ReturnIndent,
     pub fn_args_paren_newline: bool,
-    pub struct_trailing_comma: bool,
+    pub struct_trailing_comma: SeparatorTactic,
     pub struct_lit_trailing_comma: SeparatorTactic,
     pub enum_trailing_comma: bool,
     pub report_todo: ReportTactic,
diff --git a/src/default.toml b/src/default.toml
index 75ab3a60164..2d5ba9c03ee 100644
--- a/src/default.toml
+++ b/src/default.toml
@@ -6,7 +6,7 @@ newline_style = "Unix"
 fn_brace_style = "SameLineWhere"
 fn_return_indent = "WithArgs"
 fn_args_paren_newline = true
-struct_trailing_comma = true
+struct_trailing_comma = "Vertical"
 struct_lit_trailing_comma = "Vertical"
 enum_trailing_comma = true
 report_todo = "Always"
diff --git a/src/expr.rs b/src/expr.rs
index dae64891ab3..d66630e5179 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -8,17 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use utils::*;
-use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
 use rewrite::{Rewrite, RewriteContext};
+use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
+use string::{StringFormat, rewrite_string};
+use utils::span_after;
 
 use syntax::{ast, ptr};
-use syntax::codemap::{Pos, Span};
+use syntax::codemap::{Pos, Span, BytePos};
 use syntax::parse::token;
 use syntax::print::pprust;
 
-use MIN_STRING;
-
 impl Rewrite for ast::Expr {
     fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
         match self.node {
@@ -33,20 +32,22 @@ impl Rewrite for ast::Expr {
                 }
             }
             ast::Expr_::ExprCall(ref callee, ref args) => {
-                return rewrite_call(context, callee, args, width, offset);
+                return rewrite_call(context, callee, args, self.span, width, offset);
             }
             ast::Expr_::ExprParen(ref subexpr) => {
                 return rewrite_paren(context, subexpr, width, offset);
             }
             ast::Expr_::ExprStruct(ref path, ref fields, ref base) => {
-                return rewrite_struct_lit(context, path,
-                                               fields,
-                                               base.as_ref().map(|e| &**e),
-                                               width,
-                                               offset);
+                return rewrite_struct_lit(context,
+                                          path,
+                                          fields,
+                                          base.as_ref().map(|e| &**e),
+                                          self.span,
+                                          width,
+                                          offset);
             }
             ast::Expr_::ExprTup(ref items) => {
-                return rewrite_tuple_lit(context, items, width, offset);
+                return rewrite_tuple_lit(context, items, self.span, width, offset);
             }
             _ => {}
         }
@@ -55,9 +56,12 @@ impl Rewrite for ast::Expr {
     }
 }
 
-fn rewrite_string_lit(context: &RewriteContext, s: &str, span: Span, width: usize, offset: usize) -> Option<String> {
-    // FIXME I bet this stomps unicode escapes in the source string
-
+fn rewrite_string_lit(context: &RewriteContext,
+                      s: &str,
+                      span: Span,
+                      width: usize,
+                      offset: usize)
+    -> Option<String> {
     // Check if there is anything to fix: we always try to fixup multi-line
     // strings, or if the string is too long for the line.
     let l_loc = context.codemap.lookup_char_pos(span.lo);
@@ -65,102 +69,65 @@ fn rewrite_string_lit(context: &RewriteContext, s: &str, span: Span, width: usiz
     if l_loc.line == r_loc.line && r_loc.col.to_usize() <= context.config.max_width {
         return context.codemap.span_to_snippet(span).ok();
     }
+    let fmt = StringFormat {
+        opener: "\"",
+        closer: "\"",
+        line_start: " ",
+        line_end: "\\",
+        width: width,
+        offset: offset,
+        trim_end: false
+    };
 
-    // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
-
-    let s = s.escape_default();
-
-    let offset = offset + 1;
-    let indent = make_indent(offset);
-    let indent = &indent;
-
-    let mut cur_start = 0;
-    let mut result = String::with_capacity(round_up_to_power_of_two(s.len()));
-    result.push('"');
-    loop {
-        let max_chars = if cur_start == 0 {
-            // First line.
-            width - 2 // 2 = " + \
-        } else {
-            context.config.max_width - offset - 1 // 1 = either \ or ;
-        };
-
-        let mut cur_end = cur_start + max_chars;
-
-        if cur_end >= s.len() {
-            result.push_str(&s[cur_start..]);
-            break;
-        }
-
-        // Make sure we're on a char boundary.
-        cur_end = next_char(&s, cur_end);
-
-        // Push cur_end left until we reach whitespace
-        while !s.char_at(cur_end-1).is_whitespace() {
-            cur_end = prev_char(&s, cur_end);
-
-            if cur_end - cur_start < MIN_STRING {
-                // We can't break at whitespace, fall back to splitting
-                // anywhere that doesn't break an escape sequence
-                cur_end = next_char(&s, cur_start + max_chars);
-                while s.char_at(prev_char(&s, cur_end)) == '\\' {
-                    cur_end = prev_char(&s, cur_end);
-                }
-                break;
-            }
-        }
-        // Make sure there is no whitespace to the right of the break.
-        while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
-            cur_end = next_char(&s, cur_end+1);
-        }
-        result.push_str(&s[cur_start..cur_end]);
-        result.push_str("\\\n");
-        result.push_str(indent);
-
-        cur_start = cur_end;
-    }
-    result.push('"');
-
-    Some(result)
+    Some(rewrite_string(&s.escape_default(), &fmt))
 }
 
 fn rewrite_call(context: &RewriteContext,
                 callee: &ast::Expr,
                 args: &[ptr::P<ast::Expr>],
+                span: Span,
                 width: usize,
                 offset: usize)
-        -> Option<String>
-{
+        -> Option<String> {
     debug!("rewrite_call, width: {}, offset: {}", width, offset);
 
     // TODO using byte lens instead of char lens (and probably all over the place too)
     let callee_str = try_opt!(callee.rewrite(context, width, offset));
-    debug!("rewrite_call, callee_str: `{:?}`", callee_str);
+    debug!("rewrite_call, callee_str: `{}`", callee_str);
+
+    if args.len() == 0 {
+        return Some(format!("{}()", callee_str));
+    }
+
     // 2 is for parens.
     let remaining_width = width - callee_str.len() - 2;
     let offset = callee_str.len() + 1 + offset;
-    let arg_count = args.len();
 
-    let args_str = if arg_count > 0 {
-        let args_rewritten: Vec<_> =
-            try_opt!(args.iter()
-                         .map(|arg| arg.rewrite(context, remaining_width, offset)
-                                       .map(|arg_str| (arg_str, String::new())))
-                         .collect());
-        let fmt = ListFormatting {
-            tactic: ListTactic::HorizontalVertical,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: offset,
-            h_width: remaining_width,
-            v_width: remaining_width,
-        };
-        write_list(&args_rewritten, &fmt)
-    } else {
-        String::new()
+    let items = itemize_list(context.codemap,
+                             Vec::new(),
+                             args.iter(),
+                             ",",
+                             ")",
+                             |item| item.span.lo,
+                             |item| item.span.hi,
+                             // Take old span when rewrite fails.
+                             |item| item.rewrite(context, remaining_width, offset)
+                                        .unwrap_or(context.codemap.span_to_snippet(item.span)
+                                                                  .unwrap()),
+                             callee.span.hi + BytePos(1),
+                             span.hi);
+
+    let fmt = ListFormatting {
+        tactic: ListTactic::HorizontalVertical,
+        separator: ",",
+        trailing_separator: SeparatorTactic::Never,
+        indent: offset,
+        h_width: remaining_width,
+        v_width: remaining_width,
+        ends_with_newline: true,
     };
 
-    Some(format!("{}({})", callee_str, args_str))
+    Some(format!("{}({})", callee_str, write_list(&items, &fmt)))
 }
 
 fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, offset: usize) -> Option<String> {
@@ -172,34 +139,68 @@ fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, of
     subexpr_str.map(|s| format!("({})", s))
 }
 
-fn rewrite_struct_lit(context: &RewriteContext,
-                      path: &ast::Path,
-                      fields: &[ast::Field],
-                      base: Option<&ast::Expr>,
-                      width: usize,
-                      offset: usize)
+fn rewrite_struct_lit<'a>(context: &RewriteContext,
+                          path: &ast::Path,
+                          fields: &'a [ast::Field],
+                          base: Option<&'a ast::Expr>,
+                          span: Span,
+                          width: usize,
+                          offset: usize)
         -> Option<String>
 {
     debug!("rewrite_struct_lit: width {}, offset {}", width, offset);
     assert!(fields.len() > 0 || base.is_some());
 
+    enum StructLitField<'a> {
+        Regular(&'a ast::Field),
+        Base(&'a ast::Expr)
+    }
+
     let path_str = pprust::path_to_string(path);
     // Foo { a: Foo } - indent is +3, width is -5.
     let indent = offset + path_str.len() + 3;
     let budget = width - (path_str.len() + 5);
 
-    let field_strs: Vec<_> =
-        try_opt!(fields.iter()
-                       .map(|field| rewrite_field(context, field, budget, indent))
-                       .chain(base.iter()
-                                  .map(|expr| expr.rewrite(context,
-                                                           // 2 = ".."
-                                                           budget - 2,
-                                                           indent + 2)
-                                                  .map(|s| format!("..{}", s))))
-                       .collect());
-    // FIXME comments
-    let field_strs: Vec<_> = field_strs.into_iter().map(|s| (s, String::new())).collect();
+    let field_iter = fields.into_iter().map(StructLitField::Regular)
+                           .chain(base.into_iter().map(StructLitField::Base));
+
+    let items = itemize_list(context.codemap,
+                             Vec::new(),
+                             field_iter,
+                             ",",
+                             "}",
+                             |item| {
+                                 match *item {
+                                     StructLitField::Regular(ref field) => field.span.lo,
+                                     // 2 = ..
+                                     StructLitField::Base(ref expr) => expr.span.lo - BytePos(2)
+                                 }
+                             },
+                             |item| {
+                                 match *item {
+                                     StructLitField::Regular(ref field) => field.span.hi,
+                                     StructLitField::Base(ref expr) => expr.span.hi
+                                 }
+                             },
+                             |item| {
+                                 match *item {
+                                     StructLitField::Regular(ref field) => {
+                                         rewrite_field(context, &field, budget, indent)
+                                            .unwrap_or(context.codemap.span_to_snippet(field.span)
+                                                                      .unwrap())
+                                     },
+                                     StructLitField::Base(ref expr) => {
+                                         // 2 = ..
+                                         expr.rewrite(context, budget - 2, indent + 2)
+                                             .map(|s| format!("..{}", s))
+                                             .unwrap_or(context.codemap.span_to_snippet(expr.span)
+                                                                       .unwrap())
+                                     }
+                                 }
+                             },
+                             span_after(span, "{", context.codemap),
+                             span.hi);
+
     let fmt = ListFormatting {
         tactic: ListTactic::HorizontalVertical,
         separator: ",",
@@ -211,14 +212,15 @@ fn rewrite_struct_lit(context: &RewriteContext,
         indent: indent,
         h_width: budget,
         v_width: budget,
+        ends_with_newline: true,
     };
-    let fields_str = write_list(&field_strs, &fmt);
+    let fields_str = write_list(&items, &fmt);
     Some(format!("{} {{ {} }}", path_str, fields_str))
 
-        // FIXME if the usual multi-line layout is too wide, we should fall back to
-        // Foo {
-        //     a: ...,
-        // }
+    // FIXME if the usual multi-line layout is too wide, we should fall back to
+    // Foo {
+    //     a: ...,
+    // }
 }
 
 fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, offset: usize) -> Option<String> {
@@ -230,43 +232,43 @@ fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, off
 
 fn rewrite_tuple_lit(context: &RewriteContext,
                      items: &[ptr::P<ast::Expr>],
+                     span: Span,
                      width: usize,
                      offset: usize)
     -> Option<String> {
-        // opening paren
-        let indent = offset + 1;
-        // In case of length 1, need a trailing comma
-        if items.len() == 1 {
-            return items[0].rewrite(context, width - 3, indent).map(|s| format!("({},)", s));
-        }
-        // Only last line has width-1 as budget, other may take max_width
-        let item_strs: Vec<_> =
-            try_opt!(items.iter()
-                          .enumerate()
-                          .map(|(i, item)| {
-                              let rem_width = if i == items.len() - 1 {
-                                  width - 2
-                              } else {
-                                  context.config.max_width - indent - 2
-                              };
-                              item.rewrite(context, rem_width, indent)
-                          })
-                          .collect());
-        let tactics = if item_strs.iter().any(|s| s.contains('\n')) {
-            ListTactic::Vertical
-        } else {
-            ListTactic::HorizontalVertical
-        };
-        // FIXME handle comments
-        let item_strs: Vec<_> = item_strs.into_iter().map(|s| (s, String::new())).collect();
-        let fmt = ListFormatting {
-            tactic: tactics,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent,
-            h_width: width - 2,
-            v_width: width - 2,
-        };
-        let item_str = write_list(&item_strs, &fmt);
-        Some(format!("({})", item_str))
-    }
+    let indent = offset + 1;
+
+    let items = itemize_list(context.codemap,
+                             Vec::new(),
+                             items.into_iter(),
+                             ",",
+                             ")",
+                             |item| item.span.lo,
+                             |item| item.span.hi,
+                             |item| item.rewrite(context,
+                                                 context.config.max_width - indent - 2,
+                                                 indent)
+                                        .unwrap_or(context.codemap.span_to_snippet(item.span)
+                                                                  .unwrap()),
+                             span.lo + BytePos(1), // Remove parens
+                             span.hi - BytePos(1));
+
+    // In case of length 1, need a trailing comma
+    let trailing_separator_tactic = if items.len() == 1 {
+        SeparatorTactic::Always
+    } else {
+        SeparatorTactic::Never
+    };
+
+    let fmt = ListFormatting {
+        tactic: ListTactic::HorizontalVertical,
+        separator: ",",
+        trailing_separator: trailing_separator_tactic,
+        indent: indent,
+        h_width: width - 2,
+        v_width: width - 2,
+        ends_with_newline: true,
+    };
+
+    Some(format!("({})", write_list(&items, &fmt)))
+}
diff --git a/src/imports.rs b/src/imports.rs
index d4fe81218b8..de33be849ba 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use visitor::FmtVisitor;
-use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
+use lists::{write_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
 use utils::format_visibility;
 
 use syntax::ast;
@@ -65,16 +65,8 @@ impl<'a> FmtVisitor<'a> {
         let used_width = indent + 2;
 
         // Break as early as possible when we've blown our budget.
-        let remaining_line_budget = if used_width > one_line_budget {
-            0
-        } else {
-            one_line_budget - used_width
-        };
-        let remaining_multi_budget = if used_width > multi_line_budget {
-            0
-        } else {
-            multi_line_budget - used_width
-        };
+        let remaining_line_budget = one_line_budget.checked_sub(used_width).unwrap_or(0);
+        let remaining_multi_budget = multi_line_budget.checked_sub(used_width).unwrap_or(0);
 
         let fmt = ListFormatting {
             tactic: ListTactic::Mixed,
@@ -83,6 +75,7 @@ impl<'a> FmtVisitor<'a> {
             indent: block_indent + indent,
             h_width: remaining_line_budget,
             v_width: remaining_multi_budget,
+            ends_with_newline: true,
         };
 
         // TODO handle any comments inbetween items.
@@ -94,7 +87,7 @@ impl<'a> FmtVisitor<'a> {
                 false
             }
         ) {
-            Some(("self".to_owned(), String::new()))
+            Some(ListItem::from_str("self"))
         } else {
             None
         };
@@ -102,12 +95,13 @@ impl<'a> FmtVisitor<'a> {
         let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
             match vpi.node {
                 ast::PathListItem_::PathListIdent{ name, .. } => {
-                    Some((token::get_ident(name).to_string(), String::new()))
+                    Some(ListItem::from_str(token::get_ident(name).to_string()))
                 }
                 // Skip `self`, because we added it above.
                 ast::PathListItem_::PathListMod{ .. } => None,
             }
         })).collect();
+
         Some(if path_str.len() == 0 {
             format!("{}use {{{}}};", vis, write_list(&items, &fmt))
         } else {
diff --git a/src/issues.rs b/src/issues.rs
index 9ee70e1aadc..0efe0c31c78 100644
--- a/src/issues.rs
+++ b/src/issues.rs
@@ -223,7 +223,6 @@ impl BadIssueSeeker {
 #[test]
 fn find_unnumbered_issue() {
     fn check_fail(text: &str, failing_pos: usize) {
-        println!("{:?}", text);
         let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
         assert_eq!(Some(failing_pos), text.chars().position(|c| seeker.inspect(c).is_some()));
     }
diff --git a/src/items.rs b/src/items.rs
index 2dfa8a5fe65..17941dcd242 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -11,9 +11,11 @@
 // 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};
+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,7 +32,7 @@ impl<'a> FmtVisitor<'a> {
                       constness: &ast::Constness,
                       abi: &abi::Abi,
                       vis: ast::Visibility,
-                      span_end: BytePos)
+                      span: Span)
         -> String
     {
         let newline_brace = self.newline_for_brace(&generics.where_clause);
@@ -44,7 +46,7 @@ impl<'a> FmtVisitor<'a> {
                                               constness,
                                               abi,
                                               vis,
-                                              span_end,
+                                              span,
                                               newline_brace);
 
         // Prepare for the function body by possibly adding a newline and indent.
@@ -68,7 +70,7 @@ impl<'a> FmtVisitor<'a> {
         -> 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 +81,7 @@ impl<'a> FmtVisitor<'a> {
                                               &sig.constness,
                                               &sig.abi,
                                               ast::Visibility::Inherited,
-                                              span_end,
+                                              span,
                                               false);
 
         // Re-attach semicolon
@@ -98,7 +100,7 @@ impl<'a> FmtVisitor<'a> {
                        constness: &ast::Constness,
                        abi: &abi::Abi,
                        vis: ast::Visibility,
-                       span_end: BytePos,
+                       span: Span,
                        newline_brace: bool)
         -> String
     {
@@ -131,7 +133,8 @@ impl<'a> FmtVisitor<'a> {
         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);
 
@@ -162,7 +165,8 @@ impl<'a> FmtVisitor<'a> {
                                            one_line_budget,
                                            multi_line_budget,
                                            arg_indent,
-                                           span_for_return(&fd.output)));
+                                           codemap::mk_sp(span_after(span, "(", self.codemap),
+                                                          span_for_return(&fd.output).lo)));
         result.push(')');
 
         // Return type.
@@ -189,7 +193,7 @@ impl<'a> FmtVisitor<'a> {
             // 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 +208,9 @@ impl<'a> FmtVisitor<'a> {
         }
 
         // 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,7 +221,7 @@ impl<'a> FmtVisitor<'a> {
                     one_line_budget: usize,
                     multi_line_budget: usize,
                     arg_indent: usize,
-                    ret_span: Span)
+                    span: Span)
         -> String
     {
         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
@@ -262,89 +268,50 @@ impl<'a> FmtVisitor<'a> {
         }
 
         // 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);
+            let comment_span_start = if min_args == 2 {
+                span_after(span, ",", self.codemap)
+            } else {
+                span.lo
+            };
+
+            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);
         }
 
-        debug!("comments: {:?}", arg_comments);
+        assert_eq!(arg_item_strs.len(), arg_items.len());
 
-        // 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;
+        for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
+            item.item = arg;
         }
 
-        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,
+            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_strs, &fmt)
-    }
-
-    // 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);
-        }
-        // 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
+        write_list(&arg_items, &fmt)
     }
 
     fn compute_budgets_for_args(&self,
@@ -412,12 +379,16 @@ impl<'a> FmtVisitor<'a> {
                       generics: &ast::Generics,
                       span: Span)
     {
-        let header_str = self.format_header("enum", ident, vis);
+        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;
@@ -447,75 +418,185 @@ impl<'a> FmtVisitor<'a> {
             return;
         }
 
-        if let ast::VariantKind::TupleVariantKind(ref types) = field.node.kind {
-            self.format_missing_with_indent(field.span.lo);
+        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);
+        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();
+                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);
+                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);
 
-                let type_strings: Vec<_> = types.iter()
-                                                .map(|arg| pprust::ty_to_string(&arg.ty))
-                                                .zip(comments.into_iter())
-                                                .collect();
+                    result.push('(');
 
-                result.push('(');
+                    let indent = self.block_indent
+                                 + vis.len()
+                                 + field.node.name.to_string().len()
+                                 + 1; // Open paren
 
-                let indent = self.block_indent
-                             + vis.len()
-                             + field.node.name.to_string().len()
-                             + 1; // 1 = (
+                    let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
+                    let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
 
-                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 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!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
-                        "Enum variant exceeded column limit");
-            }
+                self.changes.push_str_span(field.span, &result);
 
-            self.changes.push_str_span(field.span, &result);
+                if !last_field || self.config.enum_trailing_comma {
+                    self.changes.push_str_span(field.span, ",");
+                }
+            },
+            ast::VariantKind::StructVariantKind(ref struct_def) => {
+                let result = self.format_struct("",
+                                                field.node.name,
+                                                field.node.vis,
+                                                struct_def,
+                                                None,
+                                                field.span,
+                                                self.block_indent);
 
-            if !last_field || self.config.enum_trailing_comma {
-                self.changes.push_str_span(field.span, ",");
+                self.changes.push_str_span(field.span, &result)
             }
         }
 
-        // TODO: deal with struct-like variants
-
         self.last_pos = field.span.hi + BytePos(1);
     }
 
+    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 {
+            result.push(';');
+            return result;
+        }
+
+        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 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,
+        };
+
+        result.push_str(&write_list(&items, &fmt));
+
+        if break_line {
+            result.push('\n');
+            result.push_str(&make_indent(offset));
+        }
+
+        result.push_str(terminator);
+
+        if is_tuple {
+            result.push(';');
+        }
+
+        result
+    }
+
     pub fn visit_struct(&mut self,
                         ident: ast::Ident,
                         vis: ast::Visibility,
@@ -523,34 +604,16 @@ impl<'a> FmtVisitor<'a> {
                         generics: &ast::Generics,
                         span: Span)
     {
-        let header_str = self.format_header("struct", ident, vis);
-        self.changes.push_str_span(span, &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?");
-
-            self.changes.push_str_span(span, ";");
-            return;
-        }
-
-        let generics_str = self.format_generics(generics, struct_def.fields[0].span.lo);
-        self.changes.push_str_span(span, &generics_str);
-
-        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);
-
-        self.block_indent += self.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);
-        }
-        self.block_indent -= self.config.tab_spaces;
-
-        self.format_missing_with_indent(span.lo + BytePos(struct_snippet.rfind('}').unwrap() as u32));
-        self.changes.push_str_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,
@@ -559,42 +622,37 @@ impl<'a> FmtVisitor<'a> {
                      vis: ast::Visibility)
         -> String
     {
-        format!("{}{} {}", format_visibility(vis), item_name, &token::get_ident(ident))
+        format!("{}{}{}", format_visibility(vis), item_name, &token::get_ident(ident))
     }
 
     fn format_generics(&self,
                        generics: &ast::Generics,
-                       span_end: BytePos)
+                       opener: &str,
+                       offset: usize,
+                       span: Span)
         -> String
     {
-        let mut result = self.rewrite_generics(generics, self.block_indent, span_end);
+        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 +664,20 @@ impl<'a> FmtVisitor<'a> {
         };
         let typ = pprust::ty_to_string(&field.node.ty);
 
-        let mut field_str = match name {
-            Some(name) => {
-                let budget = self.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 + self.config.tab_spaces),
-                            typ)
-                } else {
-                    format!("{}{}: {}", vis, name, typ)
-                }
-            }
-            None => format!("{}{}", vis, typ),
-        };
-        if !last_field || self.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));
         }
-        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);
+        match name {
+            Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
+            None => format!("{}{}{}", attr_str, vis, typ)
+        }
     }
 
-    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 +687,7 @@ impl<'a> FmtVisitor<'a> {
             return result;
         }
 
-        let budget = self.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 +705,32 @@ impl<'a> FmtVisitor<'a> {
             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,
+            tactic: ListTactic::HorizontalVertical,
             separator: ",",
             trailing_separator: SeparatorTactic::Never,
-            indent: indent + 1,
+            indent: offset + 1,
             h_width: budget,
             v_width: budget,
+            ends_with_newline: true,
         };
-        result.push_str(&write_list(&generics_strs, &fmt));
+        result.push_str(&write_list(&items, &fmt));
 
         result.push('>');
 
@@ -710,18 +752,17 @@ impl<'a> FmtVisitor<'a> {
         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 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 {
@@ -731,8 +772,9 @@ impl<'a> FmtVisitor<'a> {
             indent: indent + 10,
             h_width: budget,
             v_width: budget,
+            ends_with_newline: true,
         };
-        result.push_str(&write_list(&where_strs, &fmt));
+        result.push_str(&write_list(&items, &fmt));
 
         result
     }
diff --git a/src/lib.rs b/src/lib.rs
index dfcf4c70424..5ee68e70cf5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -62,6 +62,8 @@ mod expr;
 mod imports;
 mod issues;
 mod rewrite;
+mod string;
+mod comment;
 
 const MIN_STRING: usize = 10;
 // When we get scoped annotations, we should have rustfmt::skip.
diff --git a/src/lists.rs b/src/lists.rs
index 9f1006c39f0..adabb00566c 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -8,7 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use utils::make_indent;
+use std::cmp;
+
+use syntax::codemap::{self, CodeMap, BytePos};
+
+use utils::{round_up_to_power_of_two, make_indent};
+use comment::{FindUncommented, rewrite_comment, find_comment_end};
+use string::before;
 
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
 pub enum ListTactic {
@@ -41,11 +47,38 @@ pub struct ListFormatting<'a> {
     pub h_width: usize,
     // Available width if we layout vertically
     pub v_width: usize,
+    // Non-expressions, e.g. items, will have a new line at the end of the list.
+    // Important for comment styles.
+    pub ends_with_newline: bool
 }
 
-// Format a list of strings into a string.
-// Precondition: all strings in items are trimmed.
-pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b>) -> String {
+pub struct ListItem {
+    pub pre_comment: Option<String>,
+    // Item should include attributes and doc comments
+    pub item: String,
+    pub post_comment: Option<String>
+}
+
+impl ListItem {
+    pub fn is_multiline(&self) -> bool {
+        self.item.contains('\n') ||
+        self.pre_comment.is_some() ||
+        self.post_comment.as_ref().map(|s| s.contains('\n')).unwrap_or(false)
+    }
+
+    pub fn from_str<S: Into<String>>(s: S) -> ListItem {
+        ListItem {
+            pre_comment: None,
+            item: s.into(),
+            post_comment: None
+        }
+    }
+}
+
+// Format a list of commented items into a string.
+// FIXME: this has grown into a monstrosity
+// TODO: add unit tests
+pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> String {
     if items.len() == 0 {
         return String::new();
     }
@@ -68,7 +101,7 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
         debug!("write_list: total_width: {}, total_sep_len: {}, h_width: {}",
                total_width, total_sep_len, formatting.h_width);
         tactic = if fits_single &&
-                    !items.iter().any(|&(ref s, _)| s.contains('\n')) {
+                    !items.iter().any(ListItem::is_multiline) {
             ListTactic::Horizontal
         } else {
             ListTactic::Vertical
@@ -81,6 +114,11 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
         tactic = ListTactic::Horizontal;
     }
 
+    // Switch to vertical mode if we find non-block comments.
+    if items.iter().any(has_line_pre_comment) {
+        tactic = ListTactic::Vertical;
+    }
+
     // Now that we know how we will layout, we can decide for sure if there
     // will be a trailing separator.
     let trailing_separator = needs_trailing_separator(formatting.trailing_separator, tactic);
@@ -92,13 +130,16 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
     } else {
         total_width + items.len() * (formatting.indent + 1)
     };
-    let mut result = String::with_capacity(alloc_width);
+    let mut result = String::with_capacity(round_up_to_power_of_two(alloc_width));
 
     let mut line_len = 0;
     let indent_str = &make_indent(formatting.indent);
-    for (i, &(ref item, ref comment)) in items.iter().enumerate() {
+    for (i, item) in items.iter().enumerate() {
         let first = i == 0;
-        let separate = i != items.len() - 1 || trailing_separator;
+        let last = i == items.len() - 1;
+        let separate = !last || trailing_separator;
+        let item_sep_len = if separate { sep_len } else { 0 };
+        let item_width = item.item.len() + item_sep_len;
 
         match tactic {
             ListTactic::Horizontal if !first => {
@@ -109,12 +150,9 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
                 result.push_str(indent_str);
             }
             ListTactic::Mixed => {
-                let mut item_width = item.len();
-                if separate {
-                    item_width += sep_len;
-                }
+                let total_width = total_item_width(item) + item_sep_len;
 
-                if line_len > 0 && line_len + item_width > formatting.v_width {
+                if line_len > 0 && line_len + total_width > formatting.v_width {
                     result.push('\n');
                     result.push_str(indent_str);
                     line_len = 0;
@@ -125,35 +163,160 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
                     line_len += 1;
                 }
 
-                line_len += item_width;
+                line_len += total_width;
             }
             _ => {}
         }
 
-        result.push_str(item);
+        // Pre-comments
+        if let Some(ref comment) = item.pre_comment {
+            result.push_str(&rewrite_comment(comment,
+                                             // Block style in non-vertical mode
+                                             tactic != ListTactic::Vertical,
+                                             // Width restriction is only
+                                             // relevant in vertical mode.
+                                             formatting.v_width,
+                                             formatting.indent));
 
-        if tactic != ListTactic::Vertical && comment.len() > 0 {
-            if !comment.starts_with('\n') {
+            if tactic == ListTactic::Vertical {
+                result.push('\n');
+                result.push_str(indent_str);
+            } else {
                 result.push(' ');
             }
-            result.push_str(comment);
+        }
+
+        result.push_str(&item.item);
+
+        // Post-comments
+        if tactic != ListTactic::Vertical && item.post_comment.is_some() {
+            let formatted_comment = rewrite_comment(item.post_comment.as_ref().unwrap(),
+                                                    true,
+                                                    formatting.v_width,
+                                                    0);
+
+            result.push(' ');
+            result.push_str(&formatted_comment);
         }
 
         if separate {
             result.push_str(formatting.separator);
         }
 
-        if tactic == ListTactic::Vertical && comment.len() > 0 {
-            if !comment.starts_with('\n') {
-                result.push(' ');
-            }
-            result.push_str(comment);
+        if tactic == ListTactic::Vertical && item.post_comment.is_some() {
+            // 1 = space between item and comment.
+            let width = formatting.v_width.checked_sub(item_width + 1).unwrap_or(1);
+            let offset = formatting.indent + item_width + 1;
+            let comment = item.post_comment.as_ref().unwrap();
+            // Use block-style only for the last item or multiline comments.
+            let block_style = formatting.ends_with_newline && last ||
+                              comment.trim().contains('\n') ||
+                              comment.trim().len() > width;
+
+            let formatted_comment = rewrite_comment(comment, block_style, width, offset);
+
+            result.push(' ');
+            result.push_str(&formatted_comment);
         }
     }
 
     result
 }
 
+fn has_line_pre_comment(item: &ListItem) -> bool {
+    match item.pre_comment {
+        Some(ref comment) => comment.starts_with("//"),
+        None => false
+    }
+}
+
+// Turns a list into a vector of items with associated comments.
+// TODO: we probably do not want to take a terminator any more. Instead, we
+// should demand a proper span end.
+pub fn itemize_list<T, I, F1, F2, F3>(codemap: &CodeMap,
+                                      prefix: Vec<ListItem>,
+                                      it: I,
+                                      separator: &str,
+                                      terminator: &str,
+                                      get_lo: F1,
+                                      get_hi: F2,
+                                      get_item_string: F3,
+                                      mut prev_span_end: BytePos,
+                                      next_span_start: BytePos)
+    -> Vec<ListItem>
+    where I: Iterator<Item=T>,
+          F1: Fn(&T) -> BytePos,
+          F2: Fn(&T) -> BytePos,
+          F3: Fn(&T) -> String
+{
+    let mut result = prefix;
+    let mut new_it = it.peekable();
+    let white_space: &[_] = &[' ', '\t'];
+
+    while let Some(item) = new_it.next() {
+        // Pre-comment
+        let pre_snippet = codemap.span_to_snippet(codemap::mk_sp(prev_span_end,
+                                                                get_lo(&item)))
+                                 .unwrap();
+        let pre_snippet = pre_snippet.trim();
+        let pre_comment = if pre_snippet.len() > 0 {
+            Some(pre_snippet.to_owned())
+        } else {
+            None
+        };
+
+        // Post-comment
+        let next_start = match new_it.peek() {
+            Some(ref next_item) => get_lo(next_item),
+            None => next_span_start
+        };
+        let post_snippet = codemap.span_to_snippet(codemap::mk_sp(get_hi(&item),
+                                                                  next_start))
+                                  .unwrap();
+
+        let comment_end = match new_it.peek() {
+            Some(..) => {
+                if let Some(start) = before(&post_snippet, "/*", "\n") {
+                    // Block-style post-comment. Either before or after the separator.
+                    cmp::max(find_comment_end(&post_snippet[start..]).unwrap() + start,
+                             post_snippet.find_uncommented(separator).unwrap() + separator.len())
+                } else if let Some(idx) = post_snippet.find('\n') {
+                    idx + 1
+                } else {
+                    post_snippet.len()
+                }
+            },
+            None => {
+                post_snippet.find_uncommented(terminator)
+                            .unwrap_or(post_snippet.len())
+            }
+        };
+
+        prev_span_end = get_hi(&item) + BytePos(comment_end as u32);
+        let mut post_snippet = post_snippet[..comment_end].trim();
+
+        if post_snippet.starts_with(separator) {
+            post_snippet = post_snippet[separator.len()..]
+                .trim_matches(white_space);
+        } else if post_snippet.ends_with(separator) {
+            post_snippet = post_snippet[..post_snippet.len()-separator.len()]
+                .trim_matches(white_space);
+        }
+
+        result.push(ListItem {
+            pre_comment: pre_comment,
+            item: get_item_string(&item),
+            post_comment: if post_snippet.len() > 0 {
+                Some(post_snippet.to_owned())
+            } else {
+                None
+            }
+        });
+    }
+
+    result
+}
+
 fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: ListTactic) -> bool {
     match separator_tactic {
         SeparatorTactic::Always => true,
@@ -162,16 +325,25 @@ fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: List
     }
 }
 
-fn calculate_width(items:&[(String, String)]) -> usize {
-    let missed_width = items.iter().map(|&(_, ref s)| {
-        let text_len = s.trim().len();
-        if text_len > 0 {
-            // We'll put a space before any comment.
-            text_len + 1
-        } else {
-            text_len
-        }
-    }).fold(0, |a, l| a + l);
-    let item_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l);
-    missed_width + item_width
+fn calculate_width(items: &[ListItem]) -> usize {
+    items.iter().map(total_item_width).fold(0, |a, l| a + l)
+}
+
+fn total_item_width(item: &ListItem) -> usize {
+    comment_len(&item.pre_comment) + comment_len(&item.post_comment) + item.item.len()
+}
+
+fn comment_len(comment: &Option<String>) -> usize {
+    match comment {
+        &Some(ref s) => {
+            let text_len = s.trim().len();
+            if text_len > 0 {
+                // We'll put " /*" before and " */" after inline comments.
+                text_len + 6
+            } else {
+                text_len
+            }
+        },
+        &None => 0
+    }
 }
diff --git a/src/string.rs b/src/string.rs
new file mode 100644
index 00000000000..d474fdb468e
--- /dev/null
+++ b/src/string.rs
@@ -0,0 +1,103 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Format string literals.
+
+use utils::{make_indent, next_char, prev_char, round_up_to_power_of_two};
+
+use MIN_STRING;
+
+pub struct StringFormat<'a> {
+    pub opener: &'a str,
+    pub closer: &'a str,
+    pub line_start: &'a str,
+    pub line_end: &'a str,
+    pub width: usize,
+    pub offset: usize,
+    pub trim_end: bool,
+}
+
+// TODO: simplify this!
+pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String {
+    // FIXME I bet this stomps unicode escapes in the source string
+    // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
+
+    let indent = make_indent(fmt.offset);
+    let indent = &indent;
+
+    let mut cur_start = 0;
+    let mut result = String::with_capacity(round_up_to_power_of_two(s.len()));
+    result.push_str(fmt.opener);
+
+    let ender_length = fmt.line_end.len();
+    let max_chars = fmt.width.checked_sub(fmt.opener.len()).unwrap_or(0)
+                             .checked_sub(ender_length).unwrap_or(1);
+
+    loop {
+        let mut cur_end = cur_start + max_chars;
+
+        if cur_end >= s.len() {
+            result.push_str(&s[cur_start..]);
+            break;
+        }
+
+        // Make sure we're on a char boundary.
+        cur_end = next_char(&s, cur_end);
+
+        // Push cur_end left until we reach whitespace.
+        while !s.char_at(cur_end - 1).is_whitespace() {
+            cur_end = prev_char(&s, cur_end);
+
+            if cur_end - cur_start < MIN_STRING {
+                // We can't break at whitespace, fall back to splitting
+                // anywhere that doesn't break an escape sequence.
+                cur_end = next_char(&s, cur_start + max_chars);
+                while s.char_at(prev_char(&s, cur_end)) == '\\' {
+                    cur_end = prev_char(&s, cur_end);
+                }
+                break;
+            }
+        }
+        // Make sure there is no whitespace to the right of the break.
+        while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
+            cur_end = next_char(&s, cur_end + 1);
+        }
+
+        let line: &str = if fmt.trim_end {
+            &s[cur_start..cur_end].trim_right_matches(char::is_whitespace)
+        } else {
+            &s[cur_start..cur_end]
+        };
+
+        result.push_str(line);
+        result.push_str(fmt.line_end);
+        result.push('\n');
+        result.push_str(indent);
+        result.push_str(fmt.line_start);
+
+        cur_start = cur_end;
+    }
+    result.push_str(fmt.closer);
+
+    result
+}
+
+#[inline]
+// Checks if a appears before b in given string and, if so, returns the index of
+// a.
+// FIXME: could be more generic
+pub fn before<'x>(s: &'x str, a: &str, b: &str) -> Option<usize> {
+    s.find(a).and_then(|i| {
+        match s.find(b) {
+            Some(j) if j <= i => None,
+            _ => Some(i)
+        }
+    })
+}
diff --git a/src/utils.rs b/src/utils.rs
index 795c0d98309..de17f989f7a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -8,72 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast::Visibility;
+use syntax::ast::{Visibility, Attribute, MetaItem, MetaItem_};
+use syntax::codemap::{CodeMap, Span, BytePos};
 
-pub trait FindUncommented {
-    fn find_uncommented(&self, pat: &str) -> Option<usize>;
-}
+use comment::FindUncommented;
 
-impl FindUncommented for str {
-    fn find_uncommented(&self, pat: &str) -> Option<usize> {
-        let mut needle_iter = pat.chars();
-        let mut possible_comment = false;
+use SKIP_ANNOTATION;
 
-        for (i, b) in self.char_indices() {
-            match needle_iter.next() {
-                Some(c) => {
-                    if b != c {
-                        needle_iter = pat.chars();
-                    }
-                },
-                None => return Some(i - pat.len())
-            }
+#[inline]
+pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
+    let snippet = codemap.span_to_snippet(original).unwrap();
 
-            if possible_comment {
-                if b == '/' {
-                    return self[(i+1)..].find('\n')
-                                        .and_then(|end| {
-                                            self[(end + i + 2)..].find_uncommented(pat)
-                                                                 .map(|idx| idx + end + i + 2)
-                                        });
-                } else if b == '*' {
-                    return self[(i+1)..].find("*/")
-                                        .and_then(|end| {
-                                            self[(end + i + 3)..].find_uncommented(pat)
-                                                                 .map(|idx| idx + end + i + 3)
-                                        });
-                } else {
-                    possible_comment = false;
-                }
-            } else {
-                possible_comment = b == '/';
-            }
-        }
-
-        // Handle case where the pattern is a suffix of the search string
-        match needle_iter.next() {
-            Some(_) => None,
-            None => Some(self.len() - pat.len())
-        }
-    }
-}
-
-#[test]
-fn test_find_uncommented() {
-    fn check(haystack: &str, needle: &str, expected: Option<usize>) {
-        assert_eq!(expected, haystack.find_uncommented(needle));
-    }
-
-    check("/*//*/test", "test", Some(6));
-    check("//test\ntest", "test", Some(7));
-    check("/* comment only */", "whatever", None);
-    check("/* comment */ some text /* more commentary */ result", "result", Some(46));
-    check("sup // sup", "p", Some(2));
-    check("sup", "x", None);
-    check("π? /**/ π is nice!", "π is nice", Some(9));
-    check("/*sup yo? \n sup*/ sup", "p", Some(20));
-    check("hel/*lohello*/lo", "hello", None);
-    check("acb", "ab", None);
+    original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1)
 }
 
 #[inline]
@@ -114,6 +60,18 @@ pub fn format_visibility(vis: Visibility) -> &'static str {
     }
 }
 
+fn is_skip(meta_item: &MetaItem) -> bool {
+    match meta_item.node {
+        MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
+        _ => false,
+    }
+}
+
+#[inline]
+pub fn contains_skip(attrs: &[Attribute]) -> bool {
+    attrs.iter().any(|a| is_skip(&a.node.value))
+}
+
 #[inline]
 #[cfg(target_pointer_width="64")]
 // Based on the trick layed out at
diff --git a/src/visitor.rs b/src/visitor.rs
index 88b5d4b18dc..ebd59f0cf20 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -15,7 +15,6 @@ use syntax::visit;
 use utils;
 use config::Config;
 
-use SKIP_ANNOTATION;
 use changes::ChangeSet;
 use rewrite::{Rewrite, RewriteContext};
 
@@ -120,7 +119,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
                                              constness,
                                              abi,
                                              vis,
-                                             b.span.lo);
+                                             codemap::mk_sp(s.lo, b.span.lo));
                 self.changes.push_str_span(s, &new_fn);
             }
             visit::FkMethod(ident, ref sig, vis) => {
@@ -133,7 +132,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
                                              &sig.constness,
                                              &sig.abi,
                                              vis.unwrap_or(ast::Visibility::Inherited),
-                                             b.span.lo);
+                                             codemap::mk_sp(s.lo, b.span.lo));
                 self.changes.push_str_span(s, &new_fn);
             }
             visit::FkFnBlock(..) => {}
@@ -305,26 +304,22 @@ impl<'a> FmtVisitor<'a> {
         let first = &attrs[0];
         self.format_missing_with_indent(first.span.lo);
 
-        match self.rewrite_attrs(attrs, self.block_indent) {
-            Some(s) => {
-                self.changes.push_str_span(first.span, &s);
-                let last = attrs.last().unwrap();
-                self.last_pos = last.span.hi;
-                false
-            }
-            None => true
+        if utils::contains_skip(attrs) {
+            true
+        } else {
+            let rewrite = self.rewrite_attrs(attrs, self.block_indent);
+            self.changes.push_str_span(first.span, &rewrite);
+            let last = attrs.last().unwrap();
+            self.last_pos = last.span.hi;
+            false
         }
     }
 
-    fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> Option<String> {
+    pub fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> String {
         let mut result = String::new();
         let indent = utils::make_indent(indent);
 
         for (i, a) in attrs.iter().enumerate() {
-            if is_skip(&a.node.value) {
-                return None;
-            }
-
             let a_str = self.snippet(a.span);
 
             if i > 0 {
@@ -351,13 +346,6 @@ impl<'a> FmtVisitor<'a> {
             }
         }
 
-        Some(result)
-    }
-}
-
-fn is_skip(meta_item: &ast::MetaItem) -> bool {
-    match meta_item.node {
-        ast::MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
-        _ => false,
+        result
     }
 }
diff --git a/tests/config/small_tabs.toml b/tests/config/small_tabs.toml
index 68824838644..da39ac1a8f4 100644
--- a/tests/config/small_tabs.toml
+++ b/tests/config/small_tabs.toml
@@ -6,7 +6,7 @@ newline_style = "Unix"
 fn_brace_style = "SameLineWhere"
 fn_return_indent = "WithArgs"
 fn_args_paren_newline = true
-struct_trailing_comma = true
+struct_trailing_comma = "Vertical"
 struct_lit_trailing_comma = "Vertical"
 enum_trailing_comma = true
 report_todo = "Always"
diff --git a/tests/source/fn-simple.rs b/tests/source/fn-simple.rs
new file mode 100644
index 00000000000..9bcf3166722
--- /dev/null
+++ b/tests/source/fn-simple.rs
@@ -0,0 +1,11 @@
+
+fn simple(/*pre-comment on a function!?*/ i: i32/*yes, it's possible!  */   
+                                        ,response: NoWay /* hose */) {"cool"}
+
+
+fn weird_comment(/* /*/ double level */ comment */ x: Hello /*/*/* tripple, even */*/*/,
+// Does this work?
+y: World
+) {
+        simple(/* does this preserve comments now? */ 42, NoWay)
+}
diff --git a/tests/source/multiple.rs b/tests/source/multiple.rs
index 024817353da..04ecb2bc734 100644
--- a/tests/source/multiple.rs
+++ b/tests/source/multiple.rs
@@ -43,7 +43,7 @@ fn qux(a: dadsfa,   // Comment 1
 
 /// Blah blah blah.
 impl Bar {
-    fn foo(&mut self, a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
+    fn foo(&mut self, a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
            b: sdfasdfsdfasfs /*closing comment*/ ) -> isize {}
 
     /// Blah blah blah.
@@ -107,21 +107,3 @@ fn main() {
       let x = "Hello!!!!!!!!! abcd  abcd abcd abcd abcd abcd\n abcd abcd abcd abcd abcd abcd abcd abcd abcd \
                    abcd  abcd abcd abcd abcd abcd abcd abcd abcd abcd \
                     abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd";  }
-
-
-fn struct_lits() {
-    let x = Bar;
-    // Comment
-    let y = Foo { a: x };
-    Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
-    Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
-    Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
-        // Comment
-        a: foo(), // Comment
-        // Comment
-        b: bar(), // Comment
-    };
-
-    Foo { a: Bar,
-          b: foo() };
-}
diff --git a/tests/source/struct_lits.rs b/tests/source/struct_lits.rs
new file mode 100644
index 00000000000..b7e1a854b0b
--- /dev/null
+++ b/tests/source/struct_lits.rs
@@ -0,0 +1,37 @@
+// Struct literal expressions.
+
+fn main() {
+    let x = Bar;
+
+    // Comment
+    let y = Foo {a: x };
+
+    Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
+
+    Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
+
+    Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
+        // Comment
+        a: foo(), // Comment
+        // Comment
+        b: bar(), // Comment
+    };
+
+    Foo { a:Bar,
+          b:foo() };
+
+    A { 
+    // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor.
+    first: item(),
+        // Praesent et diam eget libero egestas mattis sit amet vitae augue.
+        // Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
+        second: Item
+    };
+
+    Diagram { /*                 o        This graph demonstrates how                  
+               *                / \       significant whitespace is           
+               *               o   o      preserved.  
+               *              /|\   \
+               *             o o o   o */
+              graph: G, }
+}
diff --git a/tests/source/structs.rs b/tests/source/structs.rs
new file mode 100644
index 00000000000..ace13fe2889
--- /dev/null
+++ b/tests/source/structs.rs
@@ -0,0 +1,70 @@
+
+                                                                       /// A Doc comment
+#[AnAttribute]
+pub struct Foo {
+    #[rustfmt_skip]
+    f :   SomeType, // Comment beside a field
+    f: SomeType, // Comment beside a field
+    // Comment on a field
+    #[AnAttribute]
+    g: SomeOtherType,
+      /// A doc comment on a field
+    h: AThirdType,
+    pub i: TypeForPublicField
+}
+
+struct Bar;
+
+struct NewType(Type,       OtherType);
+
+struct
+NewInt     <T: Copy>(pub i32, SomeType /* inline comment */, T /* sup */
+
+
+    );
+
+struct Qux<'a,
+           N: Clone + 'a,
+           E: Clone + 'a,
+           G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
+           W: Write + Copy>
+(
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment
+    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
+    #[AnAttr]    
+    // Comment
+    /// Testdoc
+    G,
+    pub W,
+);
+
+struct Tuple(/*Comment 1*/ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
+             /* Comment 2   */ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,);
+
+// With a where clause and generics.
+pub struct Foo<'a, Y: Baz>
+    where X: Whatever
+{
+    f: SomeType, // Comment beside a field
+}
+
+struct Baz {
+    a: A,  // Comment A
+    b: B, // Comment B
+    c: C,   // Comment C
+}
+
+struct Baz
+{
+    // Comment A
+    a: A,
+    // Comment B
+b: B,
+    // Comment C
+      c: C,}
+
+// Will this be a one-liner?
+struct Tuple(
+    A, //Comment
+    B
+);
diff --git a/tests/target/comments-fn.rs b/tests/target/comments-fn.rs
index 748a6edb6ca..0b21d84074a 100644
--- a/tests/target/comments-fn.rs
+++ b/tests/target/comments-fn.rs
@@ -2,13 +2,13 @@
 
 // Comment on foo.
 fn foo<F, G>(a: aaaaaaaaaaaaa, // A comment
-             b: bbbbbbbbbbbbb, /* a second comment */
+             b: bbbbbbbbbbbbb, // a second comment
              c: ccccccccccccc,
              // Newline comment
              d: ddddddddddddd,
              //  A multi line comment
              // between args.
-             e: eeeeeeeeeeeee /* comment before paren*/)
+             e: eeeeeeeeeeeee /* comment before paren */)
              -> bar
     where F: Foo, // COmment after where clause
           G: Goo /* final comment */
diff --git a/tests/target/enum.rs b/tests/target/enum.rs
index 2f7b3bb66a9..8c534ef89c8 100644
--- a/tests/target/enum.rs
+++ b/tests/target/enum.rs
@@ -3,7 +3,7 @@
 #[atrr]
 pub enum Test {
     A,
-    B(u32, A /* comment */),
+    B(u32, A /* comment */, SomeType),
     /// Doc comment
     C,
 }
@@ -27,8 +27,18 @@ enum Bar {
 }
 
 enum LongVariants {
-    First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // small comment
+    First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
           VARIANT),
     // This is the second variant
     Second,
 }
+
+enum StructLikeVariants {
+    Normal(u32, String),
+    StructLike {
+        x: i32, // Test comment
+        // Pre-comment
+        #[Attr50]
+        y: SomeType, // Aanother Comment
+    }
+}
diff --git a/tests/target/fn-simple.rs b/tests/target/fn-simple.rs
new file mode 100644
index 00000000000..eb133d568ed
--- /dev/null
+++ b/tests/target/fn-simple.rs
@@ -0,0 +1,16 @@
+
+fn simple(// pre-comment on a function!?
+          i: i32, // yes, it's possible!
+          response: NoWay /* hose */) {
+    "cool"
+}
+
+
+fn weird_comment(// /*/ double level */ comment
+                 x: Hello, // /*/* tripple, even */*/
+                 // Does this work?
+                 y: World) {
+    simple(// does this preserve comments now?
+           42,
+           NoWay)
+}
diff --git a/tests/target/multiple.rs b/tests/target/multiple.rs
index 0e89af29c15..0fb0c94e3a8 100644
--- a/tests/target/multiple.rs
+++ b/tests/target/multiple.rs
@@ -36,10 +36,10 @@ fn foo()
     hello!()
 }
 
-fn baz<'a: 'b, /* comment on 'a */
+fn baz<'a: 'b, // comment on 'a
        T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType /* comment on T */>
     (a: A,
-     b: B, /* comment on b */
+     b: B, // comment on b
      c: C)
      -> Bob {
     #[attr1]
@@ -65,8 +65,9 @@ fn qux(a: dadsfa,   // Comment 1
 /// Blah blah blah.
 impl Bar {
     fn foo(&mut self,
-           a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
-           b: sdfasdfsdfasfs /*closing comment*/)
+           a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, /* commen
+                                                                         * t on a */
+           b: sdfasdfsdfasfs /* closing comment */)
            -> isize {
     }
 
@@ -138,17 +139,3 @@ fn main() {
              abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \
              abcd";
 }
-
-
-fn struct_lits() {
-    let x = Bar;
-    // Comment
-    let y = Foo { a: x };
-    Foo { a: foo(), b: bar(), ..something };
-    Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
-                                                                               b: bar(), };
-    Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
-                                                                                         b: bar(), };
-
-    Foo { a: Bar, b: foo() };
-}
diff --git a/tests/target/struct_lits.rs b/tests/target/struct_lits.rs
index 1d32171d7d0..c46909825a1 100644
--- a/tests/target/struct_lits.rs
+++ b/tests/target/struct_lits.rs
@@ -6,19 +6,45 @@ fn main() {
     // Comment
     let y = Foo { a: x };
 
-    Foo { a: Bar, b: foo() };
+    Foo { a: foo(), // comment
+          // comment
+          b: bar(),
+          ..something };
 
-    Foo { a: foo(), b: bar(), ..something };
-
-    Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar() };
     Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
                                                                                b: bar(), };
 
-    Fooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
-                                                             b: bar(),
-                                                             c: bar(),
-                                                             d: bar(),
-                                                             e: bar(),
-                                                             f: bar(),
-                                                             ..baz() };
+    Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment
+                                                                                        a: foo(), /* C
+                                                                                                   * o
+                                                                                                   * m
+                                                                                                   * m
+                                                                                                   * e
+                                                                                                   * n
+                                                                                                   * t */
+                                                                                        // Comment
+                                                                                        b: bar(), /* C
+                                                                                                   * o
+                                                                                                   * m
+                                                                                                   * m
+                                                                                                   * e
+                                                                                                   * n
+                                                                                                   * t */ };
+
+    Foo { a: Bar, b: foo() };
+
+    A { // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit
+        // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante
+        // hendrerit. Donec et mollis dolor.
+        first: item(),
+        // Praesent et diam eget libero egestas mattis sit amet vitae augue.
+        // Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
+        second: Item, };
+
+    Diagram { //                 o        This graph demonstrates how
+              //                / \       significant whitespace is
+              //               o   o      preserved.
+              //              /|\   \
+              //             o o o   o
+              graph: G, }
 }
diff --git a/tests/target/structs.rs b/tests/target/structs.rs
index 507c9eba46b..84b7334008d 100644
--- a/tests/target/structs.rs
+++ b/tests/target/structs.rs
@@ -15,6 +15,32 @@ pub struct Foo {
 
 struct Bar;
 
+struct NewType(Type, OtherType);
+
+struct NewInt<T: Copy>(pub i32, SomeType /* inline comment */, T /* sup */);
+
+struct Qux<'a,
+           N: Clone + 'a,
+           E: Clone + 'a,
+           G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
+           W: Write + Copy>
+(
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment
+    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
+    #[AnAttr]
+    // Comment
+    /// Testdoc
+    G,
+    pub W,
+);
+
+struct Tuple(
+    // Comment 1
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
+    // Comment 2
+    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
+);
+
 // With a where clause and generics.
 pub struct Foo<'a, Y: Baz>
     where X: Whatever
@@ -36,3 +62,6 @@ struct Baz {
     // Comment C
     c: C,
 }
+
+// Will this be a one-liner?
+struct Tuple(A /* Comment */, B);
diff --git a/tests/target/tuple.rs b/tests/target/tuple.rs
index 3655cab0663..2d126cacf96 100644
--- a/tests/target/tuple.rs
+++ b/tests/target/tuple.rs
@@ -8,4 +8,8 @@ fn foo() {
                                   aaaaaaaaaaaaaaaaaaaaaaaaa,
                                   aaaa);
     let a = (a,);
+
+    let b = (// This is a comment
+             b, // Comment
+             b /* Trailing comment */);
 }