From 890370f61444fe46dc651dbd3f48734113c5b510 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sat, 12 Apr 2025 19:13:11 +0300 Subject: [PATCH 1/5] Clarify logic in FmtVisitor::push_vertical_spaces --- src/missed_spans.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 384de1ce9ae..3e69be03cba 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -113,26 +113,15 @@ impl<'a> FmtVisitor<'a> { } } - fn push_vertical_spaces(&mut self, mut newline_count: usize) { - let offset = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); + fn push_vertical_spaces(&mut self, newline_count: usize) { + let old_count = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); let newline_upper_bound = self.config.blank_lines_upper_bound() + 1; let newline_lower_bound = self.config.blank_lines_lower_bound() + 1; - if newline_count + offset > newline_upper_bound { - if offset >= newline_upper_bound { - newline_count = 0; - } else { - newline_count = newline_upper_bound - offset; - } - } else if newline_count + offset < newline_lower_bound { - if offset >= newline_lower_bound { - newline_count = 0; - } else { - newline_count = newline_lower_bound - offset; - } - } + // Clamp new total count to configuration. + let total = (old_count + newline_count).clamp(newline_lower_bound, newline_upper_bound); - let blank_lines = "\n".repeat(newline_count); + let blank_lines = "\n".repeat(total.saturating_sub(old_count)); self.push_str(&blank_lines); } From a27214bd22a93550307ee39a4114b251bd574d8a Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sat, 12 Apr 2025 22:39:48 +0300 Subject: [PATCH 2/5] Separate logic for termination of the current line in push_vertical_spaces --- src/missed_spans.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 3e69be03cba..1b23ade00d0 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -113,15 +113,27 @@ impl<'a> FmtVisitor<'a> { } } - fn push_vertical_spaces(&mut self, newline_count: usize) { - let old_count = self.buffer.chars().rev().take_while(|c| *c == '\n').count(); - let newline_upper_bound = self.config.blank_lines_upper_bound() + 1; - let newline_lower_bound = self.config.blank_lines_lower_bound() + 1; + fn push_vertical_spaces(&mut self, mut newline_count: usize) { + if let [] | [.., b'\n'] = self.buffer.as_bytes() { + /* Already at the start of a new line */ + } else { + self.push_str("\n"); + newline_count = newline_count.saturating_sub(1); + } + + let existing_blanks = self + .buffer + .rsplit_terminator('\n') + .take_while(|s| s.is_empty()) + .count(); // Clamp new total count to configuration. - let total = (old_count + newline_count).clamp(newline_lower_bound, newline_upper_bound); + let total = (existing_blanks + newline_count).clamp( + self.config.blank_lines_lower_bound(), + self.config.blank_lines_upper_bound(), + ); - let blank_lines = "\n".repeat(total.saturating_sub(old_count)); + let blank_lines = "\n".repeat(total.saturating_sub(existing_blanks)); self.push_str(&blank_lines); } From 57daae8028df4ba8784d85e6901f8e516ba931e2 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sun, 13 Apr 2025 06:25:17 +0300 Subject: [PATCH 3/5] New configuration option: indent_blank_lines --- Configurations.md | 50 +++++++++++++++++++++++++++++++++++++++++++ src/config/mod.rs | 4 ++++ src/config/options.rs | 1 + src/missed_spans.rs | 14 ++++++++++-- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Configurations.md b/Configurations.md index 58d7aac52ef..10fed5b4c1e 100644 --- a/Configurations.md +++ b/Configurations.md @@ -180,6 +180,56 @@ fn bar() { See also: [`blank_lines_lower_bound`](#blank_lines_lower_bound) + +## `indent_blank_lines` + +Empty lines are indented like items in the same block. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No (tracking issue: TBD) + +### Example + +#### `false` (default): +```rust +fn foo() { + println!("a"); + + if true { + println!("b"); + + let Some(x) = Some(x) else { + println!("c"); + + println!("d"); + }; + + println!("e"); + } +} +``` + +#### `true`: +```rust +fn foo() { + println!("a"); + + if true { + println!("b"); + + let Some(x) = Some(x) else { + println!("c"); + + println!("d"); + }; + + println!("e"); + } +} +``` + + ## `brace_style` Brace style for items diff --git a/src/config/mod.rs b/src/config/mod.rs index b03674b6b3c..5f5cd24fbef 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -152,6 +152,8 @@ create_config! { "Maximum number of blank lines which can be put between items"; blank_lines_lower_bound: BlankLinesLowerBound, false, "Minimum number of blank lines which must be put between items"; + indent_blank_lines: IndentBlankLines, false, + "Indent blank lines"; edition: EditionConfig, true, "The edition of the parser (RFC 2052)"; style_edition: StyleEditionConfig, true, "The edition of the Style Guide (RFC 3338)"; version: VersionConfig, false, "Version of formatting rules"; @@ -815,6 +817,7 @@ trailing_comma = "Vertical" match_block_trailing_comma = false blank_lines_upper_bound = 1 blank_lines_lower_bound = 0 +indent_blank_lines = false edition = "2015" style_edition = "2015" version = "One" @@ -907,6 +910,7 @@ trailing_comma = "Vertical" match_block_trailing_comma = false blank_lines_upper_bound = 1 blank_lines_lower_bound = 0 +indent_blank_lines = false edition = "2015" style_edition = "2024" version = "Two" diff --git a/src/config/options.rs b/src/config/options.rs index 00f9c3f7ec1..69a3c8b8ff7 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -676,6 +676,7 @@ config_option_with_style_edition_default!( MatchBlockTrailingComma, bool, _ => false; BlankLinesUpperBound, usize, _ => 1; BlankLinesLowerBound, usize, _ => 0; + IndentBlankLines, bool, _ => false; EditionConfig, Edition, _ => Edition::Edition2015; StyleEditionConfig, StyleEdition, Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015; diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 1b23ade00d0..ce01b9d6ed2 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -121,10 +121,17 @@ impl<'a> FmtVisitor<'a> { newline_count = newline_count.saturating_sub(1); } + let indent = if self.config.indent_blank_lines() { + // FIXME: Should this include the alignment as well? + &*self.block_indent.block_only().to_string(self.config) + } else { + "" + }; + let existing_blanks = self .buffer .rsplit_terminator('\n') - .take_while(|s| s.is_empty()) + .take_while(|&line| line == indent) .count(); // Clamp new total count to configuration. @@ -133,7 +140,10 @@ impl<'a> FmtVisitor<'a> { self.config.blank_lines_upper_bound(), ); - let blank_lines = "\n".repeat(total.saturating_sub(existing_blanks)); + let blank_lines = std::iter::repeat(indent) + .take(total.saturating_sub(existing_blanks)) + .fold(String::new(), |s, indent| s + indent + "\n"); + self.push_str(&blank_lines); } From 44dfbf2c429c074aa0f3e9addd651d58ba6a0a88 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sun, 13 Apr 2025 03:29:24 +0300 Subject: [PATCH 4/5] indent_blank_lines: Inhibit erroring on trailing whitespace when the line is empty --- src/formatting.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/formatting.rs b/src/formatting.rs index 1e1e329f624..4edae7ca17b 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -494,6 +494,7 @@ struct FormatLines<'a> { name: &'a FileName, skipped_range: &'a [(usize, usize)], last_was_space: bool, + line_all_space: bool, line_len: usize, cur_line: usize, newline_count: usize, @@ -514,6 +515,7 @@ impl<'a> FormatLines<'a> { name, skipped_range, last_was_space: false, + line_all_space: true, line_len: 0, cur_line: 1, newline_count: 0, @@ -546,6 +548,7 @@ impl<'a> FormatLines<'a> { if self.last_was_space { if self.should_report_error(kind, &ErrorKind::TrailingWhitespace) && !self.is_skipped_line() + && !(self.line_all_space && self.config.indent_blank_lines()) { self.push_err( ErrorKind::TrailingWhitespace, @@ -575,6 +578,7 @@ impl<'a> FormatLines<'a> { .contains_line(self.name, self.cur_line); self.newline_count += 1; self.last_was_space = false; + self.line_all_space = true; self.line_buffer.clear(); self.current_line_contains_string_literal = false; } @@ -587,6 +591,7 @@ impl<'a> FormatLines<'a> { 1 }; self.last_was_space = c.is_whitespace(); + self.line_all_space &= self.last_was_space; self.line_buffer.push(c); if kind.is_string() { self.current_line_contains_string_literal = true; From 1f9c15b840303fe2eac0b8d8d373e8bfc53915fe Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Sun, 13 Apr 2025 06:26:20 +0300 Subject: [PATCH 5/5] indent_blank_lines: Add basic tests --- .../source/configs/indent_blank_lines/true.rs | 40 ++++++++++++++++++ .../target/configs/indent_blank_lines/true.rs | 41 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/source/configs/indent_blank_lines/true.rs create mode 100644 tests/target/configs/indent_blank_lines/true.rs diff --git a/tests/source/configs/indent_blank_lines/true.rs b/tests/source/configs/indent_blank_lines/true.rs new file mode 100644 index 00000000000..91aa11708d6 --- /dev/null +++ b/tests/source/configs/indent_blank_lines/true.rs @@ -0,0 +1,40 @@ +// rustfmt-indent_blank_lines: true + +fn foo() { + if true { + println!("a"); + + match 4 { + 0 => { + println!("b"); + + println!("c"); + // FIXME: empty lines between match arms currently ignore this configuration: + } + + x => { + x = x + .wrapping_add({ + // inner block + let a = 4; + + a + 5 + }) + .unwrap(); + + if x > 10 { + println!("{x}"); + + println!("{x}"); + } + } + } + } +} + +fn bar() +where + u32: Sized, // FIXME: empty lines between where bounds ignores it + + i32: Sized, +{} \ No newline at end of file diff --git a/tests/target/configs/indent_blank_lines/true.rs b/tests/target/configs/indent_blank_lines/true.rs new file mode 100644 index 00000000000..614b956f0a4 --- /dev/null +++ b/tests/target/configs/indent_blank_lines/true.rs @@ -0,0 +1,41 @@ +// rustfmt-indent_blank_lines: true + +fn foo() { + if true { + println!("a"); + + match 4 { + 0 => { + println!("b"); + + println!("c"); + // FIXME: empty lines between match arms currently ignore this configuration: + } + + x => { + x = x + .wrapping_add({ + // inner block + let a = 4; + + a + 5 + }) + .unwrap(); + + if x > 10 { + println!("{x}"); + + println!("{x}"); + } + } + } + } +} + +fn bar() +where + u32: Sized, // FIXME: empty lines between where bounds ignores it + + i32: Sized, +{ +}