Add struct_field_align_threshold for vertical alignment
This commit is contained in:
parent
bd991851c8
commit
34b4a9d3c4
@ -1209,6 +1209,33 @@ fn lorem<T: Eq>(t: T) {
|
||||
|
||||
See also: [`space_before_bound`](#space_before_bound).
|
||||
|
||||
## `struct_field_align_threshold`
|
||||
|
||||
The maximum diff of width between struct fields to be aligned with each other.
|
||||
|
||||
- **Default value** : 0
|
||||
- **Possible values**: any positive integer
|
||||
|
||||
#### `0`:
|
||||
|
||||
```rust
|
||||
struct Foo {
|
||||
x: u32,
|
||||
yy: u32,
|
||||
zzz: u32,
|
||||
}
|
||||
```
|
||||
|
||||
#### `20`:
|
||||
|
||||
```rust
|
||||
struct Foo {
|
||||
x: u32,
|
||||
yy: u32,
|
||||
zzz: u32,
|
||||
}
|
||||
```
|
||||
|
||||
## `space_after_struct_lit_field_colon`
|
||||
|
||||
Leave a space after the colon in a struct literal field
|
||||
|
@ -598,7 +598,9 @@ create_config! {
|
||||
"What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
|
||||
condense_wildcard_suffixes: bool, false, "Replace strings of _ wildcards by a single .. in \
|
||||
tuple patterns";
|
||||
combine_control_expr: bool, true, "Combine control expressions with funciton calls."
|
||||
combine_control_expr: bool, true, "Combine control expressions with funciton calls.";
|
||||
struct_field_align_threshold: usize, 0, "Align struct fields if their diffs fits within \
|
||||
threshold."
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
132
src/expr.rs
132
src/expr.rs
@ -30,6 +30,7 @@ use items::{span_lo_for_arg, span_hi_for_arg};
|
||||
use chains::rewrite_chain;
|
||||
use macros::{rewrite_macro, MacroPosition};
|
||||
use patterns::{TuplePatField, can_be_overflowed_pat};
|
||||
use vertical::rewrite_with_alignment;
|
||||
|
||||
use syntax::{ast, ptr};
|
||||
use syntax::codemap::{CodeMap, Span, BytePos};
|
||||
@ -2528,6 +2529,14 @@ fn rewrite_index(
|
||||
))
|
||||
}
|
||||
|
||||
fn struct_lit_can_be_aligned(fields: &[ast::Field], base: &Option<&ast::Expr>) -> bool {
|
||||
if base.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
fields.iter().all(|field| !field.is_shorthand)
|
||||
}
|
||||
|
||||
fn rewrite_struct_lit<'a>(
|
||||
context: &RewriteContext,
|
||||
path: &ast::Path,
|
||||
@ -2557,50 +2566,71 @@ fn rewrite_struct_lit<'a>(
|
||||
return Some(format!("{} {{}}", path_str));
|
||||
}
|
||||
|
||||
let field_iter = fields
|
||||
.into_iter()
|
||||
.map(StructLitField::Regular)
|
||||
.chain(base.into_iter().map(StructLitField::Base));
|
||||
|
||||
// Foo { a: Foo } - indent is +3, width is -5.
|
||||
let (h_shape, v_shape) = try_opt!(struct_lit_shape(shape, context, path_str.len() + 3, 2));
|
||||
|
||||
let span_lo = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => field.span.lo,
|
||||
StructLitField::Base(expr) => {
|
||||
let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
|
||||
let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
|
||||
let pos = snippet.find_uncommented("..").unwrap();
|
||||
last_field_hi + BytePos(pos as u32)
|
||||
}
|
||||
};
|
||||
let span_hi = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => field.span.hi,
|
||||
StructLitField::Base(expr) => expr.span.hi,
|
||||
};
|
||||
let rewrite = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => {
|
||||
// The 1 taken from the v_budget is for the comma.
|
||||
rewrite_field(context, field, try_opt!(v_shape.sub_width(1)))
|
||||
}
|
||||
StructLitField::Base(expr) => {
|
||||
// 2 = ..
|
||||
expr.rewrite(context, try_opt!(v_shape.shrink_left(2)))
|
||||
.map(|s| format!("..{}", s))
|
||||
}
|
||||
let one_line_width = h_shape.map_or(0, |shape| shape.width);
|
||||
let body_lo = context.codemap.span_after(span, "{");
|
||||
let fields_str = if struct_lit_can_be_aligned(fields, &base) &&
|
||||
context.config.struct_field_align_threshold() > 0
|
||||
{
|
||||
try_opt!(rewrite_with_alignment(
|
||||
fields,
|
||||
context,
|
||||
shape,
|
||||
mk_sp(body_lo, span.hi),
|
||||
one_line_width,
|
||||
))
|
||||
} else {
|
||||
let field_iter = fields
|
||||
.into_iter()
|
||||
.map(StructLitField::Regular)
|
||||
.chain(base.into_iter().map(StructLitField::Base));
|
||||
|
||||
let span_lo = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => field.span().lo,
|
||||
StructLitField::Base(expr) => {
|
||||
let last_field_hi = fields.last().map_or(span.lo, |field| field.span.hi);
|
||||
let snippet = context.snippet(mk_sp(last_field_hi, expr.span.lo));
|
||||
let pos = snippet.find_uncommented("..").unwrap();
|
||||
last_field_hi + BytePos(pos as u32)
|
||||
}
|
||||
};
|
||||
let span_hi = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => field.span().hi,
|
||||
StructLitField::Base(expr) => expr.span.hi,
|
||||
};
|
||||
let rewrite = |item: &StructLitField| match *item {
|
||||
StructLitField::Regular(field) => {
|
||||
// The 1 taken from the v_budget is for the comma.
|
||||
rewrite_field(context, field, try_opt!(v_shape.sub_width(1)), 0)
|
||||
}
|
||||
StructLitField::Base(expr) => {
|
||||
// 2 = ..
|
||||
expr.rewrite(context, try_opt!(v_shape.shrink_left(2)))
|
||||
.map(|s| format!("..{}", s))
|
||||
}
|
||||
};
|
||||
|
||||
let items = itemize_list(
|
||||
context.codemap,
|
||||
field_iter,
|
||||
"}",
|
||||
span_lo,
|
||||
span_hi,
|
||||
rewrite,
|
||||
body_lo,
|
||||
span.hi,
|
||||
);
|
||||
let item_vec = items.collect::<Vec<_>>();
|
||||
|
||||
let tactic = struct_lit_tactic(h_shape, context, &item_vec);
|
||||
let nested_shape = shape_for_tactic(tactic, h_shape, v_shape);
|
||||
let fmt = struct_lit_formatting(nested_shape, tactic, context, base.is_some());
|
||||
|
||||
try_opt!(write_list(&item_vec, &fmt))
|
||||
};
|
||||
|
||||
let items = itemize_list(
|
||||
context.codemap,
|
||||
field_iter,
|
||||
"}",
|
||||
span_lo,
|
||||
span_hi,
|
||||
rewrite,
|
||||
context.codemap.span_after(span, "{"),
|
||||
span.hi,
|
||||
);
|
||||
let item_vec = items.collect::<Vec<_>>();
|
||||
let fields_str = wrap_struct_field(context, &fields_str, shape, v_shape, one_line_width);
|
||||
Some(format!("{} {{{}}}", path_str, fields_str))
|
||||
|
||||
@ -2639,18 +2669,32 @@ pub fn struct_lit_field_separator(config: &Config) -> &str {
|
||||
)
|
||||
}
|
||||
|
||||
fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
|
||||
pub fn rewrite_field(
|
||||
context: &RewriteContext,
|
||||
field: &ast::Field,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String> {
|
||||
if contains_skip(&field.attrs) {
|
||||
return wrap_str(
|
||||
context.snippet(field.span()),
|
||||
context.config.max_width(),
|
||||
shape,
|
||||
);
|
||||
}
|
||||
let name = &field.ident.node.to_string();
|
||||
if field.is_shorthand {
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
let separator = struct_lit_field_separator(context.config);
|
||||
let mut separator = String::from(struct_lit_field_separator(context.config));
|
||||
for _ in 0..prefix_max_width.checked_sub(name.len()).unwrap_or(0) {
|
||||
separator.push(' ');
|
||||
}
|
||||
let overhead = name.len() + separator.len();
|
||||
let mut expr_shape = try_opt!(shape.sub_width(overhead));
|
||||
expr_shape.offset += overhead;
|
||||
let expr_shape = try_opt!(shape.offset_left(overhead));
|
||||
let expr = field.expr.rewrite(context, expr_shape);
|
||||
|
||||
let mut attrs_str = try_opt!((*field.attrs).rewrite(context, shape));
|
||||
let mut attrs_str = try_opt!(field.attrs.rewrite(context, shape));
|
||||
if !attrs_str.is_empty() {
|
||||
attrs_str.push_str(&format!("\n{}", shape.indent.to_string(context.config)));
|
||||
};
|
||||
|
256
src/items.rs
256
src/items.rs
@ -23,6 +23,7 @@ use visitor::FmtVisitor;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use config::{Config, IndentStyle, Density, ReturnIndent, BraceStyle, Style};
|
||||
use types::join_bounds;
|
||||
use vertical::rewrite_with_alignment;
|
||||
|
||||
use syntax::{ast, abi, ptr, symbol};
|
||||
use syntax::codemap::{Span, BytePos};
|
||||
@ -1100,49 +1101,14 @@ fn format_struct_struct(
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
let item_indent = offset.block_indent(context.config);
|
||||
// 1 = ","
|
||||
let item_budget = try_opt!(
|
||||
context
|
||||
.config
|
||||
.max_width()
|
||||
.checked_sub(item_indent.width() + 1)
|
||||
);
|
||||
let items_str = try_opt!(rewrite_with_alignment(
|
||||
fields,
|
||||
context,
|
||||
Shape::indented(offset, context.config),
|
||||
mk_sp(body_lo, span.hi),
|
||||
one_line_width.unwrap_or(0),
|
||||
));
|
||||
|
||||
let items = itemize_list(
|
||||
context.codemap,
|
||||
fields.iter(),
|
||||
"}",
|
||||
|field| {
|
||||
// Include attributes and doc comments, if present
|
||||
if !field.attrs.is_empty() {
|
||||
field.attrs[0].span.lo
|
||||
} else {
|
||||
field.span.lo
|
||||
}
|
||||
},
|
||||
|field| field.ty.span.hi,
|
||||
|field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
|
||||
context.codemap.span_after(span, "{"),
|
||||
span.hi,
|
||||
).collect::<Vec<_>>();
|
||||
// 1 = ,
|
||||
let budget = context.config.max_width() - offset.width() + context.config.tab_spaces() - 1;
|
||||
|
||||
let tactic = match one_line_width {
|
||||
Some(w) => definitive_tactic(&items, ListTactic::LimitedHorizontalVertical(w), budget),
|
||||
None => DefinitiveListTactic::Vertical,
|
||||
};
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: tactic,
|
||||
separator: ",",
|
||||
trailing_separator: context.config.trailing_comma(),
|
||||
shape: Shape::legacy(budget, item_indent),
|
||||
ends_with_newline: true,
|
||||
config: context.config,
|
||||
};
|
||||
let items_str = try_opt!(write_list(&items, &fmt));
|
||||
if one_line_width.is_some() && !items_str.contains('\n') {
|
||||
Some(format!("{} {} }}", result, items_str))
|
||||
} else {
|
||||
@ -1257,7 +1223,9 @@ fn format_tuple_struct(
|
||||
}
|
||||
},
|
||||
|field| field.ty.span.hi,
|
||||
|field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
|
||||
|field| {
|
||||
rewrite_struct_field(context, field, Shape::legacy(item_budget, item_indent), 0)
|
||||
},
|
||||
context.codemap.span_after(span, "("),
|
||||
span.hi,
|
||||
);
|
||||
@ -1430,96 +1398,124 @@ fn rewrite_missing_comment_on_field(
|
||||
}
|
||||
}
|
||||
|
||||
impl Rewrite for ast::StructField {
|
||||
fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
|
||||
if contains_skip(&self.attrs) {
|
||||
let span = context.snippet(mk_sp(self.attrs[0].span.lo, self.span.hi));
|
||||
return wrap_str(span, context.config.max_width(), shape);
|
||||
}
|
||||
|
||||
let name = self.ident;
|
||||
let vis = format_visibility(&self.vis);
|
||||
let mut attr_str = try_opt!(
|
||||
self.attrs
|
||||
.rewrite(context, Shape::indented(shape.indent, context.config))
|
||||
);
|
||||
// Try format missing comments after attributes
|
||||
let missing_comment = if !self.attrs.is_empty() {
|
||||
rewrite_missing_comment_on_field(
|
||||
context,
|
||||
shape,
|
||||
self.attrs[self.attrs.len() - 1].span.hi,
|
||||
self.span.lo,
|
||||
&mut attr_str,
|
||||
).unwrap_or(String::new())
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let type_annotation_spacing = type_annotation_spacing(context.config);
|
||||
let mut result = match name {
|
||||
Some(name) => {
|
||||
format!(
|
||||
"{}{}{}{}{}:",
|
||||
attr_str,
|
||||
missing_comment,
|
||||
vis,
|
||||
name,
|
||||
type_annotation_spacing.0
|
||||
)
|
||||
}
|
||||
None => format!("{}{}{}", attr_str, missing_comment, vis),
|
||||
};
|
||||
|
||||
let type_offset = shape.indent.block_indent(context.config);
|
||||
let rewrite_type_in_next_line = || {
|
||||
self.ty
|
||||
.rewrite(context, Shape::indented(type_offset, context.config))
|
||||
};
|
||||
|
||||
let last_line_width = last_line_width(&result) + type_annotation_spacing.1.len();
|
||||
let budget = try_opt!(shape.width.checked_sub(last_line_width));
|
||||
let ty_rewritten = self.ty.rewrite(
|
||||
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::legacy(budget, shape.indent + last_line_width),
|
||||
);
|
||||
match ty_rewritten {
|
||||
Some(ref ty) if ty.contains('\n') => {
|
||||
let new_ty = rewrite_type_in_next_line();
|
||||
match new_ty {
|
||||
Some(ref new_ty)
|
||||
if !new_ty.contains('\n') &&
|
||||
new_ty.len() + type_offset.width() <= context.config.max_width() => {
|
||||
Some(format!(
|
||||
"{}\n{}{}",
|
||||
result,
|
||||
type_offset.to_string(&context.config),
|
||||
&new_ty
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
if name.is_some() {
|
||||
result.push_str(type_annotation_spacing.1);
|
||||
}
|
||||
Some(result + &ty)
|
||||
}
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
fn rewrite_struct_field_type(
|
||||
context: &RewriteContext,
|
||||
last_line_width: usize,
|
||||
field: &ast::StructField,
|
||||
spacing: &str,
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
let ty_shape = try_opt!(shape.offset_left(last_line_width + spacing.len()));
|
||||
field
|
||||
.ty
|
||||
.rewrite(context, ty_shape)
|
||||
.map(|ty| format!("{}{}", spacing, ty))
|
||||
}
|
||||
|
||||
|
||||
pub fn rewrite_struct_field(
|
||||
context: &RewriteContext,
|
||||
field: &ast::StructField,
|
||||
shape: Shape,
|
||||
lhs_max_width: usize,
|
||||
) -> Option<String> {
|
||||
if contains_skip(&field.attrs) {
|
||||
let span = context.snippet(mk_sp(field.attrs[0].span.lo, field.span.hi));
|
||||
return wrap_str(span, context.config.max_width(), shape);
|
||||
}
|
||||
|
||||
let type_annotation_spacing = type_annotation_spacing(context.config);
|
||||
let prefix = try_opt!(rewrite_struct_field_prefix(context, field, shape));
|
||||
|
||||
// Try to put everything on a single line.
|
||||
let last_line_width = last_line_width(&prefix);
|
||||
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);
|
||||
for _ in 0..lhs_offset {
|
||||
spacing.push(' ');
|
||||
}
|
||||
let ty_rewritten = rewrite_struct_field_type(context, last_line_width, field, &spacing, shape);
|
||||
if let Some(ref ty) = ty_rewritten {
|
||||
if !ty.contains('\n') {
|
||||
return Some(prefix + &ty);
|
||||
}
|
||||
}
|
||||
|
||||
// We must use multiline.
|
||||
let type_offset = shape.indent.block_indent(context.config);
|
||||
let rewrite_type_in_next_line = || {
|
||||
field
|
||||
.ty
|
||||
.rewrite(context, Shape::indented(type_offset, context.config))
|
||||
};
|
||||
|
||||
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!(
|
||||
"{}\n{}{}",
|
||||
prefix,
|
||||
type_offset.to_string(&context.config),
|
||||
&new_ty
|
||||
))
|
||||
}
|
||||
_ => Some(prefix + &ty),
|
||||
}
|
||||
Some(ty) => {
|
||||
if name.is_some() {
|
||||
result.push_str(type_annotation_spacing.1);
|
||||
}
|
||||
Some(result + &ty)
|
||||
}
|
||||
None => {
|
||||
let ty = try_opt!(rewrite_type_in_next_line());
|
||||
Some(format!(
|
||||
"{}\n{}{}",
|
||||
result,
|
||||
type_offset.to_string(&context.config),
|
||||
&ty
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let ty = try_opt!(rewrite_type_in_next_line());
|
||||
Some(format!(
|
||||
"{}\n{}{}",
|
||||
prefix,
|
||||
type_offset.to_string(&context.config),
|
||||
&ty
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ mod chains;
|
||||
mod macros;
|
||||
mod patterns;
|
||||
mod summary;
|
||||
mod vertical;
|
||||
|
||||
const MIN_STRING: usize = 10;
|
||||
// When we get scoped annotations, we should have rustfmt::skip.
|
||||
|
271
src/vertical.rs
Normal file
271
src/vertical.rs
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Format with vertical alignment.
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use {Indent, Shape, Spanned};
|
||||
use codemap::SpanUtils;
|
||||
use comment::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};
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use utils::{contains_skip, mk_sp};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{Span, BytePos};
|
||||
|
||||
pub trait AlignedItem {
|
||||
fn skip(&self) -> bool;
|
||||
fn get_span(&self) -> Span;
|
||||
fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String>;
|
||||
fn rewrite_aligned_item(
|
||||
&self,
|
||||
context: &RewriteContext,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String>;
|
||||
}
|
||||
|
||||
impl AlignedItem for ast::StructField {
|
||||
fn skip(&self) -> bool {
|
||||
contains_skip(&self.attrs)
|
||||
}
|
||||
|
||||
fn get_span(&self) -> Span {
|
||||
self.span()
|
||||
}
|
||||
|
||||
fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
|
||||
rewrite_struct_field_prefix(context, self, shape)
|
||||
}
|
||||
|
||||
fn rewrite_aligned_item(
|
||||
&self,
|
||||
context: &RewriteContext,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String> {
|
||||
rewrite_struct_field(context, self, shape, prefix_max_width)
|
||||
}
|
||||
}
|
||||
|
||||
impl AlignedItem for ast::Field {
|
||||
fn skip(&self) -> bool {
|
||||
contains_skip(&self.attrs)
|
||||
}
|
||||
|
||||
fn get_span(&self) -> Span {
|
||||
self.span()
|
||||
}
|
||||
|
||||
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 name = &self.ident.node.to_string();
|
||||
Some(format!("{}{}", attrs_str, name))
|
||||
}
|
||||
|
||||
fn rewrite_aligned_item(
|
||||
&self,
|
||||
context: &RewriteContext,
|
||||
shape: Shape,
|
||||
prefix_max_width: usize,
|
||||
) -> Option<String> {
|
||||
rewrite_field(context, self, shape, prefix_max_width)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewrite_with_alignment<T: AlignedItem>(
|
||||
fields: &[T],
|
||||
context: &RewriteContext,
|
||||
shape: Shape,
|
||||
span: Span,
|
||||
one_line_width: usize,
|
||||
) -> Option<String> {
|
||||
let (spaces, group_index) = if context.config.struct_field_align_threshold() > 0 {
|
||||
group_aligned_items(context, fields)
|
||||
} else {
|
||||
("", fields.len() - 1)
|
||||
};
|
||||
let init = &fields[0..group_index + 1];
|
||||
let rest = &fields[group_index + 1..];
|
||||
let init_last_pos = if rest.is_empty() {
|
||||
span.hi
|
||||
} else {
|
||||
// Decide whether the missing comments should stick to init or rest.
|
||||
let init_hi = init[init.len() - 1].get_span().hi;
|
||||
let rest_lo = rest[0].get_span().lo;
|
||||
let missing_span = mk_sp(init_hi, rest_lo);
|
||||
let missing_span = mk_sp(
|
||||
context.codemap.span_after(missing_span, ","),
|
||||
missing_span.hi,
|
||||
);
|
||||
|
||||
let snippet = context.snippet(missing_span);
|
||||
if snippet.trim_left().starts_with("//") {
|
||||
let offset = snippet.lines().next().map_or(0, |l| l.len());
|
||||
// 2 = "," + "\n"
|
||||
init_hi + BytePos(offset as u32 + 2)
|
||||
} else if snippet.trim_left().starts_with("/*") {
|
||||
let comment_lines = snippet
|
||||
.lines()
|
||||
.position(|line| line.trim_right().ends_with("*/"))
|
||||
.unwrap_or(0);
|
||||
|
||||
let offset = snippet
|
||||
.lines()
|
||||
.take(comment_lines + 1)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.len();
|
||||
|
||||
init_hi + BytePos(offset as u32 + 2)
|
||||
} else {
|
||||
missing_span.lo
|
||||
}
|
||||
};
|
||||
let init_span = mk_sp(span.lo, init_last_pos);
|
||||
let one_line_width = if rest.is_empty() { one_line_width } else { 0 };
|
||||
let result = try_opt!(rewrite_aligned_items_inner(
|
||||
context,
|
||||
init,
|
||||
init_span,
|
||||
shape.indent,
|
||||
one_line_width,
|
||||
));
|
||||
if rest.is_empty() {
|
||||
Some(result + spaces)
|
||||
} else {
|
||||
let rest_span = mk_sp(init_last_pos, span.hi);
|
||||
let rest_str = try_opt!(rewrite_with_alignment(
|
||||
rest,
|
||||
context,
|
||||
shape,
|
||||
rest_span,
|
||||
one_line_width,
|
||||
));
|
||||
Some(
|
||||
result + spaces + "\n" +
|
||||
&shape
|
||||
.indent
|
||||
.block_indent(context.config)
|
||||
.to_string(context.config) + &rest_str,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_field_preix_max_min_width<T: AlignedItem>(
|
||||
context: &RewriteContext,
|
||||
fields: &[T],
|
||||
shape: Shape,
|
||||
) -> (usize, usize) {
|
||||
fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
field.rewrite_prefix(context, shape).and_then(
|
||||
|field_str| if field_str.contains('\n') {
|
||||
None
|
||||
} else {
|
||||
Some(field_str.len())
|
||||
},
|
||||
)
|
||||
})
|
||||
.fold(Some((0, ::std::usize::MAX)), |acc, len| match (acc, len) {
|
||||
(Some((max_len, min_len)), Some(len)) => {
|
||||
Some((cmp::max(max_len, len), cmp::min(min_len, len)))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or((0, 0))
|
||||
}
|
||||
|
||||
fn rewrite_aligned_items_inner<T: AlignedItem>(
|
||||
context: &RewriteContext,
|
||||
fields: &[T],
|
||||
span: Span,
|
||||
offset: Indent,
|
||||
one_line_width: usize,
|
||||
) -> Option<String> {
|
||||
let item_indent = offset.block_indent(context.config);
|
||||
// 1 = ","
|
||||
let item_shape = try_opt!(Shape::indented(item_indent, context.config).sub_width(1));
|
||||
let (mut field_prefix_max_width, field_prefix_min_width) =
|
||||
struct_field_preix_max_min_width(context, fields, item_shape);
|
||||
let max_diff = field_prefix_max_width
|
||||
.checked_sub(field_prefix_min_width)
|
||||
.unwrap_or(0);
|
||||
if max_diff > context.config.struct_field_align_threshold() {
|
||||
field_prefix_max_width = 0;
|
||||
}
|
||||
|
||||
let items = itemize_list(
|
||||
context.codemap,
|
||||
fields.iter(),
|
||||
"}",
|
||||
|field| field.get_span().lo,
|
||||
|field| field.get_span().hi,
|
||||
|field| field.rewrite_aligned_item(context, item_shape, field_prefix_max_width),
|
||||
span.lo,
|
||||
span.hi,
|
||||
).collect::<Vec<_>>();
|
||||
|
||||
let tactic = definitive_tactic(&items, ListTactic::HorizontalVertical, one_line_width);
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: tactic,
|
||||
separator: ",",
|
||||
trailing_separator: context.config.trailing_comma(),
|
||||
shape: item_shape,
|
||||
ends_with_newline: true,
|
||||
config: context.config,
|
||||
};
|
||||
write_list(&items, &fmt)
|
||||
}
|
||||
|
||||
fn group_aligned_items<T: AlignedItem>(
|
||||
context: &RewriteContext,
|
||||
fields: &[T],
|
||||
) -> (&'static str, usize) {
|
||||
let mut index = 0;
|
||||
for i in 0..fields.len() - 1 {
|
||||
if fields[i].skip() {
|
||||
return ("", index);
|
||||
}
|
||||
// See if there are comments or empty lines between fields.
|
||||
let span = mk_sp(fields[i].get_span().hi, fields[i + 1].get_span().lo);
|
||||
let snippet = context
|
||||
.snippet(span)
|
||||
.lines()
|
||||
.skip(1)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let spacings = if snippet
|
||||
.lines()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.find(|l| l.trim().is_empty())
|
||||
.is_some()
|
||||
{
|
||||
"\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if contains_comment(&snippet) || snippet.lines().count() > 1 {
|
||||
return (spacings, index);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
("", index)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user