From c680bb4030784ca15cc7d6c39caf297c9fb010e4 Mon Sep 17 00:00:00 2001 From: Marcus Klaas Date: Fri, 11 Sep 2015 00:52:16 +0200 Subject: [PATCH] Implement basic chain formatting --- src/chains.rs | 133 ++++++++++++++++++++++++++++++++++++++++++++ src/comment.rs | 45 +++++++++++---- src/expr.rs | 109 ++++++++++++++++++++---------------- src/items.rs | 59 ++++++++++---------- src/lib.rs | 1 + src/lists.rs | 28 +++++----- src/missed_spans.rs | 25 ++++----- src/string.rs | 4 +- src/types.rs | 56 +++++++++---------- src/utils.rs | 22 ++++++-- src/visitor.rs | 24 ++------ tests/system.rs | 30 +++++----- 12 files changed, 352 insertions(+), 184 deletions(-) create mode 100644 src/chains.rs diff --git a/src/chains.rs b/src/chains.rs new file mode 100644 index 00000000000..094cd358696 --- /dev/null +++ b/src/chains.rs @@ -0,0 +1,133 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rewrite::{Rewrite, RewriteContext}; +use utils::{span_after, make_indent, extra_offset}; +use expr::rewrite_call; + +use syntax::{ast, ptr}; +use syntax::codemap::{mk_sp, Span}; +use syntax::print::pprust; + +pub fn rewrite_chain(orig_expr: &ast::Expr, + context: &RewriteContext, + width: usize, + offset: usize) + -> Option { + let mut expr = orig_expr; + let mut rewrites = Vec::new(); + let indent = context.block_indent + context.config.tab_spaces; + let max_width = context.config.max_width - context.config.tab_spaces; + + loop { + match expr.node { + ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => { + // FIXME: a lot of duplication between this and the + // rewrite_method_call in expr.rs. + let new_span = mk_sp(expressions[0].span.hi, expr.span.hi); + let lo = span_after(new_span, "(", context.codemap); + let new_span = mk_sp(lo, expr.span.hi); + + let rewrite = rewrite_method_call(method_name.node, + types, + &expressions[1..], + new_span, + context, + max_width, + indent); + rewrites.push(try_opt!(rewrite)); + expr = &expressions[0]; + } + ast::Expr_::ExprField(ref subexpr, ref field) => { + expr = subexpr; + rewrites.push(format!(".{}", field.node)); + } + ast::Expr_::ExprTupField(ref subexpr, ref field) => { + expr = subexpr; + rewrites.push(format!(".{}", field.node)); + } + _ => break, + } + } + + let parent_rewrite = try_opt!(expr.rewrite(context, width, offset)); + + // TODO: add exception for when rewrites.len() == 1 + if rewrites.len() == 1 { + let extra_offset = extra_offset(&parent_rewrite, offset); + let max_width = try_opt!(width.checked_sub(extra_offset)); + // FIXME: massive duplication + let rerewrite = match orig_expr.node { + ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => { + let new_span = mk_sp(expressions[0].span.hi, orig_expr.span.hi); + let lo = span_after(new_span, "(", context.codemap); + let new_span = mk_sp(lo, orig_expr.span.hi); + + rewrite_method_call(method_name.node, + types, + &expressions[1..], + new_span, + context, + max_width, + offset + extra_offset) + } + ast::Expr_::ExprField(_, ref field) => { + Some(format!(".{}", field.node)) + } + ast::Expr_::ExprTupField(_, ref field) => { + Some(format!(".{}", field.node)) + } + _ => unreachable!(), + }; + + return Some(format!("{}{}", parent_rewrite, try_opt!(rerewrite))); + } + + let total_width = rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len(); + + let connector = if total_width <= width && rewrites.iter().all(|s| !s.contains('\n')) { + String::new() + } else { + format!("\n{}", make_indent(indent)) + }; + + // FIXME: don't do this. There's a more efficient way. VecDeque? + rewrites.reverse(); + + // Put the first link on the same line as parent, if it fits. + let first_connector = if parent_rewrite.len() + rewrites[0].len() <= width && + !rewrites[0].contains('\n') { + "" + } else { + &connector[..] + }; + + Some(format!("{}{}{}", parent_rewrite, first_connector, rewrites.join(&connector))) +} + +fn rewrite_method_call(method_name: ast::Ident, + types: &[ptr::P], + args: &[ptr::P], + span: Span, + context: &RewriteContext, + width: usize, + offset: usize) + -> Option { + let type_str = if types.is_empty() { + String::new() + } else { + let type_list = types.iter().map(|ty| pprust::ty_to_string(ty)).collect::>(); + format!("::<{}>", type_list.join(", ")) + }; + + let callee_str = format!(".{}{}", method_name, type_str); + + rewrite_call(context, &callee_str, args, span, width, offset) +} diff --git a/src/comment.rs b/src/comment.rs index f1cecd7318b..5cec1678ac2 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -25,8 +25,7 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz ("// ", "", "// ") }; - let max_chars = width.checked_sub(closer.len()).unwrap_or(1) - .checked_sub(opener.len()).unwrap_or(1); + let max_chars = width.checked_sub(closer.len() + opener.len()).unwrap_or(1); let fmt = StringFormat { opener: "", @@ -41,17 +40,18 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz let indent_str = make_indent(offset); let line_breaks = s.chars().filter(|&c| c == '\n').count(); - let (_, mut s) = s.lines().enumerate() + let (_, mut s) = s.lines() + .enumerate() .map(|(i, mut line)| { - line = line.trim(); + line = line.trim(); // Drop old closer. - if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") { - line = &line[..(line.len() - 2)]; - } + if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") { + line = &line[..(line.len() - 2)]; + } - line.trim_right() - }) + line.trim_right() + }) .map(left_trim_comment_line) .map(|line| { if line_breaks == 0 { @@ -160,9 +160,34 @@ fn comment_end() { /// Returns true if text contains any comment. pub fn contains_comment(text: &str) -> bool { - CharClasses::new(text.chars()).any(|(kind, _)| kind == CodeCharKind::Comment ) + CharClasses::new(text.chars()).any(|(kind, _)| kind == CodeCharKind::Comment) } +pub fn uncommented(text: &str) -> String { + CharClasses::new(text.chars()) + .filter_map(|(s, c)| { + match s { + CodeCharKind::Normal => Some(c), + CodeCharKind::Comment => None, + } + }) + .collect() +} + +#[test] +fn test_uncommented() { + assert_eq!(&uncommented("abc/*...*/"), "abc"); + assert_eq!(&uncommented("// .... /* \n../* /* *** / */ */a/* // */c\n"), "..ac\n"); + assert_eq!(&uncommented("abc \" /* */\" qsdf"), "abc \" /* */\" qsdf"); +} + +#[test] +fn test_contains_comment() { + assert_eq!(contains_comment("abc"), false); + assert_eq!(contains_comment("abc // qsdf"), true); + assert_eq!(contains_comment("abc /* kqsdf"), true); + assert_eq!(contains_comment("abc \" /* */\" qsdf"), false); +} struct CharClasses where T: Iterator, diff --git a/src/expr.rs b/src/expr.rs index e641d7c6762..2c77cdc9e78 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -9,6 +9,7 @@ // except according to those terms. use std::cmp::Ordering; +use std::borrow::Borrow; use rewrite::{Rewrite, RewriteContext}; use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic}; @@ -21,6 +22,7 @@ use config::{BlockIndentStyle, MultilineStyle}; use comment::{FindUncommented, rewrite_comment, contains_comment}; use types::rewrite_path; use items::{span_lo_for_arg, span_hi_for_arg, rewrite_fn_input}; +use chains::rewrite_chain; use syntax::{ast, ptr}; use syntax::codemap::{CodeMap, Span, BytePos, mk_sp}; @@ -38,7 +40,16 @@ impl Rewrite for ast::Expr { } } ast::Expr_::ExprCall(ref callee, ref args) => { - rewrite_call(context, callee, args, self.span, width, offset) + // // FIXME using byte lens instead of char lens (and probably all over the place too) + // // 2 is for parens + // let max_callee_width = try_opt!(width.checked_sub(2)); + // let callee_str = try_opt!(callee.rewrite(context, max_callee_width, offset)); + + // let new_span = mk_sp(callee.span.hi, self.span.hi); + // let lo = span_after(new_span, "(", context.codemap); + // let new_span = mk_sp(lo, self.span.hi); + + rewrite_call(context, &**callee, args, self.span, width, offset) } ast::Expr_::ExprParen(ref subexpr) => { rewrite_paren(context, subexpr, width, offset) @@ -137,6 +148,11 @@ impl Rewrite for ast::Expr { ast::Expr_::ExprClosure(capture, ref fn_decl, ref body) => { rewrite_closure(capture, fn_decl, body, self.span, context, width, offset) } + ast::Expr_::ExprField(..) | + ast::Expr_::ExprTupField(..) | + ast::Expr_::ExprMethodCall(..) => { + rewrite_chain(self, context, width, offset) + } // We do not format these expressions yet, but they should still // satisfy our width restrictions. _ => wrap_str(context.snippet(self.span), context.config.max_width, width, offset), @@ -393,9 +409,9 @@ impl<'a> Rewrite for Loop<'a> { }; // FIXME: this drops any comment between "loop" and the block. - self.block.rewrite(context, width, offset).map(|result| { - format!("{}{}{} {}", label_string, self.keyword, pat_expr_string, result) - }) + self.block + .rewrite(context, width, offset) + .map(|result| format!("{}{}{} {}", label_string, self.keyword, pat_expr_string, result)) } } @@ -762,9 +778,7 @@ fn rewrite_guard(context: &RewriteContext, // 4 = ` if `, 5 = ` => {` let overhead = pattern_width + 4 + 5; if overhead < width { - let cond_str = guard.rewrite(context, - width - overhead, - offset + pattern_width + 4); + let cond_str = guard.rewrite(context, width - overhead, offset + pattern_width + 4); if let Some(cond_str) = cond_str { return Some(format!(" if {}", cond_str)); } @@ -866,36 +880,39 @@ fn rewrite_string_lit(context: &RewriteContext, Some(rewrite_string(str_lit, &fmt)) } -fn rewrite_call(context: &RewriteContext, - callee: &ast::Expr, - args: &[ptr::P], - span: Span, - width: usize, - offset: usize) - -> Option { - let callback = |callee_max_width| { - rewrite_call_inner(context, - callee, - callee_max_width, - args, - span, - width, - offset) - }; - +pub fn rewrite_call(context: &RewriteContext, + callee: &R, + args: &[ptr::P], + span: Span, + width: usize, + offset: usize) + -> Option + where R: Rewrite +{ // 2 is for parens let max_width = try_opt!(width.checked_sub(2)); - binary_search(1, max_width, callback) + binary_search(1, max_width, |callee_max_width| { + rewrite_call_inner(context, + callee, + callee_max_width, + args, + span, + width, + offset) + }) } -fn rewrite_call_inner(context: &RewriteContext, - callee: &ast::Expr, - max_callee_width: usize, - args: &[ptr::P], - span: Span, - width: usize, - offset: usize) - -> Result { +fn rewrite_call_inner(context: &RewriteContext, + callee: &R, + max_callee_width: usize, + args: &[ptr::P], + span: Span, + width: usize, + offset: usize) + -> Result + where R: Rewrite +{ + let callee = callee.borrow(); // FIXME using byte lens instead of char lens (and probably all over the // place too) let callee_str = match callee.rewrite(context, max_callee_width, offset) { @@ -929,7 +946,7 @@ fn rewrite_call_inner(context: &RewriteContext, item.rewrite(&inner_context, remaining_width, offset) .unwrap_or(context.snippet(item.span)) }, - callee.span.hi + BytePos(1), + span.lo, span.hi); let fmt = ListFormatting::for_fn(remaining_width, offset); @@ -1004,8 +1021,9 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, } }; - let field_iter = fields.into_iter().map(StructLitField::Regular) - .chain(base.into_iter().map(StructLitField::Base)); + let field_iter = fields.into_iter() + .map(StructLitField::Regular) + .chain(base.into_iter().map(StructLitField::Base)); let inner_context = &RewriteContext { block_indent: indent, ..*context }; @@ -1016,10 +1034,10 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, match *item { StructLitField::Regular(ref field) => field.span.lo, StructLitField::Base(ref expr) => { - let last_field_hi = - fields.last().map_or(span.lo, |field| field.span.hi); - let snippet = - context.snippet(mk_sp(last_field_hi, expr.span.lo)); + let last_field_hi = fields.last() + .map_or(span.lo, |field| field.span.hi); + let snippet = context.snippet(mk_sp(last_field_hi, + expr.span.lo)); let pos = snippet.find_uncommented("..").unwrap(); last_field_hi + BytePos(pos as u32) } @@ -1035,7 +1053,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, match *item { StructLitField::Regular(ref field) => { rewrite_field(inner_context, &field, h_budget, indent) - .unwrap_or(context.snippet(field.span)) + .unwrap_or(context.snippet(field.span)) } StructLitField::Base(ref expr) => { // 2 = .. @@ -1152,10 +1170,7 @@ fn rewrite_binary_op(context: &RewriteContext, // worth trying to put everything on one line. if rhs_result.len() + 2 + operator_str.len() < width && !rhs_result.contains('\n') { // 1 = space between lhs expr and operator - if let Some(mut result) = lhs.rewrite(context, - width - 1 - operator_str.len(), - offset) { - + if let Some(mut result) = lhs.rewrite(context, width - 1 - operator_str.len(), offset) { result.push(' '); result.push_str(&operator_str); result.push(' '); @@ -1167,9 +1182,7 @@ fn rewrite_binary_op(context: &RewriteContext, return Some(result); } - if let Some(rhs_result) = rhs.rewrite(context, - remaining_width, - offset + result.len()) { + if let Some(rhs_result) = rhs.rewrite(context, remaining_width, offset + result.len()) { if rhs_result.len() <= remaining_width { result.push_str(&rhs_result); return Some(result); diff --git a/src/items.rs b/src/items.rs index 7555ea01199..c7a8c4eb0d7 100644 --- a/src/items.rs +++ b/src/items.rs @@ -211,8 +211,10 @@ impl<'a> FmtVisitor<'a> { let ret_str = fd.output.rewrite(&context, self.config.max_width - indent, indent).unwrap(); // Args. - let (one_line_budget, multi_line_budget, mut arg_indent) = - self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace); + let (one_line_budget, multi_line_budget, mut arg_indent) = self.compute_budgets_for_args(&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); @@ -237,10 +239,7 @@ impl<'a> FmtVisitor<'a> { } // A conservative estimation, to goal is to be over all parens in generics - let args_start = generics.ty_params - .last() - .map(|tp| end_typaram(tp)) - .unwrap_or(span.lo); + let args_start = generics.ty_params.last().map(|tp| end_typaram(tp)).unwrap_or(span.lo); let args_span = codemap::mk_sp(span_after(codemap::mk_sp(args_start, span.hi), "(", self.codemap), @@ -333,12 +332,13 @@ impl<'a> FmtVisitor<'a> { // Account for sugary self. // FIXME: the comment for the self argument is dropped. This is blocked // on rust issue #27522. - let min_args = explicit_self.and_then(|explicit_self| { - rewrite_explicit_self(explicit_self, args) - }).map(|self_str| { - arg_item_strs[0] = self_str; - 2 - }).unwrap_or(1); + let min_args = explicit_self + .and_then(|explicit_self| rewrite_explicit_self(explicit_self, args)) + .map(|self_str| { + arg_item_strs[0] = self_str; + 2 + }) + .unwrap_or(1); // Comments between args let mut arg_items = Vec::new(); @@ -760,11 +760,10 @@ impl<'a> FmtVisitor<'a> { let typ = field.node.ty.rewrite(&self.get_context(), 1000, 0).unwrap(); let indent = self.block_indent + self.config.tab_spaces; - let mut attr_str = field.node.attrs - .rewrite(&self.get_context(), - self.config.max_width - indent, - indent) - .unwrap(); + let mut attr_str = field.node + .attrs + .rewrite(&self.get_context(), self.config.max_width - indent, indent) + .unwrap(); if !attr_str.is_empty() { attr_str.push('\n'); attr_str.push_str(&make_indent(indent)); @@ -803,22 +802,20 @@ impl<'a> FmtVisitor<'a> { // Strings for the generics. let context = self.get_context(); // FIXME: don't unwrap - let lt_strs = lifetimes.iter().map(|lt| { - lt.rewrite(&context, h_budget, offset).unwrap() - }); - let ty_strs = tys.iter().map(|ty_param| { - ty_param.rewrite(&context, h_budget, offset).unwrap() - }); + let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(&context, h_budget, offset).unwrap()); + let ty_strs = tys.iter() + .map(|ty_param| ty_param.rewrite(&context, h_budget, offset).unwrap()); // Extract comments between generics. - let lt_spans = lifetimes.iter().map(|l| { - let hi = if l.bounds.is_empty() { - l.lifetime.span.hi - } else { - l.bounds[l.bounds.len() - 1].span.hi - }; - codemap::mk_sp(l.lifetime.span.lo, hi) - }); + let lt_spans = lifetimes.iter() + .map(|l| { + let hi = if l.bounds.is_empty() { + 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 items = itemize_list(self.codemap, diff --git a/src/lib.rs b/src/lib.rs index 7975c77e06d..6993d3044d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,7 @@ mod string; mod comment; mod modules; pub mod rustfmt_diff; +mod chains; const MIN_STRING: usize = 10; // When we get scoped annotations, we should have rustfmt::skip. diff --git a/src/lists.rs b/src/lists.rs index cf68d86dd2b..a8cf0830f97 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -283,22 +283,22 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3> }; // Post-comment - let next_start = match self.inner.peek() { - Some(ref next_item) => (self.get_lo)(next_item), - None => self.next_span_start - }; - let post_snippet = self.codemap.span_to_snippet(codemap::mk_sp((self.get_hi)(&item), - next_start)) - .unwrap(); + let next_start = match self.inner.peek() { + Some(ref next_item) => (self.get_lo)(next_item), + None => self.next_span_start, + }; + let post_snippet = self.codemap + .span_to_snippet(codemap::mk_sp((self.get_hi)(&item), next_start)) + .unwrap(); - let comment_end = match self.inner.peek() { - Some(..) => { - let block_open_index = post_snippet.find("/*"); - let newline_index = post_snippet.find('\n'); - let separator_index = post_snippet.find_uncommented(",").unwrap(); + let comment_end = match self.inner.peek() { + Some(..) => { + let block_open_index = post_snippet.find("/*"); + let newline_index = post_snippet.find('\n'); + let separator_index = post_snippet.find_uncommented(",").unwrap(); - match (block_open_index, newline_index) { - // Separator before comment, with the next item on same line. + match (block_open_index, newline_index) { + // Separator before comment, with the next item on same line. // Comment belongs to next item. (Some(i), None) if i > separator_index => { separator_index + 1 diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 33d096c3ba0..535fc4c8ad9 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -17,21 +17,20 @@ impl<'a> FmtVisitor<'a> { // TODO these format_missing methods are ugly. Refactor and add unit tests // for the central whitespace stripping loop. pub fn format_missing(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, _| { - this.buffer.push_str(last_snippet) - }) + self.format_missing_inner(end, |this, last_snippet, _| this.buffer.push_str(last_snippet)) } pub fn format_missing_with_indent(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, snippet| { - this.buffer.push_str(last_snippet.trim_right()); - if last_snippet == snippet { + self.format_missing_inner(end, + |this, last_snippet, snippet| { + this.buffer.push_str(last_snippet.trim_right()); + if last_snippet == snippet { // No new lines in the snippet. - this.buffer.push_str("\n"); - } - let indent = make_indent(this.block_indent); - this.buffer.push_str(&indent); - }) + this.buffer.push_str("\n"); + } + let indent = make_indent(this.block_indent); + this.buffer.push_str(&indent); + }) } fn format_missing_inner(&mut self, @@ -60,9 +59,7 @@ impl<'a> FmtVisitor<'a> { let span = codemap::mk_sp(start, end); let snippet = self.snippet(span); - self.write_snippet(&snippet, - true, - &process_last_snippet); + self.write_snippet(&snippet, true, &process_last_snippet); } fn write_snippet(&mut self, diff --git a/src/string.rs b/src/string.rs index aba6a7f48da..5e5af61faf5 100644 --- a/src/string.rs +++ b/src/string.rs @@ -44,8 +44,8 @@ pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String { result.push_str(fmt.opener); let ender_length = fmt.line_end.len(); - let max_chars = fmt.width.checked_sub(fmt.opener.len()).unwrap_or(0) - .checked_sub(ender_length).unwrap_or(1); + let max_chars = fmt.width.checked_sub(fmt.opener.len() + ender_length).unwrap_or(1); + loop { let mut cur_end = cur_start + max_chars; diff --git a/src/types.rs b/src/types.rs index dace1d84bca..1e0da1aba4d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -196,13 +196,12 @@ fn rewrite_segment(segment: &ast::PathSegment, ast::PathParameters::AngleBracketedParameters(ref data) if !data.lifetimes.is_empty() || !data.types.is_empty() || !data.bindings.is_empty() => { - let param_list = data.lifetimes.iter() - .map(SegmentParam::LifeTime) - .chain(data.types.iter() - .map(|x| SegmentParam::Type(&*x))) - .chain(data.bindings.iter() - .map(|x| SegmentParam::Binding(&*x))) - .collect::>(); + let param_list = data.lifetimes + .iter() + .map(SegmentParam::LifeTime) + .chain(data.types.iter().map(|x| SegmentParam::Type(&*x))) + .chain(data.bindings.iter().map(|x| SegmentParam::Binding(&*x))) + .collect::>(); let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1); let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "<", context.codemap); @@ -279,30 +278,27 @@ impl Rewrite for ast::WherePredicate { ref bounds, ..}) => { if !bound_lifetimes.is_empty() { - let lifetime_str = bound_lifetimes.iter().map(|lt| { - lt.rewrite(context, width, offset).unwrap() - }).collect::>().join(", "); + let lifetime_str = bound_lifetimes.iter() + .map(|lt| lt.rewrite(context, width, offset).unwrap()) + .collect::>() + .join(", "); let type_str = pprust::ty_to_string(bounded_ty); // 8 = "for<> : ".len() let used_width = lifetime_str.len() + type_str.len() + 8; - let bounds_str = bounds.iter().map(|ty_bound| { - ty_bound.rewrite(context, - width - used_width, - offset + used_width) - .unwrap() - }).collect::>().join(" + "); + let bounds_str = bounds.iter() + .map(|ty_bound| ty_bound.rewrite(context, width - used_width, offset + used_width).unwrap()) + .collect::>() + .join(" + "); format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str) } else { let type_str = pprust::ty_to_string(bounded_ty); // 2 = ": ".len() let used_width = type_str.len() + 2; - let bounds_str = bounds.iter().map(|ty_bound| { - ty_bound.rewrite(context, - width - used_width, - offset + used_width) - .unwrap() - }).collect::>().join(" + "); + let bounds_str = bounds.iter() + .map(|ty_bound| ty_bound.rewrite(context, width - used_width, offset + used_width).unwrap()) + .collect::>() + .join(" + "); format!("{}: {}", type_str, bounds_str) } @@ -373,9 +369,11 @@ impl Rewrite for ast::TyParam { if !self.bounds.is_empty() { result.push_str(": "); - let bounds = self.bounds.iter().map(|ty_bound| { - ty_bound.rewrite(context, width, offset).unwrap() - }).collect::>().join(" + "); + let bounds = self.bounds + .iter() + .map(|ty_bound| ty_bound.rewrite(context, width, offset).unwrap()) + .collect::>() + .join(" + "); result.push_str(&bounds); } @@ -392,9 +390,11 @@ impl Rewrite for ast::TyParam { impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { if !self.bound_lifetimes.is_empty() { - let lifetime_str = self.bound_lifetimes.iter().map(|lt| { - lt.rewrite(context, width, offset).unwrap() - }).collect::>().join(", "); + let lifetime_str = self.bound_lifetimes + .iter() + .map(|lt| lt.rewrite(context, width, offset).unwrap()) + .collect::>() + .join(", "); // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; let max_path_width = try_opt!(width.checked_sub(extra_offset)); diff --git a/src/utils.rs b/src/utils.rs index 94f8fec2b0d..d8acea40d2a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,6 +14,7 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_}; use syntax::codemap::{CodeMap, Span, BytePos}; use comment::FindUncommented; +use rewrite::{Rewrite, RewriteContext}; use SKIP_ANNOTATION; @@ -93,10 +94,16 @@ pub fn contains_skip(attrs: &[Attribute]) -> bool { // Find the end of a TyParam #[inline] pub fn end_typaram(typaram: &ast::TyParam) -> BytePos { - typaram.bounds.last().map(|bound| match *bound { - ast::RegionTyParamBound(ref lt) => lt.span, - ast::TraitTyParamBound(ref prt, _) => prt.span, - }).unwrap_or(typaram.span).hi + typaram.bounds + .last() + .map(|bound| { + match *bound { + ast::RegionTyParamBound(ref lt) => lt.span, + ast::TraitTyParamBound(ref prt, _) => prt.span, + } + }) + .unwrap_or(typaram.span) + .hi } #[inline] @@ -202,6 +209,13 @@ pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: usi Some(s) } +impl Rewrite for String { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + // FIXME: unnecessary clone + wrap_str(self.clone(), context.config.max_width, width, offset) + } +} + // Binary search in integer range. Returns the first Ok value returned by the // callback. // The callback takes an integer and returns either an Ok, or an Err indicating diff --git a/src/visitor.rs b/src/visitor.rs index c1c1f2fcbbd..7e02d0f1d1f 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -202,19 +202,11 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { } 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.visit_struct(item.ident, item.vis, def, generics, item.span); } ast::Item_::ItemEnum(ref def, ref generics) => { self.format_missing_with_indent(item.span.lo); - self.visit_enum(item.ident, - item.vis, - def, - generics, - item.span); + self.visit_enum(item.ident, item.vis, def, generics, item.span); self.last_pos = item.span.hi; } ast::Item_::ItemMod(ref module) => { @@ -236,10 +228,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { self.format_missing_with_indent(ti.span.lo); let indent = self.block_indent; - let new_fn = self.rewrite_required_fn(indent, - ti.ident, - sig, - ti.span); + let new_fn = self.rewrite_required_fn(indent, ti.ident, sig, ti.span); if let Some(fn_str) = new_fn { @@ -299,10 +288,9 @@ impl<'a> FmtVisitor<'a> { if utils::contains_skip(attrs) { true } else { - let rewrite = attrs.rewrite(&self.get_context(), - self.config.max_width - self.block_indent, - self.block_indent) - .unwrap(); + let rewrite = attrs + .rewrite(&self.get_context(), self.config.max_width - self.block_indent, self.block_indent) + .unwrap(); self.buffer.push_str(&rewrite); let last = attrs.last().unwrap(); self.last_pos = last.span.hi; diff --git a/tests/system.rs b/tests/system.rs index b0d96468eca..10dc629c805 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -120,10 +120,8 @@ pub fn idempotent_check(filename: String) -> Result<(), HashMap HashMap { let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1."); // Matches lines containing significant comments or whitespace. - let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)") - .ok().expect("Failed creating pattern 2."); + let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)").ok() + .expect("Failed creating pattern 2."); reader.lines() - .map(|line| line.ok().expect("Failed getting line.")) - .take_while(|line| line_regex.is_match(&line)) - .filter_map(|line| { - regex.captures_iter(&line).next().map(|capture| { - (capture.at(1).expect("Couldn't unwrap capture.").to_owned(), - capture.at(2).expect("Couldn't unwrap capture.").to_owned()) - }) - }) - .collect() + .map(|line| line.ok().expect("Failed getting line.")) + .take_while(|line| line_regex.is_match(&line)) + .filter_map(|line| { + regex.captures_iter(&line) + .next() + .map(|capture| { + (capture.at(1).expect("Couldn't unwrap capture.").to_owned(), + capture.at(2).expect("Couldn't unwrap capture.").to_owned()) + }) + }) + .collect() } // Compare output to input.