lx_cli/formatter/
short.rs1use crate::config::Config;
2use crate::file_entry::{FileEntry, FileType};
3use colored::Colorize;
4use unicode_width::UnicodeWidthStr;
5
6pub fn format_short(entries: Vec<FileEntry>, config: &Config) {
7 let mut directories: Vec<FileEntry> = Vec::new();
8 let mut executables: Vec<FileEntry> = Vec::new();
9 let mut regular_files: Vec<FileEntry> = Vec::new();
10
11 for entry in entries {
12 match entry.get_file_type() {
13 FileType::Directory => directories.push(entry),
14 FileType::Executable => executables.push(entry),
15 FileType::RegularFile => regular_files.push(entry),
16 }
17 }
18
19 directories.sort_by(|a, b| {
21 let a_name = a.path.file_name().unwrap().to_string_lossy();
22 let b_name = b.path.file_name().unwrap().to_string_lossy();
23 a_name.cmp(&b_name)
24 });
25 executables.sort_by(|a, b| {
26 let a_name = a.path.file_name().unwrap().to_string_lossy();
27 let b_name = b.path.file_name().unwrap().to_string_lossy();
28 a_name.cmp(&b_name)
29 });
30 regular_files.sort_by(|a, b| {
31 let a_name = a.path.file_name().unwrap().to_string_lossy();
32 let b_name = b.path.file_name().unwrap().to_string_lossy();
33 a_name.cmp(&b_name)
34 });
35
36 let column_spacing = config.display.column_spacing;
37 let max_rows = config.display.max_rows;
38
39 if max_rows > 0 {
42 format_with_max_rows(
43 directories,
44 executables,
45 regular_files,
46 max_rows,
47 column_spacing,
48 config,
49 );
50 } else {
51 format_single_column_per_type(
52 directories,
53 executables,
54 regular_files,
55 column_spacing,
56 config,
57 );
58 }
59}
60
61fn format_with_max_rows(
62 directories: Vec<FileEntry>,
63 executables: Vec<FileEntry>,
64 regular_files: Vec<FileEntry>,
65 max_rows: usize,
66 column_spacing: usize,
67 config: &Config,
68) {
69 let dir_num_cols = if directories.is_empty() {
71 0
72 } else {
73 (directories.len() + max_rows - 1) / max_rows
74 };
75
76 let exec_num_cols = if executables.is_empty() {
77 0
78 } else {
79 (executables.len() + max_rows - 1) / max_rows
80 };
81
82 let file_num_cols = if regular_files.is_empty() {
83 0
84 } else {
85 (regular_files.len() + max_rows - 1) / max_rows
86 };
87
88 let dir_width = directories
90 .iter()
91 .map(|e| {
92 let filename = e.path.file_name().unwrap().to_string_lossy();
93 UnicodeWidthStr::width(e.get_icon().as_str())
94 + 1
95 + UnicodeWidthStr::width(filename.as_ref())
96 })
97 .max()
98 .unwrap_or(0);
99
100 let exec_width = executables
101 .iter()
102 .map(|e| {
103 let filename = e.path.file_name().unwrap().to_string_lossy();
104 UnicodeWidthStr::width(e.get_icon().as_str())
105 + 1
106 + UnicodeWidthStr::width(filename.as_ref())
107 })
108 .max()
109 .unwrap_or(0);
110
111 let file_width = regular_files
112 .iter()
113 .map(|e| {
114 let filename = e.path.file_name().unwrap().to_string_lossy();
115 UnicodeWidthStr::width(e.get_icon().as_str())
116 + 1
117 + UnicodeWidthStr::width(filename.as_ref())
118 })
119 .max()
120 .unwrap_or(0);
121
122 for row in 0..max_rows {
124 let mut line = String::new();
125 let mut has_any_content = false;
126
127 if dir_num_cols > 0 {
129 for col in 0..dir_num_cols {
130 let idx = col * max_rows + row;
131 if idx < directories.len() {
132 if col > 0 {
133 line.push_str(&" ".repeat(column_spacing));
134 }
135
136 let entry = &directories[idx];
137 let filename = entry.path.file_name().unwrap().to_string_lossy();
138 let icon = entry.get_icon();
139 let actual_width = UnicodeWidthStr::width(icon.as_str())
140 + 1
141 + UnicodeWidthStr::width(filename.as_ref());
142
143 line.push_str(&format!(
144 "{} {}",
145 icon,
146 filename.color(entry.get_color(&config.colors)).bold()
147 ));
148
149 if actual_width < dir_width {
150 line.push_str(&" ".repeat(dir_width - actual_width));
151 }
152 has_any_content = true;
153 } else if col == 0 {
154 line.push_str(&" ".repeat(dir_width));
156 } else {
157 line.push_str(&" ".repeat(column_spacing + dir_width));
158 }
159 }
160
161 if exec_num_cols > 0 || file_num_cols > 0 {
163 line.push_str(&" ".repeat(column_spacing));
164 }
165 }
166
167 if exec_num_cols > 0 {
169 for col in 0..exec_num_cols {
170 let idx = col * max_rows + row;
171 if idx < executables.len() {
172 if col > 0 {
173 line.push_str(&" ".repeat(column_spacing));
174 }
175
176 let entry = &executables[idx];
177 let filename = entry.path.file_name().unwrap().to_string_lossy();
178 let icon = entry.get_icon();
179 let actual_width = UnicodeWidthStr::width(icon.as_str())
180 + 1
181 + UnicodeWidthStr::width(filename.as_ref());
182
183 line.push_str(&format!(
184 "{} {}",
185 icon,
186 filename.color(entry.get_color(&config.colors)).bold()
187 ));
188
189 if actual_width < exec_width {
190 line.push_str(&" ".repeat(exec_width - actual_width));
191 }
192 has_any_content = true;
193 } else if col == 0 {
194 line.push_str(&" ".repeat(exec_width));
195 } else {
196 line.push_str(&" ".repeat(column_spacing + exec_width));
197 }
198 }
199
200 if file_num_cols > 0 {
202 line.push_str(&" ".repeat(column_spacing));
203 }
204 }
205
206 if file_num_cols > 0 {
208 for col in 0..file_num_cols {
209 let idx = col * max_rows + row;
210 if idx < regular_files.len() {
211 if col > 0 {
212 line.push_str(&" ".repeat(column_spacing));
213 }
214
215 let entry = ®ular_files[idx];
216 let filename = entry.path.file_name().unwrap().to_string_lossy();
217 let icon = entry.get_icon();
218 let actual_width = UnicodeWidthStr::width(icon.as_str())
219 + 1
220 + UnicodeWidthStr::width(filename.as_ref());
221
222 line.push_str(&format!(
223 "{} {}",
224 icon,
225 filename.color(entry.get_color(&config.colors))
226 ));
227
228 if col < file_num_cols - 1 && actual_width < file_width {
229 line.push_str(&" ".repeat(file_width - actual_width));
230 }
231 has_any_content = true;
232 }
233 }
234 }
235
236 if has_any_content {
237 println!("{}", line.trim_end());
238 }
239 }
240}
241
242fn format_single_column_per_type(
243 directories: Vec<FileEntry>,
244 executables: Vec<FileEntry>,
245 regular_files: Vec<FileEntry>,
246 column_spacing: usize,
247 config: &Config,
248) {
249 let dir_width = directories
251 .iter()
252 .map(|e| {
253 let filename = e.path.file_name().unwrap().to_string_lossy();
254 UnicodeWidthStr::width(e.get_icon().as_str())
255 + 1
256 + UnicodeWidthStr::width(filename.as_ref())
257 })
258 .max()
259 .unwrap_or(0);
260
261 let exec_width = executables
262 .iter()
263 .map(|e| {
264 let filename = e.path.file_name().unwrap().to_string_lossy();
265 UnicodeWidthStr::width(e.get_icon().as_str())
266 + 1
267 + UnicodeWidthStr::width(filename.as_ref())
268 })
269 .max()
270 .unwrap_or(0);
271
272 let file_width = regular_files
273 .iter()
274 .map(|e| {
275 let filename = e.path.file_name().unwrap().to_string_lossy();
276 UnicodeWidthStr::width(e.get_icon().as_str())
277 + 1
278 + UnicodeWidthStr::width(filename.as_ref())
279 })
280 .max()
281 .unwrap_or(0);
282
283 let max_rows = *[directories.len(), executables.len(), regular_files.len()]
285 .iter()
286 .max()
287 .unwrap_or(&0);
288
289 for i in 0..max_rows {
291 let mut line = String::new();
292
293 if dir_width > 0 {
295 if i < directories.len() {
296 let entry = &directories[i];
297 let filename = entry.path.file_name().unwrap().to_string_lossy();
298 let icon = entry.get_icon();
299 let actual_width = UnicodeWidthStr::width(icon.as_str())
300 + 1
301 + UnicodeWidthStr::width(filename.as_ref());
302
303 line.push_str(&format!(
304 "{} {}",
305 icon,
306 filename.color(entry.get_color(&config.colors)).bold()
307 ));
308 if actual_width < dir_width {
310 line.push_str(&" ".repeat(dir_width - actual_width));
311 }
312 } else {
313 line.push_str(&" ".repeat(dir_width));
315 }
316
317 if exec_width > 0 || file_width > 0 {
319 line.push_str(&" ".repeat(column_spacing));
320 }
321 }
322
323 if exec_width > 0 {
325 if i < executables.len() {
326 let entry = &executables[i];
327 let filename = entry.path.file_name().unwrap().to_string_lossy();
328 let icon = entry.get_icon();
329 let actual_width = UnicodeWidthStr::width(icon.as_str())
330 + 1
331 + UnicodeWidthStr::width(filename.as_ref());
332
333 line.push_str(&format!(
334 "{} {}",
335 icon,
336 filename.color(entry.get_color(&config.colors)).bold()
337 ));
338 if actual_width < exec_width {
340 line.push_str(&" ".repeat(exec_width - actual_width));
341 }
342 } else {
343 line.push_str(&" ".repeat(exec_width));
345 }
346
347 if file_width > 0 {
349 line.push_str(&" ".repeat(column_spacing));
350 }
351 }
352
353 if i < regular_files.len() {
355 let entry = ®ular_files[i];
356 let filename = entry.path.file_name().unwrap().to_string_lossy();
357 let icon = entry.get_icon();
358 let actual_width = UnicodeWidthStr::width(icon.as_str())
359 + 1
360 + UnicodeWidthStr::width(filename.as_ref());
361
362 line.push_str(&format!(
363 "{} {}",
364 icon,
365 filename.color(entry.get_color(&config.colors))
366 ));
367 if actual_width < file_width {
369 line.push_str(&" ".repeat(file_width - actual_width));
370 }
371 }
372
373 println!("{}", line.trim_end());
374 }
375}