From 9cf8529e335995d54b178194d00172f7f7dd15d5 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 23 Nov 2015 12:00:22 +1300 Subject: [PATCH] Implement formatting for impls Fixes #614 --- src/items.rs | 100 ++++++++++++++++++++++++++++++++++++++++++++++++- src/types.rs | 16 ++++---- src/utils.rs | 7 ++++ src/visitor.rs | 14 +++---- 4 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/items.rs b/src/items.rs index bbe51c73c40..b4bd89ebb91 100644 --- a/src/items.rs +++ b/src/items.rs @@ -12,7 +12,7 @@ use Indent; use utils::{format_mutability, format_visibility, contains_skip, span_after, end_typaram, - wrap_str, last_line_width, semicolon_for_expr}; + wrap_str, last_line_width, semicolon_for_expr, format_unsafety, trim_newlines}; use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, DefinitiveListTactic, definitive_tactic, format_item_list}; use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs}; @@ -435,6 +435,104 @@ fn format_variant(&self, field: &ast::Variant) -> Option { } } +pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -> Option { + if let ast::Item_::ItemImpl(unsafety, + polarity, + ref generics, + ref trait_ref, + ref self_ty, + ref items) = item.node { + let mut result = String::new(); + result.push_str(format_visibility(item.vis)); + result.push_str(format_unsafety(unsafety)); + result.push_str("impl"); + + let lo = span_after(item.span, "impl", context.codemap); + let hi = match *trait_ref { + Some(ref tr) => tr.path.span.lo, + None => self_ty.span.lo, + }; + let generics_str = try_opt!(rewrite_generics(context, + generics, + offset, + offset + result.len(), + mk_sp(lo, hi))); + result.push_str(&generics_str); + + // FIXME might need to linebreak in the impl header, here would be a + // good place. + result.push(' '); + if polarity == ast::ImplPolarity::Negative { + result.push_str("!"); + } + if let &Some(ref trait_ref) = trait_ref { + let budget = try_opt!(context.config.max_width.checked_sub(result.len())); + let indent = offset + result.len(); + result.push_str(&*try_opt!(trait_ref.rewrite(context, budget, indent))); + result.push_str(" for "); + } + + let budget = try_opt!(context.config.max_width.checked_sub(result.len())); + let indent = offset + result.len(); + result.push_str(&*try_opt!(self_ty.rewrite(context, budget, indent))); + + let where_clause_str = try_opt!(rewrite_where_clause(context, + &generics.where_clause, + context.config, + context.block_indent, + context.config.where_density, + "{", + None)); + if !where_clause_str.contains('\n') && + result.len() + where_clause_str.len() + offset.width() > context.config.max_width { + result.push('\n'); + let width = context.block_indent.width() + context.config.tab_spaces - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string(context.config)); + } + result.push_str(&where_clause_str); + + match context.config.item_brace_style { + BraceStyle::AlwaysNextLine => result.push('\n'), + BraceStyle::PreferSameLine => result.push(' '), + BraceStyle::SameLineWhere => { + if where_clause_str.len() > 0 { + result.push('\n') + } else { + result.push(' ') + } + } + } + result.push('{'); + + if !items.is_empty() { + result.push('\n'); + let indent_str = context.block_indent.to_string(context.config); + result.push_str(&indent_str); + + let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config, None); + visitor.block_indent = context.block_indent.block_indent(context.config); + + let snippet = context.snippet(item.span); + let open_pos = try_opt!(snippet.find_uncommented("{")) + 1; + visitor.last_pos = item.span.lo + BytePos(open_pos as u32); + + for item in items { + visitor.visit_impl_item(&item); + } + + result.push_str(trim_newlines(&visitor.buffer.to_string())); + result.push('\n'); + result.push_str(&indent_str); + } + result.push('}'); + + Some(result) + } else { + unreachable!(); + } +} + pub fn format_struct(context: &RewriteContext, item_name: &str, ident: ast::Ident, diff --git a/src/types.rs b/src/types.rs index 807f0f17a43..4faa0a05f91 100644 --- a/src/types.rs +++ b/src/types.rs @@ -432,20 +432,22 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Opt // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; let max_path_width = try_opt!(width.checked_sub(extra_offset)); - let path_str = try_opt!(rewrite_path(context, - false, - None, - &self.trait_ref.path, - max_path_width, - offset + extra_offset)); + let path_str = try_opt!(self.trait_ref + .rewrite(context, max_path_width, offset + extra_offset)); Some(format!("for<{}> {}", lifetime_str, path_str)) } else { - rewrite_path(context, false, None, &self.trait_ref.path, width, offset) + self.trait_ref.rewrite(context, width, offset) } } } +impl Rewrite for ast::TraitRef { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { + rewrite_path(context, false, None, &self.path, width, offset) + } +} + impl Rewrite for ast::Ty { fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match self.node { diff --git a/src/utils.rs b/src/utils.rs index 6170b0c5ee0..195ca265c1f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -142,6 +142,13 @@ pub fn semicolon_for_stmt(stmt: &ast::Stmt) -> bool { } } +#[inline] +pub fn trim_newlines(input: &str) -> &str { + let start = input.find(|c| c != '\n' && c != '\r').unwrap_or(0); + let end = input.rfind(|c| c != '\n' && c != '\r').unwrap_or(0) + 1; + &input[start..end] +} + #[inline] #[cfg(target_pointer_width="64")] // Based on the trick layed out at diff --git a/src/visitor.rs b/src/visitor.rs index 6cbac8592dd..fcc017c98f2 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -21,7 +21,7 @@ use rewrite::{Rewrite, RewriteContext}; use comment::rewrite_comment; use macros::rewrite_macro; -use items::rewrite_static; +use items::{rewrite_static, format_impl}; pub struct FmtVisitor<'a> { pub parse_session: &'a ParseSess, @@ -200,14 +200,12 @@ fn visit_item(&mut self, item: &ast::Item) { ast::Item_::ItemUse(ref vp) => { self.format_import(item.vis, vp, item.span); } - // FIXME(#78): format impl definitions. - ast::Item_::ItemImpl(_, _, _, _, _, ref impl_items) => { + ast::Item_::ItemImpl(..) => { self.format_missing_with_indent(item.span.lo); - self.block_indent = self.block_indent.block_indent(self.config); - for item in impl_items { - self.visit_impl_item(&item); + if let Some(impl_str) = format_impl(&self.get_context(), item, self.block_indent) { + self.buffer.push_str(&impl_str); + self.last_pos = item.span.hi; } - self.block_indent = self.block_indent.block_unindent(self.config); } // FIXME(#78): format traits. ast::Item_::ItemTrait(_, _, _, ref trait_items) => { @@ -334,7 +332,7 @@ fn visit_trait_item(&mut self, ti: &ast::TraitItem) { } } - fn visit_impl_item(&mut self, ii: &ast::ImplItem) { + pub fn visit_impl_item(&mut self, ii: &ast::ImplItem) { if self.visit_attrs(&ii.attrs) { return; }