sqrust_rules/layout/
max_blank_lines.rs1use sqrust_core::{Diagnostic, FileContext, Rule};
2
3pub struct MaxBlankLines {
4 pub max_blank_lines: usize,
5}
6
7impl Default for MaxBlankLines {
8 fn default() -> Self {
9 Self { max_blank_lines: 1 }
10 }
11}
12
13impl Rule for MaxBlankLines {
14 fn name(&self) -> &'static str {
15 "MaxBlankLines"
16 }
17
18 fn check(&self, ctx: &FileContext) -> Vec<Diagnostic> {
19 let mut diags = Vec::new();
20 let lines: Vec<&str> = ctx.source.lines().collect();
21
22 let mut i = 0;
23 while i < lines.len() {
24 if lines[i].trim().is_empty() {
25 let run_start = i; let mut run_len = 0usize;
28 while i < lines.len() && lines[i].trim().is_empty() {
29 run_len += 1;
30 i += 1;
31 }
32 if run_len > self.max_blank_lines {
34 let violation_line = run_start + self.max_blank_lines + 1; diags.push(Diagnostic {
37 rule: self.name(),
38 message: format!(
39 "Too many consecutive blank lines ({} found, maximum is {})",
40 run_len,
41 self.max_blank_lines
42 ),
43 line: violation_line,
44 col: 1,
45 });
46 }
47 } else {
48 i += 1;
49 }
50 }
51
52 diags
53 }
54
55 fn fix(&self, ctx: &FileContext) -> Option<String> {
56 let violations = self.check(ctx);
57 if violations.is_empty() {
58 return None;
59 }
60
61 let lines: Vec<&str> = ctx.source.lines().collect();
62 let mut result: Vec<&str> = Vec::with_capacity(lines.len());
63 let mut blank_run = 0usize;
64
65 for line in &lines {
66 if line.trim().is_empty() {
67 blank_run += 1;
68 if blank_run <= self.max_blank_lines {
69 result.push(line);
70 }
71 } else {
73 blank_run = 0;
74 result.push(line);
75 }
76 }
77
78 let mut fixed = result.join("\n");
79 if ctx.source.ends_with('\n') {
80 fixed.push('\n');
81 }
82 Some(fixed)
83 }
84}