Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b5b6aba

Browse files
committed
Refactors entries panel by moving code to separate functions
1 parent 917339f commit b5b6aba

1 file changed

Lines changed: 207 additions & 138 deletions

File tree

src/interactive/widgets/entries.rs

Lines changed: 207 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::interactive::{
44
DisplayOptions, EntryDataBundle, SortMode,
55
};
66
use chrono::DateTime;
7-
use dua::traverse::{Tree, TreeIndex};
7+
use dua::traverse::{EntryData, Tree, TreeIndex};
88
use itertools::Itertools;
99
use std::{borrow::Borrow, path::Path};
1010
use tui::{
@@ -65,36 +65,12 @@ impl Entries {
6565
};
6666

6767
let total: u128 = entries.iter().map(|b| b.data.size).sum();
68-
let title = match path_of(tree, *root).to_string_lossy().to_string() {
69-
ref p if p.is_empty() => Path::new(".")
70-
.canonicalize()
71-
.map(|p| p.to_string_lossy().to_string())
72-
.unwrap_or_else(|_| String::from(".")),
73-
p => p,
74-
};
75-
let title = format!(
76-
" {} ({} item{}) ",
77-
title,
78-
entries.len(),
79-
match entries.len() {
80-
1 => "",
81-
_ => "s",
82-
}
83-
);
84-
let block = Block::default()
85-
.title(title.as_str())
86-
.border_style(*border_style)
87-
.borders(Borders::ALL);
88-
let entry_in_view = selected.map(|selected| {
89-
entries
90-
.iter()
91-
.find_position(|b| b.index == selected)
92-
.map(|(idx, _)| idx)
93-
.unwrap_or(0)
94-
});
68+
let title = title(&current_path(tree, root), entries.len());
69+
let title_block = title_block(&title, border_style);
70+
let entry_in_view = entry_in_view(selected, entries);
9571

9672
let props = ListProps {
97-
block: Some(block),
73+
block: Some(title_block),
9874
entry_in_view,
9975
};
10076
let lines = entries.iter().map(
@@ -104,129 +80,222 @@ impl Entries {
10480
is_dir,
10581
exists,
10682
}| {
107-
let mut style = Style::default();
108-
let is_selected = if let Some(idx) = selected {
109-
*idx == *node_idx
110-
} else {
111-
false
112-
};
113-
if is_selected {
114-
style.add_modifier.insert(Modifier::REVERSED);
115-
}
116-
if *is_focussed & is_selected {
117-
style.add_modifier.insert(Modifier::BOLD);
118-
}
119-
83+
let is_selected = is_selected(selected, node_idx);
12084
let fraction = w.size as f32 / total as f32;
121-
let should_avoid_showing_a_big_reversed_bar = fraction > 0.9;
122-
let local_style = if should_avoid_showing_a_big_reversed_bar {
123-
style.remove_modifier(Modifier::REVERSED)
124-
} else {
125-
style
126-
};
127-
128-
let datetime = DateTime::<chrono::Utc>::from(w.mtime);
129-
let formatted_time = datetime.format("%d/%m/%Y %H:%M:%S").to_string();
130-
let mtime = Span::styled(
131-
format!("{:>20}", formatted_time),
132-
Style {
133-
fg: match sort_mode {
134-
SortMode::SizeAscending | SortMode::SizeDescending => style.fg,
135-
SortMode::MTimeAscending | SortMode::MTimeDescending => {
136-
Color::Green.into()
137-
}
138-
},
139-
..style
140-
},
141-
);
142-
143-
let bar = Span::styled(" | ", local_style);
144-
145-
let bytes = Span::styled(
146-
format!(
147-
"{:>byte_column_width$}",
148-
display.byte_format.display(w.size).to_string(), // we would have to impl alignment/padding ourselves otherwise...
149-
byte_column_width = display.byte_format.width()
150-
),
151-
Style {
152-
fg: match sort_mode {
153-
SortMode::SizeAscending | SortMode::SizeDescending => {
154-
Color::Green.into()
155-
}
156-
SortMode::MTimeAscending | SortMode::MTimeDescending => style.fg,
157-
},
158-
..style
159-
},
160-
);
161-
162-
let left_bar = Span::styled(" |", local_style);
163-
let percentage = Span::styled(
164-
format!("{}", display.byte_vis.display(fraction)),
165-
local_style,
166-
);
167-
let right_bar = Span::styled("| ", local_style);
168-
169-
let name = Span::styled(
170-
fill_background_to_right(
171-
format!(
172-
"{prefix}{}",
173-
w.name.to_string_lossy(),
174-
prefix = if *is_dir && !is_top(*root) { "/" } else { " " }
175-
),
176-
area.width,
177-
),
178-
{
179-
let is_marked = marked.map(|m| m.contains_key(node_idx)).unwrap_or(false);
180-
let fg = if !exists {
181-
// non-existing - always red!
182-
Some(Color::Red)
183-
} else {
184-
entry_color(style.fg, !*is_dir, is_marked)
185-
};
186-
Style { fg, ..style }
187-
},
188-
);
85+
let style = style(is_selected, is_focussed);
18986

87+
let mut columns = Vec::new();
19088
if should_show_mtime_column(sort_mode) {
191-
vec![mtime, bar, bytes, left_bar, percentage, right_bar, name]
192-
} else {
193-
vec![bytes, left_bar, percentage, right_bar, name]
89+
columns.push(mtime_column(w, sort_mode, style));
19490
}
91+
columns.push(bytes_column(display, w, sort_mode, style));
92+
columns.push(percentage_column(display, fraction, style));
93+
columns.push(name_column(
94+
w, is_dir, is_top, root, area, marked, node_idx, exists, style,
95+
));
96+
97+
columns_with_separators(columns, style)
19598
},
19699
);
197100

198101
list.render(props, lines, area, buf);
199102

200103
if *is_focussed {
201-
let help_text = " . = o|.. = u ── ⇊ = Ctrl+d|↓ = j|⇈ = Ctrl+u|↑ = k ";
202-
let help_text_block_width = block_width(help_text);
203-
let bound = Rect {
204-
width: area.width.saturating_sub(1),
205-
..area
206-
};
207-
if block_width(&title) + help_text_block_width <= bound.width {
208-
draw_text_nowrap_fn(
209-
rect::snap_to_right(bound, help_text_block_width),
210-
buf,
211-
help_text,
212-
|_, _, _| Style::default(),
213-
);
214-
}
215-
let bound = line_bound(bound, bound.height.saturating_sub(1) as usize);
216-
let help_text = " mark-move = d | mark-toggle = space | toggle-all = a";
217-
let help_text_block_width = block_width(help_text);
218-
if help_text_block_width <= bound.width {
219-
draw_text_nowrap_fn(
220-
rect::snap_to_right(bound, help_text_block_width),
221-
buf,
222-
help_text,
223-
|_, _, _| Style::default(),
224-
);
225-
}
104+
let bound = draw_top_right_help(area, title, buf);
105+
draw_bottom_right_help(bound, buf);
226106
}
227107
}
228108
}
229109

