use forge::signal::compactor;
use once_cell::sync::Lazy;
use regex::Regex;
static FINDING_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^([^\s:]+\.[a-zA-Z]+:\d+:\d+)\s+(lint/[^\s]+|format/[^\s]+)").unwrap()
});
static RULE_EXPLAIN_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^ ─+[^\n]*\n?").unwrap());
static CHECKED_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?m)^Checked (\d+) files in [^\n]+\n?").unwrap());
static FOUND_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^Found [^\n]+\n?").unwrap());
pub fn compress_biome(subcmd: &str, raw: &str) -> String {
let sub = subcmd.trim();
if sub.starts_with("format") && raw.contains("Formatted") {
return compress_format(raw);
}
compress_lint(raw)
}
fn compress_lint(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
if cleaned.contains("Found 0 diagnostics") || cleaned.trim().is_empty() {
let checked = CHECKED_RE
.captures(&cleaned)
.and_then(|c| c.get(1))
.map(|m| m.as_str())
.unwrap_or("?");
return format!("biome: no issues ({checked} files checked)");
}
let s = RULE_EXPLAIN_RE.replace_all(&cleaned, "");
let mut by_file: std::collections::HashMap<String, Vec<String>> =
std::collections::HashMap::new();
let mut summary_lines: Vec<String> = Vec::new();
let mut current_file = String::new();
for line in s.lines() {
let t = line.trim();
if t.is_empty() {
continue;
}
if CHECKED_RE.is_match(t) || FOUND_RE.is_match(t) {
summary_lines.push(t.to_string());
continue;
}
if let Some(caps) = FINDING_RE.captures(t) {
let loc = &caps[1];
let rule = &caps[2];
let file = loc.split(':').next().unwrap_or(loc).to_string();
current_file = file.clone();
by_file
.entry(file)
.or_default()
.push(format!("{loc} {rule}"));
continue;
}
if !current_file.is_empty()
&& (t.starts_with('×')
|| t.starts_with('!')
|| t.starts_with("error")
|| t.starts_with("warn"))
{
if let Some(v) = by_file.get_mut(¤t_file) {
if let Some(last) = v.last_mut() {
last.push_str(&format!(" — {t}"));
}
}
}
}
let mut result: Vec<String> = Vec::new();
let mut files: Vec<&String> = by_file.keys().collect();
files.sort();
for file in files {
let findings = &by_file[file];
result.push(format!("{file} — {} finding(s)", findings.len()));
for f in findings.iter().take(5) {
result.push(format!(" {f}"));
}
if findings.len() > 5 {
result.push(format!(" … {} more", findings.len() - 5));
}
}
result.extend(summary_lines);
if result.is_empty() {
return compactor::collapse_blanks(&cleaned);
}
result.join("\n")
}
fn compress_format(raw: &str) -> String {
let cleaned = compactor::normalise(raw);
let formatted: Vec<&str> = cleaned
.lines()
.filter(|l| l.trim().starts_with("Formatted"))
.collect();
let errors: Vec<&str> = cleaned
.lines()
.filter(|l| l.to_lowercase().contains("error"))
.collect();
let mut result: Vec<&str> = formatted;
result.extend(errors);
if result.is_empty() {
return compactor::collapse_blanks(&cleaned);
}
result.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_issues_returns_clean() {
let raw = "Checked 42 files in 1234ms\nFound 0 diagnostics\n";
let out = compress_biome("check", raw);
assert!(out.contains("no issues"), "{out}");
assert!(out.contains("42"), "{out}");
}
#[test]
fn groups_findings_by_file() {
let raw = "src/foo.ts:10:5 lint/suspicious/noExplicitAny ━━━━\n× Unexpected any\nsrc/foo.ts:20:1 lint/complexity/noUselessFragments ━━━━\n× Remove fragment\nsrc/bar.ts:5:3 lint/style/noVar ━━━━\n× Use let/const\nChecked 2 files in 200ms\nFound 3 diagnostics\n";
let out = compress_biome("check", &raw);
assert!(out.contains("foo.ts"), "{out}");
assert!(out.contains("bar.ts"), "{out}");
assert!(
out.contains("finding") || out.contains("diagnostic"),
"{out}"
);
}
#[test]
fn format_write_keeps_summary() {
let raw = "Formatted 12 files\n";
let out = compress_biome("format", raw);
assert!(out.contains("Formatted 12"), "{out}");
}
#[test]
fn strips_rule_explanation_dividers() {
let raw = "src/a.ts:1:1 lint/suspicious/noExplicitAny ━━━━━━━━━━\n ─────────────────────────────────────────\n × avoid using any\n ─────────────────────────────────────────\nFound 1 diagnostic\n";
let out = compress_biome("lint", &raw);
assert!(!out.contains("━━━━━━━━━━") || out.contains("a.ts"), "{out}");
}
}