Implement formatting for impls

Fixes #614
This commit is contained in:
Nick Cameron 2015-11-23 12:00:22 +13:00
parent 08e6e74e69
commit 9cf8529e33
4 changed files with 121 additions and 16 deletions

View File

@ -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<String> {
}
}
pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -> Option<String> {
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,

View File

@ -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<String> {
rewrite_path(context, false, None, &self.path, width, offset)
}
}
impl Rewrite for ast::Ty {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {

View File

@ -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

View File

@ -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;
}