]> git.lizzy.rs Git - rust.git/commitdiff
Merge pull request #75 from nrc/structs
authorNick Cameron <nrc@ncameron.org>
Mon, 25 May 2015 07:16:25 +0000 (19:16 +1200)
committerNick Cameron <nrc@ncameron.org>
Mon, 25 May 2015 07:16:25 +0000 (19:16 +1200)
Structs

12 files changed:
src/config.rs
src/default.toml
src/expr.rs
src/functions.rs [deleted file]
src/imports.rs
src/items.rs [new file with mode: 0644]
src/lib.rs
src/lists.rs
src/visitor.rs
tests/idem.rs
tests/idem/struct_lits.rs [new file with mode: 0644]
tests/idem/structs.rs [new file with mode: 0644]

index d1045293ee34949db0a6c4130e939d6b98536367..8e6c376209f338f7702d5eb2f29600ae2c8cf2ef 100644 (file)
@@ -19,11 +19,12 @@ pub struct Config {
     pub newline_style: ::NewlineStyle,
     pub fn_brace_style: ::BraceStyle,
     pub fn_return_indent: ::ReturnIndent,
+    pub struct_trailing_comma: bool,
+    pub struct_lit_trailing_comma: ::lists::SeparatorTactic,
 }
 
 impl Config {
     fn from_toml(toml: &str) -> Config {
-        println!("About to parse: {}", toml);
         let parsed = toml.parse().unwrap();
         toml::decode(parsed).unwrap()
     }
index 218f2a04fb29784873c9692064c4096190566e4c..e8c74879b597df5a017be5d3c040a4ad5a556b03 100644 (file)
@@ -5,3 +5,5 @@ tab_spaces = 4
 newline_style = "Unix"
 fn_brace_style = "SameLineWhere"
 fn_return_indent = "WithArgs"
+struct_trailing_comma = true
+struct_lit_trailing_comma = "Vertical"
index 79234eb7f1376a481977d1d32e04c0a53d319b05..4b32301fe9142cae5fe179bbff8adaf068ed7c17 100644 (file)
@@ -13,7 +13,9 @@
 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
 
 use syntax::{ast, ptr};
-use syntax::codemap::{Span, Pos};
+use syntax::codemap::{Pos, Span};
+use syntax::parse::token;
+use syntax::print::pprust;
 
 use MIN_STRING;
 
@@ -134,6 +136,64 @@ fn rewrite_paren(&mut self, subexpr: &ast::Expr, width: usize, offset: usize) ->
         format!("({})", subexpr_str)
     }
 
+    fn rewrite_struct_lit(&mut self,
+                          path: &ast::Path,
+                          fields: &[ast::Field],
+                          base: Option<&ast::Expr>,
+                          width: usize,
+                          offset: usize)
+        -> String
+    {
+        debug!("rewrite_struct_lit: width {}, offset {}", width, offset);
+        assert!(fields.len() > 0 || base.is_some());
+
+        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 mut field_strs: Vec<_> =
+            fields.iter().map(|f| self.rewrite_field(f, budget, indent)).collect();
+        if let Some(expr) = base {
+            // Another 2 on the width/indent for the ..
+            field_strs.push(format!("..{}", self.rewrite_expr(expr, budget - 2, indent + 2)))
+        }
+
+        // FIXME comments
+        let field_strs: Vec<_> = field_strs.into_iter().map(|s| (s, String::new())).collect();
+        let tactics = if field_strs.iter().any(|&(ref s, _)| s.contains('\n')) {
+            ListTactic::Vertical
+        } else {
+            ListTactic::HorizontalVertical
+        };
+        let fmt = ListFormatting {
+            tactic: tactics,
+            separator: ",",
+            trailing_separator: if base.is_some() {
+                    SeparatorTactic::Never
+                } else {
+                    config!(struct_lit_trailing_comma)
+                },
+            indent: indent,
+            h_width: budget,
+            v_width: budget,
+        };
+        let fields_str = write_list(&field_strs, &fmt);
+        format!("{} {{ {} }}", path_str, fields_str)
+
+        // FIXME if the usual multi-line layout is too wide, we should fall back to
+        // Foo {
+        //     a: ...,
+        // }
+    }
+
+    fn rewrite_field(&mut self, field: &ast::Field, width: usize, offset: usize) -> String {
+        let name = &token::get_ident(field.ident.node);
+        let overhead = name.len() + 2;
+        let expr = self.rewrite_expr(&field.expr, width - overhead, offset + overhead);
+        format!("{}: {}", name, expr)
+    }
+
     pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
         match expr.node {
             ast::Expr_::ExprLit(ref l) => {
@@ -152,6 +212,13 @@ pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) ->
             ast::Expr_::ExprParen(ref subexpr) => {
                 return self.rewrite_paren(subexpr, width, offset);
             }
+            ast::Expr_::ExprStruct(ref path, ref fields, ref base) => {
+                return self.rewrite_struct_lit(path,
+                                               fields,
+                                               base.as_ref().map(|e| &**e),
+                                               width,
+                                               offset);
+            }
             _ => {}
         }
 
