use config::lists::*;
use syntax::ast;
-use syntax::codemap::Span;
+use syntax::parse::token::DelimToken;
+use syntax::source_map::Span;
use closures;
-use codemap::SpanUtils;
-use expr::{is_nested_call, maybe_get_args_offset, ToExpr};
+use expr::{is_every_expr_simple, is_method_call, is_nested_call, maybe_get_args_offset, ToExpr};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
+use source_map::SpanUtils;
use spanned::Spanned;
-use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp, paren_overhead};
+use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
use std::cmp::min;
+const SHORT_ITEM_THRESHOLD: usize = 10;
+
pub fn rewrite_with_parens<T>(
context: &RewriteContext,
ident: &str,
")",
item_max_width,
force_separator_tactic,
+ None,
).rewrite(shape)
}
">",
context.config.max_width(),
None,
+ None,
+ ).rewrite(shape)
+}
+
+pub fn rewrite_with_square_brackets<T>(
+ context: &RewriteContext,
+ name: &str,
+ items: &[&T],
+ shape: Shape,
+ span: Span,
+ force_separator_tactic: Option<SeparatorTactic>,
+ delim_token: Option<DelimToken>,
+) -> Option<String>
+where
+ T: Rewrite + ToExpr + Spanned,
+{
+ let (lhs, rhs) = match delim_token {
+ Some(DelimToken::Paren) => ("(", ")"),
+ Some(DelimToken::Brace) => ("{", "}"),
+ _ => ("[", "]"),
+ };
+ Context::new(
+ context,
+ items,
+ name,
+ shape,
+ span,
+ lhs,
+ rhs,
+ context.config.width_heuristics().array_width,
+ force_separator_tactic,
+ Some(("[", "]")),
).rewrite(shape)
}
item_max_width: usize,
one_line_width: usize,
force_separator_tactic: Option<SeparatorTactic>,
+ custom_delims: Option<(&'a str, &'a str)>,
}
impl<'a, T: 'a + Rewrite + ToExpr + Spanned> Context<'a, T> {
suffix: &'static str,
item_max_width: usize,
force_separator_tactic: Option<SeparatorTactic>,
+ custom_delims: Option<(&'a str, &'a str)>,
) -> Context<'a, T> {
- // 2 = `( `, 1 = `(`
- let paren_overhead = if context.config.spaces_within_parens_and_brackets() {
- 2
- } else {
- 1
- };
let used_width = extra_offset(ident, shape);
- let one_line_width = shape
- .width
- .checked_sub(used_width + 2 * paren_overhead)
- .unwrap_or(0);
+ // 1 = `()`
+ let one_line_width = shape.width.saturating_sub(used_width + 2);
// 1 = "(" or ")"
let one_line_shape = shape
.offset_left(last_line_width(ident) + 1)
.and_then(|shape| shape.sub_width(1))
.unwrap_or(Shape { width: 0, ..shape });
- let nested_shape = shape_from_indent_style(
- context,
- shape,
- used_width + 2 * paren_overhead,
- used_width + paren_overhead,
- );
+ let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
Context {
context,
items,
item_max_width,
one_line_width,
force_separator_tactic,
+ custom_delims,
}
}
}
fn items_span(&self) -> Span {
- let span_lo = self.context
+ let span_lo = self
+ .context
.snippet_provider
.span_after(self.span, self.prefix);
mk_sp(span_lo, self.span.hi())
}
}
+ fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
+ definitive_tactic(
+ list_items,
+ ListTactic::LimitedHorizontalVertical(self.item_max_width),
+ Separator::Comma,
+ self.one_line_width,
+ )
+ }
+
fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
// 1 = "("
- let combine_arg_with_callee = self.items.len() == 1 && self.items[0].to_expr().is_some()
- && self.ident.len() + 1 <= self.context.config.tab_spaces();
+ let combine_arg_with_callee = self.items.len() == 1
+ && self.items[0].to_expr().is_some()
+ && self.ident.len() < self.context.config.tab_spaces();
let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, self.items);
// Replace the last item with its first line to see if it fits with
let placeholder = if overflow_last {
let old_value = *self.context.force_one_line_chain.borrow();
if !combine_arg_with_callee {
- if let Some(expr) = self.last_item().and_then(|item| item.to_expr()) {
- if let ast::ExprKind::MethodCall(..) = expr.node {
+ if let Some(ref expr) = self.last_item().and_then(|item| item.to_expr()) {
+ if is_method_call(expr) {
self.context.force_one_line_chain.replace(true);
}
}
if self.items.len() == 1 =>
{
// When we are rewriting a nested function call, we restrict the
- // bugdet for the inner function to avoid them being deeply nested.
+ // budget for the inner function to avoid them being deeply nested.
// However, when the inner function has a prefix or a suffix
// (e.g. `foo() as u32`), this budget reduction may produce poorly
// formatted code, where a prefix or a suffix being left on its own
// line. Here we explicitlly check those cases.
if count_newlines(overflowed) == 1 {
- let rw = self.items
+ let rw = self
+ .items
.last()
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
(true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
list_items[self.items.len() - 1].item = placeholder;
}
- _ if self.items.len() >= 1 => {
- list_items[self.items.len() - 1].item = self.items
+ _ if !self.items.is_empty() => {
+ list_items[self.items.len() - 1].item = self
+ .items
.last()
.and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
- let default_tactic = || {
- definitive_tactic(
- &*list_items,
- ListTactic::LimitedHorizontalVertical(self.item_max_width),
- Separator::Comma,
- self.one_line_width,
- )
- };
-
// Use horizontal layout for a function with a single argument as long as
// everything fits in a single line.
+ // `self.one_line_width == 0` means vertical layout is forced.
if self.items.len() == 1
- && self.one_line_width != 0 // Vertical layout is forced.
- && !list_items[0].has_comment()
+ && self.one_line_width != 0
+ && !list_items[0].has_comment()
&& !list_items[0].inner_as_ref().contains('\n')
&& ::lists::total_item_width(&list_items[0]) <= self.one_line_width
{
tactic = DefinitiveListTactic::Horizontal;
} else {
- tactic = default_tactic();
+ tactic = self.default_tactic(list_items);
if tactic == DefinitiveListTactic::Vertical {
if let Some((all_simple, num_args_before)) =
ListTactic::HorizontalVertical,
Separator::Comma,
self.nested_shape.width,
- )
- == DefinitiveListTactic::Horizontal
+ ) == DefinitiveListTactic::Horizontal
&& definitive_tactic(
&list_items[num_args_before + 1..],
ListTactic::HorizontalVertical,
Separator::Comma,
self.nested_shape.width,
- )
- == DefinitiveListTactic::Horizontal;
+ ) == DefinitiveListTactic::Horizontal;
if one_line {
tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
};
+ } else if is_every_expr_simple(self.items) && no_long_items(list_items) {
+ tactic = DefinitiveListTactic::Mixed;
}
}
}
// indentation. If its first line fits on one line with the other arguments,
// we format the function arguments horizontally.
let tactic = self.try_overflow_last_item(&mut list_items);
-
- let fmt = ListFormatting {
- tactic,
- separator: ",",
- trailing_separator: if let Some(tactic) = self.force_separator_tactic {
- tactic
- } else if !self.context.use_block_indent() {
- SeparatorTactic::Never
- } else {
- self.context.config.trailing_comma()
- },
- separator_place: SeparatorPlace::Back,
- shape: self.nested_shape,
- ends_with_newline: self.context.use_block_indent()
- && tactic == DefinitiveListTactic::Vertical,
- preserve_newline: false,
- config: self.context.config,
+ let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
+ tactic
+ } else if !self.context.use_block_indent() {
+ SeparatorTactic::Never
+ } else if tactic == DefinitiveListTactic::Mixed {
+ // We are using mixed layout because everything did not fit within a single line.
+ SeparatorTactic::Always
+ } else {
+ self.context.config.trailing_comma()
+ };
+ let ends_with_newline = match tactic {
+ DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
+ self.context.use_block_indent()
+ }
+ _ => false,
};
+ let fmt = ListFormatting::new(self.nested_shape, self.context.config)
+ .tactic(tactic)
+ .trailing_separator(trailing_separator)
+ .ends_with_newline(ends_with_newline);
+
write_list(&list_items, &fmt)
.map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
}
fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
let shape = Shape {
- width: shape
- .width
- .checked_sub(last_line_width(self.ident))
- .unwrap_or(0),
+ width: shape.width.saturating_sub(last_line_width(self.ident)),
..shape
};
- let paren_overhead = paren_overhead(self.context);
- let fits_one_line = items_str.len() + paren_overhead <= shape.width;
+ let (prefix, suffix) = match self.custom_delims {
+ Some((lhs, rhs)) => (lhs, rhs),
+ _ => (self.prefix, self.suffix),
+ };
+
+ // 2 = `()`
+ let fits_one_line = items_str.len() + 2 <= shape.width;
let extend_width = if items_str.is_empty() {
- paren_overhead
+ 2
} else {
- paren_overhead / 2
+ first_line_width(items_str) + 1
};
- let nested_indent_str = self.nested_shape
+ let nested_indent_str = self
+ .nested_shape
.indent
.to_string_with_newline(self.context.config);
let indent_str = shape
self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
);
result.push_str(self.ident);
- result.push_str(self.prefix);
+ result.push_str(prefix);
if !self.context.use_block_indent()
- || (self.context.inside_macro && !items_str.contains('\n') && fits_one_line)
+ || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
|| (is_extendable && extend_width <= shape.width)
{
- if self.context.config.spaces_within_parens_and_brackets() && !items_str.is_empty() {
- result.push(' ');
- result.push_str(items_str);
- result.push(' ');
- } else {
- result.push_str(items_str);
- }
+ result.push_str(items_str);
} else {
if !items_str.is_empty() {
result.push_str(&nested_indent_str);
}
result.push_str(&indent_str);
}
- result.push_str(self.suffix);
+ result.push_str(suffix);
result
}
let (extendable, items_str) = self.rewrite_items()?;
// If we are using visual indent style and failed to format, retry with block indent.
- if !self.context.use_block_indent() && need_block_indent(&items_str, self.nested_shape)
+ if !self.context.use_block_indent()
+ && need_block_indent(&items_str, self.nested_shape)
&& !extendable
{
self.context.use_block.replace(true);
overhead: usize,
offset: usize,
) -> Shape {
- if context.use_block_indent() {
- // 1 = ","
- shape
+ let (shape, overhead) = if context.use_block_indent() {
+ let shape = shape
.block()
.block_indent(context.config.tab_spaces())
- .with_max_width(context.config)
- .sub_width(1)
- .unwrap()
+ .with_max_width(context.config);
+ (shape, 1) // 1 = ","
} else {
- let shape = shape.visual_indent(offset);
- if let Some(shape) = shape.sub_width(overhead) {
- shape
- } else {
- Shape { width: 0, ..shape }
- }
+ (shape.visual_indent(offset), overhead)
+ };
+ Shape {
+ width: shape.width.saturating_sub(overhead),
+ ..shape
}
}
+
+fn no_long_items(list: &[ListItem]) -> bool {
+ list.iter()
+ .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
+}