1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Rewrite a list some items with overflow.
12 // FIXME: Replace `ToExpr` with some enum.
16 use syntax::codemap::Span;
19 use codemap::SpanUtils;
20 use expr::{is_nested_call, maybe_get_args_offset, ToExpr};
21 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
22 use rewrite::{Rewrite, RewriteContext};
25 use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp, paren_overhead};
29 pub fn rewrite_with_parens<T>(
30 context: &RewriteContext,
35 item_max_width: usize,
36 force_separator_tactic: Option<SeparatorTactic>,
39 T: Rewrite + ToExpr + Spanned,
50 force_separator_tactic,
54 pub fn rewrite_with_angle_brackets<T>(
55 context: &RewriteContext,
62 T: Rewrite + ToExpr + Spanned,
72 context.config.max_width(),
77 struct Context<'a, T: 'a> {
78 context: &'a RewriteContext<'a>,
83 one_line_shape: Shape,
86 item_max_width: usize,
87 one_line_width: usize,
88 force_separator_tactic: Option<SeparatorTactic>,
91 impl<'a, T: 'a + Rewrite + ToExpr + Spanned> Context<'a, T> {
93 context: &'a RewriteContext,
100 item_max_width: usize,
101 force_separator_tactic: Option<SeparatorTactic>,
102 ) -> Context<'a, T> {
104 let paren_overhead = if context.config.spaces_within_parens_and_brackets() {
109 let used_width = extra_offset(ident, shape);
110 let one_line_width = shape
112 .checked_sub(used_width + 2 * paren_overhead)
116 let one_line_shape = shape
117 .offset_left(last_line_width(ident) + 1)
118 .and_then(|shape| shape.sub_width(1))
119 .unwrap_or(Shape { width: 0, ..shape });
120 let nested_shape = shape_from_indent_style(
123 used_width + 2 * paren_overhead,
124 used_width + paren_overhead,
137 force_separator_tactic,
141 fn last_item(&self) -> Option<&&T> {
145 fn items_span(&self) -> Span {
146 let span_lo = self.context
148 .span_after(self.span, self.prefix);
149 mk_sp(span_lo, self.span.hi())
152 fn rewrite_last_item_with_overflow(
154 last_list_item: &mut ListItem,
156 ) -> Option<String> {
157 let last_item = self.last_item()?;
158 let rewrite = if let Some(expr) = last_item.to_expr() {
160 // When overflowing the closure which consists of a single control flow expression,
161 // force to use block if its condition uses multi line.
162 ast::ExprKind::Closure(..) => {
163 // If the argument consists of multiple closures, we do not overflow
165 if closures::args_have_many_closure(self.items) {
168 closures::rewrite_last_closure(self.context, expr, shape)
171 _ => expr.rewrite(self.context, shape),
174 last_item.rewrite(self.context, shape)
177 if let Some(rewrite) = rewrite {
178 let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
179 last_list_item.item = rewrite_first_line;
186 fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
189 ListTactic::LimitedHorizontalVertical(self.item_max_width),
195 fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
197 let combine_arg_with_callee = self.items.len() == 1 && self.items[0].to_expr().is_some()
198 && self.ident.len() + 1 <= self.context.config.tab_spaces();
199 let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, self.items);
201 // Replace the last item with its first line to see if it fits with
203 let placeholder = if overflow_last {
204 let old_value = *self.context.force_one_line_chain.borrow();
205 if !combine_arg_with_callee {
206 if let Some(expr) = self.last_item().and_then(|item| item.to_expr()) {
207 if let ast::ExprKind::MethodCall(..) = expr.node {
208 self.context.force_one_line_chain.replace(true);
212 let result = last_item_shape(
217 ).and_then(|arg_shape| {
218 self.rewrite_last_item_with_overflow(
219 &mut list_items[self.items.len() - 1],
223 self.context.force_one_line_chain.replace(old_value);
229 let mut tactic = definitive_tactic(
231 ListTactic::LimitedHorizontalVertical(self.item_max_width),
236 // Replace the stub with the full overflowing last argument if the rewrite
237 // succeeded and its first line fits with the other arguments.
238 match (overflow_last, tactic, placeholder) {
239 (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
240 if self.items.len() == 1 =>
242 // When we are rewriting a nested function call, we restrict the
243 // bugdet for the inner function to avoid them being deeply nested.
244 // However, when the inner function has a prefix or a suffix
245 // (e.g. `foo() as u32`), this budget reduction may produce poorly
246 // formatted code, where a prefix or a suffix being left on its own
247 // line. Here we explicitlly check those cases.
248 if count_newlines(overflowed) == 1 {
251 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
252 let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
254 list_items[self.items.len() - 1].item = rw;
256 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
259 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
262 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
263 list_items[self.items.len() - 1].item = placeholder;
265 _ if self.items.len() >= 1 => {
266 list_items[self.items.len() - 1].item = self.items
268 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
270 // Use horizontal layout for a function with a single argument as long as
271 // everything fits in a single line.
272 // `self.one_line_width == 0` means vertical layout is forced.
273 if self.items.len() == 1 && self.one_line_width != 0 && !list_items[0].has_comment()
274 && !list_items[0].inner_as_ref().contains('\n')
275 && ::lists::total_item_width(&list_items[0]) <= self.one_line_width
277 tactic = DefinitiveListTactic::Horizontal;
279 tactic = self.default_tactic(list_items);
281 if tactic == DefinitiveListTactic::Vertical {
282 if let Some((all_simple, num_args_before)) =
283 maybe_get_args_offset(self.ident, self.items)
285 let one_line = all_simple
286 && definitive_tactic(
287 &list_items[..num_args_before],
288 ListTactic::HorizontalVertical,
290 self.nested_shape.width,
292 == DefinitiveListTactic::Horizontal
293 && definitive_tactic(
294 &list_items[num_args_before + 1..],
295 ListTactic::HorizontalVertical,
297 self.nested_shape.width,
299 == DefinitiveListTactic::Horizontal;
302 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
314 fn rewrite_items(&self) -> Option<(bool, String)> {
315 let span = self.items_span();
316 let items = itemize_list(
317 self.context.snippet_provider,
321 |item| item.span().lo(),
322 |item| item.span().hi(),
323 |item| item.rewrite(self.context, self.nested_shape),
328 let mut list_items: Vec<_> = items.collect();
330 // Try letting the last argument overflow to the next line with block
331 // indentation. If its first line fits on one line with the other arguments,
332 // we format the function arguments horizontally.
333 let tactic = self.try_overflow_last_item(&mut list_items);
335 let fmt = ListFormatting {
338 trailing_separator: if let Some(tactic) = self.force_separator_tactic {
340 } else if !self.context.use_block_indent() {
341 SeparatorTactic::Never
343 self.context.config.trailing_comma()
345 separator_place: SeparatorPlace::Back,
346 shape: self.nested_shape,
347 ends_with_newline: self.context.use_block_indent()
348 && tactic == DefinitiveListTactic::Vertical,
349 preserve_newline: false,
350 config: self.context.config,
353 write_list(&list_items, &fmt)
354 .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
357 fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
361 .checked_sub(last_line_width(self.ident))
366 let paren_overhead = paren_overhead(self.context);
367 let fits_one_line = items_str.len() + paren_overhead <= shape.width;
368 let extend_width = if items_str.is_empty() {
373 let nested_indent_str = self.nested_shape
375 .to_string_with_newline(self.context.config);
376 let indent_str = shape
379 .to_string_with_newline(self.context.config);
380 let mut result = String::with_capacity(
381 self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
383 result.push_str(self.ident);
384 result.push_str(self.prefix);
385 if !self.context.use_block_indent()
386 || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
387 || (is_extendable && extend_width <= shape.width)
389 if self.context.config.spaces_within_parens_and_brackets() && !items_str.is_empty() {
391 result.push_str(items_str);
394 result.push_str(items_str);
397 if !items_str.is_empty() {
398 result.push_str(&nested_indent_str);
399 result.push_str(items_str);
401 result.push_str(&indent_str);
403 result.push_str(self.suffix);
407 fn rewrite(&self, shape: Shape) -> Option<String> {
408 let (extendable, items_str) = self.rewrite_items()?;
410 // If we are using visual indent style and failed to format, retry with block indent.
411 if !self.context.use_block_indent() && need_block_indent(&items_str, self.nested_shape)
414 self.context.use_block.replace(true);
415 let result = self.rewrite(shape);
416 self.context.use_block.replace(false);
420 Some(self.wrap_items(&items_str, shape, extendable))
424 fn need_block_indent(s: &str, shape: Shape) -> bool {
425 s.lines().skip(1).any(|s| {
426 s.find(|c| !char::is_whitespace(c))
427 .map_or(false, |w| w + 1 < shape.indent.width())
431 fn can_be_overflowed<'a, T>(context: &RewriteContext, items: &[&T]) -> bool
433 T: Rewrite + Spanned + ToExpr + 'a,
437 .map_or(false, |x| x.can_be_overflowed(context, items.len()))
440 /// Returns a shape for the last argument which is going to be overflowed.
441 fn last_item_shape<T>(
445 args_max_width: usize,
448 T: Rewrite + Spanned + ToExpr,
450 let is_nested_call = lists
453 .and_then(|item| item.to_expr())
454 .map_or(false, is_nested_call);
455 if items.len() == 1 && !is_nested_call {
458 let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
460 acc + 2 + i.inner_as_ref().len()
463 width: min(args_max_width, shape.width),
465 }.offset_left(offset)
468 fn shape_from_indent_style(
469 context: &RewriteContext,
474 if context.use_block_indent() {
478 .block_indent(context.config.tab_spaces())
479 .with_max_width(context.config)
483 let shape = shape.visual_indent(offset);
484 if let Some(shape) = shape.sub_width(overhead) {
487 Shape { width: 0, ..shape }