-// 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 syntax::ast;
-use syntax::codemap::{BytePos, Span};
+use itertools::Itertools;
+use rustc_ast::ast;
+use rustc_span::{BytePos, Span};
-use spanned::Spanned;
-use codemap::SpanUtils;
-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,
- SeparatorPlace};
-use rewrite::{Rewrite, RewriteContext};
-use shape::{Indent, Shape};
-use utils::{contains_skip, is_attributes_extendable, mk_sp};
+use crate::comment::combine_strs_with_missing_comments;
+use crate::config::lists::*;
+use crate::expr::rewrite_field;
+use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
+use crate::lists::{
+ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
+};
+use crate::rewrite::{Rewrite, RewriteContext};
+use crate::shape::{Indent, Shape};
+use crate::source_map::SpanUtils;
+use crate::spanned::Spanned;
+use crate::utils::{
+ contains_skip, is_attributes_extendable, mk_sp, rewrite_ident, trimmed_last_line_width,
+};
-pub trait AlignedItem {
+pub(crate) trait AlignedItem {
fn skip(&self) -> bool;
fn get_span(&self) -> Span;
- fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String>;
+ fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
fn rewrite_aligned_item(
&self,
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String>;
}
-impl AlignedItem for ast::StructField {
+impl AlignedItem for ast::FieldDef {
fn skip(&self) -> bool {
contains_skip(&self.attrs)
}
self.span()
}
- fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+ fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let attrs_str = 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())
};
- let attrs_extendable = context.config.attributes_on_same_line_as_field()
- && is_attributes_extendable(&attrs_str);
+ let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str);
rewrite_struct_field_prefix(context, self).and_then(|field_str| {
combine_strs_with_missing_comments(
context,
fn rewrite_aligned_item(
&self,
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String> {
}
}
-impl AlignedItem for ast::Field {
+impl AlignedItem for ast::ExprField {
fn skip(&self) -> bool {
contains_skip(&self.attrs)
}
self.span()
}
- fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+ fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
let attrs_str = self.attrs.rewrite(context, shape)?;
- let name = &self.ident.node.to_string();
+ let name = rewrite_ident(context, self.ident);
let missing_span = if self.attrs.is_empty() {
mk_sp(self.span.lo(), self.span.lo())
} else {
fn rewrite_aligned_item(
&self,
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
shape: Shape,
prefix_max_width: usize,
) -> Option<String> {
}
}
-pub fn rewrite_with_alignment<T: AlignedItem>(
+pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
fields: &[T],
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
shape: Shape,
span: Span,
one_line_width: usize,
} else {
("", fields.len() - 1)
};
- let init = &fields[0..group_index + 1];
+ let init = &fields[0..=group_index];
let rest = &fields[group_index + 1..];
let init_last_pos = if rest.is_empty() {
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, ","),
+ context.snippet_provider.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());
+ if snippet.trim_start().starts_with("//") {
+ let offset = snippet.lines().next().map_or(0, str::len);
// 2 = "," + "\n"
init_hi + BytePos(offset as u32 + 2)
- } else if snippet.trim_left().starts_with("/*") {
+ } else if snippet.trim_start().starts_with("/*") {
let comment_lines = snippet
.lines()
- .position(|line| line.trim_right().ends_with("*/"))
+ .position(|line| line.trim_end().ends_with("*/"))
.unwrap_or(0);
let offset = snippet
} else {
let rest_span = mk_sp(init_last_pos, span.hi());
let rest_str = 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,
- )
+ Some(format!(
+ "{}{}\n{}{}",
+ result,
+ spaces,
+ &shape.indent.to_string(context.config),
+ &rest_str
+ ))
}
}
fn struct_field_prefix_max_min_width<T: AlignedItem>(
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
fields: &[T],
shape: Shape,
) -> (usize, usize) {
.map(|field| {
field
.rewrite_prefix(context, shape)
- .and_then(|field_str| if field_str.contains('\n') {
- None
- } else {
- Some(field_str.len())
- })
+ .map(|field_str| trimmed_last_line_width(&field_str))
})
- .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,
+ .fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| {
+ (cmp::max(max_len, len), cmp::min(min_len, len))
})
.unwrap_or((0, 0))
}
fn rewrite_aligned_items_inner<T: AlignedItem>(
- context: &RewriteContext,
+ 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 = Shape::indented(item_indent, context.config).sub_width(1)?;
+ let item_shape = Shape::indented(offset, context.config).sub_width(1)?;
let (mut field_prefix_max_width, field_prefix_min_width) =
struct_field_prefix_max_min_width(context, fields, item_shape);
- let max_diff = field_prefix_max_width
- .checked_sub(field_prefix_min_width)
- .unwrap_or(0);
+ let max_diff = field_prefix_max_width.saturating_sub(field_prefix_min_width);
if max_diff > context.config.struct_field_align_threshold() {
field_prefix_max_width = 0;
}
- let items = itemize_list(
- context.codemap,
+ let mut items = itemize_list(
+ context.snippet_provider,
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(),
false,
- ).collect::<Vec<_>>();
+ )
+ .collect::<Vec<_>>();
let tactic = definitive_tactic(
&items,
one_line_width,
);
- let fmt = ListFormatting {
- tactic: tactic,
- separator: ",",
- trailing_separator: context.config.trailing_comma(),
- separator_place: SeparatorPlace::Back,
- shape: item_shape,
- ends_with_newline: true,
- preserve_newline: true,
- config: context.config,
- };
+ if tactic == DefinitiveListTactic::Horizontal {
+ // since the items fits on a line, there is no need to align them
+ let do_rewrite =
+ |field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) };
+ fields
+ .iter()
+ .zip(items.iter_mut())
+ .for_each(|(field, list_item): (&T, &mut ListItem)| {
+ if list_item.item.is_some() {
+ list_item.item = do_rewrite(field);
+ }
+ });
+ }
+
+ let fmt = ListFormatting::new(item_shape, context.config)
+ .tactic(tactic)
+ .trailing_separator(context.config.trailing_comma())
+ .preserve_newline(true);
write_list(&items, &fmt)
}
+/// Returns the index in `fields` up to which a field belongs to the current group.
+/// The returned string is the group separator to use when rewriting the fields.
+/// Groups are defined by blank lines.
fn group_aligned_items<T: AlignedItem>(
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
fields: &[T],
) -> (&'static str, usize) {
let mut index = 0;
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)
.skip(1)
.collect::<Vec<_>>()
.join("\n");
- let spacings = if snippet.lines().rev().skip(1).any(|l| l.trim().is_empty()) {
- "\n"
- } else {
- ""
- };
- if contains_comment(&snippet) || snippet.lines().count() > 1 {
- return (spacings, index);
+ let has_blank_line = snippet
+ .lines()
+ .dropping_back(1)
+ .any(|l| l.trim().is_empty());
+ if has_blank_line {
+ return ("\n", index);
}
index += 1;
}