diff --git a/src/functions.rs b/src/functions.rs
deleted file mode 100644 (file)
index 2ca5423..0000000
+++ /dev/null
@@ -1,537 +0,0 @@
-// 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.
-
-use {ReturnIndent, BraceStyle};
-use utils::make_indent;
-use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
-use visitor::FmtVisitor;
-use syntax::{ast, abi};
-use syntax::codemap::{self, Span, BytePos};
-use syntax::print::pprust;
-use syntax::parse::token;
-
-impl<'a> FmtVisitor<'a> {
-    pub fn rewrite_fn(&mut self,
-                      indent: usize,
-                      ident: ast::Ident,
-                      fd: &ast::FnDecl,
-                      explicit_self: Option<&ast::ExplicitSelf>,
-                      generics: &ast::Generics,
-                      unsafety: &ast::Unsafety,
-                      abi: &abi::Abi,
-                      vis: ast::Visibility,
-                      span_end: BytePos)
-        -> String
-    {
-        let newline_brace = self.newline_for_brace(&generics.where_clause);
-
-        let mut result = self.rewrite_fn_base(indent,
-                                              ident,
-                                              fd,
-                                              explicit_self,
-                                              generics,
-                                              unsafety,
-                                              abi,
-                                              vis,
-                                              span_end,
-                                              newline_brace);
-
-        // Prepare for the function body by possibly adding a newline and indent.
-        // FIXME we'll miss anything between the end of the signature and the start
-        // of the body, but we need more spans from the compiler to solve this.
-        if newline_brace {
-            result.push('\n');
-            result.push_str(&make_indent(indent));
-        } else {
-            result.push(' ');
-        }
-
-        result
-    }
-
-    pub fn rewrite_required_fn(&mut self,
-                               indent: usize,
-                               ident: ast::Ident,
-                               sig: &ast::MethodSig,
-                               span: Span)
-        -> String
-    {
-        // Drop semicolon or it will be interpreted as comment
-        let span_end = span.hi - BytePos(1);
-
-        let mut result = self.rewrite_fn_base(indent,
-                                              ident,
-                                              &sig.decl,
-                                              Some(&sig.explicit_self),
-                                              &sig.generics,
-                                              &sig.unsafety,
-                                              &sig.abi,
-                                              ast::Visibility::Inherited,
-                                              span_end,
-                                              false);
-
-        // Re-attach semicolon
-        result.push(';');
-
-        result
-    }
-
-    fn rewrite_fn_base(&mut self,
-                       indent: usize,
-                       ident: ast::Ident,
-                       fd: &ast::FnDecl,
-                       explicit_self: Option<&ast::ExplicitSelf>,
-                       generics: &ast::Generics,
-                       unsafety: &ast::Unsafety,
-                       abi: &abi::Abi,
-                       vis: ast::Visibility,
-                       span_end: BytePos,
-                       newline_brace: bool)
-        -> String
-    {
-        // FIXME we'll lose any comments in between parts of the function decl, but anyone
-        // who comments there probably deserves what they get.
-
-        let where_clause = &generics.where_clause;
-
-        let mut result = String::with_capacity(1024);
-        // Vis unsafety abi.
-        if vis == ast::Visibility::Public {
-            result.push_str("pub ");
-        }
-        if let &ast::Unsafety::Unsafe = unsafety {
-            result.push_str("unsafe ");
-        }
-        if *abi != abi::Rust {
-            result.push_str("extern ");
-            result.push_str(&abi.to_string());
-            result.push(' ');
-        }
-
-        // fn foo
-        result.push_str("fn ");
-        result.push_str(&token::get_ident(ident));
-
-        // Generics.
-        let generics_indent = indent + result.len();
-        result.push_str(&self.rewrite_generics(generics,
-                                               generics_indent,
-                                               span_for_return(&fd.output)));
-
-        let ret_str = self.rewrite_return(&fd.output);
-
-        // Args.
-        let (one_line_budget, multi_line_budget, arg_indent) =
-            self.compute_budgets_for_args(&mut result, indent, ret_str.len(), newline_brace);
-
-        debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
-               one_line_budget, multi_line_budget, arg_indent);
-
-        result.push('(');
-        result.push_str(&self.rewrite_args(&fd.inputs,
-                                           explicit_self,
-                                           one_line_budget,
-                                           multi_line_budget,
-                                           arg_indent,
-                                           span_for_return(&fd.output)));
-        result.push(')');
-
-        // Return type.
-        if ret_str.len() > 0 {
-            // If we've already gone multi-line, or the return type would push
-            // over the max width, then put the return type on a new line.
-            if result.contains("\n") ||
-               result.len() + indent + ret_str.len() > config!(max_width) {
-                let indent = match config!(fn_return_indent) {
-                    ReturnIndent::WithWhereClause => indent + 4,
-                    // TODO we might want to check that using the arg indent doesn't
-                    // blow our budget, and if it does, then fallback to the where
-                    // clause indent.
-                    _ => arg_indent,
-                };
-
-                result.push('\n');
-                result.push_str(&make_indent(indent));
-            } else {
-                result.push(' ');
-            }
-            result.push_str(&ret_str);
-
-            // 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 = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
-                let snippet = snippet.trim();
-                if snippet.len() > 0 {
-                    result.push(' ');
-                    result.push_str(snippet);
-                }
-            } else {
-                // FIXME it would be nice to catch comments between the return type
-                // and the where clause, but we don't have a span for the where
-                // clause.
-            }
-        }
-
-        // Where clause.
-        result.push_str(&self.rewrite_where_clause(where_clause, indent, span_end));
-
-        result
-    }
-
-    fn rewrite_args(&self,
-                    args: &[ast::Arg],
-                    explicit_self: Option<&ast::ExplicitSelf>,
-                    one_line_budget: usize,
-                    multi_line_budget: usize,
-                    arg_indent: usize,
-                    ret_span: Span)
-        -> String
-    {
-        let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
-        // Account for sugary self.
-        let mut min_args = 1;
-        if let Some(explicit_self) = explicit_self {
-            match explicit_self.node {
-                ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
-                    let lt_str = match lt {
-                        &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
-                        &None => String::new(),
-                    };
-                    let mut_str = match m {
-                        &ast::Mutability::MutMutable => "mut ".to_owned(),
-                        &ast::Mutability::MutImmutable => String::new(),
-                    };
-                    arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
-                    min_args = 2;
-                }
-                ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
-                    arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
-                }
-                ast::ExplicitSelf_::SelfValue(_) => {
-                    assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
-
-                    // this hacky solution caused by absence of `Mutability` in `SelfValue`.
-                    let mut_str = {
-                        if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
-                                = args[0].pat.node {
-                            match mutability {
-                                ast::Mutability::MutMutable => "mut ",
-                                ast::Mutability::MutImmutable => "",
-                            }
-                        } else {
-                            panic!("there is a bug or change in structure of AST, aborting.");
-                        }
-                    };
-
-                    arg_item_strs[0] = format!("{}self", mut_str);
-                    min_args = 2;
-                }
-                _ => {}
-            }
-        }
-
-        // Comments between args
-        let mut arg_comments = Vec::new();
-        if min_args == 2 {
-            arg_comments.push("".to_owned());
-        }
-        // TODO if there are no args, there might still be a comment, but without
-        // spans for the comment or parens, there is no chance of getting it right.
-        // You also don't get to put a comment on self, unless it is explicit.
-        if args.len() >= min_args {
-            arg_comments = self.make_comments_for_list(arg_comments,
-                                                       args[min_args-1..].iter(),
-                                                       ",",
-                                                       ")",
-                                                       |arg| arg.pat.span.lo,
-                                                       |arg| arg.ty.span.hi,
-                                                       ret_span.lo);
-        }
-
-        debug!("comments: {:?}", arg_comments);
-
-        // 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;
-        }
-
-        assert_eq!(arg_item_strs.len(), arg_comments.len());
-        let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
-
-        let fmt = ListFormatting {
-            tactic: list_tactic,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: arg_indent,
-            h_width: one_line_budget,
-            v_width: multi_line_budget,
-        };
-
-        write_list(&arg_strs, &fmt)
-    }
-
-    // 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. Obviously, this will break if there is a
-        // closing paren in the comment.
-        // 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(terminator)
-                                    .unwrap_or(snippet.find(separator).unwrap_or(snippet.len()))];
-        let snippet = snippet.trim();
-        result.push(snippet.to_owned());
-
-        result
-    }
-
-    fn compute_budgets_for_args(&self,
-                                result: &mut String,
-                                indent: usize,
-                                ret_str_len: usize,
-                                newline_brace: bool)
-        -> (usize, usize, usize)
-    {
-        let mut budgets = None;
-
-        // Try keeping everything on the same line
-        if !result.contains("\n") {
-            // 3 = `() `, space is before ret_string
-            let mut used_space = indent + result.len() + ret_str_len + 3;
-            if !newline_brace {
-                used_space += 2;
-            }
-            let one_line_budget = if used_space > config!(max_width) {
-                0
-            } else {
-                config!(max_width) - used_space
-            };
-
-            // 2 = `()`
-            let used_space = indent + result.len() + 2;
-            let max_space = config!(ideal_width) + config!(leeway);
-            debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
-                   used_space, max_space);
-            if used_space < max_space {
-                budgets = Some((one_line_budget,
-                                max_space - used_space,
-                                indent + result.len() + 1));
-            }
-        }
-
-        // Didn't work. we must force vertical layout and put args on a newline.
-        if let None = budgets {
-            result.push('\n');
-            result.push_str(&make_indent(indent + 4));
-            // 6 = new indent + `()`
-            let used_space = indent + 6;
-            let max_space = config!(ideal_width) + config!(leeway);
-            if used_space > max_space {
-                // Whoops! bankrupt.
-                // TODO take evasive action, perhaps kill the indent or something.
-            } else {
-                // 5 = new indent + `(`
-                budgets = Some((0, max_space - used_space, indent + 5));
-            }
-        }
-
-        budgets.unwrap()
-    }
-
-    fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
-        match config!(fn_brace_style) {
-            BraceStyle::AlwaysNextLine => true,
-            BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
-            _ => false,
-        }
-    }
-
-    fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, ret_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();
-        let lifetimes: &[_] = &generics.lifetimes;
-        let tys: &[_] = &generics.ty_params;
-        if lifetimes.len() + tys.len() == 0 {
-            return result;
-        }
-
-        let budget = config!(max_width) - indent - 2;
-        // TODO might need to insert a newline if the generics are really long
-        result.push('<');
-
-        // Strings for the generics.
-        let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
-        let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
-
-        // Extract comments between generics.
-        let lt_spans = lifetimes.iter().map(|l| {
-            let hi = if l.bounds.len() == 0 {
-                l.lifetime.span.hi
-            } else {
-                l.bounds[l.bounds.len() - 1].span.hi
-            };
-            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,
-                                                   ret_span.lo);
-
-        // 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 generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
-        let fmt = ListFormatting {
-            tactic: list_tactic,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent + 1,
-            h_width: budget,
-            v_width: budget,
-        };
-        result.push_str(&write_list(&generics_strs, &fmt));
-
-        result.push('>');
-
-        result
-    }
-
-    fn rewrite_where_clause(&self,
-                            where_clause: &ast::WhereClause,
-                            indent: usize,
-                            span_end: BytePos)
-        -> String
-    {
-        let mut result = String::new();
-        if where_clause.predicates.len() == 0 {
-            return result;
-        }
-
-        result.push('\n');
-        result.push_str(&make_indent(indent + 4));
-        result.push_str("where ");
-
-        let comments = self.make_comments_for_list(Vec::new(),
-                                                   where_clause.predicates.iter(),
-                                                   ",",
-                                                   "{",
-                                                   |pred| span_for_where_pred(pred).lo,
-                                                   |pred| span_for_where_pred(pred).hi,
-                                                   span_end);
-
-        let where_strs: Vec<_> = where_clause.predicates.iter()
-                                                        .map(|p| (self.rewrite_pred(p)))
-                                                        .zip(comments.into_iter())
-                                                        .collect();
-
-        let budget = config!(ideal_width) + config!(leeway) - indent - 10;
-        let fmt = ListFormatting {
-            tactic: ListTactic::Vertical,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent + 10,
-            h_width: budget,
-            v_width: budget,
-        };
-        result.push_str(&write_list(&where_strs, &fmt));
-
-        result
-    }
-
-    fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
-        match *ret {
-            ast::FunctionRetTy::DefaultReturn(_) => String::new(),
-            ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
-            ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
-        }
-    }
-
-    // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
-    fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
-        format!("{}: {}",
-                pprust::pat_to_string(&arg.pat),
-                pprust::ty_to_string(&arg.ty))
-    }
-}
-
-fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
-    match *ret {
-        ast::FunctionRetTy::NoReturn(ref span) |
-        ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
-        ast::FunctionRetTy::Return(ref ty) => ty.span,
-    }
-}
-
-fn span_for_ty_param(ty: &ast::TyParam) -> Span {
-    // Note that ty.span is the span for ty.ident, not the whole item.
-    let lo = ty.span.lo;
-    if let Some(ref def) = ty.default {
-        return codemap::mk_sp(lo, def.span.hi);
-    }
-    if ty.bounds.len() == 0 {
-        return ty.span;
-    }
-    let hi = match ty.bounds[ty.bounds.len() - 1] {
-        ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
-        ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
-    };
-    codemap::mk_sp(lo, hi)
-}
-
-fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
-    match *pred {
-        ast::WherePredicate::BoundPredicate(ref p) => p.span,
-        ast::WherePredicate::RegionPredicate(ref p) => p.span,
-        ast::WherePredicate::EqPredicate(ref p) => p.span,
-    }
-}
index 9c0b81cc902043e3f82f3b9d3c1b90eba289df4d..cab3fa452e49fd21903a6996fb6af9be44217fa2 100644 (file)
@@ -47,7 +47,7 @@ pub fn rewrite_use_list(&mut self,
                             path: &ast::Path,
                             path_list: &[ast::PathListItem],
                             visibility: ast::Visibility) -> String {
-        let path_str = pprust::path_to_string(&path);
+        let path_str = pprust::path_to_string(path);
 
         let vis = match visibility {
             ast::Public => "pub ",
diff --git a/src/items.rs b/src/items.rs
new file mode 100644 (file)
index 0000000..55613ab
--- /dev/null
@@ -0,0 +1,662 @@
+// 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.
+
+// Formatting top-level items - functions, structs, enums, traits, impls.
+
+use {ReturnIndent, BraceStyle};
+use utils::make_indent;
+use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
+use visitor::FmtVisitor;
+use syntax::{ast, abi};
+use syntax::codemap::{self, Span, BytePos};
+use syntax::print::pprust;
+use syntax::parse::token;
+
+impl<'a> FmtVisitor<'a> {
+    pub fn rewrite_fn(&mut self,
+                      indent: usize,
+                      ident: ast::Ident,
+                      fd: &ast::FnDecl,
+                      explicit_self: Option<&ast::ExplicitSelf>,
+                      generics: &ast::Generics,
+                      unsafety: &ast::Unsafety,
+                      abi: &abi::Abi,
+                      vis: ast::Visibility,
+                      span_end: BytePos)
+        -> String
+    {
+        let newline_brace = self.newline_for_brace(&generics.where_clause);
+
+        let mut result = self.rewrite_fn_base(indent,
+                                              ident,
+                                              fd,
+                                              explicit_self,
+                                              generics,
+                                              unsafety,
+                                              abi,
+                                              vis,
+                                              span_end,
+                                              newline_brace);
+
+        // Prepare for the function body by possibly adding a newline and indent.
+        // FIXME we'll miss anything between the end of the signature and the start
+        // of the body, but we need more spans from the compiler to solve this.
+        if newline_brace {
+            result.push('\n');
+            result.push_str(&make_indent(indent));
+        } else {
+            result.push(' ');
+        }
+
+        result
+    }
+
+    pub fn rewrite_required_fn(&mut self,
+                               indent: usize,
+                               ident: ast::Ident,
+                               sig: &ast::MethodSig,
+                               span: Span)
+        -> String
+    {
+        // Drop semicolon or it will be interpreted as comment
+        let span_end = span.hi - BytePos(1);
+
+        let mut result = self.rewrite_fn_base(indent,
+                                              ident,
+                                              &sig.decl,
+                                              Some(&sig.explicit_self),
+                                              &sig.generics,
+                                              &sig.unsafety,
+                                              &sig.abi,
+                                              ast::Visibility::Inherited,
+                                              span_end,
+                                              false);
+
+        // Re-attach semicolon
+        result.push(';');
+
+        result
+    }
+
+    fn rewrite_fn_base(&mut self,
+                       indent: usize,
+                       ident: ast::Ident,
+                       fd: &ast::FnDecl,
+                       explicit_self: Option<&ast::ExplicitSelf>,
+                       generics: &ast::Generics,
+                       unsafety: &ast::Unsafety,
+                       abi: &abi::Abi,
+                       vis: ast::Visibility,
+                       span_end: BytePos,
+                       newline_brace: bool)
+        -> String
+    {
+        // FIXME we'll lose any comments in between parts of the function decl, but anyone
+        // who comments there probably deserves what they get.
+
+        let where_clause = &generics.where_clause;
+
+        let mut result = String::with_capacity(1024);
+        // Vis unsafety abi.
+        if vis == ast::Visibility::Public {
+            result.push_str("pub ");
+        }
+        if let &ast::Unsafety::Unsafe = unsafety {
+            result.push_str("unsafe ");
+        }
+        if *abi != abi::Rust {
+            result.push_str("extern ");
+            result.push_str(&abi.to_string());
+            result.push(' ');
+        }
+
+        // fn foo
+        result.push_str("fn ");
+        result.push_str(&token::get_ident(ident));
+
+        // Generics.
+        let generics_indent = indent + result.len();
+        result.push_str(&self.rewrite_generics(generics,
+                                               generics_indent,
+                                               span_for_return(&fd.output).lo));
+
+        let ret_str = self.rewrite_return(&fd.output);
+
+        // Args.
+        let (one_line_budget, multi_line_budget, arg_indent) =
+            self.compute_budgets_for_args(&mut result, indent, ret_str.len(), newline_brace);
+
+        debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
+               one_line_budget, multi_line_budget, arg_indent);
+
+        result.push('(');
+        result.push_str(&self.rewrite_args(&fd.inputs,
+                                           explicit_self,
+                                           one_line_budget,
+                                           multi_line_budget,
+                                           arg_indent,
+                                           span_for_return(&fd.output)));
+        result.push(')');
+
+        // Return type.
+        if ret_str.len() > 0 {
+            // If we've already gone multi-line, or the return type would push
+            // over the max width, then put the return type on a new line.
+            if result.contains("\n") ||
+               result.len() + indent + ret_str.len() > config!(max_width) {
+                let indent = match config!(fn_return_indent) {
+                    ReturnIndent::WithWhereClause => indent + 4,
+                    // TODO we might want to check that using the arg indent doesn't
+                    // blow our budget, and if it does, then fallback to the where
+                    // clause indent.
+                    _ => arg_indent,
+                };
+
+                result.push('\n');
+                result.push_str(&make_indent(indent));
+            } else {
+                result.push(' ');
+            }
+            result.push_str(&ret_str);
+
+            // 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 = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
+                let snippet = snippet.trim();
+                if snippet.len() > 0 {
+                    result.push(' ');
+                    result.push_str(snippet);
+                }
+            } else {
+                // FIXME it would be nice to catch comments between the return type
+                // and the where clause, but we don't have a span for the where
+                // clause.
+            }
+        }
+
+        // Where clause.
+        result.push_str(&self.rewrite_where_clause(where_clause, indent, span_end));
+
+        result
+    }
+
+    fn rewrite_args(&self,
+                    args: &[ast::Arg],
+                    explicit_self: Option<&ast::ExplicitSelf>,
+                    one_line_budget: usize,
+                    multi_line_budget: usize,
+                    arg_indent: usize,
+                    ret_span: Span)
+        -> String
+    {
+        let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
+        // Account for sugary self.
+        let mut min_args = 1;
+        if let Some(explicit_self) = explicit_self {
+            match explicit_self.node {
+                ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
+                    let lt_str = match lt {
+                        &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
+                        &None => String::new(),
+                    };
+                    let mut_str = match m {
+                        &ast::Mutability::MutMutable => "mut ".to_owned(),
+                        &ast::Mutability::MutImmutable => String::new(),
+                    };
+                    arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
+                    min_args = 2;
+                }
+                ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
+                    arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
+                }
+                ast::ExplicitSelf_::SelfValue(_) => {
+                    assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
+
+                    // this hacky solution caused by absence of `Mutability` in `SelfValue`.
+                    let mut_str = {
+                        if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
+                                = args[0].pat.node {
+                            match mutability {
+                                ast::Mutability::MutMutable => "mut ",
+                                ast::Mutability::MutImmutable => "",
+                            }
+                        } else {
+                            panic!("there is a bug or change in structure of AST, aborting.");
+                        }
+                    };
+
+                    arg_item_strs[0] = format!("{}self", mut_str);
+                    min_args = 2;
+                }
+                _ => {}
+            }
+        }
+
+        // Comments between args
+        let mut arg_comments = Vec::new();
+        if min_args == 2 {
+            arg_comments.push("".to_owned());
+        }
+        // TODO if there are no args, there might still be a comment, but without
+        // spans for the comment or parens, there is no chance of getting it right.
+        // You also don't get to put a comment on self, unless it is explicit.
+        if args.len() >= min_args {
+            arg_comments = self.make_comments_for_list(arg_comments,
+                                                       args[min_args-1..].iter(),
+                                                       ",",
+                                                       ")",
+                                                       |arg| arg.pat.span.lo,
+                                                       |arg| arg.ty.span.hi,
+                                                       ret_span.lo);
+        }
+
+        debug!("comments: {:?}", arg_comments);
+
+        // 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;
+        }
+
+        assert_eq!(arg_item_strs.len(), arg_comments.len());
+        let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
+
+        let fmt = ListFormatting {
+            tactic: list_tactic,
+            separator: ",",
+            trailing_separator: SeparatorTactic::Never,
+            indent: arg_indent,
+            h_width: one_line_budget,
+            v_width: multi_line_budget,
+        };
+
+        write_list(&arg_strs, &fmt)
+    }
+
+    // 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. Obviously, this will break if there is a
+        // closing paren in the comment.
+        // 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(terminator)
+                                    .unwrap_or(snippet.find(separator).unwrap_or(snippet.len()))];
+        let snippet = snippet.trim();
+        result.push(snippet.to_owned());
+
+        result
+    }
+
+    fn compute_budgets_for_args(&self,
+                                result: &mut String,
+                                indent: usize,
+                                ret_str_len: usize,
+                                newline_brace: bool)
+        -> (usize, usize, usize)
+    {
+        let mut budgets = None;
+
+        // Try keeping everything on the same line
+        if !result.contains("\n") {
+            // 3 = `() `, space is before ret_string
+            let mut used_space = indent + result.len() + ret_str_len + 3;
+            if !newline_brace {
+                used_space += 2;
+            }
+            let one_line_budget = if used_space > config!(max_width) {
+                0
+            } else {
+                config!(max_width) - used_space
+            };
+
+            // 2 = `()`
+            let used_space = indent + result.len() + 2;
+            let max_space = config!(ideal_width) + config!(leeway);
+            debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
+                   used_space, max_space);
+            if used_space < max_space {
+                budgets = Some((one_line_budget,
+                                max_space - used_space,
+                                indent + result.len() + 1));
+            }
+        }
+
+        // Didn't work. we must force vertical layout and put args on a newline.
+        if let None = budgets {
+            result.push('\n');
+            result.push_str(&make_indent(indent + 4));
+            // 6 = new indent + `()`
+            let used_space = indent + 6;
+            let max_space = config!(ideal_width) + config!(leeway);
+            if used_space > max_space {
+                // Whoops! bankrupt.
+                // TODO take evasive action, perhaps kill the indent or something.
+            } else {
+                // 5 = new indent + `(`
+                budgets = Some((0, max_space - used_space, indent + 5));
+            }
+        }
+
+        budgets.unwrap()
+    }
+
+    fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
+        match config!(fn_brace_style) {
+            BraceStyle::AlwaysNextLine => true,
+            BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
+            _ => false,
+        }
+    }
+
+    pub fn visit_struct(&mut self,
+                        ident: ast::Ident,
+                        vis: ast::Visibility,
+                        struct_def: &ast::StructDef,
+                        generics: &ast::Generics,
+                        span: Span)
+    {
+        let header_str = self.struct_header(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 mut generics_buf = String::new();
+        let generics_str = self.rewrite_generics(generics, self.block_indent, struct_def.fields[0].span.lo);
+        generics_buf.push_str(&generics_str);
+
+        if generics.where_clause.predicates.len() > 0 {
+            generics_buf.push_str(&self.rewrite_where_clause(&generics.where_clause,
+                                                             self.block_indent,
+                                                             struct_def.fields[0].span.lo));
+            generics_buf.push_str(&make_indent(self.block_indent));
+            generics_buf.push_str("\n{");
+
+        } else {
+            generics_buf.push_str(" {");
+        }
+        self.changes.push_str_span(span, &generics_buf);
+
+        let struct_snippet = self.snippet(span);
+        // FIXME this will give incorrect results if there is a { in a commet.
+        self.last_pos = span.lo + BytePos(struct_snippet.find('{').unwrap() as u32 + 1);
+
+        self.block_indent += config!(tab_spaces);
+        for (i, f) in struct_def.fields.iter().enumerate() {
+            self.visit_field(f, i == struct_def.fields.len() - 1, span.lo, &struct_snippet);
+        }
+        self.block_indent -= config!(tab_spaces);
+
+        self.format_missing_with_indent(span.lo + BytePos(struct_snippet.rfind('}').unwrap() as u32));
+        self.changes.push_str_span(span, "}");
+    }
+
+    fn struct_header(&self,
+                     ident: ast::Ident,
+                     vis: ast::Visibility)
+        -> String
+    {
+        let vis = if vis == ast::Visibility::Public {
+            "pub "
+        } else {
+            ""
+        };
+
+        format!("{}struct {}", vis, &token::get_ident(ident))
+    }
+
+    // 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;
+        }
+        self.format_missing_with_indent(field.span.lo);
+
+        let name = match field.node.kind {
+            ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
+            ast::StructFieldKind::UnnamedField(_) => None,
+        };
+        let vis = match field.node.kind {
+            ast::StructFieldKind::NamedField(_, vis) |
+            ast::StructFieldKind::UnnamedField(vis) => if vis == ast::Visibility::Public {
+                "pub "
+            } else {
+                ""
+            }
+        };
+        let typ = pprust::ty_to_string(&field.node.ty);
+
+        let mut field_str = match name {
+            Some(name) => {
+                let budget = config!(ideal_width) - self.block_indent;
+                // 3 is being conservative and assuming that there will be a trailing comma.
+                if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
+                    format!("{}{}:\n{}{}",
+                            vis,
+                            name,
+                            &make_indent(self.block_indent + config!(tab_spaces)),
+                            typ)
+                } else {
+                    format!("{}{}: {}", vis, name, typ)
+                }
+            }
+            None => format!("{}{}", vis, typ),
+        };
+        if !last_field || config!(struct_trailing_comma) {
+            field_str.push(',');
+        }
+        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;
+        // FIXME a comma in a comment will break this hack.
+        let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find(',') {
+            Some(i) => i,
+            None => 0,
+        };
+        self.last_pos = hi + BytePos(comma_pos as u32 + 1);
+    }
+
+    fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: BytePos) -> String {
+        // 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();
+        let lifetimes: &[_] = &generics.lifetimes;
+        let tys: &[_] = &generics.ty_params;
+        if lifetimes.len() + tys.len() == 0 {
+            return result;
+        }
+
+        let budget = config!(max_width) - indent - 2;
+        // TODO might need to insert a newline if the generics are really long
+        result.push('<');
+
+        // Strings for the generics.
+        let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
+        let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
+
+        // Extract comments between generics.
+        let lt_spans = lifetimes.iter().map(|l| {
+            let hi = if l.bounds.len() == 0 {
+                l.lifetime.span.hi
+            } else {
+                l.bounds[l.bounds.len() - 1].span.hi
+            };
+            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 generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
+        let fmt = ListFormatting {
+            tactic: list_tactic,
+            separator: ",",
+            trailing_separator: SeparatorTactic::Never,
+            indent: indent + 1,
+            h_width: budget,
+            v_width: budget,
+        };
+        result.push_str(&write_list(&generics_strs, &fmt));
+
+        result.push('>');
+
+        result
+    }
+
+    fn rewrite_where_clause(&self,
+                            where_clause: &ast::WhereClause,
+                            indent: usize,
+                            span_end: BytePos)
+        -> String
+    {
+        let mut result = String::new();
+        if where_clause.predicates.len() == 0 {
+            return result;
+        }
+
+        result.push('\n');
+        result.push_str(&make_indent(indent + 4));
+        result.push_str("where ");
+
+        let comments = self.make_comments_for_list(Vec::new(),
+                                                   where_clause.predicates.iter(),
+                                                   ",",
+                                                   "{",
+                                                   |pred| span_for_where_pred(pred).lo,
+                                                   |pred| span_for_where_pred(pred).hi,
+                                                   span_end);
+
+        let where_strs: Vec<_> = where_clause.predicates.iter()
+                                                        .map(|p| (self.rewrite_pred(p)))
+                                                        .zip(comments.into_iter())
+                                                        .collect();
+
+        let budget = config!(ideal_width) + config!(leeway) - indent - 10;
+        let fmt = ListFormatting {
+            tactic: ListTactic::Vertical,
+            separator: ",",
+            trailing_separator: SeparatorTactic::Never,
+            indent: indent + 10,
+            h_width: budget,
+            v_width: budget,
+        };
+        result.push_str(&write_list(&where_strs, &fmt));
+
+        result
+    }
+
+    fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
+        match *ret {
+            ast::FunctionRetTy::DefaultReturn(_) => String::new(),
+            ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
+            ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
+        }
+    }
+
+    // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
+    fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
+        format!("{}: {}",
+                pprust::pat_to_string(&arg.pat),
+                pprust::ty_to_string(&arg.ty))
+    }
+}
+
+fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
+    match *ret {
+        ast::FunctionRetTy::NoReturn(ref span) |
+        ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
+        ast::FunctionRetTy::Return(ref ty) => ty.span,
+    }
+}
+
+fn span_for_ty_param(ty: &ast::TyParam) -> Span {
+    // Note that ty.span is the span for ty.ident, not the whole item.
+    let lo = ty.span.lo;
+    if let Some(ref def) = ty.default {
+        return codemap::mk_sp(lo, def.span.hi);
+    }
+    if ty.bounds.len() == 0 {
+        return ty.span;
+    }
+    let hi = match ty.bounds[ty.bounds.len() - 1] {
+        ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
+        ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
+    };
+    codemap::mk_sp(lo, hi)
+}
+
+fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
+    match *pred {
+        ast::WherePredicate::BoundPredicate(ref p) => p.span,
+        ast::WherePredicate::RegionPredicate(ref p) => p.span,
+        ast::WherePredicate::EqPredicate(ref p) => p.span,
+    }
+}
index 386c39ad472be24460ad2790fac041f7b5e817ba..1a690b4945aeee6dd3e4d109803b6d289b737530 100644 (file)
@@ -50,7 +50,7 @@
 mod config;
 mod changes;
 mod visitor;
