Skip to content

Commit 03c6606

Browse files
committed
Refine chain breaking heuristics
Don't make a single line chain when it is was multi line in the source; allow overflow of the last chain element onto the next lines without breaking the chain.
1 parent 48d17f5 commit 03c6606

18 files changed

+356
-207
lines changed

src/chains.rs

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// argument function argument strategy.
2121

2222
use rewrite::{Rewrite, RewriteContext};
23-
use utils::make_indent;
23+
use utils::{first_line_width, make_indent};
2424
use expr::rewrite_call;
2525

2626
use syntax::{ast, ptr};
@@ -51,19 +51,67 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
5151
let indent = offset + extra_indent;
5252

5353
let max_width = try_opt!(width.checked_sub(extra_indent));
54-
let rewrites = try_opt!(subexpr_list.into_iter()
55-
.rev()
56-
.map(|e| {
57-
rewrite_chain_expr(e,
58-
total_span,
59-
context,
60-
max_width,
61-
indent)
62-
})
63-
.collect::<Option<Vec<_>>>());
64-
65-
let total_width = rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len();
66-
let fits_single_line = total_width <= width && rewrites.iter().all(|s| !s.contains('\n'));
54+
let mut rewrites = try_opt!(subexpr_list.iter()
55+
.rev()
56+
.map(|e| {
57+
rewrite_chain_expr(e,
58+
total_span,
59+
context,
60+
max_width,
61+
indent)
62+
})
63+
.collect::<Option<Vec<_>>>());
64+
65+
// Total of all items excluding the last.
66+
let almost_total = rewrites.split_last()
67+
.unwrap()
68+
.1
69+
.iter()
70+
.fold(0, |a, b| a + first_line_width(b)) +
71+
parent_rewrite.len();
72+
let total_width = almost_total + first_line_width(rewrites.last().unwrap());
73+
let veto_single_line = if context.config.take_source_hints && subexpr_list.len() > 1 {
74+
// Look at the source code. Unless all chain elements start on the same
75+
// line, we won't consider putting them on a single line either.
76+
let first_line_no = context.codemap.lookup_char_pos(subexpr_list[0].span.lo).line;
77+
78+
subexpr_list[1..]
79+
.iter()
80+
.any(|ex| context.codemap.lookup_char_pos(ex.span.hi).line != first_line_no)
81+
} else {
82+
false
83+
};
84+
85+
let fits_single_line = !veto_single_line &&
86+
match subexpr_list[0].node {
87+
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions)
88+
if context.config.chains_overflow_last => {
89+
let (last, init) = rewrites.split_last_mut().unwrap();
90+
91+
if init.iter().all(|s| !s.contains('\n')) && total_width <= width {
92+
let last_rewrite = width.checked_sub(almost_total)
93+
.and_then(|inner_width| {
94+
rewrite_method_call(method_name.node,
95+
types,
96+
expressions,
97+
total_span,
98+
context,
99+
inner_width,
100+
offset + almost_total)
101+
});
102+
match last_rewrite {
103+
Some(mut string) => {
104+
::std::mem::swap(&mut string, last);
105+
true
106+
}
107+
None => false,
108+
}
109+
} else {
110+
false
111+
}
112+
}
113+
_ => total_width <= width && rewrites.iter().all(|s| !s.contains('\n')),
114+
};
67115

