Skip to content

Commit 62689ef

Browse files
authored
Merge pull request #1861 from topecongiro/refactor-chain
Remove unnecessary rewriting
2 parents 7da7764 + beeaf8d commit 62689ef

File tree

13 files changed

+185
-300
lines changed

13 files changed

+185
-300
lines changed

src/chains.rs

Lines changed: 84 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -98,40 +98,31 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
9898
if chain_only_try(&subexpr_list) {
9999
return rewrite_try(&parent, subexpr_list.len(), context, shape);
100100
}
101-
let trailing_try_num = subexpr_list
102-
.iter()
103-
.take_while(|e| match e.node {
104-
ast::ExprKind::Try(..) => true,
105-
_ => false,
106-
})
107-
.count();
101+
let suffix_try_num = subexpr_list.iter().take_while(|e| is_try(e)).count();
102+
let prefix_try_num = subexpr_list.iter().rev().take_while(|e| is_try(e)).count();
108103

109104
// Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
110105
let parent_shape = if is_block_expr(context, &parent, "\n") {
111106
match context.config.chain_indent() {
112107
IndentStyle::Visual => shape.visual_indent(0),
113-
IndentStyle::Block => shape.block(),
108+
IndentStyle::Block => shape,
114109
}
115110
} else {
116111
shape
117112
};
118-
let parent_rewrite = try_opt!(parent.rewrite(context, parent_shape));
113+
let parent_rewrite = try_opt!(
114+
parent
115+
.rewrite(context, parent_shape)
116+
.map(|parent_rw| parent_rw + &repeat_try(prefix_try_num))
117+
);
119118
let parent_rewrite_contains_newline = parent_rewrite.contains('\n');
120119
let is_small_parent = parent_rewrite.len() <= context.config.tab_spaces();
121120

122121
// Decide how to layout the rest of the chain. `extend` is true if we can
123122
// put the first non-parent item on the same line as the parent.
124-
let first_subexpr_is_try = subexpr_list.last().map_or(false, is_try);
125123
let (nested_shape, extend) = if !parent_rewrite_contains_newline && is_continuable(&parent) {
126-
let nested_shape = if first_subexpr_is_try {
127-
parent_shape
128-
.block_indent(context.config.tab_spaces())
129-
.with_max_width(context.config)
130-
} else {
131-
chain_indent(context, shape.add_offset(parent_rewrite.len()))
132-
};
133124
(
134-
nested_shape,
125+
chain_indent(context, shape.add_offset(parent_rewrite.len())),
135126
context.config.chain_indent() == IndentStyle::Visual || is_small_parent,
136127
)
137128
} else if is_block_expr(context, &parent, &parent_rewrite) {
@@ -142,13 +133,9 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
142133
// brace.
143134
IndentStyle::Visual => (parent_shape, false),
144135
}
145-
} else if parent_rewrite_contains_newline {
146-
(chain_indent(context, parent_shape), false)
147136
} else {
148137
(
149-
shape
150-
.block_indent(context.config.tab_spaces())
151-
.with_max_width(context.config),
138+
chain_indent(context, shape.add_offset(parent_rewrite.len())),
152139
false,
153140
)
154141
};
@@ -171,95 +158,60 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
171158
other_child_shape
172159
);
173160

174-
let child_shape_iter = Some(first_child_shape).into_iter().chain(
175-
::std::iter::repeat(other_child_shape).take(subexpr_list.len() - 1),
176-
);
177-
let iter = subexpr_list.iter().rev().zip(child_shape_iter);
161+
let child_shape_iter = Some(first_child_shape)
162+
.into_iter()
163+
.chain(iter::repeat(other_child_shape));
164+
let subexpr_num = subexpr_list.len();
165+
let last_subexpr = &subexpr_list[suffix_try_num];
166+
let subexpr_list = &subexpr_list[suffix_try_num..subexpr_num - prefix_try_num];
167+
let iter = subexpr_list.iter().skip(1).rev().zip(child_shape_iter);
178168
let mut rewrites = try_opt!(
179169
iter.map(|(e, shape)| {
180170
rewrite_chain_subexpr(e, total_span, context, shape)
181171
}).collect::<Option<Vec<_>>>()
182172
);
183173

