lean_ctx/core/patterns/
find.rs1use std::collections::HashMap;
2
3pub fn compress(output: &str) -> Option<String> {
4 let lines: Vec<&str> = output.lines().filter(|l| !l.trim().is_empty()).collect();
5 if lines.len() < 5 {
6 return None;
7 }
8
9 let mut by_dir: HashMap<String, Vec<String>> = HashMap::new();
10 let mut total_files = 0usize;
11
12 for line in &lines {
13 let path = line.trim().strip_prefix("./").unwrap_or(line.trim());
14
15 if should_skip(path) {
16 continue;
17 }
18
19 total_files += 1;
20
21 if let Some(slash_pos) = path.rfind('/') {
22 let dir = &path[..slash_pos];
23 let file = &path[slash_pos + 1..];
24 by_dir
25 .entry(dir.to_string())
26 .or_default()
27 .push(file.to_string());
28 } else {
29 by_dir
30 .entry(".".to_string())
31 .or_default()
32 .push(path.to_string());
33 }
34 }
35
36 if total_files == 0 {
37 return None;
38 }
39
40 let mut result = format!("{total_files}F {}D:\n", by_dir.len());
41 let mut sorted_dirs: Vec<_> = by_dir.iter().collect();
42 sorted_dirs.sort_by_key(|(dir, _)| (*dir).clone());
43
44 for (dir, files) in &sorted_dirs {
45 result.push_str(&format!("\n{dir}/"));
46 let show: Vec<_> = files.iter().take(10).collect();
47 let mut line_buf = String::new();
48 for f in &show {
49 if line_buf.len() + f.len() + 1 > 60 {
50 result.push_str(&format!("\n {line_buf}"));
51 line_buf.clear();
52 }
53 if !line_buf.is_empty() {
54 line_buf.push(' ');
55 }
56 line_buf.push_str(f);
57 }
58 if !line_buf.is_empty() {
59 result.push_str(&format!("\n {line_buf}"));
60 }
61 if files.len() > 10 {
62 result.push_str(&format!("\n ... +{} more", files.len() - 10));
63 }
64 }
65
66 Some(result)
67}
68
69fn should_skip(path: &str) -> bool {
70 let skip_dirs = [
71 "node_modules/",
72 ".git/",
73 "target/debug/",
74 "target/release/",
75 "__pycache__/",
76 ".svelte-kit/",
77 ".next/",
78 "dist/",
79 ".DS_Store",
80 ];
81 skip_dirs.iter().any(|d| path.contains(d))
82}