From 00fae90e0dffc468c75bd362fa4220bc8650fb86 Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Sun, 27 Jun 2021 15:30:35 +0200 Subject: Implement Ctrl+t move to trash --- Cargo.lock | 199 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/interactive/app/handlers.rs | 59 ++++++++++++ src/interactive/widgets/help.rs | 5 + src/interactive/widgets/mark.rs | 9 ++ 5 files changed, 273 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 96ed3e1..1fec4eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.12.1" @@ -59,6 +61,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clap" version = "3.0.0-beta.2" @@ -102,6 +117,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "const-sha1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d" + [[package]] name = "core-foundation-sys" version = "0.8.2" @@ -253,6 +274,7 @@ dependencies = [ "petgraph", "pretty_assertions", "sysinfo", + "trash", "tui", "tui-react", "unicode-segmentation", @@ -280,6 +302,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "glob" version = "0.3.0" @@ -310,6 +342,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.6.2" @@ -378,6 +421,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memoffset" version = "0.6.4" @@ -418,6 +476,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -434,6 +511,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "once_cell" version = "1.7.2" @@ -490,6 +576,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "petgraph" version = "0.5.1" @@ -691,6 +783,47 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "trash" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea78c1e8e861fed535e67f56601a59879f3b2d639aed32e98589d5a2d8378fbd" +dependencies = [ + "chrono", + "libc", + "log", + "objc", + "scopeguard", + "url", + "windows", +] + [[package]] name = "tui" version = "0.15.0" @@ -717,6 +850,24 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -735,6 +886,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "utf8-width" version = "0.1.5" @@ -753,6 +916,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "which" version = "4.1.0" @@ -802,3 +971,33 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e50fd72a86bac7e21de2ce132629b7921e57b75ed607025f850a97a7ce2aec3" +dependencies = [ + "const-sha1", + "windows_gen", + "windows_macros", +] + +[[package]] +name = "windows_gen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41030a241566acc784e1883355aebf4fb87bae609682a613ce62bf7639d5ec5" +dependencies = [ + "syn", +] + +[[package]] +name = "windows_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eb153f12764a88cd8f6898ba9b4b03b57704b26289a15c66495923d23e4208" +dependencies = [ + "syn", + "windows_gen", +] diff --git a/Cargo.toml b/Cargo.toml index 18d7218..0b9e6de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ num_cpus = "1.10.0" filesize = "0.2.0" anyhow = "1.0.31" colored = "2.0.0" +trash = "2.0.1" # 'tui' related unicode-segmentation = { version = "1.3.0", optional = true } diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs index 2360aae..c15c764 100644 --- a/src/interactive/app/handlers.rs +++ b/src/interactive/app/handlers.rs @@ -9,6 +9,7 @@ use dua::traverse::{Traversal, TreeIndex}; use itertools::Itertools; use petgraph::{visit::Bfs, Direction}; use std::{fs, io, path::PathBuf}; +use trash; use tui::backend::Backend; use tui_react::Terminal; @@ -229,6 +230,26 @@ impl AppState { self.message = None; res } + Some(MarkMode::Trash) => { + self.message = Some("Trashing entries...".to_string()); + 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(); + pane = window.mark_pane.take().expect("option to be filled"); + match self.trash_entry(entry_to_trash, traversal) { + Ok(ed) => { + entries_trashed += ed; + self.message = + Some(format!("Trashed {} entries...", entries_trashed)); + Ok(pane) + } + Err(c) => Err((pane, c)), + } + }); + self.message = None; + res + } None => Some(pane), }, None => None, @@ -274,6 +295,44 @@ impl AppState { Ok(entries_deleted) } + pub fn trash_entry( + &mut self, + index: TreeIndex, + traversal: &mut Traversal, + ) -> Result { + let mut entries_trashed = 0; + if let Some(_entry) = traversal.tree.node_weight(index) { + let path_to_delete = path_of(&traversal.tree, index); + if let Err(_) = trash::delete(path_to_delete) { + return Err(1); + } + let parent_idx = traversal + .tree + .neighbors_directed(index, Direction::Incoming) + .next() + .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_trashed += 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); + } + if self + .selected + .and_then(|selected| self.entries.iter().find(|e| e.index == selected)) + .is_none() + { + self.selected = self.entries.get(0).map(|e| e.index); + } + self.recompute_sizes_recursively(parent_idx, traversal); + } + Ok(entries_trashed) + } + fn set_root(&mut self, root: TreeIndex, traversal: &Traversal) { self.root = root; self.entries = sorted_entries(&traversal.tree, root, self.sorting); diff --git a/src/interactive/widgets/help.rs b/src/interactive/widgets/help.rs index 89678db..d410035 100644 --- a/src/interactive/widgets/help.rs +++ b/src/interactive/widgets/help.rs @@ -168,6 +168,11 @@ impl HelpPane { "Permanently delete all marked entries without prompt!", Some("This operation cannot be undone!"), ); + hotkey( + "Ctrl + t", + "Move all marked entries to the trash bin", + Some("The entries can be restored from the trash bin"), + ); spacer(); } title("Keys for application control"); diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs index 4e5829a..e01604b 100644 --- a/src/interactive/widgets/mark.rs +++ b/src/interactive/widgets/mark.rs @@ -28,6 +28,7 @@ use unicode_segmentation::UnicodeSegmentation; pub enum MarkMode { Delete, + Trash, } pub type EntryMarkMap = BTreeMap; @@ -109,6 +110,7 @@ impl MarkPane { let action = None; match key { Ctrl('r') => return Some(self.prepare_deletion()), + Ctrl('t') => return Some(self.prepare_trashing()), Char('x') | Char('d') | Char(' ') => { return self.remove_selected().map(|s| (s, action)) } @@ -184,6 +186,13 @@ impl MarkPane { self.selected = Some(0); (self, Some(MarkMode::Delete)) } + fn prepare_trashing(mut self) -> (Self, Option) { + for entry in self.marked.values_mut() { + entry.num_errors_during_deletion = 0; + } + self.selected = Some(0); + (self, Some(MarkMode::Trash)) + } fn remove_selected(mut self) -> Option { if let Some(mut selected) = self.selected { let idx = self.tree_index_by_list_position(selected); -- cgit v1.2.3