Allow attributes to stay on the same line with fields

This commit is contained in:
topecongiro 2017-08-11 17:52:49 +09:00
parent 0af19985fc
commit a4e0fe2b02
2 changed files with 118 additions and 123 deletions

View File

@ -18,7 +18,8 @@ use syntax::codemap::{BytePos, Span};
use {Indent, Shape, Spanned};
use codemap::{LineRangeUtils, SpanUtils};
use comment::{contains_comment, recover_comment_removed, rewrite_comment, FindUncommented};
use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
rewrite_comment, FindUncommented};
use config::{BraceStyle, Config, Density, IndentStyle, ReturnIndent, Style};
use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs,
rewrite_call_inner, ExprType};
@ -28,8 +29,9 @@ use rewrite::{Rewrite, RewriteContext};
use types::join_bounds;
use utils::{colon_spaces, contains_skip, end_typaram, first_line_width, format_abi,
format_constness, format_defaultness, format_mutability, format_unsafety,
format_visibility, last_line_used_width, last_line_width, mk_sp, semicolon_for_expr,
stmt_expr, trim_newlines, trimmed_last_line_width, wrap_str};
format_visibility, is_attributes_extendable, last_line_contains_single_line_comment,
last_line_used_width, last_line_width, mk_sp, semicolon_for_expr, stmt_expr,
trim_newlines, trimmed_last_line_width, wrap_str};
use vertical::rewrite_with_alignment;
use visitor::FmtVisitor;
@ -501,32 +503,19 @@ impl<'a> FmtVisitor<'a> {
let context = self.get_context();
let indent = self.block_indent;
let mut result = try_opt!(
field
.node
.attrs
.rewrite(&context, Shape::indented(indent, self.config))
);
if !result.is_empty() {
let shape = Shape {
width: context.config.max_width(),
indent: self.block_indent,
offset: self.block_indent.alignment,
};
let missing_comment = rewrite_missing_comment_on_field(
&context,
shape,
field.node.attrs[field.node.attrs.len() - 1].span.hi,
field.span.lo,
&mut result,
).unwrap_or(String::new());
result.push_str(&missing_comment);
}
let shape = Shape::indented(indent, self.config);
let attrs_str = try_opt!(field.node.attrs.rewrite(&context, shape));
let lo = field
.node
.attrs
.last()
.map_or(field.span.lo, |attr| attr.span.hi);
let span = mk_sp(lo, field.span.lo);
let variant_body = match field.node.data {
ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => {
// FIXME: Should limit the width, as we have a trailing comma
format_struct(
try_opt!(format_struct(
&context,
"",
field.node.name,
@ -536,29 +525,37 @@ impl<'a> FmtVisitor<'a> {
field.span,
indent,
Some(self.config.struct_variant_width()),
)
))
}
ast::VariantData::Unit(..) => {
let tag = if let Some(ref expr) = field.node.disr_expr {
ast::VariantData::Unit(..) => if let Some(ref expr) = field.node.disr_expr {
let one_line_width =
field.node.name.to_string().len() + self.snippet(expr.span).len() + 3;
if one_line_width <= shape.width {
format!("{} = {}", field.node.name, self.snippet(expr.span))
} else {
field.node.name.to_string()
};
wrap_str(
tag,
self.config.max_width(),
Shape::indented(indent, self.config),
)
}
format!(
"{}\n{}{}",
field.node.name,
shape
.indent
.block_indent(self.config)
.to_string(self.config),
self.snippet(expr.span)
)
}
} else {
String::from(field.node.name.to_string())
},
};
if let Some(variant_str) = variant_body {
result.push_str(&variant_str);
Some(result)
} else {
None
}
combine_strs_with_missing_comments(
&context,
&attrs_str,
&variant_body,
span,
shape,
is_attributes_extendable(&attrs_str),
)
}
}
@ -1369,68 +1366,15 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) {
)
}
fn rewrite_missing_comment_on_field(
context: &RewriteContext,
shape: Shape,
lo: BytePos,
hi: BytePos,
result: &mut String,
) -> Option<String> {
let possibly_comment_snippet = context.snippet(mk_sp(lo, hi));
let newline_index = possibly_comment_snippet.find('\n');
let comment_index = possibly_comment_snippet.find('/');
match (newline_index, comment_index) {
(Some(i), Some(j)) if i > j => result.push(' '),
_ => {
result.push('\n');
result.push_str(&shape.indent.to_string(context.config));
}
}
let trimmed = possibly_comment_snippet.trim();
if trimmed.is_empty() {
None
} else {
rewrite_comment(trimmed, false, shape, context.config).map(|s| {
format!("{}\n{}", s, shape.indent.to_string(context.config))
})
}
}
pub fn rewrite_struct_field_prefix(
context: &RewriteContext,
field: &ast::StructField,
shape: Shape,
) -> Option<String> {
let vis = format_visibility(&field.vis);
let mut attr_str = try_opt!(
field
.attrs
.rewrite(context, Shape::indented(shape.indent, context.config))
);
// Try format missing comments after attributes
let missing_comment = if !field.attrs.is_empty() {
rewrite_missing_comment_on_field(
context,
shape,
field.attrs[field.attrs.len() - 1].span.hi,
field.span.lo,
&mut attr_str,
).unwrap_or(String::new())
} else {
String::new()
};
let type_annotation_spacing = type_annotation_spacing(context.config);
Some(match field.ident {
Some(name) => format!(
"{}{}{}{}{}:",
attr_str,
missing_comment,
vis,
name,
type_annotation_spacing.0
),
None => format!("{}{}{}", attr_str, missing_comment, vis),
Some(name) => format!("{}{}{}:", vis, name, type_annotation_spacing.0),
None => format!("{}", vis),
})
}
@ -1466,27 +1410,50 @@ pub fn rewrite_struct_field(
}
let type_annotation_spacing = type_annotation_spacing(context.config);
let prefix = try_opt!(rewrite_struct_field_prefix(context, field, shape));
let prefix = try_opt!(rewrite_struct_field_prefix(context, field));
// Try to put everything on a single line.
let last_line_width = last_line_width(&prefix);
let attrs_str = try_opt!(field.attrs.rewrite(context, shape));
let missing_span = if field.attrs.is_empty() {
mk_sp(field.span.lo, field.span.lo)
} else {
mk_sp(field.attrs.last().unwrap().span.hi, field.span.lo)
};
let mut spacing = String::from(if field.ident.is_some() {
type_annotation_spacing.1
} else {
""
});
let lhs_offset = lhs_max_width.checked_sub(last_line_width).unwrap_or(0);
// Try to put everything on a single line.
let attr_prefix = try_opt!(combine_strs_with_missing_comments(
context,
&attrs_str,
&prefix,
missing_span,
shape,
is_attributes_extendable(&attrs_str),
));
let overhead = last_line_width(&attr_prefix);
let lhs_offset = lhs_max_width.checked_sub(overhead).unwrap_or(0);
for _ in 0..lhs_offset {
spacing.push(' ');
}
let ty_rewritten = rewrite_struct_field_type(context, last_line_width, field, &spacing, shape);
// In this extreme case we will be missing a space betweeen an attribute and a field.
if prefix.is_empty() && !attrs_str.is_empty() && is_attributes_extendable(&attrs_str) &&
spacing.is_empty()
{
spacing.push(' ');
}
let ty_rewritten = rewrite_struct_field_type(context, overhead, field, &spacing, shape);
if let Some(ref ty) = ty_rewritten {
if !ty.contains('\n') {
return Some(prefix + &ty);
return Some(attr_prefix + &ty);
}
}
// We must use multiline.
let last_line_width = last_line_width(&prefix);
let ty_rewritten = rewrite_struct_field_type(context, last_line_width, field, &spacing, shape);
let type_offset = shape.indent.block_indent(context.config);
let rewrite_type_in_next_line = || {
field
@ -1494,27 +1461,35 @@ pub fn rewrite_struct_field(
.rewrite(context, Shape::indented(type_offset, context.config))
};
match ty_rewritten {
let field_str = match ty_rewritten {
// If we start from the next line and type fits in a single line, then do so.
Some(ref ty) => match rewrite_type_in_next_line() {
Some(ref new_ty) if !new_ty.contains('\n') => Some(format!(
Some(ref new_ty) if !new_ty.contains('\n') => format!(
"{}\n{}{}",
prefix,
type_offset.to_string(&context.config),
&new_ty
)),
_ => Some(prefix + &ty),
),
_ => prefix + &ty,
},
_ => {
let ty = try_opt!(rewrite_type_in_next_line());
Some(format!(
format!(
"{}\n{}{}",
prefix,
type_offset.to_string(&context.config),
&ty
))
)
}
}
};
combine_strs_with_missing_comments(
context,
&attrs_str,
&field_str,
missing_span,
shape,
is_attributes_extendable(&attrs_str),
)
}
pub fn rewrite_static(
@ -2148,10 +2123,6 @@ impl WhereClauseOption {
}
}
fn last_line_contains_single_line_comment(s: &str) -> bool {
s.lines().last().map_or(false, |l| l.contains("//"))
}
fn rewrite_args(
context: &RewriteContext,
args: &[ast::Arg],

View File

@ -17,12 +17,12 @@ use syntax::codemap::{BytePos, Span};
use {Indent, Shape, Spanned};
use codemap::SpanUtils;
use comment::contains_comment;
use comment::{combine_strs_with_missing_comments, contains_comment};
use expr::rewrite_field;
use items::{rewrite_struct_field, rewrite_struct_field_prefix};
use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListTactic, Separator};
use rewrite::{Rewrite, RewriteContext};
use utils::{contains_skip, mk_sp};
use utils::{contains_skip, is_attributes_extendable, mk_sp};
pub trait AlignedItem {
fn skip(&self) -> bool;
@ -46,7 +46,22 @@ impl AlignedItem for ast::StructField {
}
fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
rewrite_struct_field_prefix(context, self, shape)
let attrs_str = try_opt!(self.attrs.rewrite(context, shape));
let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo, self.span.lo)
} else {
mk_sp(self.attrs.last().unwrap().span.hi, self.span.lo)
};
rewrite_struct_field_prefix(context, self).and_then(|field_str| {
combine_strs_with_missing_comments(
context,
&attrs_str,
&field_str,
missing_span,
shape,
is_attributes_extendable(&attrs_str),
)
})
}
fn rewrite_aligned_item(
@ -69,12 +84,21 @@ impl AlignedItem for ast::Field {
}
fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
let mut attrs_str = try_opt!(self.attrs.rewrite(context, shape));
if !attrs_str.is_empty() {
attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
};
let attrs_str = try_opt!(self.attrs.rewrite(context, shape));
let name = &self.ident.node.to_string();
Some(format!("{}{}", attrs_str, name))
let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo, self.span.lo)
} else {
mk_sp(self.attrs.last().unwrap().span.hi, self.span.lo)
};
combine_strs_with_missing_comments(
context,
&attrs_str,
name,
missing_span,
shape,
is_attributes_extendable(&attrs_str),
)
}
fn rewrite_aligned_item(