mdlint/lint/rules/
md028.rs1use crate::lint::rule::Rule;
2use crate::markdown::MarkdownParser;
3use crate::types::Violation;
4use serde_json::Value;
5
6pub struct MD028;
7
8impl Rule for MD028 {
9 fn name(&self) -> &str {
10 "MD028"
11 }
12
13 fn description(&self) -> &str {
14 "Blank line inside blockquote"
15 }
16
17 fn tags(&self) -> &[&str] {
18 &["blockquote", "whitespace"]
19 }
20
21 fn check(&self, parser: &MarkdownParser, _config: Option<&Value>) -> Vec<Violation> {
22 let mut violations = Vec::new();
23 let lines = parser.lines();
24 let mut in_blockquote = false;
25
26 for (line_num, line) in lines.iter().enumerate() {
27 let line_number = line_num + 1;
28 let trimmed = line.trim_start();
29
30 let is_blockquote_line = trimmed.starts_with('>');
31 let is_blank = line.trim().is_empty();
32
33 if is_blockquote_line {
34 in_blockquote = true;
35 } else if is_blank && in_blockquote {
36 let mut found_continuation = false;
38 for future_line in lines.iter().skip(line_num + 1) {
39 let trimmed_start = future_line.trim_start();
40 if trimmed_start.starts_with('>') {
41 found_continuation = true;
42 break;
43 } else if !future_line.trim().is_empty() {
44 break;
46 }
47 }
48
49 if found_continuation {
50 violations.push(Violation {
51 line: line_number,
52 column: Some(1),
53 rule: self.name().to_string(),
54 message: "Blank line inside blockquote".to_string(),
55 fix: None,
56 });
57 in_blockquote = false;
59 } else {
60 in_blockquote = false;
61 }
62 } else if !is_blank {
63 in_blockquote = false;
65 }
66 }
67
68 violations
69 }
70
71 fn fixable(&self) -> bool {
72 false
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_continuous_blockquote() {
82 let content = "> Line 1\n> Line 2\n> Line 3";
83 let parser = MarkdownParser::new(content);
84 let rule = MD028;
85 let violations = rule.check(&parser, None);
86
87 assert_eq!(violations.len(), 0);
88 }
89
90 #[test]
91 fn test_blank_inside_blockquote() {
92 let content = "> Line 1\n\n> Line 2";
93 let parser = MarkdownParser::new(content);
94 let rule = MD028;
95 let violations = rule.check(&parser, None);
96
97 assert_eq!(violations.len(), 1);
98 assert_eq!(violations[0].line, 2);
99 }
100
101 #[test]
102 fn test_blank_ends_blockquote() {
103 let content = "> Line 1\n\nNormal text";
104 let parser = MarkdownParser::new(content);
105 let rule = MD028;
106 let violations = rule.check(&parser, None);
107
108 assert_eq!(violations.len(), 0); }
110
111 #[test]
112 fn test_multiple_blank_lines() {
113 let content = "> Line 1\n\n\n> Line 2";
114 let parser = MarkdownParser::new(content);
115 let rule = MD028;
116 let violations = rule.check(&parser, None);
117
118 assert_eq!(violations.len(), 1); }
120}