X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flists.rs;h=6005ae6fd259a429db2d41c8070da685655381b6;hb=cbd568083d87a90dfe5ab0e90f404454946c9f20;hp=193cd4f3c32e0bb33063a32bcff0522697a67ecf;hpb=d3c2523c3288284c9e5b4df4774295032f4356b6;p=rust.git diff --git a/src/lists.rs b/src/lists.rs index 193cd4f3c32..6005ae6fd25 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -8,70 +8,87 @@ // 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 syntax::codemap::{BytePos, CodeMap}; +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; -/// Formatting tactic for lists. This will be cast down to a -/// `DefinitiveListTactic` depending on the number and length of the items and -/// their comments. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum ListTactic { - // One item per row. - Vertical, - // All items on one row. - Horizontal, - // Try Horizontal layout, if that fails then vertical. - HorizontalVertical, - // HorizontalVertical with a soft limit of n characters. - LimitedHorizontalVertical(usize), - // Pack as many items as possible per row over (possibly) many rows. - Mixed, +pub 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. + // 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, + config: &'a Config, } -impl_enum_serialize_and_deserialize!(ListTactic, Vertical, Horizontal, HorizontalVertical, Mixed); +impl<'a> ListFormatting<'a> { + pub fn new(shape: Shape, config: &'a Config) -> Self { + ListFormatting { + tactic: DefinitiveListTactic::Vertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + separator_place: SeparatorPlace::Back, + shape, + ends_with_newline: true, + preserve_newline: false, + nested: false, + config: config, + } + } -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum SeparatorTactic { - Always, - Never, - Vertical, -} + pub fn tactic(mut self, tactic: DefinitiveListTactic) -> Self { + self.tactic = tactic; + self + } -impl_enum_serialize_and_deserialize!(SeparatorTactic, Always, Never, Vertical); + pub fn separator(mut self, separator: &'a str) -> Self { + self.separator = separator; + self + } -impl SeparatorTactic { - pub fn from_bool(b: bool) -> SeparatorTactic { - if b { - SeparatorTactic::Always - } else { - SeparatorTactic::Never - } + pub fn trailing_separator(mut self, trailing_separator: SeparatorTactic) -> Self { + self.trailing_separator = trailing_separator; + self } -} -pub struct ListFormatting<'a> { - pub tactic: DefinitiveListTactic, - pub separator: &'a str, - pub trailing_separator: SeparatorTactic, - pub separator_place: SeparatorPlace, - pub shape: Shape, - // Non-expressions, e.g. items, will have a new line at the end of the list. - // Important for comment styles. - pub ends_with_newline: bool, - // Remove newlines between list elements for expressions. - pub preserve_newline: bool, - pub config: &'a Config, -} + pub 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 { + self.ends_with_newline = ends_with_newline; + self + } + + pub fn preserve_newline(mut self, preserve_newline: bool) -> Self { + self.preserve_newline = preserve_newline; + self + } + + pub fn nested(mut self, nested: bool) -> Self { + self.nested = nested; + self + } -impl<'a> ListFormatting<'a> { pub fn needs_trailing_separator(&self) -> bool { match self.trailing_separator { // We always put separator in front. @@ -90,7 +107,7 @@ fn as_ref(&self) -> &ListItem { } } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum ListItemCommentStyle { // Try to keep the comment on the same line with the item. SameLine, @@ -100,6 +117,7 @@ pub enum ListItemCommentStyle { None, } +#[derive(Debug, Clone)] pub struct ListItem { // None for comments mean that they are not present. pub pre_comment: Option, @@ -113,36 +131,55 @@ pub struct ListItem { } impl ListItem { + pub fn empty() -> ListItem { + ListItem { + pre_comment: None, + pre_comment_style: ListItemCommentStyle::None, + item: None, + post_comment: None, + new_lines: false, + } + } + pub 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 + 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 { self.inner_as_ref().contains('\n') - || self.pre_comment + || self + .pre_comment .as_ref() .map_or(false, |s| s.contains('\n')) - || self.post_comment + || self + .post_comment .as_ref() .map_or(false, |s| s.contains('\n')) } - pub fn has_comment(&self) -> bool { + pub fn has_single_line_comment(&self) -> bool { self.pre_comment .as_ref() .map_or(false, |comment| comment.trim_left().starts_with("//")) - || self.post_comment + || self + .post_comment .as_ref() .map_or(false, |comment| comment.trim_left().starts_with("//")) } + pub fn has_comment(&self) -> bool { + self.pre_comment.is_some() || self.post_comment.is_some() + } + pub fn from_str>(s: S) -> ListItem { ListItem { pre_comment: None, @@ -152,24 +189,17 @@ pub fn from_str>(s: S) -> ListItem { new_lines: false, } } -} -/// The definitive formatting tactic for lists. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum DefinitiveListTactic { - Vertical, - Horizontal, - Mixed, - /// Special case tactic for `format!()`, `write!()` style macros. - SpecialMacro(usize), -} - -impl DefinitiveListTactic { - pub fn ends_with_newline(&self, indent_style: IndentStyle) -> bool { - match indent_style { - IndentStyle::Block => *self != DefinitiveListTactic::Horizontal, - IndentStyle::Visual => false, + // true if the item causes something to be written. + fn is_substantial(&self) -> bool { + fn empty(s: &Option) -> bool { + match *s { + Some(ref s) if !s.is_empty() => false, + _ => true, + } } + + !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) } } @@ -181,8 +211,8 @@ pub enum Separator { } impl Separator { - pub fn len(&self) -> usize { - match *self { + pub fn len(self) -> usize { + match self { // 2 = `, ` Separator::Comma => 2, // 3 = ` | ` @@ -191,40 +221,6 @@ pub fn len(&self) -> usize { } } -/// Where to put separator. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum SeparatorPlace { - Front, - Back, -} - -impl_enum_serialize_and_deserialize!(SeparatorPlace, Front, Back); - -impl SeparatorPlace { - pub fn is_front(&self) -> bool { - *self == SeparatorPlace::Front - } - - pub fn is_back(&self) -> bool { - *self == SeparatorPlace::Back - } - - pub fn from_tactic( - default: SeparatorPlace, - tactic: DefinitiveListTactic, - sep: &str, - ) -> SeparatorPlace { - match tactic { - DefinitiveListTactic::Vertical => default, - _ => if sep == "," { - SeparatorPlace::Back - } else { - default - }, - } - } -} - pub fn definitive_tactic( items: I, tactic: ListTactic, @@ -238,32 +234,34 @@ pub fn definitive_tactic( let pre_line_comments = items .clone() .into_iter() - .any(|item| item.as_ref().has_comment()); + .any(|item| item.as_ref().has_single_line_comment()); let limit = match tactic { _ if pre_line_comments => return DefinitiveListTactic::Vertical, - ListTactic::Mixed => return DefinitiveListTactic::Mixed, ListTactic::Horizontal => return DefinitiveListTactic::Horizontal, ListTactic::Vertical => return DefinitiveListTactic::Vertical, ListTactic::LimitedHorizontalVertical(limit) => ::std::cmp::min(width, limit), - ListTactic::HorizontalVertical => width, + ListTactic::Mixed | ListTactic::HorizontalVertical => width, }; let (sep_count, total_width) = calculate_width(items.clone()); - let total_sep_len = sep.len() * sep_count.checked_sub(1).unwrap_or(0); + let total_sep_len = sep.len() * sep_count.saturating_sub(1); let real_total = total_width + total_sep_len; - if real_total <= limit && !pre_line_comments + if real_total <= limit + && !pre_line_comments && !items.into_iter().any(|item| item.as_ref().is_multiline()) { DefinitiveListTactic::Horizontal } else { - DefinitiveListTactic::Vertical + match tactic { + ListTactic::Mixed => DefinitiveListTactic::Mixed, + _ => DefinitiveListTactic::Vertical, + } } } // Format a list of commented items into a string. -// TODO: add unit tests pub fn write_list(items: I, formatting: &ListFormatting) -> Option where I: IntoIterator + Clone, @@ -281,6 +279,8 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option let mut item_max_width: Option = None; let sep_place = SeparatorPlace::from_tactic(formatting.separator_place, tactic, formatting.separator); + let mut prev_item_had_post_comment = false; + let mut prev_item_is_nested_import = false; let mut line_len = 0; let indent_str = &formatting.shape.indent.to_string(formatting.config); @@ -307,6 +307,10 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option item_last_line_width -= indent_str.len(); } + if !item.is_substantial() { + continue; + } + match tactic { DefinitiveListTactic::Horizontal if !first => { result.push(' '); @@ -323,7 +327,9 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option result.push(' '); } } - DefinitiveListTactic::Vertical if !first => { + DefinitiveListTactic::Vertical + if !first && !inner_item.is_empty() && !result.is_empty() => + { result.push('\n'); result.push_str(indent_str); } @@ -331,24 +337,26 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option let total_width = total_item_width(item) + item_sep_len; // 1 is space between separator and item. - if line_len > 0 && line_len + 1 + total_width > formatting.shape.width { + if (line_len > 0 && line_len + 1 + total_width > formatting.shape.width) + || prev_item_had_post_comment + || (formatting.nested + && (prev_item_is_nested_import || (!first && inner_item.contains("::")))) + { result.push('\n'); result.push_str(indent_str); line_len = 0; if formatting.ends_with_newline { - if last { - separate = true; - } else { - trailing_separator = true; - } + trailing_separator = true; } - } - - if line_len > 0 { + } else if line_len > 0 { result.push(' '); line_len += 1; } + if last && formatting.ends_with_newline { + separate = formatting.trailing_separator != SeparatorTactic::Never; + } + line_len += total_width; } _ => {} @@ -357,32 +365,37 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option // Pre-comments if let Some(ref comment) = item.pre_comment { // Block style in non-vertical mode. - let block_mode = tactic != DefinitiveListTactic::Vertical; + let block_mode = tactic == DefinitiveListTactic::Horizontal; // Width restriction is only relevant in vertical mode. let comment = rewrite_comment(comment, block_mode, formatting.shape, formatting.config)?; result.push_str(&comment); - if tactic == DefinitiveListTactic::Vertical { - // We cannot keep pre-comments on the same line if the comment if normalized. - let keep_comment = if formatting.config.normalize_comments() - || item.pre_comment_style == ListItemCommentStyle::DifferentLine + if !inner_item.is_empty() { + if tactic == DefinitiveListTactic::Vertical || tactic == DefinitiveListTactic::Mixed { - false + // We cannot keep pre-comments on the same line if the comment if normalized. + let keep_comment = if formatting.config.normalize_comments() + || item.pre_comment_style == ListItemCommentStyle::DifferentLine + { + false + } else { + // We will try to keep the comment on the same line with the item here. + // 1 = ` ` + let total_width = total_item_width(item) + item_sep_len + 1; + total_width <= formatting.shape.width + }; + if keep_comment { + result.push(' '); + } else { + 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()); + } } else { - // We will try to keep the comment on the same line with the item here. - // 1 = ` ` - let total_width = total_item_width(item) + item_sep_len + 1; - total_width <= formatting.shape.width - }; - if keep_comment { result.push(' '); - } else { - result.push('\n'); - result.push_str(indent_str); } - } else { - result.push(' '); } item_max_width = None; } @@ -391,10 +404,10 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option result.push_str(formatting.separator.trim()); result.push(' '); } - result.push_str(&inner_item[..]); + result.push_str(inner_item); // Post-comments - if tactic != DefinitiveListTactic::Vertical && item.post_comment.is_some() { + if tactic == DefinitiveListTactic::Horizontal && item.post_comment.is_some() { let comment = item.post_comment.as_ref().unwrap(); let formatted_comment = rewrite_comment( comment, @@ -411,7 +424,7 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option result.push_str(formatting.separator); } - if tactic == DefinitiveListTactic::Vertical && item.post_comment.is_some() { + if tactic != DefinitiveListTactic::Horizontal && item.post_comment.is_some() { let comment = item.post_comment.as_ref().unwrap(); let overhead = last_line_width(&result) + first_line_width(comment.trim()); @@ -454,14 +467,17 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option 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() + 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) { + for _ in 0..=comment_alignment { result.push(' '); } // An additional space for the missing trailing separator. @@ -481,12 +497,17 @@ pub fn write_list(items: I, formatting: &ListFormatting) -> Option item_max_width = None; } - if formatting.preserve_newline && !last && tactic == DefinitiveListTactic::Vertical + if formatting.preserve_newline + && !last + && tactic == DefinitiveListTactic::Vertical && item.new_lines { item_max_width = None; result.push('\n'); } + + prev_item_had_post_comment = item.post_comment.is_some(); + prev_item_is_nested_import = inner_item.contains("::"); } Some(result) @@ -508,7 +529,8 @@ fn max_width_of_item_with_post_comment( let item = item.as_ref(); let inner_item_width = item.inner_as_ref().len(); if !first - && (item.is_different_group() || !item.post_comment.is_some() + && (item.is_different_group() + || item.post_comment.is_none() || inner_item_width + overhead > max_budget) { return max_width; @@ -525,16 +547,14 @@ fn max_width_of_item_with_post_comment( } fn post_comment_alignment(item_max_width: Option, inner_item_len: usize) -> usize { - item_max_width - .and_then(|max_line_width| max_line_width.checked_sub(inner_item_len)) - .unwrap_or(0) + item_max_width.unwrap_or(0).saturating_sub(inner_item_len) } pub struct ListItems<'a, I, F1, F2, F3> where I: Iterator, { - codemap: &'a CodeMap, + snippet_provider: &'a SnippetProvider<'a>, inner: Peekable, get_lo: F1, get_hi: F2, @@ -546,6 +566,139 @@ pub struct ListItems<'a, I, F1, F2, F3> leave_last: bool, } +pub fn extract_pre_comment(pre_snippet: &str) -> (Option, 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("//"); + 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') + { + ( + Some(trimmed_pre_snippet.to_owned()), + ListItemCommentStyle::DifferentLine, + ) + } else { + ( + Some(trimmed_pre_snippet.to_owned()), + 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( + post_snippet: &str, + comment_end: usize, + separator: &str, +) -> Option { + let white_space: &[_] = &[' ', '\t']; + + // Cleanup post-comment: strip separators and whitespace. + let post_snippet = post_snippet[..comment_end].trim(); + let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') { + 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(',') { + post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space) + } else { + post_snippet + }; + + if !post_snippet_trimmed.is_empty() { + Some(post_snippet_trimmed.to_owned()) + } else { + None + } +} + +pub fn get_comment_end( + post_snippet: &str, + separator: &str, + terminator: &str, + is_last: bool, +) -> usize { + if is_last { + return post_snippet + .find_uncommented(terminator) + .unwrap_or_else(|| post_snippet.len()); + } + + let mut block_open_index = post_snippet.find("/*"); + // check if it really is a block comment (and not `//*` or a nested comment) + 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, + _ => (), + } + } + let newline_index = post_snippet.find('\n'); + if let Some(separator_index) = post_snippet.find_uncommented(separator) { + match (block_open_index, newline_index) { + // Separator before comment, with the next item on same line. + // Comment belongs to next item. + (Some(i), None) if i > separator_index => separator_index + 1, + // Block-style post-comment before the separator. + (Some(i), None) => cmp::max( + find_comment_end(&post_snippet[i..]).unwrap() + i, + separator_index + 1, + ), + // Block-style post-comment. Either before or after the separator. + (Some(i), Some(j)) if i < j => cmp::max( + find_comment_end(&post_snippet[i..]).unwrap() + i, + separator_index + 1, + ), + // Potential *single* line comment. + (_, Some(j)) if j > separator_index => j + 1, + _ => post_snippet.len(), + } + } else if let Some(newline_index) = newline_index { + // Match arms may not have trailing comma. In any case, for match arms, + // we will assume that the post comment belongs to the next arm if they + // do not end with trailing comma. + newline_index + 1 + } else { + 0 + } +} + +// 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 { + if post_snippet.is_empty() || comment_end == 0 { + return false; + } + + // Everything from the separator to the next item. + let test_snippet = &post_snippet[comment_end - 1..]; + let first_newline = test_snippet + .find('\n') + .unwrap_or_else(|| test_snippet.len()); + // From the end of the first line of comments. + let test_snippet = &test_snippet[first_newline..]; + let first = test_snippet + .find(|c: char| !c.is_whitespace()) + .unwrap_or_else(|| test_snippet.len()); + // From the end of the first line of comments to the next non-whitespace char. + let test_snippet = &test_snippet[..first]; + + // There were multiple line breaks which got trimmed to nothing. + count_newlines(test_snippet) > 1 +} + impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3> where I: Iterator, @@ -556,157 +709,53 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3> type Item = ListItem; fn next(&mut self) -> Option { - let white_space: &[_] = &[' ', '\t']; - self.inner.next().map(|item| { - let mut new_lines = false; // Pre-comment - let pre_snippet = self.codemap + let pre_snippet = self + .snippet_provider .span_to_snippet(mk_sp(self.prev_span_end, (self.get_lo)(&item))) - .unwrap(); - let trimmed_pre_snippet = pre_snippet.trim(); - let has_single_line_comment = trimmed_pre_snippet.starts_with("//"); - let has_block_comment = trimmed_pre_snippet.starts_with("/*"); - let (pre_comment, pre_comment_style) = 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') - { - ( - Some(trimmed_pre_snippet.to_owned()), - ListItemCommentStyle::DifferentLine, - ) - } else { - ( - Some(trimmed_pre_snippet.to_owned()), - ListItemCommentStyle::SameLine, - ) - } - } else { - (None, ListItemCommentStyle::None) - }; + .unwrap_or(""); + let (pre_comment, pre_comment_style) = extract_pre_comment(pre_snippet); // Post-comment let next_start = match self.inner.peek() { Some(next_item) => (self.get_lo)(next_item), None => self.next_span_start, }; - let post_snippet = self.codemap + let post_snippet = self + .snippet_provider .span_to_snippet(mk_sp((self.get_hi)(&item), next_start)) - .unwrap(); - - let comment_end = match self.inner.peek() { - Some(..) => { - let mut block_open_index = post_snippet.find("/*"); - // check if it really is a block comment (and not `//*` or a nested comment) - 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, - _ => (), - } - } - let newline_index = post_snippet.find('\n'); - if let Some(separator_index) = post_snippet.find_uncommented(self.separator) { - match (block_open_index, newline_index) { - // Separator before comment, with the next item on same line. - // Comment belongs to next item. - (Some(i), None) if i > separator_index => separator_index + 1, - // Block-style post-comment before the separator. - (Some(i), None) => cmp::max( - find_comment_end(&post_snippet[i..]).unwrap() + i, - separator_index + 1, - ), - // Block-style post-comment. Either before or after the separator. - (Some(i), Some(j)) if i < j => cmp::max( - find_comment_end(&post_snippet[i..]).unwrap() + i, - separator_index + 1, - ), - // Potential *single* line comment. - (_, Some(j)) if j > separator_index => j + 1, - _ => post_snippet.len(), - } - } else if let Some(newline_index) = newline_index { - // Match arms may not have trailing comma. In any case, for match arms, - // we will assume that the post comment belongs to the next arm if they - // do not end with trailing comma. - newline_index + 1 - } else { - 0 - } - } - None => post_snippet - .find_uncommented(self.terminator) - .unwrap_or_else(|| post_snippet.len()), - }; + .unwrap_or(""); + let comment_end = get_comment_end( + post_snippet, + self.separator, + self.terminator, + self.inner.peek().is_none(), + ); + let new_lines = has_extra_newline(post_snippet, comment_end); + let post_comment = extract_post_comment(post_snippet, comment_end, self.separator); - if !post_snippet.is_empty() && comment_end > 0 { - // Account for extra whitespace between items. This is fiddly - // because of the way we divide pre- and post- comments. - - // Everything from the separator to the next item. - let test_snippet = &post_snippet[comment_end - 1..]; - let first_newline = test_snippet - .find('\n') - .unwrap_or_else(|| test_snippet.len()); - // From the end of the first line of comments. - let test_snippet = &test_snippet[first_newline..]; - let first = test_snippet - .find(|c: char| !c.is_whitespace()) - .unwrap_or_else(|| test_snippet.len()); - // From the end of the first line of comments to the next non-whitespace char. - let test_snippet = &test_snippet[..first]; - - if count_newlines(test_snippet) > 1 { - // There were multiple line breaks which got trimmed to nothing. - new_lines = true; - } - } - - // Cleanup post-comment: strip separators and whitespace. self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32); - let post_snippet = post_snippet[..comment_end].trim(); - - let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') { - post_snippet[1..].trim_matches(white_space) - } else if post_snippet.ends_with(',') { - post_snippet[..(post_snippet.len() - 1)].trim_matches(white_space) - } else { - post_snippet - }; - - let post_comment = if !post_snippet_trimmed.is_empty() { - Some(post_snippet_trimmed.to_owned()) - } else { - None - }; ListItem { - pre_comment: pre_comment, - pre_comment_style: pre_comment_style, + pre_comment, + pre_comment_style, item: if self.inner.peek().is_none() && self.leave_last { None } else { (self.get_item_string)(&item) }, - post_comment: post_comment, - new_lines: new_lines, + post_comment, + new_lines, } }) } } +#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] // Creates an iterator over a list's items with associated comments. pub fn itemize_list<'a, T, I, F1, F2, F3>( - codemap: &'a CodeMap, + snippet_provider: &'a SnippetProvider, inner: I, terminator: &'a str, separator: &'a str, @@ -724,16 +773,16 @@ pub fn itemize_list<'a, T, I, F1, F2, F3>( F3: Fn(&T) -> Option, { ListItems { - codemap: codemap, + snippet_provider, inner: inner.peekable(), - get_lo: get_lo, - get_hi: get_hi, - get_item_string: get_item_string, - prev_span_end: prev_span_end, - next_span_start: next_span_start, - terminator: terminator, - separator: separator, - leave_last: leave_last, + get_lo, + get_hi, + get_item_string, + prev_span_end, + next_span_start, + terminator, + separator, + leave_last, } } @@ -841,7 +890,7 @@ pub fn struct_lit_formatting<'a>( let ends_with_newline = context.config.indent_style() != IndentStyle::Visual && tactic == DefinitiveListTactic::Vertical; ListFormatting { - tactic: tactic, + tactic, separator: ",", trailing_separator: if force_no_trailing_comma { SeparatorTactic::Never @@ -849,9 +898,10 @@ pub fn struct_lit_formatting<'a>( context.config.trailing_comma() }, separator_place: SeparatorPlace::Back, - shape: shape, - ends_with_newline: ends_with_newline, + shape, + ends_with_newline, preserve_newline: true, + nested: false, config: context.config, } }