110+
fn entry_in_view(
111+
selected: &Option<petgraph::stable_graph::NodeIndex>,
112+
entries: &&[EntryDataBundle],
113+
) -> Option<usize> {
114+
selected.map(|selected| {
115+
entries
116+
.iter()
117+
.find_position(|b| b.index == selected)
118+
.map(|(idx, _)| idx)
119+
.unwrap_or(0)
120+
})
121+
}
122+
123+
fn title_block<'a>(title: &'a str, border_style: &'a Style) -> Block<'a> {
124+
Block::default()
125+
.title(title)
126+
.border_style(*border_style)
127+
.borders(Borders::ALL)
128+
}
129+
130+
fn title(current_path: &str, item_count: usize) -> String {
131+
let title = format!(
132+
" {} ({} item{}) ",
133+
current_path,
134+
item_count,
135+
match item_count {
136+
1 => "",
137+
_ => "s",
138+
}
139+
);
140+
title
141+
}
142+
143+
fn current_path(
144+
tree: &petgraph::stable_graph::StableGraph<EntryData, ()>,
145+
root: &petgraph::stable_graph::NodeIndex,
146+
) -> String {
147+
match path_of(tree, *root).to_string_lossy().to_string() {
148+
ref p if p.is_empty() => Path::new(".")
149+
.canonicalize()
150+
.map(|p| p.to_string_lossy().to_string())
151+
.unwrap_or_else(|_| String::from(".")),
152+
p => p,
153+
}
154+
}
155+
156+
fn draw_bottom_right_help(bound: Rect, buf: &mut Buffer) {
157+
let bound = line_bound(bound, bound.height.saturating_sub(1) as usize);
158+
let help_text = " mark-move = d | mark-toggle = space | toggle-all = a";
159+
let help_text_block_width = block_width(help_text);
160+
if help_text_block_width <= bound.width {
161+
draw_text_nowrap_fn(
162+
rect::snap_to_right(bound, help_text_block_width),
163+
buf,
164+
help_text,
165+
|_, _, _| Style::default(),
166+
);
167+
}
168+
}
169+
170+
fn draw_top_right_help(area: Rect, title: String, buf: &mut Buffer) -> Rect {
171+
let help_text = " . = o|.. = u ── ⇊ = Ctrl+d|↓ = j|⇈ = Ctrl+u|↑ = k ";
172+
let help_text_block_width = block_width(help_text);
173+
let bound = Rect {
174+
width: area.width.saturating_sub(1),
175+
..area
176+
};
177+
if block_width(&title) + help_text_block_width <= bound.width {
178+
draw_text_nowrap_fn(
179+
rect::snap_to_right(bound, help_text_block_width),
180+
buf,
181+
help_text,
182+
|_, _, _| Style::default(),
183+
);
184+
}
185+
bound
186+
}
187+
188+
fn is_selected(
189+
selected: &Option<petgraph::stable_graph::NodeIndex>,
190+
node_idx: &petgraph::stable_graph::NodeIndex,
191+
) -> bool {
192+
let is_selected = if let Some(idx) = selected {
193+
*idx == *node_idx
194+
} else {
195+
false
196+
};
197+
is_selected
198+
}
199+
200+
fn style(is_selected: bool, is_focussed: &bool) -> Style {
201+
let mut style = Style::default();
202+
if is_selected {
203+
style.add_modifier.insert(Modifier::REVERSED);
204+
}
205+
if *is_focussed & is_selected {
206+
style.add_modifier.insert(Modifier::BOLD);
207+
}
208+
style
209+
}
210+
211+
fn columns_with_separators(columns: Vec<Span<'_>>, style: Style) -> Vec<Span<'_>> {
212+
let mut columns_with_separators = Vec::new();
213+
let column_count = columns.len();
214+
for (idx, column) in columns.into_iter().enumerate() {
215+
columns_with_separators.push(column);
216+
if idx != column_count - 1 {
217+
columns_with_separators.push(Span::styled(" | ", style))
218+
}
219+
}
220+
columns_with_separators
221+
}
222+
223+
fn mtime_column<'a>(w: &'a EntryData, sort_mode: &'a SortMode, style: Style) -> Span<'a> {
224+
let datetime = DateTime::<chrono::Utc>::from(w.mtime);
225+
let formatted_time = datetime.format("%d/%m/%Y %H:%M:%S").to_string();
226+
Span::styled(
227+
format!("{:>20}", formatted_time),
228+
Style {
229+
fg: match sort_mode {
230+
SortMode::SizeAscending | SortMode::SizeDescending => style.fg,
231+
SortMode::MTimeAscending | SortMode::MTimeDescending => Color::Green.into(),
232+
},
233+
..style
234+
},
235+
)
236+
}
237+
238+
fn name_column<'a>(
239+
w: &'a dua::traverse::EntryData,
240+
is_dir: &'a bool,
241+
is_top: impl Fn(petgraph::stable_graph::NodeIndex) -> bool,
242+
root: &'a petgraph::stable_graph::NodeIndex,
243+
area: Rect,
244+
marked: &Option<
245+
&std::collections::BTreeMap<petgraph::stable_graph::NodeIndex, super::EntryMark>,
246+
>,
247+
node_idx: &'a petgraph::stable_graph::NodeIndex,
248+
exists: &'a bool,
249+
style: Style,
250+
) -> Span<'a> {
251+
Span::styled(
252+
fill_background_to_right(
253+
format!(
254+
"{prefix}{}",
255+
w.name.to_string_lossy(),
256+
prefix = if *is_dir && !is_top(*root) { "/" } else { " " }
257+
),
258+
area.width,
259+
),
260+
{
261+
let is_marked = marked.map(|m| m.contains_key(&node_idx)).unwrap_or(false);
262+
let fg = if !exists {
263+
// non-existing - always red!
264+
Some(Color::Red)
265+
} else {
266+
entry_color(style.fg, !is_dir, is_marked)
267+
};
268+
Style { fg, ..style }
269+
},
270+
)
271+
}
272+
273+
fn percentage_column<'a>(display: &'a DisplayOptions, fraction: f32, style: Style) -> Span<'a> {
274+
Span::styled(format!("{}", display.byte_vis.display(fraction)), style)
275+
}
276+
277+
fn bytes_column<'a>(
278+
display: &'a DisplayOptions,
279+
w: &'a dua::traverse::EntryData,
280+
sort_mode: &'a SortMode,
281+
style: Style,
282+
) -> Span<'a> {
283+
Span::styled(
284+
format!(
285+
"{:>byte_column_width$}",
286+
display.byte_format.display(w.size).to_string(), // we would have to impl alignment/padding ourselves otherwise...
287+
byte_column_width = display.byte_format.width()
288+
),
289+
Style {
290+
fg: match sort_mode {
291+
SortMode::SizeAscending | SortMode::SizeDescending => Color::Green.into(),
292+
SortMode::MTimeAscending | SortMode::MTimeDescending => style.fg,
293+
},
294+
..style
295+
},
296+
)
297+
}
298+
230299
fn should_show_mtime_column(sort_mode: &SortMode) -> bool {
231300
matches!(
232301
sort_mode,

0 commit comments

Comments
 (0)