68116
let connector = if fits_single_line {
69117
String::new()
@@ -101,7 +149,11 @@ fn rewrite_chain_expr(expr: &ast::Expr,
101149
-> Option<String> {
102150
match expr.node {
103151
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => {
104-
rewrite_method_call(method_name.node, types, expressions, span, context, width, offset)
152+
let inner = &RewriteContext {
153+
block_indent: offset,
154+
..*context
155+
};
156+
rewrite_method_call(method_name.node, types, expressions, span, inner, width, offset)
105157
}
106158
ast::Expr_::ExprField(_, ref field) => {
107159
Some(format!(".{}", field.node))
@@ -137,10 +189,7 @@ fn rewrite_method_call(method_name: ast::Ident,
137189
};
138190

139191
let callee_str = format!(".{}{}", method_name, type_str);
140-
let inner_context = &RewriteContext {
141-
block_indent: offset,
142-
..*context
143-
};
192+
let span = mk_sp(args[0].span.hi, span.hi);
144193

145-
rewrite_call(inner_context, &callee_str, args, span, width, offset)
194+
rewrite_call(context, &callee_str, &args[1..], span, width, offset)
146195
}

src/config.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ create_config! {
133133
fn_args_density: Density,
134134
fn_args_layout: StructLitStyle,
135135
fn_arg_indent: BlockIndentStyle,
136-
where_density: Density, // Should we at least try to put the where clause on the same line as
137-
// the rest of the function decl?
136+
where_density: Density, // Should we at least try to put the where clause on
137+
// the same line as the rest of the function decl?
138138
where_indent: BlockIndentStyle, // Visual will be treated like Tabbed
139139
where_layout: ListTactic,
140140
where_pred_indent: BlockIndentStyle,
@@ -147,14 +147,14 @@ create_config! {
147147
report_todo: ReportTactic,
148148
report_fixme: ReportTactic,
149149
reorder_imports: bool, // Alphabetically, case sensitive.
150-
expr_indent_style: BlockIndentStyle,
151-
closure_indent_style: BlockIndentStyle,
152150
single_line_if_else: bool,
153151
format_strings: bool,
152+
chains_overflow_last: bool,
153+
take_source_hints: bool, // Retain some formatting characteristics from
154+
// the source code.
154155
}
155156

156157
impl Default for Config {
157-
158158
fn default() -> Config {
159159
Config {
160160
max_width: 100,
@@ -181,11 +181,10 @@ impl Default for Config {
181181
report_todo: ReportTactic::Always,
182182
report_fixme: ReportTactic::Never,
183183
reorder_imports: false,
184-
expr_indent_style: BlockIndentStyle::Tabbed,
185-
closure_indent_style: BlockIndentStyle::Visual,
186184
single_line_if_else: false,
187185
format_strings: true,
186+
chains_overflow_last: true,
187+
take_source_hints: true,
188188
}
189189
}
190-
191190
}

src/expr.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ impl Rewrite for ast::Expr {
4040
}
4141
}
4242
ast::Expr_::ExprCall(ref callee, ref args) => {
43+
// FIXME using byte lens instead of char lens (and probably all over the place too)
44+
// 2 is for parens
45+
let max_callee_width = try_opt!(width.checked_sub(2));
46+
let callee_str = try_opt!(callee.rewrite(context, max_callee_width, offset));
47+
let span = mk_sp(callee.span.hi, self.span.hi);
48+
4349
rewrite_call(context, &**callee, args, self.span, width, offset)
4450
}
4551
ast::Expr_::ExprParen(ref subexpr) => {
@@ -284,8 +290,10 @@ impl Rewrite for ast::Block {
284290
};
285291

286292
if is_simple_block(self, context.codemap) && prefix.len() < width {
287-
let body =
288-
self.expr.as_ref().unwrap().rewrite(context, width - prefix.len(), offset);
293+
let body = self.expr
294+
.as_ref()
295+
.unwrap()
296+
.rewrite(context, width - prefix.len(), offset);
289297
if let Some(ref expr_str) = body {
290298
let result = format!("{}{{ {} }}", prefix, expr_str);
291299
if result.len() <= width && !result.contains('\n') {
@@ -677,15 +685,13 @@ impl Rewrite for ast::Arm {
677685
total_width += (pat_strs.len() - 1) * 3;
678686

679687
let mut vertical = total_width > pat_budget || pat_strs.iter().any(|p| p.contains('\n'));
680-
if !vertical {
688+
if !vertical && context.config.take_source_hints {
681689
// If the patterns were previously stacked, keep them stacked.
682-
// FIXME should be an option.
683690
let pat_span = mk_sp(pats[0].span.lo, pats[pats.len() - 1].span.hi);
684691
let pat_str = context.snippet(pat_span);
685692
vertical = pat_str.find('\n').is_some();
686693
}
687694

688-
689695
let pats_width = if vertical {
690696
pat_strs[pat_strs.len() - 1].len()
691697
} else {
@@ -1015,9 +1021,10 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
10151021
match *item {
10161022
StructLitField::Regular(ref field) => field.span.lo,
10171023
StructLitField::Base(ref expr) => {
1018-
let last_field_hi = fields.last()
1019-
.map_or(span.lo,
1020-
|field| field.span.hi);
1024+
let last_field_hi = fields.last().map_or(span.lo,
1025+
|field| {
1026+
field.span.hi
1027+
});
10211028
let snippet = context.snippet(mk_sp(last_field_hi,
10221029
expr.span.lo));
10231030
let pos = snippet.find_uncommented("..").unwrap();

src/items.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,9 @@ impl<'a> FmtVisitor<'a> {
512512
|arg| arg.ty.span.hi,
513513
|arg| {
514514
// FIXME silly width, indent
515-
arg.ty.rewrite(&self.get_context(), 1000, 0)
516-
.unwrap()
515+
arg.ty
516+
.rewrite(&self.get_context(), 1000, 0)
517+
.unwrap()
517518
},
518519
span_after(field.span, "(", self.codemap),
519520
next_span_start);
@@ -810,15 +811,14 @@ impl<'a> FmtVisitor<'a> {
810811
.map(|ty_param| ty_param.rewrite(&context, h_budget, offset).unwrap());
811812

812813
// Extract comments between generics.
813-
let lt_spans = lifetimes.iter()
814-
.map(|l| {
815-
let hi = if l.bounds.is_empty() {
816-
l.lifetime.span.hi
817-
} else {
818-
l.bounds[l.bounds.len() - 1].span.hi
819-
};
820-
codemap::mk_sp(l.lifetime.span.lo, hi)
821-
});
814+
let lt_spans = lifetimes.iter().map(|l| {
815+
let hi = if l.bounds.is_empty() {
816+
l.lifetime.span.hi
817+
} else {
818+
l.bounds[l.bounds.len() - 1].span.hi
819+
};
820+
codemap::mk_sp(l.lifetime.span.lo, hi)
821+
});
822822
let ty_spans = tys.iter().map(span_for_ty_param);
823823

824824
let items = itemize_list(self.codemap,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![feature(rustc_private)]
1212
#![feature(custom_attribute)]
13+
#![feature(slice_splits)]
1314
#![allow(unused_attributes)]
1415

1516
// TODO we're going to allocate a whole bunch of temp Strings, is it worth

0 commit comments

Comments
 (0)