summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2019-04-19 13:37:35 -0400
committerJiayi Zhao <jeff.no.zhao@gmail.com>2019-04-19 13:37:35 -0400
commit37f639a7b6a0f3a70d82bc7b3a09b1893bc4afcf (patch)
tree4722e1bf26718903fca0560246274de0ba601777
parent44252aa792dfae24a63cd727c1ff0bf032d8cf9f (diff)
reimplement cut/copy/paste functionality
- now renames automatically if destionation exists
-rw-r--r--src/commands/delete_files.rs4
-rw-r--r--src/commands/file_operations.rs318
-rw-r--r--src/commands/open_file.rs6
3 files changed, 216 insertions, 112 deletions
diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs
index 8f6cacb..071f629 100644
--- a/src/commands/delete_files.rs
+++ b/src/commands/delete_files.rs
@@ -1,7 +1,7 @@
use std::fs;
use std::path;
-use crate::commands::{self, JoshutoCommand, JoshutoRunnable};
+use crate::commands::{JoshutoCommand, JoshutoRunnable};
use crate::config::keymap;
use crate::context::JoshutoContext;
use crate::error::JoshutoError;
@@ -55,7 +55,7 @@ impl JoshutoRunnable for DeleteFiles {
let ch: i32 = ncurses::getch();
if ch == 'y' as i32 || ch == keymap::ENTER as i32 {
if let Some(s) = context.tabs[context.curr_tab_index].curr_list.as_ref() {
- if let Some(paths) = commands::collect_selected_paths(s) {
+ if let Some(paths) = s.get_selected_paths() {
match Self::remove_files(paths) {
Ok(_) => ui::wprint_msg(&view.bot_win, "Deleted files"),
Err(e) => {
diff --git a/src/commands/file_operations.rs b/src/commands/file_operations.rs
index 1e986e0..7659fb6 100644
--- a/src/commands/file_operations.rs
+++ b/src/commands/file_operations.rs
@@ -4,7 +4,7 @@ use std::sync::{atomic, mpsc, Mutex};
use std::thread;
use std::time;
-use crate::commands::{self, JoshutoCommand, JoshutoRunnable, ProgressInfo};
+use crate::commands::{JoshutoCommand, JoshutoRunnable, ProgressInfo};
use crate::context::JoshutoContext;
use crate::error::JoshutoError;
use crate::structs::JoshutoDirList;
@@ -21,6 +21,30 @@ enum FileOp {
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) -> bool {
+ let mut data = SELECTED_FILES.lock().unwrap();
+ match dirlist.get_selected_paths() {
+ Some(s) => {
+ *data = Some(s);
+ true
+ }
+ None => false,
+ }
+ }
+}
+
#[derive(Clone, Debug)]
pub struct CopyOptions {
pub overwrite: bool,
@@ -43,26 +67,6 @@ impl FileOperationThread {
}
}
-fn set_file_op(operation: FileOp) {
- let mut data = FILE_OPERATION.lock().unwrap();
- *data = operation;
-}
-
-fn set_tab_src(tab_index: usize) {
- TAB_SRC.store(tab_index, atomic::Ordering::Release);
-}
-
-fn repopulated_selected_files(dirlist: &JoshutoDirList) -> bool {
- match commands::collect_selected_paths(dirlist) {
- Some(s) => {
- let mut data = SELECTED_FILES.lock().unwrap();
- *data = Some(s);
- true
- }
- None => false,
- }
-}
-
#[derive(Clone, Debug)]
pub struct CutFiles;
@@ -87,9 +91,9 @@ impl JoshutoRunnable for CutFiles {
fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> {
let curr_tab = context.curr_tab_ref();
if let Some(s) = curr_tab.curr_list.as_ref() {
- if repopulated_selected_files(s) {
- set_file_op(FileOp::Cut);
- set_tab_src(context.curr_tab_index);
+ if LocalState::repopulated_selected_files(s) {
+ LocalState::set_file_op(FileOp::Cut);
+ LocalState::set_tab_src(context.curr_tab_index);
}
}
Ok(())
@@ -120,9 +124,9 @@ impl JoshutoRunnable for CopyFiles {
fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> {
let curr_tab = context.curr_tab_ref();
if let Some(s) = curr_tab.curr_list.as_ref() {
- if repopulated_selected_files(s) {
- set_file_op(FileOp::Copy);
- set_tab_src(context.curr_tab_index);
+ if LocalState::repopulated_selected_files(s) {
+ LocalState::set_file_op(FileOp::Copy);
+ LocalState::set_tab_src(context.curr_tab_index);
}
}
Ok(())
@@ -178,9 +182,121 @@ impl PasteFiles {
"paste_files"
}
+ fn fs_rename_thread(
+ options: fs_extra::dir::CopyOptions,
+ tx: mpsc::Sender<ProgressInfo>,
+ dest: path::PathBuf,
+ paths: Vec<path::PathBuf>,
+ ) -> std::result::Result<(), std::io::Error> {
+ let mut progress_info = ProgressInfo {
+ bytes_finished: 1,
+ total_bytes: paths.len() as u64 + 1,
+ };
+
+ let mut destination = dest;
+
+ for path in paths {
+ let file_name = path.file_name().unwrap().to_os_string();
+
+ destination.push(file_name.clone());
+ if !options.skip_exist {
+ let mut i = 0;
+ while destination.exists() {
+ destination.pop();
+
+ let mut file_name = file_name.clone();
+ file_name.push(&format!("_{}", i));
+
+ destination.push(file_name);
+ i += 1;
+ }
+ }
+ std::fs::rename(&path, &destination)?;
+ destination.pop();
+
+ progress_info.bytes_finished += 1;
+ match tx.send(progress_info.clone()) {
+ _ => {}
+ }
+ }
+ Ok(())
+ }
+
+ fn fs_copy_thread(
+ options: fs_extra::dir::CopyOptions,
+ tx: mpsc::Sender<ProgressInfo>,
+ dest: path::PathBuf,
+ paths: Vec<path::PathBuf>,
+ ) -> std::result::Result<(), std::io::Error> {
+ let mut progress_info = ProgressInfo {
+ bytes_finished: 1,
+ total_bytes: paths.len() as u64 + 1,
+ };
+
+ let mut destination = dest;
+
+ for path in paths {
+ let file_name = path.file_name().unwrap().to_os_string();
+
+ if path.symlink_metadata()?.is_dir() {
+ destination.push(file_name.clone());
+ if !options.skip_exist {
+ let mut i = 0;
+ while destination.exists() {
+ destination.pop();
+
+ let mut file_name = file_name.clone();
+ file_name.push(&format!("_{}", i));
+
+ destination.push(file_name);
+ i += 1;
+ }
+ }
+ std::fs::create_dir(&destination)?;
+ let path: Vec<path::PathBuf> = std::fs::read_dir(path)?
+ .filter_map(|s| {
+ match s {
+ Ok(s) => Some(s.path()),
+ _ => None
+ }
+ })
+ .collect();
+
+ match fs_extra::copy_items(&path, &destination, &options) {
+ Err(e) => {
+ let err = std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e));
+ return Err(err);
+ }
+ _ => {}
+ }
+ } else {
+ destination.push(file_name.clone());
+ if !options.skip_exist {
+ let mut i = 0;
+ while destination.exists() {
+ destination.pop();
+
+ let mut file_name = file_name.clone();
+ file_name.push(&format!("_{}", i));
+
+ destination.push(file_name);
+ i += 1;
+ }
+ }
+ std::fs::copy(&path, &destination)?;
+ }
+ progress_info.bytes_finished += 1;
+ match tx.send(progress_info.clone()) {
+ _ => {}
+ }
+ }
+ return Ok(());
+ }
+
fn same_fs_cut(
&self,
context: &mut JoshutoContext,
+ paths: Vec<path::PathBuf>,
) -> Result<FileOperationThread, std::io::Error> {
let options = self.options.clone();
@@ -188,45 +304,33 @@ impl PasteFiles {
let tab_dest = context.curr_tab_index;
let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst);
- let mut destination = context.tabs[tab_dest].curr_path.clone();
-
- let handle = thread::spawn(move || {
- let paths: Option<Vec<path::PathBuf>> = SELECTED_FILES.lock().unwrap().take();
- match paths {
- None => {}
- Some(s) => {
- let mut progress_info = ProgressInfo {
- bytes_finished: 1,
- total_bytes: s.len() as u64 + 1,
- };
+ let destination = context.tabs[tab_dest].curr_path.clone();
- for path in &s {
- let file_name = path.file_name().unwrap().to_os_string();
-
- destination.push(file_name.clone());
- if destination.exists() {
- if !options.skip_exist {
- for i in 0.. {
- if !destination.exists() {
- break;
- }
- destination.pop();
- let mut file_name = file_name.clone();
- file_name.push(&format!("_{}", i));
- destination.push(file_name);
- }
- }
- }
- std::fs::rename(&path, &destination)?;
- destination.pop();
+ let handle = thread::spawn(move || Self::fs_rename_thread(options, tx, destination, paths));
- progress_info.bytes_finished += 1;
- tx.send(progress_info.clone()).unwrap();
- }
- }
- }
- return Ok(());
- });
+ let thread = FileOperationThread {
+ tab_src,
+ tab_dest,
+ handle,
+ recv: rx,
+ };
+ Ok(thread)
+ }
+
+ fn diff_fs_cut(
+ &self,
+ context: &mut JoshutoContext,
+ paths: Vec<path::PathBuf>,
+ ) -> Result<FileOperationThread, std::io::Error> {
+ let options = self.options.clone();
+
+ let (tx, rx) = mpsc::channel();
+
+ let tab_dest = context.curr_tab_index;
+ let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst);
+ let destination = context.tabs[tab_dest].curr_path.clone();
+
+ let handle = thread::spawn(move || Self::fs_rename_thread(options, tx, destination, paths));
let thread = FileOperationThread {
tab_src,
@@ -245,35 +349,33 @@ impl PasteFiles {
use std::os::linux::fs::MetadataExt;
let src_ino;
- {
- let paths = SELECTED_FILES.lock().unwrap();
- match *paths {
- Some(ref s) => {
- if s.len() == 0 {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "no files selected",
- ));
- }
- src_ino = s[0].metadata()?.st_dev();
- }
- None => {
+ let paths = SELECTED_FILES.lock().unwrap().take();
+ match paths {
+ Some(s) => {
+ if s.len() == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"no files selected",
));
}
- }
- }
+ src_ino = s[0].metadata()?.st_dev();
- let tab_dest = context.curr_tab_index;
- let destination = &context.tabs[tab_dest].curr_path;
+ let tab_dest = context.curr_tab_index;
+ let destination = &context.tabs[tab_dest].curr_path;
- let dest_ino = destination.metadata()?.st_dev();
- if dest_ino == src_ino {
- self.same_fs_cut(context)
- } else {
- self.same_fs_cut(context)
+ let dest_ino = destination.metadata()?.st_dev();
+ if dest_ino == src_ino {
+ self.same_fs_cut(context, s)
+ } else {
+ self.diff_fs_cut(context, s)
+ }
+ }
+ None => {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ));
+ }
}
}
@@ -289,29 +391,31 @@ impl PasteFiles {
let (tx, rx) = mpsc::channel();
- let handle = thread::spawn(move || {
- let paths = SELECTED_FILES.lock().unwrap();
- match *paths {
- Some(ref s) => {
- let progress_info = ProgressInfo {
- bytes_finished: 0,
- total_bytes: 0,
+ let paths = SELECTED_FILES.lock().unwrap().take();
+ match paths {
+ Some(s) => {
+ if s.len() == 0 {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ ))
+ } else {
+ let handle =
+ thread::spawn(move || Self::fs_copy_thread(options, tx, destination, s));
+
+ let thread = FileOperationThread {
+ tab_src,
+ tab_dest,
+ handle,
+ recv: rx,
};
- match tx.send(progress_info) {
- Ok(_) => {}
- Err(e) => {}
- }
- return Ok(());
+ Ok(thread)
}
- None => return Ok(()),
}
- });
- let thread = FileOperationThread {
- tab_src,
- tab_dest,
- handle,
- recv: rx,
- };
- Ok(thread)
+ None => Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "no files selected",
+ )),
+ }
}
}
diff --git a/src/commands/open_file.rs b/src/commands/open_file.rs
index 1d8801e..a446a14 100644
--- a/src/commands/open_file.rs
+++ b/src/commands/open_file.rs
@@ -1,7 +1,7 @@
use std::env;
use std::path::{Path, PathBuf};
-use crate::commands::{self, JoshutoCommand, JoshutoRunnable};
+use crate::commands::{JoshutoCommand, JoshutoRunnable};
use crate::config::mimetype;
use crate::context::JoshutoContext;
use crate::error::JoshutoError;
@@ -138,7 +138,7 @@ impl JoshutoRunnable for OpenFile {
} else {
let paths: Option<Vec<PathBuf>> =
match context.tabs[context.curr_tab_index].curr_list.as_ref() {
- Some(s) => commands::collect_selected_paths(s),
+ Some(s) => s.get_selected_paths(),
None => None,
};
if let Some(paths) = paths {
@@ -239,7 +239,7 @@ impl std::fmt::Display for OpenFileWith {
impl JoshutoRunnable for OpenFileWith {
fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> {
if let Some(s) = context.tabs[context.curr_tab_index].curr_list.as_ref() {
- if let Some(paths) = commands::collect_selected_paths(s) {
+ if let Some(paths) = s.get_selected_paths() {
Self::open_with(&paths);
}
}