]> git.lizzy.rs Git - rust.git/blobdiff - src/vertical.rs
Merge commit 'c4416f20dcaec5d93077f72470e83e150fb923b1' into sync-rustfmt
[rust.git] / src / vertical.rs
index e76f44ca39b05f6ebda4464817e3715975ff5796..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 config::lists::*;
-use syntax::ast;
-use syntax::source_map::{BytePos, Span};
+use itertools::Itertools;
+use rustc_ast::ast;
+use rustc_span::{BytePos, Span};
 
-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, Separator};
-use rewrite::{Rewrite, RewriteContext};
-use shape::{Indent, Shape};
-use source_map::SpanUtils;
-use spanned::Spanned;
-use utils::{contains_skip, is_attributes_extendable, mk_sp, rewrite_ident};
+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,7 +81,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 name = rewrite_ident(context, self.ident);
         let missing_span = if self.attrs.is_empty() {
@@ -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()
@@ -141,14 +136,14 @@ pub fn rewrite_with_alignment<T: AlignedItem>(
         );
 
         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,59 +160,61 @@ 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())
-                }
-            })
-        }).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))
+            field
+                .rewrite_prefix(context, shape)
+                .map(|field_str| trimmed_last_line_width(&field_str))
+        })
+        .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.saturating_sub(field_prefix_min_width);
@@ -225,7 +222,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         field_prefix_max_width = 0;
     }
 
-    let items = itemize_list(
+    let mut items = itemize_list(
         context.snippet_provider,
         fields.iter(),
         "}",
@@ -236,7 +233,8 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         span.lo(),
         span.hi(),
         false,
-    ).collect::<Vec<_>>();
+    )
+    .collect::<Vec<_>>();
 
     let tactic = definitive_tactic(
         &items,
@@ -245,15 +243,38 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         one_line_width,
     );
 
+    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(context.config.trailing_comma())
+        .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;
@@ -261,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)
@@ -269,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;
     }