summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPiotr Wach <pwach@bloomberg.net>2023-12-11 21:35:12 +0000
committerPiotr Wach <pwach@bloomberg.net>2023-12-19 21:13:17 +0000
commitdf6a02cd8fdbe693f507ab34a89227431d7c112e (patch)
treee3e64cc1653b50940d28405012fdfee984b43538 /src
parentb23e13431dad1ed9efc6728f4c9ee8ab2254a42c (diff)
Implements glob search mode
Diffstat (limited to 'src')
-rw-r--r--src/interactive/app/common.rs24
-rw-r--r--src/interactive/app/eventloop.rs309
-rw-r--r--src/interactive/app/handlers.rs245
-rw-r--r--src/interactive/app/mod.rs2
-rw-r--r--src/interactive/app/navigation.rs79
-rw-r--r--src/interactive/app/tests/journeys_readonly.rs36
-rw-r--r--src/interactive/app/tests/journeys_with_writes.rs9
-rw-r--r--src/interactive/app/tests/unit.rs12
-rw-r--r--src/interactive/app/tests/utils.rs9
-rw-r--r--src/interactive/app/tree_view.rs210
-rw-r--r--src/interactive/mod.rs12
-rw-r--r--src/interactive/widgets/entries.rs99
-rw-r--r--src/interactive/widgets/glob.rs173
-rw-r--r--src/interactive/widgets/help.rs1
-rw-r--r--src/interactive/widgets/main.rs83
-rw-r--r--src/interactive/widgets/mark.rs14
-rw-r--r--src/interactive/widgets/mod.rs2
17 files changed, 980 insertions, 339 deletions
diff --git a/src/interactive/app/common.rs b/src/interactive/app/common.rs
index 6b455a1..f4d5939 100644
--- a/src/interactive/app/common.rs
+++ b/src/interactive/app/common.rs
@@ -2,7 +2,8 @@ use crate::interactive::path_of;
use dua::traverse::{EntryData, Tree, TreeIndex};
use itertools::Itertools;
use petgraph::Direction;
-use std::cmp::Ordering;
+use std::path::Path;
+use std::{cmp::Ordering, path::PathBuf};
use unicode_segmentation::UnicodeSegmentation;
#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
@@ -50,9 +51,21 @@ pub struct EntryDataBundle {
pub data: EntryData,
pub is_dir: bool,
pub exists: bool,
+ pub glob_name: Option<PathBuf>,
}
-pub fn sorted_entries(tree: &Tree, node_idx: TreeIndex, sorting: SortMode) -> Vec<EntryDataBundle> {
+impl EntryDataBundle {
+ pub fn name(&self) -> &Path {
+ self.glob_name.as_deref().unwrap_or(&self.data.name)
+ }
+}
+
+pub fn sorted_entries(
+ tree: &Tree,
+ node_idx: TreeIndex,
+ sorting: SortMode,
+ glob_root: Option<TreeIndex>,
+) -> Vec<EntryDataBundle> {
use SortMode::*;
fn cmp_count(l: &EntryDataBundle, r: &EntryDataBundle) -> Ordering {
l.data
@@ -63,13 +76,18 @@ pub fn sorted_entries(tree: &Tree, node_idx: TreeIndex, sorting: SortMode) -> Ve
tree.neighbors_directed(node_idx, Direction::Outgoing)
.filter_map(|idx| {
tree.node_weight(idx).map(|w| {
- let p = path_of(tree, idx);
+ let mut use_glob_path = false;
+ if let Some(glob_root) = glob_root {
+ use_glob_path = node_idx == glob_root;
+ }
+ let p = path_of(tree, idx, glob_root);
let pm = p.symlink_metadata();
EntryDataBundle {
index: idx,
data: w.clone(),
exists: pm.is_ok(),
is_dir: pm.ok().map_or(false, |m| m.is_dir()),
+ glob_name: if use_glob_path { Some(p) } else { None },
}
})
})
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index e0773bd..ec31ca1 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -1,36 +1,39 @@
use crate::interactive::{
+ app::navigation::NavigationState,
sorted_entries,
- widgets::{MainWindow, MainWindowProps},
+ widgets::{glob_search, MainWindow, MainWindowProps},
ByteVisualization, CursorDirection, CursorMode, DisplayOptions, EntryDataBundle, MarkEntryMode,
SortMode,
};
use anyhow::Result;
use crosstermion::input::{input_channel, Event, Key};
use dua::{
- traverse::{Traversal, TreeIndex},
+ traverse::{EntryData, Traversal},
WalkOptions, WalkResult,
};
-use std::{collections::BTreeMap, path::PathBuf};
+use std::path::PathBuf;
use tui::backend::Backend;
use tui_react::Terminal;
-#[derive(Default, Copy, Clone)]
+use super::tree_view::{GlobTreeView, NormalTreeView, TreeView};
+
+#[derive(Default, Copy, Clone, PartialEq)]
pub enum FocussedPane {
#[default]
Main,
Help,
Mark,
+ Glob,
}
#[derive(Default)]
pub struct AppState {
- pub root: TreeIndex,
- pub selected: Option<TreeIndex>,
+ pub normal_mode: NavigationState,
+ pub glob_mode: Option<NavigationState>,
pub entries: Vec<EntryDataBundle>,
pub sorting: SortMode,
pub message: Option<String>,
pub focussed: FocussedPane,
- pub bookmarks: BTreeMap<TreeIndex, TreeIndex>,
pub is_scanning: bool,
}
@@ -40,10 +43,18 @@ pub enum ProcessingResult {
}
impl AppState {
+ pub fn navigation_mut(&mut self) -> &mut NavigationState {
+ self.glob_mode.as_mut().unwrap_or(&mut self.normal_mode)
+ }
+
+ pub fn navigation(&self) -> &NavigationState {
+ self.glob_mode.as_ref().unwrap_or(&self.normal_mode)
+ }
+
pub fn draw<B>(
&mut self,
window: &mut MainWindow,
- traversal: &Traversal,
+ tree_view: &dyn TreeView,
display: DisplayOptions,
terminal: &mut Terminal<B>,
) -> Result<()>
@@ -51,7 +62,11 @@ impl AppState {
B: Backend,
{
let props = MainWindowProps {
- traversal,
+ 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,
display,
state: self,
};
@@ -72,94 +87,221 @@ impl AppState {
use crosstermion::input::Key::*;
use FocussedPane::*;
- self.draw(window, traversal, *display, terminal)?;
+ {
+ let tree_view = self.tree_view(traversal);
+ self.draw(window, tree_view.as_ref(), *display, terminal)?;
+ }
+
for event in events {
let key = match event {
Event::Key(key) => key,
Event::Resize(_, _) => Alt('\r'),
};
+ let mut tree_view = self.tree_view(traversal);
+
self.reset_message();
+ let mut handled = true;
match key {
- Char('?') => self.toggle_help_pane(window),
+ Char('?') if self.focussed != FocussedPane::Glob => self.toggle_help_pane(window),
+ Char('/') if self.focussed != FocussedPane::Glob => {
+ self.toggle_glob_search(window);
+ }
Char('\t') => {
self.cycle_focus(window);
}
- Ctrl('c') => {
+ Ctrl('c') if self.focussed != FocussedPane::Glob => {
return Ok(ProcessingResult::ExitRequested(WalkResult {
- num_errors: traversal.io_errors,
+ num_errors: tree_view.traversal().io_errors,
}))
}
- Char('q') | Esc => match self.focussed {
- Main => {
- return Ok(ProcessingResult::ExitRequested(WalkResult {
- num_errors: traversal.io_errors,
- }))
+ Char('q') if self.focussed != FocussedPane::Glob => {
+ if let Some(value) = self.handle_quit(tree_view.as_mut(), window) {
+ return value;
}
- Mark => self.focussed = Main,
- Help => {
- self.focussed = Main;
- window.help_pane = None
+ }
+ Esc => {
+ if let Some(value) = self.handle_quit(tree_view.as_mut(), window) {
+ return value;
}
- },
- _ => {}
+ }
+ _ => {
+ handled = false;
+ }
}
- match self.focussed {
- Mark => self.dispatch_to_mark_pane(key, window, traversal, *display, terminal),
- Help => {
- window
- .help_pane
- .as_mut()
- .expect("help pane")
- .process_events(key);
- }
- Main => match key {
- Char('O') => self.open_that(traversal),
- Char(' ') => self.mark_entry(
- CursorMode::KeepPosition,
- MarkEntryMode::Toggle,
- window,
- traversal,
- ),
- Char('d') => self.mark_entry(
- CursorMode::Advance,
- MarkEntryMode::Toggle,
+ if !handled {
+ match self.focussed {
+ Mark => self.dispatch_to_mark_pane(
+ key,
window,
- traversal,
+ tree_view.as_mut(),
+ *display,
+ terminal,
),
- Char('x') => self.mark_entry(
- CursorMode::Advance,
- MarkEntryMode::MarkForDeletion,
- window,
- traversal,
- ),
- Char('a') => self.mark_all_entries(MarkEntryMode::Toggle, window, traversal),
- Char('u') | Char('h') | Backspace | Left => {
- self.exit_node_with_traversal(traversal)
+ Help => {
+ window
+ .help_pane
+ .as_mut()
+ .expect("help pane")
+ .process_events(key);
}
- Char('o') | Char('l') | Char('\n') | Right => {
- self.enter_node_with_traversal(traversal)
+ Glob => {
+ let glob_pane = window.glob_pane.as_mut().expect("glob pane");
+ match key {
+ Char('\n') => {
+ self.search_glob_pattern(tree_view.as_mut(), &glob_pane.input)
+ }
+ _ => glob_pane.process_events(key),
+ }
}
- Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop),
- Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom),
- Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp),
- Char('k') | Up => self.change_entry_selection(CursorDirection::Up),
- Char('j') | Down => self.change_entry_selection(CursorDirection::Down),
- Ctrl('d') | PageDown => self.change_entry_selection(CursorDirection::PageDown),
- Char('s') => self.cycle_sorting(traversal),
- Char('m') => self.cycle_mtime_sorting(traversal),
- Char('c') => self.cycle_count_sorting(traversal),
- Char('g') => display.byte_vis.cycle(),
- _ => {}
- },
- };
- self.draw(window, traversal, *display, terminal)?;
+ Main => match key {
+ Char('O') => self.open_that(tree_view.as_ref()),
+ Char(' ') => self.mark_entry(
+ CursorMode::KeepPosition,
+ MarkEntryMode::Toggle,
+ window,
+ tree_view.as_ref(),
+ ),
+ Char('d') => self.mark_entry(
+ CursorMode::Advance,
+ MarkEntryMode::Toggle,
+ window,
+ tree_view.as_ref(),
+ ),
+ Char('x') => self.mark_entry(
+ CursorMode::Advance,
+ MarkEntryMode::MarkForDeletion,
+ window,
+ tree_view.as_ref(),
+ ),
+ Char('a') => {
+ self.mark_all_entries(MarkEntryMode::Toggle, window, tree_view.as_ref())
+ }
+ Char('u') | Char('h') | Backspace | Left => {
+ self.exit_node_with_traversal(tree_view.as_ref())
+ }
+ Char('o') | Char('l') | Char('\n') | Right => {
+ self.enter_node_with_traversal(tree_view.as_ref())
+ }
+ Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop),
+ Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom),
+ Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp),
+ Char('k') | Up => self.change_entry_selection(CursorDirection::Up),
+ Char('j') | Down => self.change_entry_selection(CursorDirection::Down),
+ Ctrl('d') | PageDown => {
+ self.change_entry_selection(CursorDirection::PageDown)
+ }
+ Char('s') => self.cycle_sorting(tree_view.as_ref()),
+ Char('m') => self.cycle_mtime_sorting(tree_view.as_ref()),
+ Char('c') => self.cycle_count_sorting(tree_view.as_ref()),
+ Char('g') => display.byte_vis.cycle(),
+ _ => {}
+ },
+ };
+ }
+ self.draw(window, tree_view.as_ref(), *display, terminal)?;
}
Ok(ProcessingResult::Finished(WalkResult {
num_errors: traversal.io_errors,
}))
}
+
+ fn tree_view<'a>(&mut self, traversal: &'a mut Traversal) -> Box<dyn TreeView + 'a> {
+ let tree_view: Box<dyn TreeView> = if let Some(glob_source) = &self.glob_mode {
+ Box::new(GlobTreeView {
+ traversal,
+ glob_tree_root: glob_source.tree_root,
+ })
+ } else {
+ Box::new(NormalTreeView { traversal })
+ };
+ tree_view
+ }
+
+ fn search_glob_pattern(&mut self, tree_view: &mut dyn TreeView, glob_pattern: &str) {
+ use FocussedPane::*;
+ let search_results =
+ glob_search(tree_view.tree(), self.normal_mode.view_root, glob_pattern);
+ match search_results {
+ Ok(search_results) => {
+ if let Some(glob_source) = &self.glob_mode {
+ tree_view.tree_as_mut().remove_node(glob_source.tree_root);
+ }
+
+ let tree_root = tree_view.tree_as_mut().add_node(EntryData::default());
+ let glob_source = NavigationState {
+ tree_root,
+ view_root: tree_root,
+ selected: Some(tree_root),
+ ..Default::default()
+ };
+ self.glob_mode = Some(glob_source);
+
+ for idx in search_results {
+ tree_view.tree_as_mut().add_edge(tree_root, idx, ());
+ }
+
+ let new_entries =
+ sorted_entries(tree_view.tree(), tree_root, self.sorting, Some(tree_root));
+
+ let new_entries = self
+ .navigation_mut()
+ .selected
+ .map(|previously_selected| (previously_selected, new_entries));
+
+ self.enter_node(new_entries);
+ self.focussed = Main;
+ }
+ _ => self.message = Some("Glob search error!".into()),
+ }
+ }
+
+ fn handle_quit(
+ &mut self,
+ tree_view: &mut dyn TreeView,
+ window: &mut MainWindow,
+ ) -> Option<std::result::Result<ProcessingResult, anyhow::Error>> {
+ use FocussedPane::*;
+ match self.focussed {
+ Main => {
+ if self.glob_mode.is_some() {
+ self.handle_glob_quit(tree_view, window);
+ } else {
+ return Some(Ok(ProcessingResult::ExitRequested(WalkResult {
+ num_errors: tree_view.traversal().io_errors,
+ })));
+ }
+ }
+ Mark => self.focussed = Main,
+ Help => {
+ self.focussed = Main;
+ window.help_pane = None
+ }
+ Glob => {
+ self.handle_glob_quit(tree_view, window);
+ }
+ }
+ None
+ }
+
+ fn handle_glob_quit(&mut self, tree_view: &mut dyn TreeView, window: &mut MainWindow) {
+ use FocussedPane::*;
+ self.focussed = Main;
+ if let Some(glob_source) = &self.glob_mode {
+ tree_view.tree_as_mut().remove_node(glob_source.tree_root);
+ }
+ self.glob_mode = None;
+ window.glob_pane = None;
+
+ let new_entries = self.navigation().selected.map(|previously_selected| {
+ (
+ previously_selected,
+ tree_view.sorted_entries(self.navigation().view_root, self.sorting),
+ )
+ });
+ self.enter_node(new_entries);
+ }
}
pub fn draw_window<B>(
@@ -171,7 +313,7 @@ where
B: Backend,
{
let area = terminal.pre_render()?;
- window.render(props, area, terminal.current_buffer_mut());
+ window.render(props, area, terminal);
terminal.post_render()?;
Ok(())
}
@@ -202,6 +344,7 @@ impl TerminalApp {
)
.ok();
}
+
pub fn process_events<B>(
&mut self,
terminal: &mut Terminal<B>,
@@ -258,11 +401,16 @@ impl TerminalApp {
let mut received_events = false;
let traversal = Traversal::from_walk(options, input_paths, |traversal| {
if !received_events {
- state.root = traversal.root_index;
+ state.navigation_mut().view_root = traversal.root_index;
}
- state.entries = sorted_entries(&traversal.tree, state.root, state.sorting);
+ state.entries = sorted_entries(
+ &traversal.tree,
+ state.navigation().view_root,
+ state.sorting,
+ state.glob_root(),
+ );
if !received_events {
- state.selected = state.entries.first().map(|b| b.index);
+ state.navigation_mut().selected = state.entries.first().map(|b| b.index);
}
state.reset_message(); // force "scanning" to appear
@@ -279,6 +427,7 @@ impl TerminalApp {
ProcessingResult::ExitRequested(_) => true,
ProcessingResult::Finished(_) => false,
};
+
Ok(should_exit)
})?;
@@ -289,10 +438,16 @@ impl TerminalApp {
state.is_scanning = false;
if !received_events {
- state.root = traversal.root_index;
+ state.navigation_mut().view_root = traversal.root_index;
}
- state.entries = sorted_entries(&traversal.tree, state.root, state.sorting);
- state.selected = state
+ state.entries = sorted_entries(
+ &traversal.tree,
+ state.navigation().view_root,
+ state.sorting,
+ state.glob_root(),
+ );
+ state.navigation_mut().selected = state
+ .navigation()
.selected
.filter(|_| received_events)
.or_else(|| state.entries.first().map(|b| b.index));
diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs
index d3761e9..3367bf3 100644
--- a/src/interactive/app/handlers.rs
+++ b/src/interactive/app/handlers.rs
@@ -1,13 +1,11 @@
use crate::interactive::{
+ app::tree_view::TreeView,
app::FocussedPane::*,
- path_of, sorted_entries,
- widgets::{HelpPane, MainWindow, MarkMode, MarkPane},
+ widgets::{GlobPane, HelpPane, MainWindow, MarkMode, MarkPane},
AppState, DisplayOptions, EntryDataBundle,
};
use crosstermion::input::Key;
-use dua::traverse::{Traversal, TreeIndex};
-use itertools::Itertools;
-use petgraph::{visit::Bfs, Direction};
+use dua::traverse::TreeIndex;
use std::{fs, io, path::PathBuf};
use tui::backend::Backend;
use tui_react::Terminal;
@@ -48,29 +46,27 @@ impl CursorDirection {
}
impl AppState {
- pub fn open_that(&self, traversal: &Traversal) {
- if let Some(idx) = self.selected {
- open::that(path_of(&traversal.tree, idx)).ok();
+ pub fn open_that(&self, tree_view: &dyn TreeView) {
+ if let Some(idx) = self.navigation().selected {
+ open::that(tree_view.path_of(idx)).ok();
}
}
- pub fn exit_node_with_traversal(&mut self, traversal: &Traversal) {
- let entries = self.entries_for_exit_node(traversal);
+ pub fn exit_node_with_traversal(&mut self, tree_view: &dyn TreeView) {
+ let entries = self.entries_for_exit_node(tree_view);
self.exit_node(entries);
}
fn entries_for_exit_node(
&self,
- traversal: &Traversal,
+ tree_view: &dyn TreeView,
) -> Option<(TreeIndex, Vec<EntryDataBundle>)> {
- traversal
- .tree
- .neighbors_directed(self.root, Direction::Incoming)
- .next()
+ tree_view
+ .view_parent_of(self.navigation().view_root)
.map(|parent_idx| {
(
parent_idx,
- sorted_entries(&traversal.tree, parent_idx, self.sorting),
+ tree_view.sorted_entries(parent_idx, self.sorting),
)
})
}
@@ -78,13 +74,8 @@ impl AppState {
pub fn exit_node(&mut self, entries: Option<(TreeIndex, Vec<EntryDataBundle>)>) {
match entries {
Some((parent_idx, entries)) => {
- self.root = parent_idx;
+ self.navigation_mut().exit_node(parent_idx, &entries);
self.entries = entries;
- self.selected = self
- .bookmarks
- .get(&parent_idx)
- .copied()
- .or_else(|| self.entries.first().map(|b| b.index));
}
None => self.message = Some("Top level reached".into()),
}
@@ -92,38 +83,30 @@ impl AppState {
fn entries_for_enter_node(
&self,
- traversal: &Traversal,
+ tree_view: &dyn TreeView,
) -> Option<(TreeIndex, Vec<EntryDataBundle>)> {
- self.selected.map(|previously_selected| {
+ self.navigation().selected.map(|previously_selected| {
(
previously_selected,
- sorted_entries(&traversal.tree, previously_selected, self.sorting),
+ tree_view.sorted_entries(previously_selected, self.sorting),
)
})
}
- pub fn enter_node_with_traversal(&mut self, traversal: &Traversal) {
- let new_entries = self.entries_for_enter_node(traversal);
+ pub fn enter_node_with_traversal(&mut self, tree_view: &dyn TreeView) {
+ let new_entries = self.entries_for_enter_node(tree_view);
self.enter_node(new_entries)
}
pub fn enter_node(&mut self, entries_at_selected: Option<(TreeIndex, Vec<EntryDataBundle>)>) {
if let Some((previously_selected, new_entries)) = entries_at_selected {
- match new_entries.get(
- self.bookmarks
- .get(&previously_selected)
- .and_then(|selected| {
- new_entries
- .iter()
- .find_position(|b| b.index == *selected)
- .map(|(pos, _)| pos)
- })
- .unwrap_or(0),
- ) {
- Some(b) => {
- self.bookmarks.insert(self.root, previously_selected);
- self.root = previously_selected;
- self.selected = Some(b.index);
+ match self
+ .navigation()
+ .get_previously_selected_index(previously_selected, &new_entries)
+ {
+ Some(selected) => {
+ self.navigation_mut()
+ .enter_node(previously_selected, selected);
self.entries = new_entries;
}
None => self.message = Some("Entry is a file or an empty directory".into()),
@@ -132,38 +115,36 @@ impl AppState {
}
pub fn change_entry_selection(&mut self, direction: CursorDirection) {
- let entries = &self.entries;
- let next_selected_pos = match self.selected {
- Some(ref selected) => entries
- .iter()
- .find_position(|b| b.index == *selected)
- .map(|(idx, _)| direction.move_cursor(idx))
- .unwrap_or(0),
- None => 0,
- };
- self.selected = entries
- .get(next_selected_pos)
- .or_else(|| entries.last())
- .map(|b| b.index)
- .or(self.selected);
- if let Some(selected) = self.selected {
- self.bookmarks.insert(self.root, selected);
- }
+ let nex_index = self.navigation().get_next_index(direction, &self.entries);
+ self.navigation_mut().select(nex_index);
}
- pub fn cycle_sorting(&mut self, traversal: &Traversal) {
+ pub fn cycle_sorting(&mut self, tree_view: &dyn TreeView) {
self.sorting.toggle_size();
- self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
+ self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting);
}
- pub fn cycle_mtime_sorting(&mut self, traversal: &Traversal) {
+ pub fn cycle_mtime_sorting(&mut self, tree_view: &dyn TreeView) {
self.sorting.toggle_mtime();
- self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
+ self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting);
}
- pub fn cycle_count_sorting(&mut self, traversal: &Traversal) {
+ pub fn cycle_count_sorting(&mut self, tree_view: &dyn TreeView) {
self.sorting.toggle_count();
- self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
+ self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting);
+ }
+
+ pub fn toggle_glob_search(&mut self, window: &mut MainWindow) {
+ self.focussed = match self.focussed {
+ Main | Mark | Help => {
+ window.glob_pane = Some(GlobPane::default());
+ Glob
+ }
+ Glob => {
+ window.glob_pane = None;
+ Main
+ }
+ }
}
pub fn reset_message(&mut self) {
@@ -176,7 +157,7 @@ impl AppState {
pub fn toggle_help_pane(&mut self, window: &mut MainWindow) {
self.focussed = match self.focussed {
- Main | Mark => {
+ Main | Mark | Glob => {
window.help_pane = Some(HelpPane::default());
Help
}
@@ -190,19 +171,28 @@ impl AppState {
if let Some(p) = window.mark_pane.as_mut() {
p.set_focus(false)
};
- self.focussed = match (self.focussed, &window.help_pane, &mut window.mark_pane) {
- (Main, Some(_), _) => Help,
- (Help, _, Some(ref mut pane)) => {
+ self.focussed = match (
+ self.focussed,
+ &window.help_pane,
+ &mut window.mark_pane,
+ &mut window.glob_pane,
+ ) {
+ (Main, Some(_), _, _) => Help,
+ (Help, _, Some(ref mut pane), _) => {
pane.set_focus(true);
Mark
}
- (Help, _, None) => Main,
- (Mark, _, _) => Main,
- (Main, None, None) => Main,
- (Main, None, Some(ref mut pane)) => {
+ (Help, _, _, Some(_)) => Glob,
+ (Help, _, None, None) => Main,
+ (Mark, _, _, Some(_)) => Glob,
+ (Mark, _, _, _) => Main,
+ (Main, None, None, None) => Main,
+ (Main, None, Some(ref mut pane), _) => {
pane.set_focus(true);
Mark
}
+ (Main, None, None, Some(_)) => Glob,
+ (Glob, _, _, _) => Main,
};
}
@@ -210,7 +200,7 @@ impl AppState {
&mut self,
key: Key,
window: &mut MainWindow,
- traversal: &mut Traversal,
+ tree_view: &mut dyn TreeView,
display: DisplayOptions,
terminal: &mut Terminal<B>,
) where
@@ -224,9 +214,9 @@ impl AppState {
let mut entries_deleted = 0;
let res = pane.iterate_deletable_items(|mut pane, entry_to_delete| {
window.mark_pane = Some(pane);
- self.draw(window, traversal, display, terminal).ok();
+ self.draw(window, tree_view, display, terminal).ok();
pane = window.mark_pane.take().expect("option to be filled");
- match self.delete_entry(entry_to_delete, traversal) {
+ match self.delete_entry(entry_to_delete, tree_view) {
Ok(ed) => {
entries_deleted += ed;
self.message =
@@ -245,9 +235,9 @@ impl AppState {
let mut entries_trashed = 0;
let res = pane.iterate_deletable_items(|mut pane, entry_to_trash| {
window.mark_pane = Some(pane);
- self.draw(window, traversal, display, terminal).ok();
+ self.draw(window, tree_view, display, terminal).ok();
pane = window.mark_pane.take().expect("option to be filled");
- match self.trash_entry(entry_to_trash, traversal) {
+ match self.trash_entry(entry_to_trash, tree_view) {
Ok(ed) => {
entries_trashed += ed;
self.message =
@@ -272,13 +262,13 @@ impl AppState {
pub fn delete_entry(
&mut self,
index: TreeIndex,
- traversal: &mut Traversal,
+ tree_view: &mut dyn TreeView,
) -> Result<usize, usize> {
let mut entries_deleted = 0;
- if let Some(_entry) = traversal.tree.node_weight(index) {
- let path_to_delete = path_of(&traversal.tree, index);
+ if tree_view.exists(index) {
+ let path_to_delete = tree_view.path_of(index);
delete_directory_recursively(path_to_delete)?;
- entries_deleted = self.delete_entries_in_traversal(index, traversal);
+ entries_deleted = self.delete_entries_in_traversal(index, tree_view);
}
Ok(entries_deleted)
}
@@ -287,15 +277,15 @@ impl AppState {
pub fn trash_entry(
&mut self,
index: TreeIndex,
- traversal: &mut Traversal,
+ tree_view: &mut dyn TreeView,
) -> Result<usize, usize> {
let mut entries_deleted = 0;
- if let Some(_entry) = traversal.tree.node_weight(index) {
- let path_to_delete = path_of(&traversal.tree, index);
+ if tree_view.exists(index) {
+ let path_to_delete = tree_view.path_of(index);
if trash::delete(path_to_delete).is_err() {
return Err(1);
}
- entries_deleted = self.delete_entries_in_traversal(index, traversal);
+ entries_deleted = self.delete_entries_in_traversal(index, tree_view);
}
Ok(entries_deleted)
}
@@ -303,64 +293,43 @@ impl AppState {
pub fn delete_entries_in_traversal(
&mut self,
index: TreeIndex,
- traversal: &mut Traversal,
+ tree_view: &mut dyn TreeView,
) -> usize {
- let mut entries_deleted = 0;
- let parent_idx = traversal
- .tree
- .neighbors_directed(index, Direction::Incoming)
- .next()
+ let parent_idx = tree_view
+ .fs_parent_of(index)
.expect("us being unable to delete the root index");
- let mut bfs = Bfs::new(&traversal.tree, index);
- while let Some(nx) = bfs.next(&traversal.tree) {
- traversal.tree.remove_node(nx);
- traversal.entries_traversed -= 1;
- entries_deleted += 1;
- }
- self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
- if traversal.tree.node_weight(self.root).is_none() {
- self.set_root(traversal.root_index, traversal);
+ let entries_deleted = tree_view.remove_entries(index);
+
+ 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);
}
+