lean_ctx_patterns/
ruff.rs1macro_rules! static_regex {
4 ($pattern:expr_2021) => {{
5 static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
6 RE.get_or_init(|| {
7 regex::Regex::new($pattern).expect(concat!("BUG: invalid static regex: ", $pattern))
8 })
9 }};
10}
11
12fn ruff_line_re() -> &'static regex::Regex {
13 static_regex!(r"^(.+?):(\d+):(\d+):\s+([A-Z]\d+)\s+(.+)$")
14}
15fn ruff_fixed_re() -> &'static regex::Regex {
16 static_regex!(r"Found (\d+) errors?.*?(\d+) fixable")
17}
18
19pub fn compress(command: &str, output: &str) -> Option<String> {
20 if command.contains("format") || command.contains("fmt") {
21 return Some(compress_format(output));
22 }
23 Some(compress_check(output))
24}
25
26fn compress_check(output: &str) -> String {
27 let trimmed = output.trim();
28 if trimmed.is_empty() || trimmed.contains("All checks passed") {
29 return "clean".to_string();
30 }
31
32 let mut by_rule: std::collections::HashMap<String, u32> = std::collections::HashMap::new();
33 let mut files: std::collections::HashSet<String> = std::collections::HashSet::new();
34 let mut issue_lines: Vec<&str> = Vec::new();
35
36 for line in trimmed.lines() {
37 if let Some(caps) = ruff_line_re().captures(line) {
38 let file = caps[1].to_string();
39 let rule = caps[4].to_string();
40 files.insert(file);
41 *by_rule.entry(rule).or_insert(0) += 1;
42 issue_lines.push(line);
43 }
44 }
45
46 if by_rule.is_empty() {
47 if let Some(caps) = ruff_fixed_re().captures(trimmed) {
48 return format!("{} errors ({} fixable)", &caps[1], &caps[2]);
49 }
50 return compact_output(trimmed, 10);
51 }
52
53 let total: u32 = by_rule.values().sum();
54
55 if total <= 30 {
56 return trimmed.to_string();
57 }
58
59 let mut rules: Vec<(String, u32)> = by_rule.into_iter().collect();
60 rules.sort_by_key(|x| std::cmp::Reverse(x.1));
61
62 let mut parts = Vec::new();
63 parts.push(format!("{total} issues in {} files", files.len()));
64 for line in issue_lines.iter().take(20) {
65 parts.push(format!(" {line}"));
66 }
67 if issue_lines.len() > 20 {
68 parts.push(format!(" ... +{} more issues", issue_lines.len() - 20));
69 }
70 parts.push(String::new());
71 parts.push("by rule:".to_string());
72 for (rule, count) in rules.iter().take(8) {
73 parts.push(format!(" {rule}: {count}"));
74 }
75 if rules.len() > 8 {
76 parts.push(format!(" ... +{} more rules", rules.len() - 8));
77 }
78
79 parts.join("\n")
80}
81
82fn compress_format(output: &str) -> String {
83 let trimmed = output.trim();
84 if trimmed.is_empty() {
85 return "ok (formatted)".to_string();
86 }
87
88 let reformatted: Vec<&str> = trimmed
89 .lines()
90 .filter(|l| l.contains("reformatted") || l.contains("would reformat"))
91 .collect();
92
93 let unchanged: Vec<&str> = trimmed
94 .lines()
95 .filter(|l| l.contains("left unchanged") || l.contains("already formatted"))
96 .collect();
97
98 if !reformatted.is_empty() {
99 return format!("{} files reformatted", reformatted.len());
100 }
101 if !unchanged.is_empty() {
102 return format!("ok ({} files already formatted)", unchanged.len());
103 }
104
105 compact_output(trimmed, 5)
106}
107
108fn compact_output(text: &str, max: usize) -> String {
109 let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
110 if lines.len() <= max {
111 return lines.join("\n");
112 }
113 format!(
114 "{}\n{}",
115 lines[..max].join("\n"),
116 super::elision_marker(lines.len() - max)
117 )
118}