-// Copyright 2015 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 list-like expressions and items.
use std::cmp;
use std::iter::Peekable;
-use config::lists::*;
use syntax::source_map::BytePos;
-use comment::{find_comment_end, rewrite_comment, FindUncommented};
-use config::{Config, IndentStyle};
-use rewrite::RewriteContext;
-use shape::{Indent, Shape};
-use utils::{count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline};
-use visitor::SnippetProvider;
+use crate::comment::{find_comment_end, rewrite_comment, FindUncommented};
+use crate::config::lists::*;
+use crate::config::{Config, IndentStyle};
+use crate::rewrite::RewriteContext;
+use crate::shape::{Indent, Shape};
+use crate::utils::{count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline};
+use crate::visitor::SnippetProvider;
-pub struct ListFormatting<'a> {
+pub(crate) struct ListFormatting<'a> {
tactic: DefinitiveListTactic,
separator: &'a str,
trailing_separator: SeparatorTactic,
separator_place: SeparatorPlace,
shape: Shape,
- // Non-expressions, e.g. items, will have a new line at the end of the list.
+ // Non-expressions, e.g., items, will have a new line at the end of the list.
// Important for comment styles.
ends_with_newline: bool,
// Remove newlines between list elements for expressions.
preserve_newline: bool,
// Nested import lists get some special handling for the "Mixed" list type
nested: bool,
+ // Whether comments should be visually aligned.
+ align_comments: bool,
config: &'a Config,
}
impl<'a> ListFormatting<'a> {
- pub fn new(shape: Shape, config: &'a Config) -> Self {
+ pub(crate) fn new(shape: Shape, config: &'a Config) -> Self {
ListFormatting {
tactic: DefinitiveListTactic::Vertical,
separator: ",",
ends_with_newline: true,
preserve_newline: false,
nested: false,
- config: config,
+ align_comments: true,
+ config,
}
}
- pub fn tactic(mut self, tactic: DefinitiveListTactic) -> Self {
+ pub(crate) fn tactic(mut self, tactic: DefinitiveListTactic) -> Self {
self.tactic = tactic;
self
}
- pub fn separator(mut self, separator: &'a str) -> Self {
+ pub(crate) fn separator(mut self, separator: &'a str) -> Self {
self.separator = separator;
self
}
- pub fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self {
+ pub(crate) fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self {
self.trailing_separator = trailing_separator;
self
}
- pub fn separator_place(mut self, separator_place: SeparatorPlace) -> Self {
+ pub(crate) fn separator_place(mut self, separator_place: SeparatorPlace) -> Self {
self.separator_place = separator_place;
self
}
- pub fn ends_with_newline(mut self, ends_with_newline: bool) -> Self {
+ pub(crate) fn ends_with_newline(mut self, ends_with_newline: bool) -> Self {
self.ends_with_newline = ends_with_newline;
self
}
- pub fn preserve_newline(mut self, preserve_newline: bool) -> Self {
+ pub(crate) fn preserve_newline(mut self, preserve_newline: bool) -> Self {
self.preserve_newline = preserve_newline;
self
}
- pub fn nested(mut self, nested: bool) -> Self {
+ pub(crate) fn nested(mut self, nested: bool) -> Self {
self.nested = nested;
self
}
- pub fn needs_trailing_separator(&self) -> bool {
+ pub(crate) fn align_comments(mut self, align_comments: bool) -> Self {
+ self.align_comments = align_comments;
+ self
+ }
+
+ pub(crate) fn needs_trailing_separator(&self) -> bool {
match self.trailing_separator {
// We always put separator in front.
SeparatorTactic::Always => true,
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-pub enum ListItemCommentStyle {
+pub(crate) enum ListItemCommentStyle {
// Try to keep the comment on the same line with the item.
SameLine,
// Put the comment on the previous or the next line of the item.
}
#[derive(Debug, Clone)]
-pub struct ListItem {
+pub(crate) struct ListItem {
// None for comments mean that they are not present.
- pub pre_comment: Option<String>,
- pub pre_comment_style: ListItemCommentStyle,
+ pub(crate) pre_comment: Option<String>,
+ pub(crate) pre_comment_style: ListItemCommentStyle,
// Item should include attributes and doc comments. None indicates a failed
// rewrite.
- pub item: Option<String>,
- pub post_comment: Option<String>,
+ pub(crate) item: Option<String>,
+ pub(crate) post_comment: Option<String>,
// Whether there is extra whitespace before this item.
- pub new_lines: bool,
+ pub(crate) new_lines: bool,
}
impl ListItem {
- pub fn empty() -> ListItem {
+ pub(crate) fn empty() -> ListItem {
ListItem {
pre_comment: None,
pre_comment_style: ListItemCommentStyle::None,
}
}
- pub fn inner_as_ref(&self) -> &str {
+ pub(crate) fn inner_as_ref(&self) -> &str {
self.item.as_ref().map_or("", |s| s)
}
- pub fn is_different_group(&self) -> bool {
- self.inner_as_ref().contains('\n') || self.pre_comment.is_some() || self
- .post_comment
- .as_ref()
- .map_or(false, |s| s.contains('\n'))
+ pub(crate) fn is_different_group(&self) -> bool {
+ self.inner_as_ref().contains('\n')
+ || self.pre_comment.is_some()
+ || self
+ .post_comment
+ .as_ref()
+ .map_or(false, |s| s.contains('\n'))
}
- pub fn is_multiline(&self) -> bool {
+ pub(crate) fn is_multiline(&self) -> bool {
self.inner_as_ref().contains('\n')
|| self
.pre_comment
.map_or(false, |s| s.contains('\n'))
}
- pub fn has_single_line_comment(&self) -> bool {
+ pub(crate) fn has_single_line_comment(&self) -> bool {
self.pre_comment
.as_ref()
- .map_or(false, |comment| comment.trim_left().starts_with("//"))
+ .map_or(false, |comment| comment.trim_start().starts_with("//"))
|| self
.post_comment
.as_ref()
- .map_or(false, |comment| comment.trim_left().starts_with("//"))
+ .map_or(false, |comment| comment.trim_start().starts_with("//"))
}
- pub fn has_comment(&self) -> bool {
+ pub(crate) fn has_comment(&self) -> bool {
self.pre_comment.is_some() || self.post_comment.is_some()
}
- pub fn from_str<S: Into<String>>(s: S) -> ListItem {
+ pub(crate) fn from_str<S: Into<String>>(s: S) -> ListItem {
ListItem {
pre_comment: None,
pre_comment_style: ListItemCommentStyle::None,
}
}
- // true if the item causes something to be written.
+ // Returns `true` if the item causes something to be written.
fn is_substantial(&self) -> bool {
fn empty(s: &Option<String>) -> bool {
match *s {
/// The type of separator for lists.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
-pub enum Separator {
+pub(crate) enum Separator {
Comma,
VerticalBar,
}
impl Separator {
- pub fn len(&self) -> usize {
- match *self {
+ pub(crate) fn len(self) -> usize {
+ match self {
// 2 = `, `
Separator::Comma => 2,
// 3 = ` | `
}
}
-pub fn definitive_tactic<I, T>(
+pub(crate) fn definitive_tactic<I, T>(
items: I,
tactic: ListTactic,
sep: Separator,
}
// Format a list of commented items into a string.
-pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
+pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
where
I: IntoIterator<Item = T> + Clone,
T: AsRef<ListItem>,
result.push('\n');
result.push_str(indent_str);
// This is the width of the item (without comments).
- line_len = item.item.as_ref().map_or(0, |str| str.len());
+ line_len = item.item.as_ref().map_or(0, String::len);
}
} else {
result.push(' ');
|| comment.trim().len() > width;
rewrite_comment(
- comment.trim_left(),
+ comment.trim_start(),
block_style,
comment_shape,
formatting.config,
let mut formatted_comment = rewrite_post_comment(&mut item_max_width)?;
if !starts_with_newline(comment) {
- let mut comment_alignment =
- post_comment_alignment(item_max_width, inner_item.len());
- if first_line_width(&formatted_comment)
- + last_line_width(&result)
- + comment_alignment
- + 1
- > formatting.config.max_width()
- {
- item_max_width = None;
- formatted_comment = rewrite_post_comment(&mut item_max_width)?;
- comment_alignment = post_comment_alignment(item_max_width, inner_item.len());
- }
- for _ in 0..(comment_alignment + 1) {
- result.push(' ');
+ if formatting.align_comments {
+ let mut comment_alignment =
+ post_comment_alignment(item_max_width, inner_item.len());
+ if first_line_width(&formatted_comment)
+ + last_line_width(&result)
+ + comment_alignment
+ + 1
+ > formatting.config.max_width()
+ {
+ item_max_width = None;
+ formatted_comment = rewrite_post_comment(&mut item_max_width)?;
+ comment_alignment =
+ post_comment_alignment(item_max_width, inner_item.len());
+ }
+ for _ in 0..=comment_alignment {
+ result.push(' ');
+ }
}
- // An additional space for the missing trailing separator.
- if last && item_max_width.is_some() && !separate && !formatting.separator.is_empty()
+ // An additional space for the missing trailing separator (or
+ // if we skipped alignment above).
+ if !formatting.align_comments
+ || (last
+ && item_max_width.is_some()
+ && !separate
+ && !formatting.separator.is_empty())
{
result.push(' ');
}
item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
}
-pub struct ListItems<'a, I, F1, F2, F3>
+pub(crate) struct ListItems<'a, I, F1, F2, F3>
where
I: Iterator,
{
leave_last: bool,
}
-pub fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommentStyle) {
+pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListItemCommentStyle) {
let trimmed_pre_snippet = pre_snippet.trim();
+ let has_block_comment = trimmed_pre_snippet.ends_with("*/");
let has_single_line_comment = trimmed_pre_snippet.starts_with("//");
- let has_block_comment = trimmed_pre_snippet.starts_with("/*");
- if has_single_line_comment {
- (
- Some(trimmed_pre_snippet.to_owned()),
- ListItemCommentStyle::DifferentLine,
- )
- } else if has_block_comment {
- let comment_end = pre_snippet.chars().rev().position(|c| c == '/').unwrap();
- if pre_snippet
- .chars()
- .rev()
- .take(comment_end + 1)
- .any(|c| c == '\n')
- {
+ if has_block_comment {
+ let comment_end = pre_snippet.rfind(|c| c == '/').unwrap();
+ if pre_snippet[comment_end..].contains('\n') {
(
Some(trimmed_pre_snippet.to_owned()),
ListItemCommentStyle::DifferentLine,
ListItemCommentStyle::SameLine,
)
}
+ } else if has_single_line_comment {
+ (
+ Some(trimmed_pre_snippet.to_owned()),
+ ListItemCommentStyle::DifferentLine,
+ )
} else {
(None, ListItemCommentStyle::None)
}
}
-pub fn extract_post_comment(
+pub(crate) fn extract_post_comment(
post_snippet: &str,
comment_end: usize,
separator: &str,
post_snippet[1..].trim_matches(white_space)
} else if post_snippet.starts_with(separator) {
post_snippet[separator.len()..].trim_matches(white_space)
- } else if post_snippet.ends_with(',') {
+ } else if post_snippet.ends_with(',') && !post_snippet.trim().starts_with("//") {
post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space)
} else {
post_snippet
};
-
- if !post_snippet_trimmed.is_empty() {
+ // FIXME(#3441): post_snippet includes 'const' now
+ // it should not include here
+ let removed_newline_snippet = post_snippet_trimmed.trim();
+ if !post_snippet_trimmed.is_empty()
+ && (removed_newline_snippet.starts_with("//") || removed_newline_snippet.starts_with("/*"))
+ {
Some(post_snippet_trimmed.to_owned())
} else {
None
}
}
-pub fn get_comment_end(
+pub(crate) fn get_comment_end(
post_snippet: &str,
separator: &str,
terminator: &str,
if let Some(i) = block_open_index {
match post_snippet.find('/') {
Some(j) if j < i => block_open_index = None,
- _ if i > 0 && &post_snippet[i - 1..i] == "/" => block_open_index = None,
+ _ if post_snippet[..i].ends_with('/') => block_open_index = None,
_ => (),
}
}
// Account for extra whitespace between items. This is fiddly
// because of the way we divide pre- and post- comments.
-fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool {
+pub(crate) fn has_extra_newline(post_snippet: &str, comment_end: usize) -> bool {
if post_snippet.is_empty() || comment_end == 0 {
return false;
}
+ let len_last = post_snippet[..comment_end]
+ .chars()
+ .last()
+ .unwrap()
+ .len_utf8();
// Everything from the separator to the next item.
- let test_snippet = &post_snippet[comment_end - 1..];
+ let test_snippet = &post_snippet[comment_end - len_last..];
let first_newline = test_snippet
.find('\n')
.unwrap_or_else(|| test_snippet.len());
}
}
-#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
+#[allow(clippy::too_many_arguments)]
// Creates an iterator over a list's items with associated comments.
-pub fn itemize_list<'a, T, I, F1, F2, F3>(
- snippet_provider: &'a SnippetProvider,
+pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>(
+ snippet_provider: &'a SnippetProvider<'_>,
inner: I,
terminator: &'a str,
separator: &'a str,
.fold((0, 0), |acc, l| (acc.0 + 1, acc.1 + l))
}
-pub fn total_item_width(item: &ListItem) -> usize {
+pub(crate) fn total_item_width(item: &ListItem) -> usize {
comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..]))
+ comment_len(item.post_comment.as_ref().map(|x| &(*x)[..]))
- + item.item.as_ref().map_or(0, |str| str.len())
+ + item.item.as_ref().map_or(0, String::len)
}
fn comment_len(comment: Option<&str>) -> usize {
}
// Compute horizontal and vertical shapes for a struct-lit-like thing.
-pub fn struct_lit_shape(
+pub(crate) fn struct_lit_shape(
shape: Shape,
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
prefix_width: usize,
suffix_width: usize,
) -> Option<(Option<Shape>, Shape)> {
}
// Compute the tactic for the internals of a struct-lit-like thing.
-pub fn struct_lit_tactic(
+pub(crate) fn struct_lit_tactic(
h_shape: Option<Shape>,
- context: &RewriteContext,
+ context: &RewriteContext<'_>,
items: &[ListItem],
) -> DefinitiveListTactic {
if let Some(h_shape) = h_shape {
// Given a tactic and possible shapes for horizontal and vertical layout,
// come up with the actual shape to use.
-pub fn shape_for_tactic(
+pub(crate) fn shape_for_tactic(
tactic: DefinitiveListTactic,
h_shape: Option<Shape>,
v_shape: Shape,
// Create a ListFormatting object for formatting the internals of a
// struct-lit-like thing, that is a series of fields.
-pub fn struct_lit_formatting<'a>(
+pub(crate) fn struct_lit_formatting<'a>(
shape: Shape,
tactic: DefinitiveListTactic,
- context: &'a RewriteContext,
+ context: &'a RewriteContext<'_>,
force_no_trailing_comma: bool,
) -> ListFormatting<'a> {
let ends_with_newline = context.config.indent_style() != IndentStyle::Visual
ends_with_newline,
preserve_newline: true,
nested: false,
+ align_comments: true,
config: context.config,
}
}