use colored::Colorize;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::path::PathBuf;
use linthis::utils::language::language_from_path;
use linthis::utils::types::{LintIssue, Severity};
use linthis::Language;
pub struct RecheckResult {
pub issues: Vec<LintIssue>,
pub files_checked: usize,
}
pub fn recheck_modified_files(
modified_files: &HashSet<PathBuf>,
original_issues: &[LintIssue],
quiet: bool,
verbose: bool,
) -> RecheckResult {
let mut file_languages: HashMap<PathBuf, Language> = HashMap::new();
for issue in original_issues {
if let Some(lang) = issue.language {
file_languages.insert(issue.file_path.clone(), lang);
}
}
let modified_count = modified_files.len();
let mut recheck_issues = Vec::new();
const SPINNER: [char; 10] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
for (i, file) in modified_files.iter().enumerate() {
if !quiet {
eprint!(
"\r\x1b[K\x1b[36m{}\x1b[0m Rechecking {}/{}...",
SPINNER[i % SPINNER.len()],
i + 1,
modified_count
);
std::io::stderr().flush().ok();
}
let lang = file_languages
.get(file)
.copied()
.or_else(|| language_from_path(file));
if let Some(lang) = lang {
if let Some(checker) = linthis::get_checker(lang) {
if checker.is_available() {
match checker.check(file) {
Ok(file_issues) => {
for mut issue in file_issues {
issue.language = Some(lang);
recheck_issues.push(issue);
}
}
Err(e) => {
if verbose {
eprintln!("\n Check error for {}: {}", file.display(), e);
}
}
}
}
}
}
}
if !quiet {
eprint!("\r");
std::io::stderr().flush().ok();
}
RecheckResult {
issues: recheck_issues,
files_checked: modified_count,
}
}
pub fn print_recheck_header() {
println!();
println!("{}", "═".repeat(60).dimmed());
println!(" {}", "Rechecking modified files...".bold());
println!("{}", "─".repeat(60).dimmed());
}
pub fn print_recheck_footer() {
println!("{}", "═".repeat(60).dimmed());
println!();
}
pub fn print_recheck_summary(recheck_result: &RecheckResult, fixed_count: usize) {
let remaining_count = recheck_result.issues.len();
let modified_count = recheck_result.files_checked;
if remaining_count == 0 {
println!(
" {} All issues in modified files have been resolved!",
"✓".green().bold()
);
println!(
" {} file(s) modified, {} issue(s) fixed",
modified_count, fixed_count
);
} else {
println!(
" {} {} remaining issue(s) in modified files",
"⚠".yellow(),
remaining_count
);
println!(
" {} file(s) modified, {} issue(s) fixed",
modified_count, fixed_count
);
println!();
let errors = recheck_result
.issues
.iter()
.filter(|i| i.severity == Severity::Error)
.count();
let warnings = recheck_result
.issues
.iter()
.filter(|i| i.severity == Severity::Warning)
.count();
for issue in &recheck_result.issues {
let severity_badge = match issue.severity {
Severity::Error => "ERROR".red().bold(),
Severity::Warning => "WARNING".yellow(),
Severity::Info => "INFO".blue(),
};
let location = if let Some(col) = issue.column {
format!("{}:{}:{}", issue.file_path.display(), issue.line, col)
} else {
format!("{}:{}", issue.file_path.display(), issue.line)
};
println!(" {} {} {}", severity_badge, location, issue.message);
}
println!();
println!(" Summary: {} error(s), {} warning(s)", errors, warnings);
}
}