From e7adf64155abb3bdc2dcb6bb52ee24ed87206eff Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 25 May 2015 11:03:26 +1200 Subject: [PATCH] Format structs --- src/bin/rustfmt.rs | 4 +- src/config.rs | 1 - src/{functions.rs => items.rs} | 126 ++++++++++++++++++++++++++++++++- src/lib.rs | 2 +- src/visitor.rs | 11 ++- tests/idem.rs | 2 +- tests/idem/structs.rs | 37 ++++++++++ 7 files changed, 174 insertions(+), 9 deletions(-) rename src/{functions.rs => items.rs} (82%) create mode 100644 tests/idem/structs.rs diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 5a12dbd639b6..3b87ac7eb038 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -24,8 +24,8 @@ fn main() { let mut def_config = String::new(); def_config_file.read_to_string(&mut def_config).unwrap(); - //run(args, WriteMode::Display, &def_config); - run(args, WriteMode::Overwrite, &def_config); + run(args, WriteMode::Display, &def_config); + //run(args, WriteMode::Overwrite, &def_config); std::env::set_exit_status(0); diff --git a/src/config.rs b/src/config.rs index d1045293ee34..080926b03a2c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -23,7 +23,6 @@ pub struct Config { impl Config { fn from_toml(toml: &str) -> Config { - println!("About to parse: {}", toml); let parsed = toml.parse().unwrap(); toml::decode(parsed).unwrap() } diff --git a/src/functions.rs b/src/items.rs similarity index 82% rename from src/functions.rs rename to src/items.rs index 2ca5423f0c8a..a2027d89b888 100644 --- a/src/functions.rs +++ b/src/items.rs @@ -8,6 +8,8 @@ // 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}; @@ -123,7 +125,7 @@ impl<'a> FmtVisitor<'a> { let generics_indent = indent + result.len(); result.push_str(&self.rewrite_generics(generics, generics_indent, - span_for_return(&fd.output))); + span_for_return(&fd.output).lo)); let ret_str = self.rewrite_return(&fd.output); @@ -388,7 +390,125 @@ impl<'a> FmtVisitor<'a> { } } - fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, ret_span: Span) -> String { + 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 f in &struct_def.fields { + self.visit_field(f, 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, + // 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 field_str = match name { + Some(name) => { + let budget = config!(ideal_width) - self.block_indent; + 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), + }; + 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(); @@ -422,7 +542,7 @@ impl<'a> FmtVisitor<'a> { ">", |sp| sp.lo, |sp| sp.hi, - ret_span.lo); + span_end); // If there are // comments, keep them multi-line. let mut list_tactic = ListTactic::HorizontalVertical; diff --git a/src/lib.rs b/src/lib.rs index 386c39ad472b..1a690b4945ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ use visitor::FmtVisitor; mod config; mod changes; mod visitor; -mod functions; +mod items; mod missed_spans; mod lists; mod utils; diff --git a/src/visitor.rs b/src/visitor.rs index 2aac931c4e6c..256c1e6e1323 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -180,6 +180,15 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { 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 @@ impl<'a> FmtVisitor<'a> { } // 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; } diff --git a/tests/idem.rs b/tests/idem.rs index 40474b8dfee0..50329b258d14 100644 --- a/tests/idem.rs +++ b/tests/idem.rs @@ -57,7 +57,7 @@ fn idempotent_tests() { // Compare output to input. fn print_mismatches(result: HashMap) { - for (file_name, fmt_text) in result { + for (_, fmt_text) in result { println!("{}", fmt_text); } } diff --git a/tests/idem/structs.rs b/tests/idem/structs.rs new file mode 100644 index 000000000000..0852b5c31990 --- /dev/null +++ b/tests/idem/structs.rs @@ -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, +}