-mod functions;
+mod items;
 mod missed_spans;
 mod lists;
 mod utils;
index 06fb93f71e104d8343c960622d6338479cc8754f..58e810fce205d845279b07a30dfdf61710a64388 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use utils::make_indent;
+use rustc_serialize::{Decodable, Decoder};
 
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
 pub enum ListTactic {
@@ -29,6 +30,19 @@ pub enum SeparatorTactic {
     Vertical,
 }
 
+// TODO could use a macro for all these Decodable impls.
+impl Decodable for SeparatorTactic {
+    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        let s = try!(d.read_str());
+        match &*s {
+            "Always" => Ok(SeparatorTactic::Always),
+            "Never" => Ok(SeparatorTactic::Never),
+            "Vertical" => Ok(SeparatorTactic::Vertical),
+            _ => Err(d.error("Bad variant")),
+        }
+    }
+}
+
 // TODO having some helpful ctors for ListFormatting would be nice.
 pub struct ListFormatting<'a> {
     pub tactic: ListTactic,
index 2aac931c4e6c1a3523ed18c73fa722e4ea56549e..256c1e6e13230557cce28f068472db2f1b5d368f 100644 (file)
@@ -180,6 +180,15 @@ fn visit_item(&mut self, item: &'v ast::Item) {
                 self.changes.push_str_span(item.span, &new_str);
                 self.last_pos = item.span.hi;
             }
