]> git.lizzy.rs Git - rust.git/blobdiff - src/vertical.rs
Merge commit 'c4416f20dcaec5d93077f72470e83e150fb923b1' into sync-rustfmt
[rust.git] / src / vertical.rs
index e56410202eb92f9a6f006e5c8575c7a7ca3593e7..a06bc995aa55e5885a918ec6a313893f530d1706 100644 (file)
@@ -1,44 +1,39 @@
-// 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 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 spanned::Spanned;
-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)
     }
@@ -47,7 +42,7 @@ fn get_span(&self) -> Span {
         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())
@@ -69,7 +64,7 @@ fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<Strin
 
     fn rewrite_aligned_item(
         &self,
-        context: &RewriteContext,
+        context: &RewriteContext<'_>,
         shape: Shape,
         prefix_max_width: usize,
     ) -> Option<String> {
@@ -77,7 +72,7 @@ fn rewrite_aligned_item(
     }
 }
 
-impl AlignedItem for ast::Field {
+impl AlignedItem for ast::ExprField {
     fn skip(&self) -> bool {
         contains_skip(&self.attrs)
     }
@@ -86,9 +81,9 @@ fn get_span(&self) -> Span {
         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 {
@@ -106,7 +101,7 @@ fn rewrite_prefix(&self, context: &RewriteContext, shape: Shape) -> Option<Strin
 
     fn rewrite_aligned_item(
         &self,
-        context: &RewriteContext,
+        context: &RewriteContext<'_>,
         shape: Shape,
         prefix_max_width: usize,
     ) -> Option<String> {
@@ -114,9 +109,9 @@ fn rewrite_aligned_item(
     }
 }
 
-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,
@@ -126,7 +121,7 @@ pub fn rewrite_with_alignment<T: AlignedItem>(
     } 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()
@@ -136,19 +131,19 @@ pub fn rewrite_with_alignment<T: AlignedItem>(
         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
@@ -165,69 +160,70 @@ pub fn rewrite_with_alignment<T: AlignedItem>(
     };
     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 =
-        rewrite_aligned_items_inner(context, init, init_span, shape.indent, one_line_width)?;
+
+    // if another group follows, we must force a separator
+    let force_separator = !rest.is_empty();
+
+    let result = rewrite_aligned_items_inner(
+        context,
+        init,
+        init_span,
+        shape.indent,
+        one_line_width,
+        force_separator,
+    )?;
     if rest.is_empty() {
         Some(result + spaces)
     } 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) {
     fields
         .iter()
         .map(|field| {
-            field.rewrite_prefix(context, shape).and_then(|field_str| {
-                if field_str.contains('\n') {
-                    None
-                } else {
-                    Some(field_str.len())
-                }
-            })
+            field
+                .rewrite_prefix(context, shape)
+                .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,
+    force_trailing_separator: bool,
 ) -> 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(),
         "}",
         ",",
@@ -237,7 +233,8 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         span.lo(),
         span.hi(),
         false,
-    ).collect::<Vec<_>>();
+    )
+    .collect::<Vec<_>>();
 
     let tactic = definitive_tactic(
         &items,
@@ -246,21 +243,38 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         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 separator_tactic = if force_trailing_separator {
+        SeparatorTactic::Always
+    } else {
+        context.config.trailing_comma()
     };
+
+    let fmt = ListFormatting::new(item_shape, context.config)
+        .tactic(tactic)
+        .trailing_separator(separator_tactic)
+        .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;
@@ -268,7 +282,6 @@ fn group_aligned_items<T: AlignedItem>(
         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)
@@ -276,13 +289,12 @@ fn group_aligned_items<T: AlignedItem>(
             .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;
     }