Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refresh all items in view / selected item #219

Merged
merged 17 commits into from Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/aggregate.rs
Expand Up @@ -41,7 +41,7 @@ pub fn aggregate(
continue;
}
};
for entry in walk_options.iter_from_path(path.as_ref(), device_id) {
for entry in walk_options.iter_from_path(path.as_ref(), device_id, false) {
stats.entries_traversed += 1;
progress.throttled(|| {
if let Some(err) = err.as_mut() {
Expand Down
3 changes: 2 additions & 1 deletion src/common.rs
Expand Up @@ -176,11 +176,12 @@ pub struct WalkOptions {
type WalkDir = jwalk::WalkDirGeneric<((), Option<Result<std::fs::Metadata, jwalk::Error>>)>;

impl WalkOptions {
pub fn iter_from_path(&self, root: &Path, root_device_id: u64) -> WalkDir {
pub fn iter_from_path(&self, root: &Path, root_device_id: u64, skip_root: bool) -> WalkDir {
let ignore_dirs = self.ignore_dirs.clone();
let cwd = std::env::current_dir().unwrap_or_else(|_| root.to_owned());
WalkDir::new(root)
.follow_links(false)
.min_depth(if skip_root { 1 } else { 0 })
.sort(match self.sorting {
TraversalSorting::None => false,
TraversalSorting::AlphabeticalByFileName => true,
Expand Down
121 changes: 93 additions & 28 deletions src/interactive/app/eventloop.rs
@@ -1,6 +1,5 @@
use crate::interactive::{
app::navigation::Navigation,
sorted_entries,
state::FocussedPane,
widgets::{glob_search, MainWindow, MainWindowProps},
CursorDirection, CursorMode, DisplayOptions, MarkEntryMode,
Expand All @@ -10,8 +9,8 @@ use crossbeam::channel::Receiver;
use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use crosstermion::input::Event;
use dua::{
traverse::{BackgroundTraversal, EntryData, Traversal},
WalkOptions, WalkResult,
traverse::{BackgroundTraversal, EntryData, Traversal, TreeIndex},
WalkResult,
};
use std::path::PathBuf;
use tui::backend::Backend;
Expand Down Expand Up @@ -43,10 +42,10 @@ impl AppState {
{
let props = MainWindowProps {
current_path: tree_view.current_path(self.navigation().view_root),
entries_traversed: tree_view.traversal.entries_traversed,
total_bytes: tree_view.traversal.total_bytes,
start: tree_view.traversal.start,
elapsed: tree_view.traversal.elapsed,
entries_traversed: self.stats.entries_traversed,
total_bytes: tree_view.total_size(),
start: self.stats.start,
elapsed: self.stats.elapsed,
display,
state: self,
};
Expand All @@ -64,19 +63,19 @@ impl AppState {
result
}

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

fn recompute_sizes_recursively(&mut self, traversal: &mut Traversal, node_index: TreeIndex) {
let mut tree_view = self.tree_view(traversal);
tree_view.recompute_sizes_recursively(node_index);
}

fn refresh_screen<B>(
&mut self,
window: &mut MainWindow,
Expand Down Expand Up @@ -130,7 +129,7 @@ impl AppState {
crossbeam::select! {
recv(events) -> event => {
let Ok(event) = event else {
return Ok(Some(WalkResult { num_errors: 0 }));
return Ok(Some(WalkResult { num_errors: self.stats.io_errors }));
};
let res = self.process_terminal_event(
window,
Expand All @@ -148,7 +147,10 @@ impl AppState {
};

if let Some(is_finished) = active_traversal.integrate_traversal_event(traversal, event) {
self.stats = active_traversal.stats;
if is_finished {
let root_index = active_traversal.root_idx;
self.recompute_sizes_recursively(traversal, root_index);
self.active_traversal = None;
}
self.update_state(traversal);
Expand All @@ -158,7 +160,9 @@ impl AppState {
}
} else {
let Ok(event) = events.recv() else {
return Ok(Some(WalkResult { num_errors: 0 }));
return Ok(Some(WalkResult {
num_errors: self.stats.io_errors,
}));
};
let result =
self.process_terminal_event(window, traversal, display, terminal, event)?;
Expand All @@ -169,13 +173,14 @@ impl AppState {
Ok(None)
}

fn update_state(&mut self, traversal: &Traversal) {
self.entries = sorted_entries(
&traversal.tree,
self.navigation().view_root,
self.sorting,
self.glob_root(),
);
fn update_state(&mut self, traversal: &mut Traversal) {
let tree_view = self.tree_view(traversal);
if !tree_view.exists(self.navigation().view_root) {
self.go_to_root(&tree_view);
} else {
self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting);
}

if !self.received_events {
self.navigation_mut().selected = self.entries.first().map(|b| b.index);
}
Expand Down Expand Up @@ -227,7 +232,7 @@ impl AppState {
Char('?') if !glob_focussed => self.toggle_help_pane(window),
Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => {
return Ok(Some(WalkResult {
num_errors: tree_view.traversal.io_errors,
num_errors: self.stats.io_errors,
}))
}
Char('q') if !glob_focussed => {
Expand Down Expand Up @@ -275,6 +280,8 @@ impl AppState {
Char('o') | Char('l') | Enter | Right => {
self.enter_node_with_traversal(&tree_view)
}
Char('R') => self.refresh(&mut tree_view, window, Refresh::Selected)?,
Char('r') => self.refresh(&mut tree_view, window, Refresh::AllInView)?,
Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop),
Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom),
PageUp => self.change_entry_selection(CursorDirection::PageUp),
Expand Down Expand Up @@ -311,6 +318,57 @@ impl AppState {
Ok(None)
}

fn refresh(
&mut self,
tree: &mut TreeView<'_>,
window: &mut MainWindow,
what: Refresh,
) -> anyhow::Result<()> {
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 {
Refresh::Selected => {
let Some(selected) = self.navigation().selected else {
return Ok(());
};
let parent_index = tree
.fs_parent_of(selected)
.expect("there is always a parent to a selection");
(true, false, selected, parent_index)
}
Refresh::AllInView => (
false,
true,
self.navigation().view_root,
self.navigation().view_root,
),
};

let mut path = tree.path_of(index);
if path.to_str() == Some("") {
path = PathBuf::from(".");
}
tree.remove_entries(index, remove_index);
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(
parent_index,
&self.walk_options,
vec![path],
skip_root,
)?);

self.received_events = false;
Ok(())
}

fn tree_view<'a>(&mut self, traversal: &'a mut Traversal) -> TreeView<'a> {
TreeView {
traversal,
Expand Down Expand Up @@ -369,10 +427,10 @@ impl AppState {
match self.focussed {
Main => {
if self.glob_navigation.is_some() {
self.handle_glob_quit(tree_view, window);
self.quit_glob_mode(tree_view, window);
} else {
return Some(Ok(WalkResult {
num_errors: tree_view.traversal.io_errors,
num_errors: self.stats.io_errors,
}));
}
}
Expand All @@ -382,13 +440,13 @@ impl AppState {
window.help_pane = None
}
Glob => {
self.handle_glob_quit(tree_view, window);
self.quit_glob_mode(tree_view, window);
}
}
None
}

fn handle_glob_quit(&mut self, tree_view: &mut TreeView<'_>, window: &mut MainWindow) {
fn quit_glob_mode(&mut self, tree_view: &mut TreeView<'_>, window: &mut MainWindow) {
use FocussedPane::*;
self.focussed = Main;
if let Some(glob_source) = &self.glob_navigation {
Expand All @@ -402,6 +460,13 @@ impl AppState {
}
}

enum Refresh {
/// Refresh the directory currently in view
AllInView,
/// Refresh only the selected item
Selected,
}

pub fn draw_window<B>(
window: &mut MainWindow,
props: MainWindowProps<'_>,
Expand Down
4 changes: 2 additions & 2 deletions src/interactive/app/handlers.rs
Expand Up @@ -312,7 +312,7 @@ 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);
let entries_deleted = tree_view.remove_entries(index, true);

if !tree_view.exists(self.navigation().view_root) {
self.go_to_root(tree_view);
Expand All @@ -334,7 +334,7 @@ impl AppState {
entries_deleted
}

fn go_to_root(&mut self, tree_view: &TreeView<'_>) {
pub fn go_to_root(&mut self, tree_view: &TreeView<'_>) {
let root = self.navigation().tree_root;
let entries = tree_view.sorted_entries(root, self.sorting);
self.navigation_mut().exit_node(root, &entries);
Expand Down
24 changes: 22 additions & 2 deletions src/interactive/app/state.rs
@@ -1,6 +1,7 @@
use std::collections::HashSet;

use dua::traverse::BackgroundTraversal;
use dua::traverse::{BackgroundTraversal, TraversalStats};
use dua::WalkOptions;

use crate::interactive::widgets::Column;

Expand All @@ -22,7 +23,6 @@ pub struct Cursor {
pub y: u16,
}

#[derive(Default)]
pub struct AppState {
pub navigation: Navigation,
pub glob_navigation: Option<Navigation>,
Expand All @@ -33,4 +33,24 @@ pub struct AppState {
pub focussed: FocussedPane,
pub received_events: bool,
pub active_traversal: Option<BackgroundTraversal>,
pub stats: TraversalStats,
pub walk_options: WalkOptions,
}

impl AppState {
pub fn new(walk_options: WalkOptions) -> Self {
AppState {
navigation: Default::default(),
glob_navigation: None,
entries: vec![],
sorting: Default::default(),
show_columns: Default::default(),
message: None,
focussed: Default::default(),
received_events: false,
active_traversal: None,
stats: TraversalStats::default(),
walk_options,
}
}
}
31 changes: 10 additions & 21 deletions src/interactive/app/terminal.rs
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use crossbeam::channel::Receiver;
use crosstermion::input::Event;
use dua::{
traverse::{EntryData, Traversal, Tree},
traverse::{Traversal, TraversalStats},
ByteFormat, WalkOptions, WalkResult,
};
use tui::prelude::Backend;
Expand All @@ -17,10 +17,10 @@ use super::{sorted_entries, state::AppState, DisplayOptions};
/// State and methods representing the interactive disk usage analyser for the terminal
pub struct TerminalApp {
pub traversal: Traversal,
pub stats: TraversalStats,
pub display: DisplayOptions,
pub state: AppState,
pub window: MainWindow,
pub walk_options: WalkOptions,
}

impl TerminalApp {
Expand All @@ -38,21 +38,9 @@ impl TerminalApp {
let display = DisplayOptions::new(byte_format);
let window = MainWindow::default();

let mut state = AppState::default();

let traversal = {
let mut tree = Tree::new();
let root_index = tree.add_node(EntryData::default());
Traversal {
tree,
root_index,
entries_traversed: 0,
start: std::time::Instant::now(),
elapsed: None,
io_errors: 0,
total_bytes: None,
}
};
let mut state = AppState::new(walk_options);
let traversal = Traversal::new();
let stats = TraversalStats::default();

state.navigation_mut().view_root = traversal.root_index;
state.entries = sorted_entries(
Expand All @@ -67,15 +55,14 @@ impl TerminalApp {
state,
display,
traversal,
stats,
window,
walk_options,
};
Ok(app)
}

pub fn traverse(&mut self, input: Vec<PathBuf>) -> Result<()> {
self.state
.traverse(&self.traversal, &self.walk_options, input)?;
self.state.traverse(&self.traversal, input)?;
Ok(())
}

Expand Down Expand Up @@ -123,7 +110,9 @@ mod tests {
return Ok(res);
}
}
Ok(WalkResult { num_errors: 0 })
Ok(WalkResult {
num_errors: self.stats.io_errors,
})
}
}
}