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()
}
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"
+struct_trailing_comma = true
+struct_lit_trailing_comma = "Vertical"
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;
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) => {
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);
+ }
_ => {}
}
+++ /dev/null
-// 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,
- }
-}
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 ",
--- /dev/null
+// 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,
+ }
+}
mod config;
mod changes;
mod visitor;
-mod functions;
+mod items;
mod missed_spans;
mod lists;
mod utils;
// except according to those terms.
use utils::make_indent;
+use rustc_serialize::{Decodable, Decoder};
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum ListTactic {
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,
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);
}
}
// 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;
}
// 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);
}
}
--- /dev/null
+// 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() };
+}
--- /dev/null
+
+/// 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,
+}