+            ast::Item_::ItemStruct(ref def, ref generics) => {
+                self.format_missing_with_indent(item.span.lo);
+                self.visit_struct(item.ident,
+                                  item.vis,
+                                  def,
+                                  generics,
+                                  item.span);
+                self.last_pos = item.span.hi;
+            }
             _ => {
                 visit::walk_item(self, item);
             }
@@ -252,7 +261,7 @@ pub fn snippet(&self, span: Span) -> String {
     }
 
     // Returns true if we should skip the following item.
-    fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
+    pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
         if attrs.len() == 0 {
             return false;
         }
index 40474b8dfee0d9691feff54d2f0fb1a81b432bac..50329b258d14ca907568e995ac11f5a2b10ad69b 100644 (file)
@@ -57,7 +57,7 @@ fn idempotent_tests() {
 
 // Compare output to input.
 fn print_mismatches(result: HashMap<String, String>) {
-    for (file_name, fmt_text) in result {
+    for (_, fmt_text) in result {
         println!("{}", fmt_text);
     }
 }
diff --git a/tests/idem/struct_lits.rs b/tests/idem/struct_lits.rs
new file mode 100644 (file)
index 0000000..1d32171
--- /dev/null
@@ -0,0 +1,24 @@
+// Struct literal expressions.
+
+fn main() {
+    let x = Bar;
+
+    // Comment
+    let y = Foo { a: x };
+
+    Foo { a: Bar, b: foo() };
+
+    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() };
+}
diff --git a/tests/idem/structs.rs b/tests/idem/structs.rs
new file mode 100644 (file)
index 0000000..0852b5c
--- /dev/null
@@ -0,0 +1,37 @@
+
+/// 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,
+}
+
+struct Bar;
+
+// 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,
+}