use crate::analysis::findings::Finding;
use crate::docs::fence::Fences;
use crate::docs::{BLANK_RUN_MAX, Check, Line, finding};
pub fn check(lines: &[Line<'_>], fences: &Fences, file_path: &str, out: &mut Vec<Finding>) {
multiple_blank_lines(lines, file_path, out);
trailing_blank_lines(lines, file_path, out);
unclosed_code_fence(lines, fences, file_path, out);
}
fn is_blank(line: &Line<'_>) -> bool {
line.text.trim().is_empty()
}
fn multiple_blank_lines(lines: &[Line<'_>], file_path: &str, out: &mut Vec<Finding>) {
let mut run: usize = 0;
for line in lines {
if line.in_fence || !is_blank(line) {
run = 0;
continue;
}
run += 1;
if run == BLANK_RUN_MAX + 1 {
out.push(finding(
Check::MultipleBlankLines,
file_path,
line.number - BLANK_RUN_MAX as u32,
1,
format!("more than {BLANK_RUN_MAX} consecutive blank lines"),
));
}
}
}
fn trailing_blank_lines(lines: &[Line<'_>], file_path: &str, out: &mut Vec<Finding>) {
let Some(last) = lines.last() else {
return;
};
if !is_blank(last) {
return;
}
let count = lines.iter().rev().take_while(|l| is_blank(l)).count();
out.push(finding(
Check::TrailingBlankLines,
file_path,
last.number,
1,
format!("file ends with {count} blank line(s)"),
));
}
fn unclosed_code_fence(
lines: &[Line<'_>],
fences: &Fences,
file_path: &str,
out: &mut Vec<Finding>,
) {
let delimiters = fences.delimiters();
if delimiters.len().is_multiple_of(2) {
return;
}
let opener = *delimiters
.last()
.expect("an odd count is at least one delimiter");
let text = lines
.get(opener as usize - 1)
.map_or("", |line| line.text.trim());
out.push(finding(
Check::UnclosedCodeFence,
file_path,
opener,
1,
format!("code fence `{text}` is never closed"),
));
}