1
Fork 0

Align dots in chained expressions

This commit is contained in:
Marcus Klaas 2015-09-09 23:14:54 +02:00
parent 95ef9dedb4
commit a9814149c9
8 changed files with 134 additions and 130 deletions

View file

@ -20,61 +20,58 @@
// argument function argument strategy. // argument function argument strategy.
use rewrite::{Rewrite, RewriteContext}; use rewrite::{Rewrite, RewriteContext};
use utils::{make_indent, extra_offset}; use utils::make_indent;
use expr::rewrite_call; use expr::rewrite_call;
use syntax::{ast, ptr}; use syntax::{ast, ptr};
use syntax::codemap::{mk_sp, Span}; use syntax::codemap::{mk_sp, Span};
use syntax::print::pprust; use syntax::print::pprust;
pub fn rewrite_chain(orig_expr: &ast::Expr, pub fn rewrite_chain(mut expr: &ast::Expr,
context: &RewriteContext, context: &RewriteContext,
width: usize, width: usize,
offset: usize) offset: usize)
-> Option<String> { -> Option<String> {
let mut expr = orig_expr; let total_span = expr.span;
let mut rewrites = Vec::new(); let mut subexpr_list = vec![expr];
let indent = offset + context.config.tab_spaces;
let max_width = try_opt!(context.config.max_width.checked_sub(indent));
while let Some(pair) = pop_expr_chain(expr, orig_expr.span, context, max_width, indent) { while let Some(subexpr) = pop_expr_chain(expr) {
let (rewrite, parent_expr) = pair; subexpr_list.push(subexpr);
expr = subexpr;
rewrites.push(try_opt!(rewrite));
expr = parent_expr;
} }
let parent = subexpr_list.pop().unwrap();
let parent_rewrite = try_opt!(expr.rewrite(context, width, offset)); let parent_rewrite = try_opt!(expr.rewrite(context, width, offset));
let (extra_indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(parent) ||
parent_rewrite.len() <= context.config.tab_spaces {
(parent_rewrite.len(), true)
} else {
(context.config.tab_spaces, false)
};
let indent = offset + extra_indent;
let max_width = try_opt!(width.checked_sub(extra_indent));
let rewrites = try_opt!(subexpr_list.into_iter()
.rev()
.map(|e| {
rewrite_chain_expr(e,
total_span,
context,
max_width,
indent)
})
.collect::<Option<Vec<_>>>());
let total_width = rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len(); let total_width = rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len();
let fits_single_line = total_width <= width && rewrites.iter().all(|s| !s.contains('\n')); let fits_single_line = total_width <= width && rewrites.iter().all(|s| !s.contains('\n'));
if rewrites.len() == 1 && !fits_single_line &&
(is_continuable(expr) || parent_rewrite.len() <= context.config.tab_spaces) {
let extra_offset = extra_offset(&parent_rewrite, offset);
let offset = offset + extra_offset;
let max_width = try_opt!(width.checked_sub(extra_offset));
let rerewrite = pop_expr_chain(orig_expr, orig_expr.span, context, max_width, offset)
.unwrap()
.0;
return Some(format!("{}{}", parent_rewrite, try_opt!(rerewrite)));
}
let connector = if fits_single_line { let connector = if fits_single_line {
String::new() String::new()
} else { } else {
format!("\n{}", make_indent(indent)) format!("\n{}", make_indent(indent))
}; };
// FIXME: don't do this. There's a more efficient way. VecDeque? let first_connector = if extend {
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 &&
is_continuable(expr) &&
!rewrites[0].contains('\n') ||
parent_rewrite.len() <= context.config.tab_spaces {
"" ""
} else { } else {
&connector[..] &connector[..]
@ -83,32 +80,36 @@ pub fn rewrite_chain(orig_expr: &ast::Expr,
Some(format!("{}{}{}", parent_rewrite, first_connector, rewrites.join(&connector))) Some(format!("{}{}{}", parent_rewrite, first_connector, rewrites.join(&connector)))
} }
// Returns None when the expression is not a chainable. Otherwise, rewrites the fn pop_expr_chain<'a>(expr: &'a ast::Expr) -> Option<&'a ast::Expr> {
// outermost chain element and returns the remaining chain. match expr.node {
fn pop_expr_chain<'a>(expr: &'a ast::Expr, ast::Expr_::ExprMethodCall(_, _, ref expressions) => {
Some(&expressions[0])
}
ast::Expr_::ExprTupField(ref subexpr, _) |
ast::Expr_::ExprField(ref subexpr, _) => {
Some(subexpr)
}
_ => None,
}
}
fn rewrite_chain_expr(expr: &ast::Expr,
span: Span, span: Span,
context: &RewriteContext, context: &RewriteContext,
width: usize, width: usize,
offset: usize) offset: usize)
-> Option<(Option<String>, &'a ast::Expr)> { -> Option<String> {
match expr.node { match expr.node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => { ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => {
Some((rewrite_method_call(method_name.node, rewrite_method_call(method_name.node, types, expressions, span, context, width, offset)
types,
expressions,
span,
context,
width,
offset),
&expressions[0]))
} }
ast::Expr_::ExprField(ref subexpr, ref field) => { ast::Expr_::ExprField(_, ref field) => {
Some((Some(format!(".{}", field.node)), subexpr)) Some(format!(".{}", field.node))
} }
ast::Expr_::ExprTupField(ref subexpr, ref field) => { ast::Expr_::ExprTupField(_, ref field) => {
Some((Some(format!(".{}", field.node)), subexpr)) Some(format!(".{}", field.node))
} }
_ => None, _ => unreachable!(),
} }
} }

