summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--development.md8
-rw-r--r--src/event_exec.rs74
-rw-r--r--src/flagged.rs51
-rw-r--r--src/lib.rs1
-rw-r--r--src/status.rs31
-rw-r--r--src/term_manager.rs6
6 files changed, 100 insertions, 71 deletions
diff --git a/development.md b/development.md
index 3d92d34..d4df011 100644
--- a/development.md
+++ b/development.md
@@ -240,10 +240,12 @@
- [ ] trait for struct or thing with index
- - [ ] trait<T> { collection: Vec<T>, index: usize}: next, prev etc.
- - [ ] harmonize code for multiple instances
- - [ ] macro to impl auto
+ - [x] trait<T> { collection: Vec<T>, index: usize}: next, prev etc.
+ - [x] harmonize code for multiple instances
+ - [x] macro to impl auto
+ - [x] struct for flagged. Use a vector instead of hashset... :(
- [ ] derive
+ - [ ] regroup shortcut, history, jump, visited
- [ ] Version 0.2.0 : tests
diff --git a/src/event_exec.rs b/src/event_exec.rs
index be9d2d3..e9bfea2 100644
--- a/src/event_exec.rs
+++ b/src/event_exec.rs
@@ -2,7 +2,6 @@ use std::borrow::Borrow;
use std::cmp::min;
use std::fs;
use std::path;
-use std::path::PathBuf;
use crate::bulkrename::Bulkrename;
use crate::completion::CompletionKind;
@@ -63,7 +62,7 @@ impl EventExec {
.content
.iter()
.for_each(|file| {
- status.flagged.insert(file.path.clone());
+ status.flagged.push(file.path.clone());
});
status.reset_tabs_view()
}
@@ -75,39 +74,28 @@ impl EventExec {
.path_content
.content
.iter()
- .for_each(|file| {
- if status.flagged.contains(&file.path.clone()) {
- status.flagged.remove(&file.path.clone());
- } else {
- status.flagged.insert(file.path.clone());
- }
- });
+ .for_each(|file| status.flagged.toggle(&file.path));
status.reset_tabs_view()
}
/// Toggle a single flag and move down one row.
pub fn event_toggle_flag(status: &mut Status) -> FmResult<()> {
- let file = status.tabs[status.index]
- .path_content
- .selected()
- .ok_or_else(|| FmError::custom("event toggle flag", "No selected file"))?;
- status.toggle_flag_on_path(file.path.clone());
- Self::event_down_one_row(status.selected());
+ if let Some(file) = status.selected().path_content.selected() {
+ let path = file.path.clone();
+ status.toggle_flag_on_path(&path);
+ Self::event_down_one_row(status.selected());
+ }
Ok(())
}
/// Move to the next file in the jump list.
pub fn event_jumplist_next(status: &mut Status) {
- if status.jump_index < status.flagged.len() {
- status.jump_index += 1;
- }
+ status.flagged.next()
}
/// Move to the previous file in the jump list.
pub fn event_jumplist_prev(status: &mut Status) {
- if status.jump_index > 0 {
- status.jump_index -= 1;
- }
+ status.flagged.prev()
}
/// Change to CHMOD mode allowing to edit permissions of a file.
@@ -117,7 +105,7 @@ impl EventExec {
}
status.selected().mode = Mode::InputSimple(InputKind::Chmod);
if status.flagged.is_empty() {
- status.flagged.insert(
+ status.flagged.push(
status.tabs[status.index]
.path_content
.selected()
@@ -133,7 +121,8 @@ impl EventExec {
/// Does nothing if no file is flagged.
pub fn event_jump(status: &mut Status) -> FmResult<()> {
if !status.flagged.is_empty() {
- status.jump_index = 0;
+ status.flagged.index = 0;
+ info!("entering jump mode");
status.selected().mode = Mode::Jump
}
Ok(())
@@ -171,7 +160,7 @@ impl EventExec {
/// Creates a symlink of every flagged file to the current directory.
pub fn event_symlink(status: &mut Status) -> FmResult<()> {
- for oldpath in status.flagged.iter() {
+ for oldpath in status.flagged.content.iter() {
let filename = oldpath
.as_path()
.file_name()
@@ -208,7 +197,7 @@ impl EventExec {
/// Recursively delete all flagged files.
pub fn exec_delete_files(status: &mut Status) -> FmResult<()> {
- for pathbuf in status.flagged.iter() {
+ for pathbuf in status.flagged.content.iter() {
if pathbuf.is_dir() {
std::fs::remove_dir_all(pathbuf)?;
} else {
@@ -230,7 +219,7 @@ impl EventExec {
let permissions: u32 =
u32::from_str_radix(&status.selected().input.string(), 8).unwrap_or(0_u32);
if permissions <= Status::MAX_PERMISSIONS {
- for path in status.flagged.iter() {
+ for path in status.flagged.content.iter() {
Status::set_permissions(path.clone(), permissions)?
}
status.flagged.clear()
@@ -254,27 +243,24 @@ impl EventExec {
/// If the user selected a directory, we jump inside it.
/// Otherwise, we jump to the parent and select the file.
pub fn exec_jump(status: &mut Status) -> FmResult<()> {
- let jump_target = Self::find_jump_path(status);
- let target_dir = match jump_target.parent() {
- Some(parent) => parent,
- None => &jump_target,
- };
- let tab = status.selected();
- tab.input.clear();
- tab.history.push(&target_dir.to_path_buf());
- tab.path_content.change_directory(target_dir)?;
- let index = tab.find_jump_index(&jump_target).unwrap_or_default();
- tab.path_content.select_index(index);
- tab.set_window();
- tab.scroll_to(index);
+ if let Some(jump_target) = status.flagged.selected() {
+ let jump_target = jump_target.to_owned();
+ let target_dir = match jump_target.parent() {
+ Some(parent) => parent,
+ None => &jump_target,
+ };
+ let tab = status.selected();
+ tab.input.clear();
+ tab.history.push(&target_dir.to_path_buf());
+ tab.path_content.change_directory(target_dir)?;
+ let index = tab.find_jump_index(&jump_target).unwrap_or_default();
+ tab.path_content.select_index(index);
+ tab.set_window();
+ tab.scroll_to(index);
+ }
Ok(())
}
- fn find_jump_path(status: &Status) -> PathBuf {
- let jump_list: Vec<&PathBuf> = status.flagged.iter().collect();
- jump_list[status.jump_index].clone()
- }
-
/// Execute a command requiring a confirmation (Delete, Move or Copy).
pub fn exec_confirmed_action(
status: &mut Status,
diff --git a/src/flagged.rs b/src/flagged.rs
new file mode 100644
index 0000000..d8e55aa
--- /dev/null
+++ b/src/flagged.rs
@@ -0,0 +1,51 @@
+use std::path::{Path, PathBuf};
+
+use crate::impl_selectable_content;
+
+#[derive(Clone, Debug, Default)]
+pub struct Flagged {
+ pub content: Vec<PathBuf>,
+ pub index: usize,
+}
+
+impl Flagged {
+ pub fn push(&mut self, path: PathBuf) {
+ if self.content.contains(&path) {
+ return;
+ }
+ self.content.push(path);
+ self.content.sort()
+ }
+
+ pub fn remove(&mut self, path: PathBuf) {
+ if let Some(index) = self.content.iter().position(|x| *x == path) {
+ self.content.remove(index);
+ }
+ }
+
+ pub fn toggle(&mut self, path: &Path) {
+ if let Some(index) = self.content.iter().position(|x| *x == path) {
+ self.content.remove(index);
+ } else {
+ self.push(path.to_path_buf());
+ };
+ }
+
+ pub fn clear(&mut self) {
+ self.content.clear()
+ }
+
+ pub fn contains(&self, path: &PathBuf) -> bool {
+ self.content.contains(path)
+ }
+
+ pub fn filtered(&self, current_path: &Path) -> Vec<&Path> {
+ self.content
+ .iter()
+ .filter(|p| p.starts_with(current_path))
+ .map(|p| p.as_path())
+ .collect()
+ }
+}
+
+impl_selectable_content!(PathBuf, Flagged);
diff --git a/src/lib.rs b/src/lib.rs
index c4bf51a..00acde8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,7 @@ pub mod event_dispatch;
pub mod event_exec;
pub mod fileinfo;
pub mod filter;
+pub mod flagged;
pub mod fm_error;
pub mod git;
pub mod help;
diff --git a/src/status.rs b/src/status.rs
index 4f9ad35..4b6fe43 100644
--- a/src/status.rs
+++ b/src/status.rs
@@ -1,4 +1,3 @@
-use std::collections::HashSet;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{self, Path, PathBuf};
@@ -14,6 +13,7 @@ use crate::color_cache::ColorCache;
use crate::config::Config;
use crate::constant_strings_paths::OPENER_PATH;
use crate::copy_move::{copy_move, CopyMove};
+use crate::flagged::Flagged;
use crate::fm_error::{FmError, FmResult};
use crate::marks::Marks;
use crate::opener::{load_opener, Opener};
@@ -35,10 +35,8 @@ pub struct Status {
pub tabs: [Tab; 2],
/// Index of the current selected tab
pub index: usize,
- /// Set of flagged files
- pub flagged: HashSet<PathBuf>,
- /// Index in the jump list
- pub jump_index: usize,
+ /// The flagged files
+ pub flagged: Flagged,
/// Marks allows you to jump to a save mark
pub marks: Marks,
/// Colors for extension
@@ -81,8 +79,7 @@ impl Status {
Ok(Self {
tabs: [tab.clone(), tab],
index: 0,
- flagged: HashSet::new(),
- jump_index: 0,
+ flagged: Flagged::default(),
marks: Marks::read_from_config_file(),
colors: ColorCache::default(),
skimer: Skimer::new(term.clone()),
@@ -127,12 +124,8 @@ impl Status {
}
/// Toggle the flagged attribute of a path.
- pub fn toggle_flag_on_path(&mut self, path: PathBuf) {
- if self.flagged.contains(&path) {
- self.flagged.remove(&path);
- } else {
- self.flagged.insert(path);
- };
+ pub fn toggle_flag_on_path(&mut self, path: &Path) {
+ self.flagged.toggle(path)
}
/// Replace the tab content with what was returned by skim.
@@ -172,22 +165,18 @@ impl Status {
/// directory.
/// It's necessary since the user may have flagged files OUTSIDE of current
/// directory before calling Bulkrename.
- /// It may creates confusion since the same filename can be used in
+ /// It may be confusing since the same filename can be used in
/// different places.
pub fn filtered_flagged_files(&self) -> Vec<&Path> {
- let path_content = self.selected_non_mut().path_content.clone();
self.flagged
- .iter()
- .filter(|p| path_content.contains(p))
- .map(|p| p.as_path())
- .collect()
+ .filtered(&self.selected_non_mut().path_content.path)
}
/// Execute a move or a copy of the flagged files to current directory.
/// A progress bar is displayed (invisible for small files) and a notification
/// is sent every time, even for 0 bytes files...
pub fn cut_or_copy_flagged_files(&mut self, cut_or_copy: CopyMove) -> FmResult<()> {
- let sources: Vec<PathBuf> = self.flagged.iter().map(|path| path.to_owned()).collect();
+ let sources = self.flagged.content.clone();
let dest = self
.selected_non_mut()
.path_str()
@@ -223,7 +212,7 @@ impl Status {
let re = Regex::new(&self.selected_non_mut().input.string())?;
for file in self.tabs[self.index].path_content.content.iter() {
if re.is_match(&file.path.to_string_lossy()) {
- self.flagged.insert(file.path.clone());
+ self.flagged.push(file.path.clone());
}
}
Ok(())
diff --git a/src/term_manager.rs b/src/term_manager.rs
index eea7c76..be7cc37 100644
--- a/src/term_manager.rs
+++ b/src/term_manager.rs
@@ -265,9 +265,9 @@ impl<'a> WinTab<'a> {
/// Display the possible jump destination from flagged files.
fn jump_list(&self, tabs: &Status, canvas: &mut dyn Canvas) -> FmResult<()> {
canvas.print(0, 0, "Jump to...")?;
- for (row, path) in tabs.flagged.iter().enumerate() {
+ for (row, path) in tabs.flagged.content.iter().enumerate() {
let mut attr = Attr::default();
- if row == tabs.jump_index {
+ if row == tabs.flagged.index {
attr.effect |= Effect::REVERSE;
}
let _ = canvas.print_with_attr(
@@ -343,7 +343,7 @@ impl<'a> WinTab<'a> {
confirmed_mode: ConfirmedAction,
canvas: &mut dyn Canvas,
) -> FmResult<()> {
- for (row, path) in status.flagged.iter().enumerate() {
+ for (row, path) in status.flagged.content.iter().enumerate() {
canvas.print_with_attr(
row + ContentWindow::WINDOW_MARGIN_TOP + 2,
4,