summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Thiel <sthiel@thoughtworks.com>2019-06-06 20:05:07 +0530
committerSebastian Thiel <sthiel@thoughtworks.com>2019-06-06 20:05:07 +0530
commit9ffacd03e256b45ecd40744e5507f37c30ae9b5e (patch)
tree6c1a22a85ae2f60116727829f89a98ba2372cc84
parent4c354f475bfe841f3797be0a3341212aeeaa60c8 (diff)
Move ownership of marked entries to the MarkPane
interesting experience... it's easier to just keep state central, and have one place where mutation happens. Now that things are all over the place, it becomes more difficult to handle. However, I believe there is a way, the problem here is the hybrid state the program is currenlty in... partly centralized, partly localized.
-rw-r--r--src/interactive/app/eventloop.rs8
-rw-r--r--src/interactive/app/handlers.rs33
-rw-r--r--src/interactive/app_test.rs28
-rw-r--r--src/interactive/widgets/entries.rs8
-rw-r--r--src/interactive/widgets/footer.rs6
-rw-r--r--src/interactive/widgets/main.rs47
-rw-r--r--src/interactive/widgets/mark.rs63
7 files changed, 104 insertions, 89 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index 6227448..adf0955 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -40,7 +40,6 @@ pub struct AppState {
pub sorting: SortMode,
pub message: Option<String>,
pub focussed: FocussedPane,
- pub marked: EntryMarkMap,
}
/// State and methods representing the interactive disk usage analyser for the terminal
@@ -110,12 +109,7 @@ impl TerminalApp {
}
match self.state.focussed {
- FocussedPane::Mark => self
- .window
- .mark_pane
- .as_mut()
- .expect("mark pane")
- .key(key, &self.state.marked),
+ FocussedPane::Mark => self.window.mark_pane.as_mut().expect("mark pane").key(key),
FocussedPane::Help => {
self.window.help_pane.as_mut().expect("help pane").key(key);
}
diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs
index 11becc1..850ce12 100644
--- a/src/interactive/app/handlers.rs
+++ b/src/interactive/app/handlers.rs
@@ -1,5 +1,5 @@
use crate::interactive::{
- app::{EntryMark, FocussedPane, TerminalApp},
+ app::{FocussedPane, TerminalApp},
sorted_entries,
widgets::{HelpPane, MarkPane},
};
@@ -125,29 +125,18 @@ impl TerminalApp {
}
pub fn mark_entry(&mut self, advance_cursor: bool) {
- if let Some(index) = self.state.selected {
- // TODO: consider using the Entry::Occupied/Vacant API to remove things
- if self.state.marked.get(&index).is_some() {
- self.state.marked.remove(&index);
- } else {
- if let Some(e) = self.state.entries.iter().find(|e| e.index == index) {
- self.state.marked.insert(
- index,
- EntryMark {
- size: e.data.size,
- path: path_of(&self.traversal.tree, index),
- },
- );
- }
- }
- if self.state.marked.is_empty() {
- self.window.mark_pane = None;
- } else {
- self.window.mark_pane = Some(MarkPane::new(&self.state.marked));
+ match (self.state.selected, self.window.mark_pane.take()) {
+ (Some(index), Some(pane)) => {
+ self.window.mark_pane = pane.toggle_index(index, &self.traversal.tree);
}
- if advance_cursor {
- self.change_entry_selection(CursorDirection::Down)
+ (Some(index), None) => {
+ self.window.mark_pane =
+ MarkPane::default().toggle_index(index, &self.traversal.tree)
}
+ _ => {}
+ };
+ if advance_cursor {
+ self.change_entry_selection(CursorDirection::Down)
}
}
}
diff --git a/src/interactive/app_test.rs b/src/interactive/app_test.rs
index fb413e6..6215c85 100644
--- a/src/interactive/app_test.rs
+++ b/src/interactive/app_test.rs
@@ -224,9 +224,15 @@ fn simple_user_journey() -> Result<(), Error> {
let previously_selected_index = *app.state.selected.as_ref().unwrap();
app.process_events(&mut terminal, b"d".keys())?;
{
- assert_eq!(1, app.state.marked.len(), "it marks only a single node",);
+ assert_eq!(
+ Some(1),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
+ "it marks only a single node",
+ );
assert!(
- app.state.marked.contains_key(&previously_selected_index),
+ app.window.mark_pane.as_ref().map_or(false, |p| p
+ .marked()
+ .contains_key(&previously_selected_index)),
"it marks the selected node"
);
assert_eq!(
@@ -241,8 +247,8 @@ fn simple_user_journey() -> Result<(), Error> {
app.process_events(&mut terminal, b"d".keys())?;
assert_eq!(
- 2,
- app.state.marked.len(),
+ Some(2),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
"it marks the currently selected, second node",
);
@@ -258,13 +264,15 @@ fn simple_user_journey() -> Result<(), Error> {
app.process_events(&mut terminal, b"d".keys())?;
assert_eq!(
- 1,
- app.state.marked.len(),
+ Some(1),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
"it toggled the previous selected entry off",
);
assert!(
- app.state.marked.contains_key(&previously_selected_index),
+ app.window.mark_pane.as_ref().map_or(false, |p| p
+ .marked()
+ .contains_key(&previously_selected_index)),
"it leaves the first selected entry marked"
);
}
@@ -272,7 +280,11 @@ fn simple_user_journey() -> Result<(), Error> {
{
app.process_events(&mut terminal, b"k ".keys())?;
- assert_eq!(0, app.state.marked.len(), "it toggles the item off",);
+ assert_eq!(
+ Some(0),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
+ "it toggles the item off",
+ );
assert_eq!(
node_by_index(&app, previously_selected_index),
diff --git a/src/interactive/widgets/entries.rs b/src/interactive/widgets/entries.rs
index 29affa5..bef91cd 100644
--- a/src/interactive/widgets/entries.rs
+++ b/src/interactive/widgets/entries.rs
@@ -19,7 +19,7 @@ pub struct EntriesProps<'a> {
pub display: DisplayOptions,
pub selected: Option<TreeIndex>,
pub entries: &'a [EntryDataBundle],
- pub marked: &'a EntryMarkMap,
+ pub marked: Option<&'a EntryMarkMap>,
pub border_style: Style,
pub is_focussed: bool,
}
@@ -152,7 +152,11 @@ impl Entries {
)
.into(),
Style {
- fg: match (!is_dir, exists, marked.contains_key(node_idx)) {
+ fg: match (
+ !is_dir,
+ exists,
+ marked.map(|m| m.contains_key(node_idx)).unwrap_or(false),
+ ) {
(true, true, false) if !is_selected => Color::DarkGray,
(true, true, false) => style.fg,
(false, true, false) => style.fg,
diff --git a/src/interactive/widgets/footer.rs b/src/interactive/widgets/footer.rs
index 56d6657..f961412 100644
--- a/src/interactive/widgets/footer.rs
+++ b/src/interactive/widgets/footer.rs
@@ -17,7 +17,7 @@ pub struct FooterProps<'a> {
pub total_bytes: Option<u64>,
pub entries_traversed: u64,
pub format: ByteFormat,
- pub marked: &'a EntryMarkMap,
+ pub marked: Option<&'a EntryMarkMap>,
pub message: Option<String>,
}
@@ -45,7 +45,7 @@ impl Footer {
)
.into(),
)),
- match marked.is_empty() {
+ marked.and_then(|marked| match marked.is_empty() {
true => None,
false => Some(Text::Styled(
format!(
@@ -60,7 +60,7 @@ impl Footer {
modifier: Modifier::BOLD | Modifier::RAPID_BLINK,
},
)),
- },
+ }),
message.as_ref().map(|m| {
Text::Styled(
m.into(),
diff --git a/src/interactive/widgets/main.rs b/src/interactive/widgets/main.rs
index 6eab313..a5640a1 100644
--- a/src/interactive/widgets/main.rs
+++ b/src/interactive/widgets/main.rs
@@ -92,28 +92,34 @@ impl MainWindow {
Mark => (grey, grey, white),
};
- let bg_color = match (state.marked.is_empty(), state.focussed) {
- (false, FocussedPane::Mark) => Color::LightRed,
- (false, _) => COLOR_MARKED_LIGHT,
- (true, _) => Color::White,
+ let bg_color = {
+ let marked = mark_pane.as_ref().map(|(_, p)| p.marked());
+ match (marked.map(|m| m.is_empty()), state.focussed) {
+ (Some(false), FocussedPane::Mark) => Color::LightRed,
+ (Some(false), _) | (None, _) => COLOR_MARKED_LIGHT,
+ (_, _) => Color::White,
+ }
};
Header.render(bg_color, header_area, buf);
- let props = EntriesProps {
- tree: &tree,
- root: state.root,
- display: *display,
- entries: &state.entries,
- marked: &state.marked,
- selected: state.selected,
- border_style: entries_style,
- is_focussed: if let Main = state.focussed {
- true
- } else {
- false
- },
- };
- self.entries_pane.render(props, entries_area, buf);
+ {
+ let marked = mark_pane.as_ref().map(|(_, p)| p.marked());
+ let props = EntriesProps {
+ tree: &tree,
+ root: state.root,
+ display: *display,
+ entries: &state.entries,
+ marked,
+ selected: state.selected,
+ border_style: entries_style,
+ is_focussed: if let Main = state.focussed {
+ true
+ } else {
+ false
+ },
+ };
+ self.entries_pane.render(props, entries_area, buf);
+ }
if let Some((help_area, pane)) = help_pane {
let props = HelpPaneProps {
@@ -124,7 +130,6 @@ impl MainWindow {
if let Some((mark_area, pane)) = mark_pane {
let props = MarkPaneProps {
border_style: mark_style,
- marked: &state.marked,
};
pane.render(props, mark_area, buf);
}
@@ -133,7 +138,7 @@ impl MainWindow {
FooterProps {
total_bytes: *total_bytes,
entries_traversed: *entries_traversed,
- marked: &state.marked,
+ marked: self.mark_pane.as_ref().map(|p| p.marked()),
format: display.byte_format,
message: state.message.clone(),
},
diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs
index 42650aa..d600b34 100644
--- a/src/interactive/widgets/mark.rs
+++ b/src/interactive/widgets/mark.rs
@@ -1,5 +1,6 @@
-use crate::interactive::{widgets::COLOR_MARKED_LIGHT, CursorDirection, EntryMarkMap};
-use dua::traverse::TreeIndex;
+use crate::interactive::{widgets::COLOR_MARKED_LIGHT, CursorDirection, EntryMark, EntryMarkMap};
+use dua::path_of;
+use dua::traverse::{Tree, TreeIndex};
use itertools::Itertools;
use std::borrow::Borrow;
use termion::{event::Key, event::Key::*};
@@ -13,47 +14,57 @@ use tui::{
};
use tui_react::{List, ListProps};
+#[derive(Default)]
pub struct MarkPane {
- list: List,
selected: Option<TreeIndex>,
+ marked: EntryMarkMap,
+ list: List,
}
-pub struct MarkPaneProps<'a> {
+pub struct MarkPaneProps {
pub border_style: Style,
- pub marked: &'a EntryMarkMap,
}
impl MarkPane {
- pub fn new(_marked: &EntryMarkMap) -> MarkPane {
- MarkPane {
- list: Default::default(),
- selected: None,
+ pub fn toggle_index(mut self, index: TreeIndex, tree: &Tree) -> Option<Self> {
+ if self.marked.get(&index).is_some() {
+ self.marked.remove(&index);
+ } else {
+ if let Some(e) = tree.node_weight(index) {
+ self.marked.insert(
+ index,
+ EntryMark {
+ size: e.size,
+ path: path_of(tree, index),
+ },
+ );
+ }
+ }
+ if self.marked.is_empty() {
+ None
+ } else {
+ Some(self)
}
}
-
- pub fn key(&mut self, key: Key, marked: &EntryMarkMap) {
+ pub fn marked(&self) -> &EntryMarkMap {
+ &self.marked
+ }
+ pub fn key(&mut self, key: Key) {
match key {
- Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp, marked),
- Char('k') | Up => self.change_selection(CursorDirection::Up, marked),
- Char('j') | Down => self.change_selection(CursorDirection::Down, marked),
- Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown, marked),
+ Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp),
+ Char('k') | Up => self.change_selection(CursorDirection::Up),
+ Char('j') | Down => self.change_selection(CursorDirection::Down),
+ Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown),
_ => {}
};
}
- fn change_selection(&mut self, _direction: CursorDirection, _marked: &EntryMarkMap) {}
+ fn change_selection(&mut self, _direction: CursorDirection) {}
- pub fn render<'a>(
- &mut self,
- props: impl Borrow<MarkPaneProps<'a>>,
- area: Rect,
- buf: &mut Buffer,
- ) {
- let MarkPaneProps {
- border_style,
- marked,
- } = props.borrow();
+ pub fn render(&mut self, props: impl Borrow<MarkPaneProps>, area: Rect, buf: &mut Buffer) {
+ let MarkPaneProps { border_style } = props.borrow();
+ let marked: &_ = &self.marked;
let block = Block::default()
.title("Marked Entries")
.border_style(*border_style)