lint 0.1.3

A versatile linting tool with CLI, MCP, and library interfaces
Documentation
use crate::output::LintMessage;
use crate::rules::Rule;
use std::path::Path;

pub struct NoConsecutiveEmptyLinesRule;

impl Rule for NoConsecutiveEmptyLinesRule {
    fn name(&self) -> &str {
        "no-consecutive-empty-lines"
    }

    fn category(&self) -> &str {
        "style"
    }

    fn description(&self) -> &str {
        "More than one consecutive empty line."
    }

    fn has_fix(&self) -> bool {
        true
    }

    fn check(&self, content: &str, _file_path: &Path) -> Vec<LintMessage> {
        let mut messages = Vec::new();
        let lines: Vec<&str> = content.lines().collect();
        let mut prev_empty = false;
        let mut had_consecutive = false;

        for (i, line) in lines.iter().enumerate() {
            let is_empty = line.trim().is_empty();
            if is_empty && prev_empty {
                messages.push(LintMessage::new(
                    i + 1,
                    1,
                    self.default_severity(),
                    "Consecutive empty lines detected".to_string(),
                    self.name().to_string(),
                    Some("Remove extra blank lines; files should not contain more than one consecutive empty line.".to_string()),
                ));
                had_consecutive = true;
            }
            prev_empty = is_empty;
        }

        if had_consecutive {
            let fixed = collapse_empty_lines(content);
            if let Some(first) = messages.first_mut() {
                first.fix = Some(crate::output::Fix {
                    line: 0,
                    replacement: fixed,
                    is_safe: true,
                });
            }
        }

        messages
    }
}

fn collapse_empty_lines(content: &str) -> String {
    let lines: Vec<&str> = content.lines().collect();
    let mut result = Vec::new();
    let mut prev_empty = false;

    for line in lines {
        let is_empty = line.trim().is_empty();
        if is_empty && prev_empty {
            continue;
        }
        result.push(line);
        prev_empty = is_empty;
    }

    let mut output = result.join("\n");
    if content.ends_with('\n') || content.ends_with("\r\n") {
        output.push('\n');
    }
    output
}