1use crate::parser::flatten_nodes_with_content;
2use crate::rules::extensions::VecExt;
3use crate::ruleset::{RuleResult, RuleResultDetails};
4use comrak::nodes::AstNode;
5
6pub(crate) fn check<'a>(root: &'a AstNode<'a>) -> RuleResult {
7 let mut details: Vec<RuleResultDetails> = Vec::new();
9 let nodes = flatten_nodes_with_content(root);
10 nodes.iter().enumerate().for_each(|(i, n)| {
11 if i == 0 {
12 return;
13 }
14 let curr = n.data.borrow();
15 let prev_end_line = nodes[i - 1].data.borrow().end_line;
16 let curr_start_line = curr.start_line;
17 if curr_start_line - prev_end_line > 2 {
18 details.push(RuleResultDetails::from_node(&curr));
19 }
20 });
21
22 RuleResult::new(
23 "MD012",
24 "no-multiple-blanks",
25 "Multiple consecutive blank lines",
26 details.to_option(),
27 )
28}
29
30#[cfg(test)]
31mod test {
32
33 use super::*;
34 use crate::parser::get_ast;
35 use crate::rules::common_tests;
36 use typed_arena::Arena;
37
38 #[test]
39 fn it_does_not_have_details_if_all_ok() {
40 common_tests::all_ok("fixtures/md012/md012_ok.md", Box::new(check));
41 }
42
43 #[test]
44 fn it_has_details_if_ko() {
45 let arena = Arena::new();
46 let root = get_ast("fixtures/md012/md012_ko.md", &arena);
47 let result = check(root);
48 assert!(result.details.is_some());
49 let details = result.details.unwrap();
50 assert_eq!(details.len(), 3);
51 let first = &details[0];
52 assert_eq!(first.line, 4);
53 assert_eq!(first.column, 1);
54 let second = &details[1];
55 assert_eq!(second.line, 9);
56 assert_eq!(second.column, 1);
57 assert_eq!(second.content, "This is another line\n");
58 let third = &details[2];
59 assert_eq!(third.line, 21);
60 assert_eq!(third.column, 3);
61 assert_eq!(third.content, "item two\n");
62 }
63}