View file

@ -1017,8 +1017,8 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
}; };
let field_iter = fields.into_iter() let field_iter = fields.into_iter()
.map(StructLitField::Regular) .map(StructLitField::Regular)
.chain(base.into_iter().map(StructLitField::Base)); .chain(base.into_iter().map(StructLitField::Base));
let inner_context = &RewriteContext { block_indent: indent, ..*context }; let inner_context = &RewriteContext { block_indent: indent, ..*context };
@ -1030,8 +1030,8 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
StructLitField::Regular(ref field) => field.span.lo, StructLitField::Regular(ref field) => field.span.lo,
StructLitField::Base(ref expr) => { StructLitField::Base(ref expr) => {
let last_field_hi = fields.last() let last_field_hi = fields.last()
.map_or(span.lo, .map_or(span.lo,
|field| field.span.hi); |field| field.span.hi);
let snippet = context.snippet(mk_sp(last_field_hi, let snippet = context.snippet(mk_sp(last_field_hi,
expr.span.lo)); expr.span.lo));
let pos = snippet.find_uncommented("..").unwrap(); let pos = snippet.find_uncommented("..").unwrap();
@ -1104,8 +1104,8 @@ fn rewrite_field(context: &RewriteContext,
-> Option<String> { -> Option<String> {
let name = &field.ident.node.to_string(); let name = &field.ident.node.to_string();
let overhead = name.len() + 2; let overhead = name.len() + 2;
let expr = let expr = field.expr
field.expr.rewrite(context, try_opt!(width.checked_sub(overhead)), offset + overhead); .rewrite(context, try_opt!(width.checked_sub(overhead)), offset + overhead);
expr.map(|s| format!("{}: {}", name, s)) expr.map(|s| format!("{}: {}", name, s))
} }

View file

@ -332,13 +332,14 @@ impl<'a> FmtVisitor<'a> {
// Account for sugary self. // Account for sugary self.
// FIXME: the comment for the self argument is dropped. This is blocked // FIXME: the comment for the self argument is dropped. This is blocked
// on rust issue #27522. // on rust issue #27522.
let min_args = explicit_self let min_args = explicit_self.and_then(|explicit_self| {
.and_then(|explicit_self| rewrite_explicit_self(explicit_self, args)) rewrite_explicit_self(explicit_self, args)
.map(|self_str| { })
arg_item_strs[0] = self_str; .map(|self_str| {
2 arg_item_strs[0] = self_str;
}) 2
.unwrap_or(1); })
.unwrap_or(1);
// Comments between args // Comments between args
let mut arg_items = Vec::new(); let mut arg_items = Vec::new();
@ -761,9 +762,11 @@ impl<'a> FmtVisitor<'a> {
let indent = self.block_indent + self.config.tab_spaces; let indent = self.block_indent + self.config.tab_spaces;
let mut attr_str = field.node let mut attr_str = field.node
.attrs .attrs
.rewrite(&self.get_context(), self.config.max_width - indent, indent) .rewrite(&self.get_context(),
.unwrap(); self.config.max_width - indent,
indent)
.unwrap();
if !attr_str.is_empty() { if !attr_str.is_empty() {
attr_str.push('\n'); attr_str.push('\n');
attr_str.push_str(&make_indent(indent)); attr_str.push_str(&make_indent(indent));
@ -804,18 +807,18 @@ impl<'a> FmtVisitor<'a> {
// FIXME: don't unwrap // FIXME: don't unwrap
let lt_strs = lifetimes.iter().map(|lt| lt.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() let ty_strs = tys.iter()
.map(|ty_param| ty_param.rewrite(&context, h_budget, offset).unwrap()); .map(|ty_param| ty_param.rewrite(&context, h_budget, offset).unwrap());
// Extract comments between generics. // Extract comments between generics.
let lt_spans = lifetimes.iter() let lt_spans = lifetimes.iter()
.map(|l| { .map(|l| {
let hi = if l.bounds.is_empty() { let hi = if l.bounds.is_empty() {
l.lifetime.span.hi l.lifetime.span.hi
} else { } else {
l.bounds[l.bounds.len() - 1].span.hi l.bounds[l.bounds.len() - 1].span.hi
}; };
codemap::mk_sp(l.lifetime.span.lo, hi) codemap::mk_sp(l.lifetime.span.lo, hi)
}); });
let ty_spans = tys.iter().map(span_for_ty_param); let ty_spans = tys.iter().map(span_for_ty_param);
let items = itemize_list(self.codemap, let items = itemize_list(self.codemap,

View file

@ -279,22 +279,24 @@ impl Rewrite for ast::WherePredicate {
.. }) => { .. }) => {
if !bound_lifetimes.is_empty() { if !bound_lifetimes.is_empty() {
let lifetime_str = bound_lifetimes.iter() let lifetime_str = bound_lifetimes.iter()
.map(|lt| lt.rewrite(context, width, offset).unwrap()) .map(|lt| {
.collect::<Vec<_>>() lt.rewrite(context, width, offset)
.join(", "); .unwrap()
})
.collect::<Vec<_>>()
.join(", ");
let type_str = pprust::ty_to_string(bounded_ty); let type_str = pprust::ty_to_string(bounded_ty);
// 8 = "for<> : ".len() // 8 = "for<> : ".len()
let used_width = lifetime_str.len() + type_str.len() + 8; let used_width = lifetime_str.len() + type_str.len() + 8;
let bounds_str = bounds.iter() let bounds_str = bounds.iter()
.map(|ty_bound| { .map(|ty_bound| {
ty_bound ty_bound.rewrite(context,
.rewrite(context, width - used_width,
width - used_width, offset + used_width)
offset + used_width) .unwrap()
.unwrap() })
}) .collect::<Vec<_>>()
.collect::<Vec<_>>() .join(" + ");
.join(" + ");
format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str) format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
} else { } else {
@ -302,15 +304,14 @@ impl Rewrite for ast::WherePredicate {
// 2 = ": ".len() // 2 = ": ".len()
let used_width = type_str.len() + 2; let used_width = type_str.len() + 2;
let bounds_str = bounds.iter() let bounds_str = bounds.iter()
.map(|ty_bound| { .map(|ty_bound| {
ty_bound ty_bound.rewrite(context,
.rewrite(context, width - used_width,
width - used_width, offset + used_width)
offset + used_width) .unwrap()
.unwrap() })
}) .collect::<Vec<_>>()
.collect::<Vec<_>>() .join(" + ");
.join(" + ");
format!("{}: {}", type_str, bounds_str) format!("{}: {}", type_str, bounds_str)
} }

View file

@ -95,15 +95,15 @@ pub fn contains_skip(attrs: &[Attribute]) -> bool {
#[inline] #[inline]
pub fn end_typaram(typaram: &ast::TyParam) -> BytePos { pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
typaram.bounds typaram.bounds
.last() .last()
.map(|bound| { .map(|bound| {
match *bound { match *bound {
ast::RegionTyParamBound(ref lt) => lt.span, ast::RegionTyParamBound(ref lt) => lt.span,
ast::TraitTyParamBound(ref prt, _) => prt.span, ast::TraitTyParamBound(ref prt, _) => prt.span,
} }
}) })
.unwrap_or(typaram.span) .unwrap_or(typaram.span)
.hi .hi
} }
#[inline] #[inline]

View file

@ -288,11 +288,10 @@ impl<'a> FmtVisitor<'a> {
if utils::contains_skip(attrs) { if utils::contains_skip(attrs) {
true true
} else { } else {
let rewrite = attrs let rewrite = attrs.rewrite(&self.get_context(),
.rewrite(&self.get_context(), self.config.max_width - self.block_indent,
self.config.max_width - self.block_indent, self.block_indent)
self.block_indent) .unwrap();
.unwrap();
self.buffer.push_str(&rewrite); self.buffer.push_str(&rewrite);
let last = attrs.last().unwrap(); let last = attrs.last().unwrap();
self.last_pos = last.span.hi; self.last_pos = last.span.hi;

View file

@ -162,17 +162,17 @@ fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
.expect("Failed creating pattern 2."); .expect("Failed creating pattern 2.");
reader.lines() reader.lines()
.map(|line| line.ok().expect("Failed getting line.")) .map(|line| line.ok().expect("Failed getting line."))
.take_while(|line| line_regex.is_match(&line)) .take_while(|line| line_regex.is_match(&line))
.filter_map(|line| { .filter_map(|line| {
regex.captures_iter(&line) regex.captures_iter(&line)
.next() .next()
.map(|capture| { .map(|capture| {
(capture.at(1).expect("Couldn\'t unwrap capture.").to_owned(), (capture.at(1).expect("Couldn\'t unwrap capture.").to_owned(),
capture.at(2).expect("Couldn\'t unwrap capture.").to_owned()) capture.at(2).expect("Couldn\'t unwrap capture.").to_owned())
}) })
}) })
.collect() .collect()
} }
// Compare output to input. // Compare output to input.

View file

@ -6,8 +6,8 @@ fn main() {
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd(); bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd();
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc
.ddddddddddddddddddddddddddd .ddddddddddddddddddddddddddd
.eeeeeeee(); .eeeeeeee();
x().y(|| { x().y(|| {
match cond() { match cond() {
@ -34,13 +34,13 @@ fn main() {
}); });
let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx.map(|x| x + 5) let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx.map(|x| x + 5)
.map(|x| x / 2) .map(|x| x / 2)
.fold(0, |acc, x| acc + x); .fold(0,
|acc, x| acc + x);
aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa.map(|x| {
.map(|x| { x += 1;
x += 1; x
x })
}) .filter(some_mod::some_filter)
.filter(some_mod::some_filter)
} }