1use crate::file_manager::FileEntry;
2use std::collections::HashSet;
3use std::ffi::OsString;
4use std::sync::Arc;
5
6pub struct Formatter {
7 dirs_first: bool,
8 show_hidden: bool,
9 show_system: bool,
10 case_insensitive: bool,
11 always_show: Arc<HashSet<OsString>>,
12 always_show_lowercase: Arc<HashSet<String>>,
13 pane_width: usize,
14}
15
16impl Formatter {
17 pub fn new(
18 dirs_first: bool,
19 show_hidden: bool,
20 show_system: bool,
21 case_insensitive: bool,
22 always_show: Arc<HashSet<OsString>>,
23 pane_width: usize,
24 ) -> Self {
25 let always_show_lowercase = Arc::new(
26 always_show
27 .iter()
28 .map(|s| s.to_string_lossy().to_lowercase())
29 .collect::<HashSet<String>>(),
30 );
31 Self {
32 dirs_first,
33 show_hidden,
34 show_system,
35 case_insensitive,
36 always_show,
37 always_show_lowercase,
38 pane_width,
39 }
40 }
41
42 pub fn format(&self, entries: &mut [FileEntry]) {
43 entries.sort_by(|a, b| {
45 if self.dirs_first {
46 match (a.is_dir(), b.is_dir()) {
47 (true, false) => return std::cmp::Ordering::Less,
48 (false, true) => return std::cmp::Ordering::Greater,
49 _ => {}
50 }
51 }
52 if self.case_insensitive {
53 a.lowercase_name().cmp(b.lowercase_name())
54 } else {
55 a.name_str().cmp(b.name_str())
56 }
57 });
58
59 for entry in entries.iter_mut() {
61 let suffix = if entry.is_dir() { "/" } else { "" };
62 let base_name = format!("{}{}", entry.name_str(), suffix);
63
64 let mut out = String::with_capacity(self.pane_width);
65 let mut current_w = 0;
66
67 for c in base_name.chars() {
68 let w = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
70 if current_w + w > self.pane_width {
71 if !out.is_empty() {
72 out.pop();
73 out.push('…');
74 }
75 break;
76 }
77 out.push(c);
78 current_w += w;
79 }
80
81 if current_w < self.pane_width {
82 out.push_str(&" ".repeat(self.pane_width - current_w));
83 }
84 entry.set_display_name(out);
85 }
86 }
87
88 pub fn filter_entries(&self, entries: &mut Vec<FileEntry>) {
89 entries.retain(|e| {
90 let is_exception = if self.case_insensitive {
91 self.always_show_lowercase.contains(e.lowercase_name())
92 } else {
93 self.always_show.contains(e.name())
94 };
95
96 if is_exception {
97 return true;
98 }
99
100 let hidden_ok = self.show_hidden || !e.is_hidden();
101 let system_ok = self.show_system || !e.is_system();
102 hidden_ok && system_ok
103 });
104 self.format(entries);
105 }
106}