184174
// Total of all items excluding the last.
185-
let last_non_try_index = rewrites.len() - (1 + trailing_try_num);
186-
let almost_total = rewrites[..last_non_try_index]
187-
.iter()
188-
.fold(0, |a, b| a + first_line_width(b)) + parent_rewrite.len();
189-
let one_line_len =
190-
rewrites.iter().fold(0, |a, r| a + first_line_width(r)) + parent_rewrite.len();
191-
192-
let one_line_budget = min(shape.width, context.config.chain_one_line_max());
193-
let veto_single_line = if one_line_len > one_line_budget {
194-
if rewrites.len() > 1 {
195-
true
196-
} else if rewrites.len() == 1 {
197-
context.config.chain_split_single_child() || one_line_len > shape.width
198-
} else {
199-
false
200-
}
201-
} else if context.config.take_source_hints() && subexpr_list.len() > 1 {
202-
// Look at the source code. Unless all chain elements start on the same
203-
// line, we won't consider putting them on a single line either.
204-
let last_span = context.snippet(mk_sp(subexpr_list[1].span.hi, total_span.hi));
205-
let first_span = context.snippet(subexpr_list[1].span);
206-
let last_iter = last_span.chars().take_while(|c| c.is_whitespace());
207-
208-
first_span.chars().chain(last_iter).any(|c| c == '\n')
175+
let extend_last_subexr = last_line_extendable(&parent_rewrite) && rewrites.is_empty();
176+
let almost_total = if extend_last_subexr {
177+
last_line_width(&parent_rewrite)
209178
} else {
210-
false
179+
rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len()
211180
};
212-
213-
let mut fits_single_line = !veto_single_line && almost_total <= shape.width;
214-
if fits_single_line {
215-
let len = rewrites.len();
216-
let (init, last) = rewrites.split_at_mut(len - (1 + trailing_try_num));
217-
fits_single_line = init.iter().all(|s| !s.contains('\n'));
218-
219-
if fits_single_line {
220-
fits_single_line = match expr.node {
221-
ref e @ ast::ExprKind::MethodCall(..) => {
222-
if rewrite_method_call_with_overflow(
223-
e,
224-
&mut last[0],
225-
almost_total,
226-
total_span,
227-
context,
228-
shape,
229-
) {
230-
// If the first line of the last method does not fit into a single line
231-
// after the others, allow new lines.
232-
almost_total + first_line_width(&last[0]) < context.config.max_width()
233-
} else {
234-
false
181+
let one_line_budget = if rewrites.is_empty() && !context.config.chain_split_single_child() {
182+
shape.width
183+
} else {
184+
min(shape.width, context.config.chain_one_line_max())
185+
};
186+
let all_in_one_line = !parent_rewrite_contains_newline &&
187+
rewrites.iter().all(|s| !s.contains('\n')) &&
188+
almost_total < one_line_budget;
189+
let rewrite_last = || rewrite_chain_subexpr(last_subexpr, total_span, context, nested_shape);
190+
let (last_subexpr_str, fits_single_line) = try_opt!(if all_in_one_line || extend_last_subexr {
191+
parent_shape.offset_left(almost_total).map(|shape| {
192+
if let Some(rw) = rewrite_chain_subexpr(last_subexpr, total_span, context, shape) {
193+
let line_count = rw.lines().count();
194+
let fits_single_line = almost_total + first_line_width(&rw) <= one_line_budget;
195+
if (line_count >= 5 && fits_single_line) || extend_last_subexr {
196+
(Some(rw), true)
197+
} else {
198+
match rewrite_last() {
199+
Some(ref new_rw) if !fits_single_line => (Some(new_rw.clone()), false),
200+
Some(ref new_rw) if new_rw.lines().count() >= line_count => {
201+
(Some(rw), fits_single_line)
202+
}
203+
new_rw @ Some(..) => (new_rw, false),
204+
_ => (Some(rw), fits_single_line),
235205
}
236206
}
237-
_ => !last[0].contains('\n'),
207+
} else {
208+
(rewrite_last(), false)
238209
}
239-
}
240-
}
241-
242-
// Try overflowing the last element if we are using block indent and it goes multi line
243-
// or it fits in a single line but goes over the max width.
244-
if !fits_single_line && context.use_block_indent() {
245-
let (init, last) = rewrites.split_at_mut(last_non_try_index);
246-
let almost_single_line = init.iter().all(|s| !s.contains('\n'));
247-
if almost_single_line && last[0].contains('\n') {
248-
let overflow_shape = Shape {
249-
width: one_line_budget,
250-
..parent_shape
251-
};
252-
fits_single_line = rewrite_last_child_with_overflow(
253-
context,
254-
&subexpr_list[trailing_try_num],
255-
overflow_shape,
256-
total_span,
257-
almost_total,
258-
one_line_budget,
259-
&mut last[0],
260-
);
261-
}
262-
}
210+
})
211+
} else {
212+
Some((rewrite_last(), false))
213+
});
214+
rewrites.push(try_opt!(last_subexpr_str));
263215

264216
let connector = if fits_single_line && !parent_rewrite_contains_newline {
265217
// Yay, we can put everything on one line.
@@ -272,52 +224,42 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
272224
format!("\n{}", nested_shape.indent.to_string(context.config))
273225
};
274226

275-
let first_connector = choose_first_connector(
276-
context,
277-
&parent_rewrite,
278-
&rewrites[0],
279-
&connector,
280-
&subexpr_list,
281-
extend,
282-
);
227+
let first_connector = if is_small_parent || fits_single_line ||
228+
last_line_extendable(&parent_rewrite) ||
229+
context.config.chain_indent() == IndentStyle::Visual
230+
{
231+
""
232+
} else {
233+
connector.as_str()
234+
};
283235

284-
if is_small_parent && rewrites.len() > 1 {
236+
let result = if is_small_parent && rewrites.len() > 1 {
285237
let second_connector = choose_first_connector(
286238
context,
287239
&rewrites[0],
288240
&rewrites[1],
289241
&connector,
290-
&subexpr_list[0..subexpr_list.len() - 1],
242+
&subexpr_list[..subexpr_num - 1],
291243
false,
292244
);
293-
wrap_str(
294-
format!(
295-
"{}{}{}{}{}",
296-
parent_rewrite,
297-
first_connector,
298-
rewrites[0],
299-
second_connector,
300-
join_rewrites(
301-
&rewrites[1..],
302-
&subexpr_list[0..subexpr_list.len() - 1],
303-
&connector,
304-
)
305-
),
306-
context.config.max_width(),
307-
shape,
245+
format!(
246+
"{}{}{}{}{}",
247+
parent_rewrite,
248+
first_connector,
249+
rewrites[0],
250+
second_connector,
251+
join_rewrites(&rewrites[1..], &subexpr_list[..subexpr_num - 1], &connector)
308252
)
309253
} else {
310-
wrap_str(
311-
format!(
312-
"{}{}{}",
313-
parent_rewrite,
314-
first_connector,
315-
join_rewrites(&rewrites, &subexpr_list, &connector)
316-
),
317-
context.config.max_width(),
318-
shape,
254+
format!(
255+
"{}{}{}",
256+
parent_rewrite,
257+
first_connector,
258+
join_rewrites(&rewrites, &subexpr_list, &connector)
319259
)
320-
}
260+
};
261+
let result = format!("{}{}", result, repeat_try(suffix_try_num));
262+
wrap_str(result, context.config.max_width(), shape)
321263
}
322264

323265
fn is_extendable_parent(context: &RewriteContext, parent_str: &str) -> bool {
@@ -335,38 +277,18 @@ fn chain_only_try(exprs: &[ast::Expr]) -> bool {
335277

336278
// Try to rewrite and replace the last non-try child. Return `true` if
337279
// replacing succeeds.
338-
fn rewrite_last_child_with_overflow(
339-
context: &RewriteContext,
340-
expr: &ast::Expr,
341-
shape: Shape,
342-
span: Span,
343-
almost_total: usize,
344-
one_line_budget: usize,
345-
last_child: &mut String,
346-
) -> bool {
347-
if let Some(shape) = shape.shrink_left(almost_total) {
348-
if let Some(ref mut rw) = rewrite_chain_subexpr(expr, span, context, shape) {
349-
if almost_total + first_line_width(rw) <= one_line_budget && rw.lines().count() > 3 {
350-
::std::mem::swap(last_child, rw);
351-
return true;
352-
}
353-
}
354-
}
355-
false
280+
fn repeat_try(try_count: usize) -> String {
281+
iter::repeat("?").take(try_count).collect::<String>()
356282
}
357283

358-
pub fn rewrite_try(
284+
fn rewrite_try(
359285
expr: &ast::Expr,
360286
try_count: usize,
361287
context: &RewriteContext,
362288
shape: Shape,
363289
) -> Option<String> {
364290
let sub_expr = try_opt!(expr.rewrite(context, try_opt!(shape.sub_width(try_count))));
365-
Some(format!(
366-
"{}{}",
367-
sub_expr,
368-
iter::repeat("?").take(try_count).collect::<String>()
369-
))
291+
Some(format!("{}{}", sub_expr, repeat_try(try_count)))
370292
}
371293

372294
fn join_rewrites(rewrites: &[String], subexps: &[ast::Expr], connector: &str) -> String {
@@ -426,47 +348,9 @@ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext) -> (ast::Expr,
426348
fn chain_indent(context: &RewriteContext, shape: Shape) -> Shape {
427349
match context.config.chain_indent() {
428350
IndentStyle::Visual => shape.visual_indent(0),
429-
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
430-
}
431-
}
432-
433-
fn rewrite_method_call_with_overflow(
434-
expr_kind: &ast::ExprKind,
435-
last: &mut String,
436-
almost_total: usize,
437-
total_span: Span,
438-
context: &RewriteContext,
439-
shape: Shape,
440-
) -> bool {
441-
if let &ast::ExprKind::MethodCall(ref segment, ref expressions) = expr_kind {
442-
let shape = match shape.shrink_left(almost_total) {
443-
Some(b) => b,
444-
None => return false,
445-
};
446-
let types = match segment.parameters {
447-
Some(ref params) => match **params {
448-
ast::PathParameters::AngleBracketed(ref data) => &data.types[..],
449-
_ => &[],
450-
},
451-
_ => &[],
452-
};
453-
let mut last_rewrite = rewrite_method_call(
454-
segment.identifier,
455-
types,
456-
expressions,
457-
total_span,
458-
context,
459-
shape,
460-
);
461-
462-
if let Some(ref mut s) = last_rewrite {
463-
::std::mem::swap(s, last);
464-
true
465-
} else {
466-
false
467-
}
468-
} else {
469-
unreachable!();
351+
IndentStyle::Block => shape
352+
.block_indent(context.config.tab_spaces())
353+
.with_max_width(context.config),
470354
}
471355
}
472356

0 commit comments

Comments
 (0)