summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-09 12:39:31 -0500
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-09 13:07:31 -0500
commitbecbb90b2f22d58c98693d653f55ba604bb03f75 (patch)
tree085b9ac9b197a9ad3f0dd20df36848c5619adefe /src
parent656582a6c867c25667661be9b327b4cc73859d7d (diff)
rework input thread and file operations
- no longer depend on fs_extra for copy/paste files - in house solution preserves permissions - ioworkers are now queued, no more parallel io tasks - input thread now listens for ioworker threads as well - cargo fmt
Diffstat (limited to 'src')
-rw-r--r--src/commands/delete_files.rs35
-rw-r--r--src/commands/file_operations.rs251
-rw-r--r--src/commands/file_ops/copy.rs36
-rw-r--r--src/commands/file_ops/cut.rs36
-rw-r--r--src/commands/file_ops/local_state.rs56
-rw-r--r--src/commands/file_ops/mod.rs11
-rw-r--r--src/commands/file_ops/name_resolution.rs15
-rw-r--r--src/commands/file_ops/paste.rs55
-rw-r--r--src/commands/file_ops/paste_copy.rs68
-rw-r--r--src/commands/file_ops/paste_cut.rs76
-rw-r--r--src/commands/mod.rs12
-rw-r--r--src/commands/open_file.rs6
-rw-r--r--src/commands/quit.rs2
-rw-r--r--src/config/keymap.rs11
-rw-r--r--src/context.rs18
-rw-r--r--src/fs/fs_extra_extra.rs323
-rw-r--r--src/fs/mod.rs1
-rw-r--r--src/io/io_worker.rs34
-rw-r--r--src/io/mod.rs3
-rw-r--r--src/main.rs9
-rw-r--r--src/run.rs152
-rw-r--r--src/textfield.rs2
-rw-r--r--src/ui.rs20
-rw-r--r--src/util/event.rs102
-rw-r--r--src/util/key_mapping.rs2
-rw-r--r--src/util/mod.rs76
-rw-r--r--src/window/panel.rs1
27 files changed, 582 insertions, 831 deletions
diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs
index adaed07..c726dd4 100644
--- a/src/commands/delete_files.rs
+++ b/src/commands/delete_files.rs
@@ -35,32 +35,23 @@ impl DeleteFiles {
fn delete_files(context: &mut JoshutoContext, view: &JoshutoView) -> std::io::Result<()> {
ui::wprint_msg(&view.bot_win, "Delete selected files? (Y/n)");
- ncurses::timeout(-1);
ncurses::doupdate();
let curr_tab = &mut context.tabs[context.curr_tab_index];
- let mut ch = ncurses::getch();
- if ch == 'y' as i32 || ch == KEYMAP_T.enter {
- let paths = curr_tab.curr_list.get_selected_paths();
- if paths.is_empty() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "no files selected",
- ));
- }
- if paths.len() > 1 {
- ui::wprint_msg(&view.bot_win, "Are you sure? (y/N)");
- ncurses::doupdate();
- ch = ncurses::getch();
- } else {
- ch = 'y' as i32;
- }
- if ch == 'y' as i32 {
- Self::remove_files(&paths)?;
- ui::wprint_msg(&view.bot_win, "Deleted files");
- ReloadDirList::reload(context.curr_tab_index, context)?;
- }
+ let paths = curr_tab.curr_list.get_selected_paths();
+ if paths.is_empty() {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ));
+ }
+ if paths.len() > 1 {
+ ui::wprint_msg(&view.bot_win, "Are you sure? (y/N)");
+ } else {
}
+ Self::remove_files(&paths)?;
+ ui::wprint_msg(&view.bot_win, "Deleted files");
+ ReloadDirList::reload(context.curr_tab_index, context)?;
Ok(())
}
}
diff --git a/src/commands/file_operations.rs b/src/commands/file_operations.rs
deleted file mode 100644
index a701473..0000000
--- a/src/commands/file_operations.rs
+++ /dev/null
@@ -1,251 +0,0 @@
-use lazy_static::lazy_static;
-use std::path;
-use std::sync::{atomic, mpsc, Mutex};
-use std::thread;
-use std::time;
-
-use crate::commands::{JoshutoCommand, JoshutoRunnable};
-use crate::context::JoshutoContext;
-use crate::error::JoshutoResult;
-use crate::fs::{fs_extra_extra, JoshutoDirList};
-use crate::window::JoshutoView;
-
-lazy_static! {
- static ref SELECTED_FILES: Mutex<Option<Vec<path::PathBuf>>> = Mutex::new(None);
- static ref FILE_OPERATION: Mutex<FileOp> = Mutex::new(FileOp::Copy);
- static ref TAB_SRC: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
-}
-
-enum FileOp {
- Cut,
- Copy,
-}
-
-struct LocalState;
-
-impl LocalState {
- pub fn set_file_op(operation: FileOp) {
- let mut data = FILE_OPERATION.lock().unwrap();
- *data = operation;
- }
-
- pub fn set_tab_src(tab_index: usize) {
- TAB_SRC.store(tab_index, atomic::Ordering::Release);
- }
-
- pub fn repopulated_selected_files(dirlist: &JoshutoDirList) -> std::io::Result<()> {
- let selected = dirlist.get_selected_paths();
- if selected.is_empty() {
- Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "no files selected",
- ))
- } else {
- let selected_clone: Vec<path::PathBuf> =
- selected.iter().map(|p| (*p).clone()).collect();
- let mut data = SELECTED_FILES.lock().unwrap();
- *data = Some(selected_clone);
- Ok(())
- }
- }
-}
-
-pub struct FileOperationThread<T, Q> {
- pub tab_src: usize,
- pub tab_dest: usize,
- pub handle: thread::JoinHandle<std::io::Result<T>>,
- pub recv: mpsc::Receiver<Q>,
-}
-
-impl<T, Q> FileOperationThread<T, Q> {
- pub fn recv_timeout(
- &self,
- wait_duration: &time::Duration,
- ) -> Result<Q, mpsc::RecvTimeoutError> {
- self.recv.recv_timeout(*wait_duration)
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct CutFiles;
-
-impl CutFiles {
- pub fn new() -> Self {
- CutFiles
- }
- pub const fn command() -> &'static str {
- "cut_files"
- }
-}
-
-impl JoshutoCommand for CutFiles {}
-
-impl std::fmt::Display for CutFiles {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- f.write_str(Self::command())
- }
-}
-
-impl JoshutoRunnable for CutFiles {
- fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
- let curr_tab = context.curr_tab_ref();
- LocalState::repopulated_selected_files(&curr_tab.curr_list)?;
- LocalState::set_file_op(FileOp::Cut);
- LocalState::set_tab_src(context.curr_tab_index);
- Ok(())
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct CopyFiles;
-
-impl CopyFiles {
- pub fn new() -> Self {
- CopyFiles
- }
- pub const fn command() -> &'static str {
- "copy_files"
- }
-}
-
-impl JoshutoCommand for CopyFiles {}
-
-impl std::fmt::Display for CopyFiles {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- f.write_str(Self::command())
- }
-}
-
-impl JoshutoRunnable for CopyFiles {
- fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
- let curr_tab = context.curr_tab_ref();
- LocalState::repopulated_selected_files(&curr_tab.curr_list)?;
- LocalState::set_file_op(FileOp::Copy);
- LocalState::set_tab_src(context.curr_tab_index);
- Ok(())
- }
-}
-
-pub struct PasteFiles {
- options: fs_extra::dir::CopyOptions,
-}
-
-impl JoshutoCommand for PasteFiles {}
-
-impl std::fmt::Display for PasteFiles {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(
- f,
- "{} overwrite={} skip_exist={}",
- Self::command(),
- self.options.overwrite,
- self.options.skip_exist,
- )
- }
-}
-
-impl std::fmt::Debug for PasteFiles {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- f.write_str(Self::command())
- }
-}
-
-impl JoshutoRunnable for PasteFiles {
- fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
- let file_operation = FILE_OPERATION.lock().unwrap();
-
- let thread = match *file_operation {
- FileOp::Copy => self.copy_paste(context),
- FileOp::Cut => self.cut_paste(context),
- }?;
-
- context.threads.push(thread);
- Ok(())
- }
-}
-
-impl PasteFiles {
- pub fn new(options: fs_extra::dir::CopyOptions) -> Self {
- PasteFiles { options }
- }
- pub const fn command() -> &'static str {
- "paste_files"
- }
-
- fn cut_paste(
- &self,
- context: &mut JoshutoContext,
- ) -> std::io::Result<FileOperationThread<u64, fs_extra::TransitProcess>> {
- let paths =
- SELECTED_FILES.lock().unwrap().take().ok_or_else(|| {
- std::io::Error::new(std::io::ErrorKind::Other, "no files selected")
- })?;
- if paths.is_empty() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "no files selected",
- ));
- }
-
- let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst);
- let tab_dest = context.curr_tab_index;
- let destination = context.tabs[tab_dest].curr_path.clone();
-
- let options = self.options.clone();
- let (tx, rx) = mpsc::channel();
-
- let handle = thread::spawn(move || {
- let progress_handle = |process_info: fs_extra::TransitProcess| {
- tx.send(process_info);
- fs_extra::dir::TransitProcessResult::ContinueOrAbort
- };
- fs_extra_extra::fs_cut_with_progress(&paths, &destination, options, progress_handle)
- });
-
- let thread = FileOperationThread {
- tab_src,
- tab_dest,
- handle,
- recv: rx,
- };
- Ok(thread)
- }
-
- fn copy_paste(
- &self,
- context: &mut JoshutoContext,
- ) -> std::io::Result<FileOperationThread<u64, fs_extra::TransitProcess>> {
- let paths =
- SELECTED_FILES.lock().unwrap().take().ok_or_else(|| {
- std::io::Error::new(std::io::ErrorKind::Other, "no files selected")
- })?;
- if paths.is_empty() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "no files selected",
- ));
- }
-
- let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst);
- let tab_dest = context.curr_tab_index;
- let destination = context.tabs[tab_dest].curr_path.clone();
-
- let options = self.options.clone();
- let (tx, rx) = mpsc::channel();
- let handle = thread::spawn(move || {
- let progress_handle = |process_info: fs_extra::TransitProcess| {
- tx.send(process_info);
- fs_extra::dir::TransitProcessResult::ContinueOrAbort
- };
- fs_extra_extra::fs_copy_with_progress(&paths, &destination, options, progress_handle)
- });
-
- let thread = FileOperationThread {
- tab_src,
- tab_dest,
- handle,
- recv: rx,
- };
- Ok(thread)
- }
-}
diff --git a/src/commands/file_ops/copy.rs b/src/commands/file_ops/copy.rs
new file mode 100644
index 0000000..a1a562e
--- /dev/null
+++ b/src/commands/file_ops/copy.rs
@@ -0,0 +1,36 @@
+use crate::commands::{JoshutoCommand, JoshutoRunnable};
+use crate::context::JoshutoContext;
+use crate::error::JoshutoResult;
+use crate::window::JoshutoView;
+
+use super::local_state::{FileOp, LocalState};
+
+#[derive(Clone, Debug)]
+pub struct CopyFiles;
+
+impl CopyFiles {
+ pub fn new() -> Self {
+ CopyFiles
+ }
+ pub const fn command() -> &'static str {
+ "copy_files"
+ }
+}
+
+impl JoshutoCommand for CopyFiles {}
+
+impl std::fmt::Display for CopyFiles {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ f.write_str(Self::command())
+ }
+}
+
+impl JoshutoRunnable for CopyFiles {
+ fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
+ let curr_tab = context.curr_tab_ref();
+ LocalState::repopulated_selected_files(&curr_tab.curr_list)?;
+ LocalState::set_file_op(FileOp::Copy);
+ LocalState::set_tab_src(context.curr_tab_index);
+ Ok(())
+ }
+}
diff --git a/src/commands/file_ops/cut.rs b/src/commands/file_ops/cut.rs
new file mode 100644
index 0000000..f466ca7
--- /dev/null
+++ b/src/commands/file_ops/cut.rs
@@ -0,0 +1,36 @@
+use crate::commands::{JoshutoCommand, JoshutoRunnable};
+use crate::context::JoshutoContext;
+use crate::error::JoshutoResult;
+use crate::window::JoshutoView;
+
+use super::local_state::{FileOp, LocalState};
+
+#[derive(Clone, Debug)]
+pub struct CutFiles;
+
+impl CutFiles {
+ pub fn new() -> Self {
+ CutFiles
+ }
+ pub const fn command() -> &'static str {
+ "cut_files"
+ }
+}
+
+impl JoshutoCommand for CutFiles {}
+
+impl std::fmt::Display for CutFiles {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ f.write_str(Self::command())
+ }
+}
+
+impl JoshutoRunnable for CutFiles {
+ fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
+ let curr_tab = context.curr_tab_ref();
+ LocalState::repopulated_selected_files(&curr_tab.curr_list)?;
+ LocalState::set_file_op(FileOp::Cut);
+ LocalState::set_tab_src(context.curr_tab_index);
+ Ok(())
+ }
+}
diff --git a/src/commands/file_ops/local_state.rs b/src/commands/file_ops/local_state.rs
new file mode 100644
index 0000000..2849893
--- /dev/null
+++ b/src/commands/file_ops/local_state.rs
@@ -0,0 +1,56 @@
+use lazy_static::lazy_static;
+
+use std::path;
+use std::sync::{atomic, Mutex};
+
+use crate::fs::JoshutoDirList;
+
+lazy_static! {
+ static ref SELECTED_FILES: Mutex<Option<Vec<path::PathBuf>>> = Mutex::new(None);
+ static ref FILE_OPERATION: Mutex<FileOp> = Mutex::new(FileOp::Copy);
+ static ref TAB_SRC: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
+}
+
+#[derive(Clone, Debug)]
+pub enum FileOp {
+ Cut,
+ Copy,
+}
+
+pub struct LocalState;
+
+impl LocalState {
+ pub fn set_file_op(operation: FileOp) {
+ let mut data = FILE_OPERATION.lock().unwrap();
+ *data = operation;
+ }
+
+ pub fn set_tab_src(tab_index: usize) {
+ TAB_SRC.store(tab_index, atomic::Ordering::Release);
+ }
+
+ pub fn repopulated_selected_files(dirlist: &JoshutoDirList) -> std::io::Result<()> {
+ let selected = dirlist.get_selected_paths();
+ if selected.is_empty() {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ))
+ } else {
+ let selected_clone: Vec<path::PathBuf> =
+ selected.iter().map(|p| (*p).clone()).collect();
+ let mut data = SELECTED_FILES.lock().unwrap();
+ *data = Some(selected_clone);
+ Ok(())
+ }
+ }
+
+ pub fn take_selected_files() -> Option<Vec<path::PathBuf>> {
+ let paths = SELECTED_FILES.lock().unwrap().take();
+ paths
+ }
+
+ pub fn get_file_operation() -> FileOp {
+ (*FILE_OPERATION.lock().unwrap()).clone()
+ }
+}
diff --git a/src/commands/file_ops/mod.rs b/src/commands/file_ops/mod.rs
new file mode 100644
index 0000000..e4a0da3
--- /dev/null
+++ b/src/commands/file_ops/mod.rs
@@ -0,0 +1,11 @@
+mod copy;
+mod cut;
+mod local_state;
+mod name_resolution;
+mod paste;
+mod paste_copy;
+mod paste_cut;
+
+pub use self::copy::CopyFiles;
+pub use self::cut::CutFiles;
+pub use self::paste::PasteFiles;
diff --git a/src/commands/file_ops/name_resolution.rs b/src/commands/file_ops/name_resolution.rs
new file mode 100644
index 0000000..a8e520b
--- /dev/null
+++ b/src/commands/file_ops/name_resolution.rs
@@ -0,0 +1,15 @@
+use std::path;
+
+pub fn rename_filename_conflict(path: &mut path::PathBuf) {
+ let file_name = path.file_name().unwrap().to_os_string();
+ for i in 0.. {
+ if !path.exists() {
+ break;
+ }
+ path.pop();
+
+ let mut file_name = file_name.clone();
+ file_name.push(&format!("_{}", i));
+ path.push(file_name);
+ }
+}
diff --git a/src/commands/file_ops/paste.rs b/src/commands/file_ops/paste.rs
new file mode 100644
index 0000000..ba5dc43
--- /dev/null
+++ b/src/commands/file_ops/paste.rs
@@ -0,0 +1,55 @@
+use crate::commands::{JoshutoCommand, JoshutoRunnable};
+use crate::context::JoshutoContext;
+use crate::error::JoshutoResult;
+use crate::io::Options;
+use crate::window::JoshutoView;
+
+use super::local_state::{FileOp, LocalState};
+use super::paste_copy::paste_copy;
+use super::paste_cut::paste_cut;
+
+pub struct PasteFiles {
+ options: Options,
+}
+
+impl JoshutoCommand for PasteFiles {}
+
+impl std::fmt::Display for PasteFiles {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(
+ f,
+ "{} overwrite={} skip_exist={}",
+ Self::command(),
+ self.options.overwrite,
+ self.options.skip_exist,
+ )
+ }
+}
+
+impl std::fmt::Debug for PasteFiles {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ f.write_str(Self::command())
+ }
+}
+
+impl JoshutoRunnable for PasteFiles {
+ fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> {
+ let file_operation = LocalState::get_file_operation();
+ let thread = match file_operation {
+ FileOp::Copy => paste_copy(context, self.options.clone()),
+ FileOp::Cut => paste_cut(context, self.options.clone()),
+ };
+ let thread = thread?;
+ context.add_new_worker(thread);
+ Ok(())
+ }
+}
+
+impl PasteFiles {
+ pub fn new(options: Options) -> Self {
+ PasteFiles { options }
+ }
+ pub const fn command() -> &'static str {
+ "paste_files"
+ }
+}
diff --git a/src/commands/file_ops/paste_copy.rs b/src/commands/file_ops/paste_copy.rs
new file mode 100644
index 0000000..26f0e5c
--- /dev/null
+++ b/src/commands/file_ops/paste_copy.rs
@@ -0,0 +1,68 @@
+use std::fs;
+use std::path::Path;
+use std::sync::mpsc;
+use std::thread;
+
+use crate::context::JoshutoContext;
+use crate::io::{IOWorkerThread, Options};
+use crate::util::event::Event;
+
+use super::local_state::LocalState;
+use super::name_resolution::rename_filename_conflict;
+
+pub fn recursive_copy(dest: &Path, src: &Path, options: &Options) -> std::io::Result<u64> {
+ let mut dest_buf = dest.to_path_buf();
+ if let Some(s) = src.file_name() {
+ dest_buf.push(s);
+ }
+ rename_filename_conflict(&mut dest_buf);
+ if !src.is_dir() {
+ std::fs::copy(src, dest_buf)
+ } else {
+ fs::create_dir(dest_buf.as_path())?;
+ let mut total = 0;
+ for entry in fs::read_dir(src)? {
+ let entry = entry?;
+ let entry_path = entry.path();
+ total += recursive_copy(dest_buf.as_path(), entry_path.as_path(), options)?;
+ }
+ Ok(total)
+ }
+}
+
+pub fn paste_copy(
+ context: &mut JoshutoContext,
+ options: Options,
+) -> std::io::Result<IOWorkerThread> {
+ let paths = LocalState::take_selected_files()
+ .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "no files selected"))?;
+ if paths.is_empty() {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ));
+ }
+
+ let tab_dest = context.curr_tab_index;
+ let dest = context.tabs[tab_dest].curr_path.clone();
+
+ let (tx_start, rx_start) = mpsc::channel();
+ let (tx, rx) = mpsc::channel();
+ let handle: thread::JoinHandle<std::io::Result<u64>> = thread::spawn(move || {
+ let mut total = 0;
+ rx_start.recv();
+ for path in paths {
+ total += recursive_copy(dest.as_path(), path.as_path(), &options)?;
+ tx.send(Event::IOWorkerProgress(total));
+ }
+ Ok(total)
+ });
+
+ let thread = IOWorkerThread {
+ handle,
+ tx_start,
+ rx,
+ };
+
+ Ok(thread)
+}
diff --git a/src/commands/file_ops/paste_cut.rs b/src/commands/file_ops/paste_cut.rs
new file mode 100644
index 0000000..1177f12
--- /dev/null
+++ b/src/commands/file_ops/paste_cut.rs
@@ -0,0 +1,76 @@
+use std::fs;
+use std::path::Path;
+use std::sync::mpsc;
+use std::thread;
+
+use crate::context::JoshutoContext;
+use crate::io::{IOWorkerThread, Options};
+use crate::util::event::Event;
+
+use super::local_state::LocalState;
+use super::name_resolution::rename_filename_conflict;
+
+pub fn recursive_cut(dest: &Path, src: &Path, options: &Options) -> std::io::Result<u64> {
+ let mut dest_buf = dest.to_path_buf();
+ if let Some(s) = src.file_name() {
+ dest_buf.push(s);
+ }
+ rename_filename_conflict(&mut dest_buf);
+ if !src.is_dir() {
+ let metadata = src.metadata()?;
+ match std::fs::rename(src, dest_buf.as_path()) {
+ Err(_) => {
+ std::fs::copy(src, dest_buf.as_path())?;
+ std::fs::remove_file(src)?;
+ }
+ _ => {}
+ }
+ Ok(metadata.len())
+ } else {
+ fs::create_dir(dest_buf.as_path())?;
+ let mut total = 0;
+ for entry in fs::read_dir(src)? {
+ let entry = entry?;
+ let entry_path = entry.path();
+ total += recursive_cut(dest_buf.as_path(), entry_path.as_path(), options)?;
+ }
+ Ok(total)
+ }
+}
+
+pub fn paste_cut(
+ context: &mut JoshutoContext,
+ options: Options,
+) -> std::io::Result<IOWorkerThread> {
+ let paths = LocalState::take_selected_files()
+ .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "no files selected"))?;
+ if paths.is_empty() {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ));
+ }
+
+ let tab_dest = context.curr_tab_index;
+ let dest = context.tabs[tab_dest].curr_path.clone();
+
+ let (tx_start, rx_start) = mpsc::channel();
+ let (tx, rx) = mpsc::channel();
+ let handle: thread::JoinHandle<std::io::Result<u64>> = thread::spawn(move || {
+ let mut total = 0;
+ rx_start.recv();
+ for path in paths {
+ total += recursive_cut(dest.as_path(), path.as_path(), &options)?;
+ tx.send(Event::IOWorkerProgress(total));
+ }
+ Ok(total)
+ });
+
+ let thread = IOWorkerThread {
+ handle,
+ tx_start,
+ rx,
+ };
+
+ Ok(thread)
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index ad60802..eecf257 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -3,7 +3,7 @@ mod change_directory;
mod command_line;
mod cursor_move;
mod delete_files;
-mod file_operations;
+mod file_ops;
mod new_directory;
mod open_file;
mod parent_directory;
@@ -25,7 +25,7 @@ pub use self::cursor_move::{
CursorMoveUp,
};
pub use self::delete_files::DeleteFiles;
-pub use self::file_operations::{CopyFiles, CutFiles, FileOperationThread, PasteFiles};
+pub use self::file_ops::{CopyFiles, CutFiles, PasteFiles};
pub use self::new_directory::NewDirectory;
pub use self::open_file::{OpenFile, OpenFileWith};
pub use self::parent_directory::ParentDirectory;
@@ -45,13 +45,14 @@ use std::path::PathBuf;
use crate::config::JoshutoCommandMapping;
use crate::context::JoshutoContext;
use crate::error::{JoshutoError, JoshutoErrorKind, JoshutoResult};
+use crate::io::Options;
use crate::window::JoshutoView;
use crate::HOME_DIR;
#[derive(Debug)]
pub enum CommandKeybind {
- SimpleKeybind(Box<JoshutoCommand>),
+ SimpleKeybind(Box<dyn JoshutoCommand>),
CompositeKeybind(JoshutoCommandMapping),
}
@@ -70,7 +71,7 @@ pub trait JoshutoRunnable {
pub trait JoshutoCommand: JoshutoRunnable + std::fmt::Display + std::fmt::Debug {}
-pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<JoshutoCommand>> {
+pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn JoshutoCommand>> {
match command.as_str() {
"bulk_rename" => Ok(Box::new(self::BulkRename::new())),
"cd" => match args.len() {
@@ -156,8 +157,7 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<Joshut
"open_file" => Ok(Box::new(self::OpenFile::new())),
"open_file_with" => Ok(Box::new(self::OpenFileWith::new())),
"paste_files" => {
- let mut options = fs_extra::dir::CopyOptions::new();
- options.buffer_size = 1024 * 1024 * 4;
+ let mut options = Options::default();
for arg in args {
match arg.as_str() {
"--overwrite" => options.overwrite = true,
diff --git a/src/commands/open_file.rs b/src/commands/open_file.rs
index 6651e3c..60dafe5 100644
--- a/src/commands/open_file.rs
+++ b/src/commands/open_file.rs
@@ -71,13 +71,15 @@ impl OpenFile {
/* try executing with user defined entries */
if !mimetype_options.is_empty() {
mimetype_options[0].execute_with(&paths)?;
- } else if context.config_t.xdg_open { // try system defined entries
+ } else if context.config_t.xdg_open {
+ // try system defined entries
ncurses::savetty();
ncurses::endwin();
open::that(paths[0]).unwrap();
ncurses::resetty();
ncurses::refresh();
- } else { // ask user for command
+ } else {
+ // ask user for command
OpenFileWith::open_with(&paths)?;
}
let curr_tab = &mut context.tabs[context.curr_tab_index];
diff --git a/src/commands/quit.rs b/src/commands/quit.rs
index cdeca68..77732d4 100644
--- a/src/commands/quit.rs
+++ b/src/commands/quit.rs
@@ -15,7 +15,7 @@ impl Quit {
}
pub fn quit(context: &mut JoshutoContext) -> JoshutoResult<()> {
- if !context.threads.is_empty() {
+ if !context.worker_queue.is_empty() {
Err(JoshutoError::new(
JoshutoErrorKind::IOOther,
String::from("operations running in background, use force_quit to quit"),
diff --git a/src/config/keymap.rs b/src/config/keymap.rs
index e8b4120..7d3713d 100644
--- a/src/config/keymap.rs
+++ b/src/config/keymap.rs
@@ -1,5 +1,4 @@
use std::collections::{hash_map::Entry, HashMap};
-use std::process::exit;
use serde_derive::Deserialize;
@@ -155,7 +154,7 @@ impl Flattenable<JoshutoCommandMapping> for JoshutoRawCommandMapping {
let result = insert_keycommand(&mut keymaps, command, &keycodes);
match result {
- Ok(_) => {},
+ Ok(_) => {}
Err(e) => eprintln!("{}", e),
}
}
@@ -175,7 +174,7 @@ impl ConfigStructure for JoshutoCommandMapping {
fn insert_keycommand(
keymap: &mut JoshutoCommandMapping,
- keycommand: Box<JoshutoCommand>,
+ keycommand: Box<dyn JoshutoCommand>,
keycodes: &[&str],
) -> Result<(), String> {
let keycode_len = keycodes.len();
@@ -186,12 +185,14 @@ fn insert_keycommand(
let key = match str_to_key(keycodes[0]) {
Some(k) => k,
- None => return Err(format!("Unknown keycode: {}", keycodes[0]))
+ None => return Err(format!("Unknown keycode: {}", keycodes[0])),
};
if keycode_len == 1 {
match keymap.entry(key) {
- Entry::Occupied(_) => return Err(format!("Error: Keybindings ambiguous for {}", keycommand)),
+ Entry::Occupied(_) => {
+ return Err(format!("Error: Keybindings ambiguous for {}", keycommand))
+ }
Entry::Vacant(entry) => entry.insert(CommandKeybind::SimpleKeybind(keycommand)),
};
return Ok(());
diff --git a/src/context.rs b/src/context.rs
index 35b9da0..8f2ce4a 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -1,12 +1,19 @@
-use crate::commands::FileOperationThread;
+use std::collections::VecDeque;
+use std::sync::mpsc;
+
use crate::config;
+use crate::io::IOWorkerThread;
use crate::tab