From c7504a2269b831438672a436d97777e540060faf Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Sat, 11 May 2024 16:15:32 -0400 Subject: [PATCH 1/2] Remove multiple trailing lines in comments Previously, rustfmt would remove one blank, trailing line in a comment every time it ran, so formatting was not idempotent if a comment had multiple trailing lines. Comments are only reformatted in this way if `comment::rewrite_comment_inner` is called, which is enabled by any of the config options `normalize_comments`, `wrap_comments`, or `format_code_in_doc_comments` (for doc comments only). Absent those config options, no trailing line reformatting occurs at all. In this commit, when the existing condition that detects a blank, trailing line is true, any preceding blank lines are removed from the reformatted comment. A source/target "system test" was added to prevent regression. Signed-off-by: Ross Williams --- src/comment.rs | 8 +++++++- tests/source/comment_trailing_lines.rs | 21 +++++++++++++++++++++ tests/target/comment_trailing_lines.rs | 14 ++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/source/comment_trailing_lines.rs create mode 100644 tests/target/comment_trailing_lines.rs diff --git a/src/comment.rs b/src/comment.rs index 24a5a1be2c3..49507f1a27d 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -811,7 +811,13 @@ impl<'a> CommentRewrite<'a> { } else if self.is_prev_line_multi_line && !line.is_empty() { self.result.push(' ') } else if is_last && line.is_empty() { - // trailing blank lines are unwanted + // Trailing blank lines are unwanted; if the last line is blank, look for additional, + // preceding blank lines to remove, also. + let trailing_line = self.comment_line_separator.trim_end_matches(' '); + while self.result.ends_with(trailing_line) { + self.result + .truncate(self.result.len() - trailing_line.len()); + } if !self.closer.is_empty() { self.result.push_str(&self.indent_str); } diff --git a/tests/source/comment_trailing_lines.rs b/tests/source/comment_trailing_lines.rs new file mode 100644 index 00000000000..b4564de9791 --- /dev/null +++ b/tests/source/comment_trailing_lines.rs @@ -0,0 +1,21 @@ +// rustfmt-wrap_comments: true + +//! Module comment with multiple trailing lines +//! +//! + +/// Doc comment with multiple trailing lines +/// +/// +pub mod foo {} + +/// Doc comment with one trailing line +/// +pub mod bar {} + +/* + * Block comment with multiple trailing lines + * + * + */ +pub mod block {} diff --git a/tests/target/comment_trailing_lines.rs b/tests/target/comment_trailing_lines.rs new file mode 100644 index 00000000000..79294e860f9 --- /dev/null +++ b/tests/target/comment_trailing_lines.rs @@ -0,0 +1,14 @@ +// rustfmt-wrap_comments: true + +//! Module comment with multiple trailing lines + +/// Doc comment with multiple trailing lines +pub mod foo {} + +/// Doc comment with one trailing line +pub mod bar {} + +/* + * Block comment with multiple trailing lines + */ +pub mod block {} From f66d7d8ab0ee702f6fcc010a3ce78cda048bd22b Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Wed, 31 Jul 2024 16:38:02 -0400 Subject: [PATCH 2/2] Also remove trailing bare lines in comments Block comments with bare lines are handled differently than those without bare lines (unless `normalize_comments` is configured). If `normalize_comments` is false but `wrap_comments` is true, trailing blank lines should be removed from block comments, including those with bare lines. Signed-off-by: Ross Williams --- src/comment.rs | 53 ++++++++++++++++++++++++-- src/utils.rs | 4 ++ tests/source/comment_trailing_lines.rs | 7 ++++ tests/target/comment_trailing_lines.rs | 5 +++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/comment.rs b/src/comment.rs index 49507f1a27d..ae44eafec83 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -10,8 +10,8 @@ use crate::rewrite::RewriteContext; use crate::shape::{Indent, Shape}; use crate::string::{rewrite_string, StringFormat}; use crate::utils::{ - count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, - trimmed_last_line_width, unicode_str_width, + count_newlines, first_line_width, is_horizontal_space, last_line_width, + trim_left_preserve_layout, trimmed_last_line_width, unicode_str_width, }; use crate::{ErrorKind, FormattingError}; @@ -346,7 +346,14 @@ fn identify_comment( let (first_group, rest) = orig.split_at(first_group_ending); let rewritten_first_group = if !config.normalize_comments() && has_bare_lines && style.is_block_comment() { - trim_left_preserve_layout(first_group, shape.indent, config)? + let reindented = trim_left_preserve_layout(first_group, shape.indent, config)?; + + // If wrap_comments is enabled, then remove any trailing blank lines + if config.wrap_comments() { + strip_comment_trailing_lines(reindented, style) + } else { + reindented + } } else if !config.normalize_comments() && !config.wrap_comments() && !( @@ -1053,6 +1060,46 @@ fn trim_end_unless_two_whitespaces(s: &str, is_doc_comment: bool) -> &str { } } +/// Removes blank lines at the end of block comments +fn strip_comment_trailing_lines(comment: String, style: CommentStyle<'_>) -> String { + debug_assert!( + style.is_block_comment(), + "strip_suffix(style.closer()) ensures this function is a no-op on non-block comments" + ); + + // If final line is a normal closing line, look for trailing blank lines + if let Some(without_closer) = comment + .strip_suffix(style.closer()) + .map(|s| s.trim_end_matches(is_horizontal_space)) + .and_then(|s| s.strip_suffix('\n')) + { + let mut trimmed = without_closer.trim_end(); + let closer_line = &comment[without_closer.len()..]; + let line_start = style.line_start().trim_end(); + // Start trimming trailing blank lines + loop { + // Remove any asterisk-prefixed or bare block comment lines + let t = trimmed.trim_end_matches(line_start).trim_end(); + if t.len() == trimmed.len() { + // If no trimming occurred + break; + } else { + // ... otherwise, keep trying to trim + trimmed = t; + } + } + + if without_closer.len() == trimmed.len() { + // No blank lines were removed + comment + } else { + trimmed.to_string() + closer_line + } + } else { + // Final line either didn't have a closer or had non-whitespace along with the closer + comment + } +} /// Trims whitespace and aligns to indent, but otherwise does not change comments. fn light_rewrite_comment( orig: &str, diff --git a/src/utils.rs b/src/utils.rs index 57f2f177960..e88da8700bb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -675,6 +675,10 @@ pub(crate) fn is_empty_line(s: &str) -> bool { s.is_empty() || s.chars().all(char::is_whitespace) } +pub(crate) fn is_horizontal_space(c: char) -> bool { + c == ' ' || c == '\t' +} + fn get_prefix_space_width(config: &Config, s: &str) -> usize { let mut width = 0; for c in s.chars() { diff --git a/tests/source/comment_trailing_lines.rs b/tests/source/comment_trailing_lines.rs index b4564de9791..bf3d2d62f62 100644 --- a/tests/source/comment_trailing_lines.rs +++ b/tests/source/comment_trailing_lines.rs @@ -19,3 +19,10 @@ pub mod bar {} * */ pub mod block {} + +/** +Outer Block doc comment with multiple trailing lines + + + */ +pub mod outer_block_doc_comment {} diff --git a/tests/target/comment_trailing_lines.rs b/tests/target/comment_trailing_lines.rs index 79294e860f9..fdb6c875f40 100644 --- a/tests/target/comment_trailing_lines.rs +++ b/tests/target/comment_trailing_lines.rs @@ -12,3 +12,8 @@ pub mod bar {} * Block comment with multiple trailing lines */ pub mod block {} + +/** +Outer Block doc comment with multiple trailing lines + */ +pub mod outer_block_doc_comment {}