Refactor rewrite_where_clause

This commit is contained in:
topecongiro 2019-05-26 21:15:38 +09:00
parent 264417e10d
commit 95c2ad37e1

View File

@ -693,7 +693,7 @@ pub(crate) fn format_impl(
{ {
option.suppress_comma(); option.suppress_comma();
option.snuggle(); option.snuggle();
option.compress_where(); option.allow_single_line();
} }
let misssing_span = mk_sp(self_ty.span.hi(), item.span.hi()); let misssing_span = mk_sp(self_ty.span.hi(), item.span.hi());
@ -708,7 +708,6 @@ pub(crate) fn format_impl(
where_span_end, where_span_end,
self_ty.span.hi(), self_ty.span.hi(),
option, option,
false,
)?; )?;
// If there is no where-clause, we may have missing comments between the trait name and // If there is no where-clause, we may have missing comments between the trait name and
@ -1068,7 +1067,6 @@ pub(crate) fn format_trait(
None, None,
pos_before_where, pos_before_where,
option, option,
false,
)?; )?;
// If the where-clause cannot fit on the same line, // If the where-clause cannot fit on the same line,
// put the where-clause on a new line // put the where-clause on a new line
@ -1376,7 +1374,7 @@ fn format_tuple_struct(
result.push_str(&generics_str); result.push_str(&generics_str);
let where_budget = context.budget(last_line_width(&result)); let where_budget = context.budget(last_line_width(&result));
let option = WhereClauseOption::new(true, false); let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
rewrite_where_clause( rewrite_where_clause(
context, context,
&generics.where_clause, &generics.where_clause,
@ -1387,7 +1385,6 @@ fn format_tuple_struct(
None, None,
body_hi, body_hi,
option, option,
false,
)? )?
} }
None => "".to_owned(), None => "".to_owned(),
@ -1464,7 +1461,6 @@ fn rewrite_type_prefix(
None, None,
generics.span.hi(), generics.span.hi(),
option, option,
false,
)?; )?;
result.push_str(&where_clause_str); result.push_str(&where_clause_str);
@ -2205,7 +2201,15 @@ fn rewrite_fn_base(
let is_args_multi_lined = arg_str.contains('\n'); let is_args_multi_lined = arg_str.contains('\n');
let option = WhereClauseOption::new(!has_body, put_args_in_block && ret_str.is_empty()); let space = if put_args_in_block && ret_str.is_empty() {
WhereClauseSpace::Space
} else {
WhereClauseSpace::Newline
};
let mut option = WhereClauseOption::new(!has_body, space);
if is_args_multi_lined {
option.veto_single_line();
}
let where_clause_str = rewrite_where_clause( let where_clause_str = rewrite_where_clause(
context, context,
where_clause, where_clause,
@ -2216,7 +2220,6 @@ fn rewrite_fn_base(
Some(span.hi()), Some(span.hi()),
pos_before_where, pos_before_where,
option, option,
is_args_multi_lined,
)?; )?;
// If there are neither where-clause nor return type, we may be missing comments between // If there are neither where-clause nor return type, we may be missing comments between
// args and `{`. // args and `{`.
@ -2244,27 +2247,45 @@ fn rewrite_fn_base(
Some((result, force_new_line_for_brace)) Some((result, force_new_line_for_brace))
} }
/// Kind of spaces to put before `where`.
#[derive(Copy, Clone)]
enum WhereClauseSpace {
/// A single space.
Space,
/// A new line.
Newline,
/// Nothing.
None,
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct WhereClauseOption { struct WhereClauseOption {
suppress_comma: bool, // Force no trailing comma suppress_comma: bool, // Force no trailing comma
snuggle: bool, // Do not insert newline before `where` snuggle: WhereClauseSpace,
compress_where: bool, // Try single line where-clause instead of vertical layout allow_single_line: bool, // Try single line where-clause instead of vertical layout
veto_single_line: bool, // Disallow a single-line where-clause.
} }
impl WhereClauseOption { impl WhereClauseOption {
fn new(suppress_comma: bool, snuggle: bool) -> WhereClauseOption { fn new(suppress_comma: bool, snuggle: WhereClauseSpace) -> WhereClauseOption {
WhereClauseOption { WhereClauseOption {
suppress_comma, suppress_comma,
snuggle, snuggle,
compress_where: false, allow_single_line: false,
veto_single_line: false,
} }
} }
fn snuggled(current: &str) -> WhereClauseOption { fn snuggled(current: &str) -> WhereClauseOption {
WhereClauseOption { WhereClauseOption {
suppress_comma: false, suppress_comma: false,
snuggle: last_line_width(current) == 1, snuggle: if last_line_width(current) == 1 {
compress_where: false, WhereClauseSpace::Space
} else {
WhereClauseSpace::Newline
},
allow_single_line: false,
veto_single_line: false,
} }
} }
@ -2272,12 +2293,16 @@ impl WhereClauseOption {
self.suppress_comma = true self.suppress_comma = true
} }
fn compress_where(&mut self) { fn allow_single_line(&mut self) {
self.compress_where = true self.allow_single_line = true
} }
fn snuggle(&mut self) { fn snuggle(&mut self) {
self.snuggle = true self.snuggle = WhereClauseSpace::Space
}
fn veto_single_line(&mut self) {
self.veto_single_line = true;
} }
} }
@ -2467,25 +2492,104 @@ fn rewrite_where_clause_rfc_style(
span_end: Option<BytePos>, span_end: Option<BytePos>,
span_end_before_where: BytePos, span_end_before_where: BytePos,
where_clause_option: WhereClauseOption, where_clause_option: WhereClauseOption,
is_args_multi_line: bool,
) -> Option<String> { ) -> Option<String> {
let (where_keyword, allow_single_line) = rewrite_where_keyword(
context,
where_clause,
shape,
span_end_before_where,
where_clause_option,
)?;
// 1 = `,`
let clause_shape = shape
.block()
.with_max_width(context.config)
.block_left(context.config.tab_spaces())?
.sub_width(1)?;
let force_single_line = context.config.where_single_line()
&& where_clause.predicates.len() == 1
&& !where_clause_option.veto_single_line;
let preds_str = rewrite_bounds_on_where_clause(
context,
where_clause,
clause_shape,
terminator,
span_end,
where_clause_option,
force_single_line,
)?;
// 6 = `where `
let clause_sep =
if allow_single_line && !preds_str.contains('\n') && 6 + preds_str.len() <= shape.width
|| force_single_line
{
Cow::from(" ")
} else {
clause_shape.indent.to_string_with_newline(context.config)
};
Some(format!("{}{}{}", where_keyword, clause_sep, preds_str))
}
/// Rewrite `where` and comment around it.
fn rewrite_where_keyword(
context: &RewriteContext<'_>,
where_clause: &ast::WhereClause,
shape: Shape,
span_end_before_where: BytePos,
where_clause_option: WhereClauseOption,
) -> Option<(String, bool)> {
let block_shape = shape.block().with_max_width(context.config); let block_shape = shape.block().with_max_width(context.config);
// 1 = `,`
let clause_shape = block_shape
.block_left(context.config.tab_spaces())?
.sub_width(1)?;
let comment_separator = |comment: &str, shape: Shape| {
if comment.is_empty() {
Cow::from("")
} else {
shape.indent.to_string_with_newline(context.config)
}
};
let (span_before, span_after) = let (span_before, span_after) =
missing_span_before_after_where(span_end_before_where, where_clause); missing_span_before_after_where(span_end_before_where, where_clause);
let (comment_before, comment_after) = let (comment_before, comment_after) =
rewrite_comments_before_after_where(context, span_before, span_after, shape)?; rewrite_comments_before_after_where(context, span_before, span_after, shape)?;
let starting_newline = if where_clause_option.snuggle && comment_before.is_empty() { let starting_newline = match where_clause_option.snuggle {
Cow::from(" ") WhereClauseSpace::Space if comment_before.is_empty() => Cow::from(" "),
} else { WhereClauseSpace::None => Cow::from(""),
block_shape.indent.to_string_with_newline(context.config) _ => block_shape.indent.to_string_with_newline(context.config),
}; };
let clause_shape = block_shape.block_left(context.config.tab_spaces())?; let newline_before_where = comment_separator(&comment_before, shape);
// 1 = `,` let newline_after_where = comment_separator(&comment_after, clause_shape);
let clause_shape = clause_shape.sub_width(1)?; let result = format!(
// each clause on one line, trailing comma (except if suppress_comma) "{}{}{}where{}{}",
starting_newline, comment_before, newline_before_where, newline_after_where, comment_after
);
let allow_single_line = where_clause_option.allow_single_line
&& comment_before.is_empty()
&& comment_after.is_empty();
Some((result, allow_single_line))
}
/// Rewrite bounds on a where clause.
fn rewrite_bounds_on_where_clause(
context: &RewriteContext<'_>,
where_clause: &ast::WhereClause,
shape: Shape,
terminator: &str,
span_end: Option<BytePos>,
where_clause_option: WhereClauseOption,
force_single_line: bool,
) -> Option<String> {
let span_start = where_clause.predicates[0].span().lo(); let span_start = where_clause.predicates[0].span().lo();
// If we don't have the start of the next span, then use the end of the // If we don't have the start of the next span, then use the end of the
// predicates, but that means we miss comments. // predicates, but that means we miss comments.
@ -2499,64 +2603,30 @@ fn rewrite_where_clause_rfc_style(
",", ",",
|pred| pred.span().lo(), |pred| pred.span().lo(),
|pred| pred.span().hi(), |pred| pred.span().hi(),
|pred| pred.rewrite(context, clause_shape), |pred| pred.rewrite(context, shape),
span_start, span_start,
span_end, span_end,
false, false,
); );
let where_single_line = context.config.where_single_line() && len == 1 && !is_args_multi_line; let comma_tactic = if where_clause_option.suppress_comma || force_single_line {
let comma_tactic = if where_clause_option.suppress_comma || where_single_line {
SeparatorTactic::Never SeparatorTactic::Never
} else { } else {
context.config.trailing_comma() context.config.trailing_comma()
}; };
// shape should be vertical only and only if we have `where_single_line` option enabled // shape should be vertical only and only if we have `force_single_line` option enabled
// and the number of items of the where-clause is equal to 1 // and the number of items of the where-clause is equal to 1
let shape_tactic = if where_single_line { let shape_tactic = if force_single_line {
DefinitiveListTactic::Horizontal DefinitiveListTactic::Horizontal
} else { } else {
DefinitiveListTactic::Vertical DefinitiveListTactic::Vertical
}; };
let fmt = ListFormatting::new(clause_shape, context.config) let fmt = ListFormatting::new(shape, context.config)
.tactic(shape_tactic) .tactic(shape_tactic)
.trailing_separator(comma_tactic) .trailing_separator(comma_tactic)
.preserve_newline(true); .preserve_newline(true);
let preds_str = write_list(&items.collect::<Vec<_>>(), &fmt)?; write_list(&items.collect::<Vec<_>>(), &fmt)
let comment_separator = |comment: &str, shape: Shape| {
if comment.is_empty() {
Cow::from("")
} else {
shape.indent.to_string_with_newline(context.config)
}
};
let newline_before_where = comment_separator(&comment_before, shape);
let newline_after_where = comment_separator(&comment_after, clause_shape);
// 6 = `where `
let clause_sep = if where_clause_option.compress_where
&& comment_before.is_empty()
&& comment_after.is_empty()
&& !preds_str.contains('\n')
&& 6 + preds_str.len() <= shape.width
|| where_single_line
{
Cow::from(" ")
} else {
clause_shape.indent.to_string_with_newline(context.config)
};
Some(format!(
"{}{}{}where{}{}{}{}",
starting_newline,
comment_before,
newline_before_where,
newline_after_where,
comment_after,
clause_sep,
preds_str
))
} }
fn rewrite_where_clause( fn rewrite_where_clause(
@ -2569,7 +2639,6 @@ fn rewrite_where_clause(
span_end: Option<BytePos>, span_end: Option<BytePos>,
span_end_before_where: BytePos, span_end_before_where: BytePos,
where_clause_option: WhereClauseOption, where_clause_option: WhereClauseOption,
is_args_multi_line: bool,
) -> Option<String> { ) -> Option<String> {
if where_clause.predicates.is_empty() { if where_clause.predicates.is_empty() {
return Some(String::new()); return Some(String::new());
@ -2584,7 +2653,6 @@ fn rewrite_where_clause(
span_end, span_end,
span_end_before_where, span_end_before_where,
where_clause_option, where_clause_option,
is_args_multi_line,
); );
} }
@ -2742,7 +2810,6 @@ fn format_generics(
Some(span.hi()), Some(span.hi()),
span_end_before_where, span_end_before_where,
option, option,
false,
)?; )?;
result.push_str(&where_clause_str); result.push_str(&where_clause_str);
( (