Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
- show messages that indicate why sometimes key-presses are ignored
- maintain previous selection in a clearer fashion
- maintain seelction from glob-mode as well
- switch title to `entry` as it's not only 'file's there, also directories.
- also show how many entries there are visible
  • Loading branch information
Byron committed Jan 17, 2024
1 parent f1fc13e commit 18a725d
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 63 deletions.
76 changes: 51 additions & 25 deletions src/interactive/app/eventloop.rs
@@ -1,3 +1,4 @@
use crate::interactive::state::FilesystemScan;
use crate::interactive::{
app::navigation::Navigation,
state::FocussedPane,
Expand Down Expand Up @@ -64,15 +65,18 @@ impl AppState {
}

pub fn traverse(&mut self, traversal: &Traversal, input: Vec<PathBuf>) -> Result<()> {
let background_traversal = BackgroundTraversal::start(
let traverasal = BackgroundTraversal::start(
traversal.root_index,
&self.walk_options,
input,
false,
true,
)?;
self.navigation_mut().view_root = traversal.root_index;
self.active_traversal = Some((background_traversal, None));
self.scan = Some(FilesystemScan {
active_traversal: traverasal,
previous_selection: None,
});
Ok(())
}

Expand Down Expand Up @@ -130,7 +134,11 @@ impl AppState {
where
B: Backend,
{
if let Some((active_traversal, selected_name)) = &mut self.active_traversal {
if let Some(FilesystemScan {
active_traversal,
previous_selection,
}) = self.scan.as_mut()
{
crossbeam::select! {
recv(events) -> event => {
let Ok(event) = event else {
Expand All @@ -153,13 +161,13 @@ impl AppState {

if let Some(is_finished) = active_traversal.integrate_traversal_event(traversal, event) {
self.stats = active_traversal.stats;
let selected_name = selected_name.clone();
let previous_selection = previous_selection.clone();
if is_finished {
let root_index = active_traversal.root_idx;
self.recompute_sizes_recursively(traversal, root_index);
self.active_traversal = None;
self.scan = None;
}
self.update_state_during_traversal(traversal, selected_name.as_ref(), is_finished);
self.update_state_during_traversal(traversal, previous_selection.as_ref(), is_finished);
self.refresh_screen(window, traversal, display, terminal)?;
};
}
Expand All @@ -182,16 +190,21 @@ impl AppState {
fn update_state_during_traversal(
&mut self,
traversal: &mut Traversal,
selected_name: Option<&PathBuf>,
previous_selection: Option<&(PathBuf, usize)>,
is_finished: bool,
) {
let tree_view = self.tree_view(traversal);
self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting);

if !self.received_events {
let selected_entry = selected_name
.and_then(|selected_name| self.entries.iter().find(|e| e.name == *selected_name));
if let Some(selected_entry) = selected_entry {
let previously_selected_entry =
previous_selection.and_then(|(selected_name, selected_idx)| {
self.entries
.iter()
.find(|e| e.name == *selected_name)
.or_else(|| self.entries.get(*selected_idx))
});
if let Some(selected_entry) = previously_selected_entry {
self.navigation_mut().selected = Some(selected_entry.index);
} else if is_finished {
self.navigation_mut().selected = self.entries.first().map(|b| b.index);
Expand Down Expand Up @@ -239,8 +252,12 @@ impl AppState {
Tab => {
self.cycle_focus(window);
}
Char('/') if !glob_focussed && self.active_traversal.is_none() => {
self.toggle_glob_search(window);
Char('/') if !glob_focussed => {
if self.scan.is_some() {
self.message = Some("glob search disabled during traversal".into());
} else {
self.toggle_glob_search(window);
}
}
Char('?') if !glob_focussed => self.toggle_help_pane(window),
Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => {
Expand Down Expand Up @@ -338,18 +355,32 @@ impl AppState {
what: Refresh,
) -> anyhow::Result<()> {
// If another traversal is already running do not do anything.
if self.active_traversal.is_some() {
if self.scan.is_some() {
self.message = Some("Traversal already running".into());
return Ok(());
}

// If we are displaing root of the glob search results then cancel the search.
let previous_selection = self.navigation().selected.and_then(|sel_index| {
tree.tree().node_weight(sel_index).map(|w| {
(
w.name.clone(),
self.entries
.iter()
.enumerate()
.find_map(|(idx, e)| (e.index == sel_index).then_some(idx))
.expect("selected item is always in entries"),
)
})
});

// If we are displaying the root of the glob search results then cancel the search.
if let Some(glob_tree_root) = tree.glob_tree_root {
if glob_tree_root == self.navigation().view_root {
self.quit_glob_mode(tree, window)
}
}

let (remove_index, skip_root, index, parent_index) = match what {
let (remove_root_node, skip_root, index, parent_index) = match what {
Refresh::Selected => {
let Some(selected) = self.navigation().selected else {
return Ok(());
Expand All @@ -367,31 +398,26 @@ impl AppState {
),
};

let selected_name = self
.navigation()
.selected
.and_then(|w| tree.tree().node_weight(w).map(|w| w.name.clone()));

let mut path = tree.path_of(index);
if path.to_str() == Some("") {
path = PathBuf::from(".");
}
tree.remove_entries(index, remove_index);
tree.remove_entries(index, remove_root_node);
tree.recompute_sizes_recursively(parent_index);

self.entries = tree.sorted_entries(self.navigation().view_root, self.sorting);
self.navigation_mut().selected = self.entries.first().map(|e| e.index);

self.active_traversal = Some((
BackgroundTraversal::start(
self.scan = Some(FilesystemScan {
active_traversal: BackgroundTraversal::start(
parent_index,
&self.walk_options,
vec![path],
skip_root,
false,
)?,
selected_name,
));
previous_selection,
});

self.received_events = false;
Ok(())
Expand Down
5 changes: 3 additions & 2 deletions src/interactive/app/handlers.rs
Expand Up @@ -162,7 +162,7 @@ impl AppState {
}

pub fn reset_message(&mut self) {
if self.active_traversal.is_some() {
if self.scan.is_some() {
self.message = Some("-> scanning <-".into());
} else {
self.message = None;
Expand Down Expand Up @@ -312,7 +312,8 @@ impl AppState {
let parent_idx = tree_view
.fs_parent_of(index)
.expect("us being unable to delete the root index");
let entries_deleted = tree_view.remove_entries(index, true);
let entries_deleted =
tree_view.remove_entries(index, true /* remove node at `index` */);

if !tree_view.exists(self.navigation().view_root) {
self.go_to_root(tree_view);
Expand Down
10 changes: 8 additions & 2 deletions src/interactive/app/state.rs
Expand Up @@ -24,6 +24,12 @@ pub struct Cursor {
pub y: u16,
}

pub(crate) struct FilesystemScan {
pub active_traversal: BackgroundTraversal,
/// The selected item prior to starting the traversal, if available, based on its name or index into [`AppState::entries`].
pub previous_selection: Option<(PathBuf, usize)>,
}

pub struct AppState {
pub navigation: Navigation,
pub glob_navigation: Option<Navigation>,
Expand All @@ -33,7 +39,7 @@ pub struct AppState {
pub message: Option<String>,
pub focussed: FocussedPane,
pub received_events: bool,
pub active_traversal: Option<(BackgroundTraversal, Option<PathBuf>)>,
pub scan: Option<FilesystemScan>,
pub stats: TraversalStats,
pub walk_options: WalkOptions,
}
Expand All @@ -49,7 +55,7 @@ impl AppState {
message: None,
focussed: Default::default(),
received_events: false,
active_traversal: None,
scan: None,
stats: TraversalStats::default(),
walk_options,
}
Expand Down
2 changes: 1 addition & 1 deletion src/interactive/app/terminal.rs
Expand Up @@ -99,7 +99,7 @@ mod tests {
where
B: Backend,
{
while self.state.active_traversal.is_some() {
while self.state.scan.is_some() {
if let Some(res) = self.state.process_event(
&mut self.window,
&mut self.traversal,
Expand Down
2 changes: 1 addition & 1 deletion src/interactive/app/tests/journeys_readonly.rs
Expand Up @@ -40,7 +40,7 @@ fn simple_user_journey_read_only() -> Result<()> {
);

assert!(
app.state.active_traversal.is_none(),
app.state.scan.is_none(),
"it will not think it is still scanning as there is no traversal"
);

Expand Down
6 changes: 3 additions & 3 deletions src/interactive/app/tree_view.rs
Expand Up @@ -59,12 +59,12 @@ impl TreeView<'_> {
current_path(&self.traversal.tree, view_root, self.glob_tree_root)
}

pub fn remove_entries(&mut self, index: TreeIndex, remove_index: bool) -> usize {
pub fn remove_entries(&mut self, root_index: TreeIndex, remove_root_node: bool) -> usize {
let mut entries_deleted = 0;
let mut bfs = Bfs::new(self.tree(), index);
let mut bfs = Bfs::new(self.tree(), root_index);

while let Some(nx) = bfs.next(&self.tree()) {
if nx == index && !remove_index {
if nx == root_index && !remove_root_node {
continue;
}
self.tree_mut().remove_node(nx);
Expand Down
26 changes: 17 additions & 9 deletions src/interactive/widgets/entries.rs
Expand Up @@ -63,12 +63,18 @@ impl Entries {
let list = &mut self.list;

let total: u128 = entries.iter().map(|b| b.size).sum();
let (item_count, item_size): (u64, u128) = entries
let (recursive_item_count, item_size): (u64, u128) = entries
.iter()
.map(|f| (f.entry_count.unwrap_or(1), f.size))
.reduce(|a, b| (a.0 + b.0, a.1 + b.1))
.unwrap_or_default();
let title = title(current_path, item_count, *display, item_size);
let title = title(
current_path,
entries.len(),
recursive_item_count,
*display,
item_size,
);
let title_block = title_block(&title, *border_style);
let inner_area = title_block.inner(area);
let entry_in_view = entry_in_view(*selected, entries);
Expand Down Expand Up @@ -155,15 +161,17 @@ fn title_block(title: &str, border_style: Style) -> Block<'_> {
.borders(Borders::ALL)
}

fn title(current_path: &str, item_count: u64, display: DisplayOptions, size: u128) -> String {
fn title(
current_path: &str,
item_count: usize,
recursive_item_count: u64,
display: DisplayOptions,
size: u128,
) -> String {
format!(
" {} ({} file{}, {}) ",
" {} ({item_count} visible, {} total, {}) ",
current_path,
COUNT.format(item_count as f64),
match item_count {
1 => "",
_ => "s",
},
COUNT.format(recursive_item_count as f64),
display.byte_format.display(size)
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/interactive/widgets/footer.rs
Expand Up @@ -37,7 +37,7 @@ impl Footer {

let spans = vec![
Span::from(format!(
"Sort mode: {} Total disk usage: {} Processed {} items {progress} ",
"Sort mode: {} Total disk usage: {} Processed {} entries {progress} ",
match sort_mode {
SortMode::SizeAscending => "size ascending",
SortMode::SizeDescending => "size descending",
Expand Down

0 comments on commit 18a725d

Please sign in to comment.