summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml10
-rw-r--r--config/keymap.toml239
-rw-r--r--src/commands/bulk_rename.rs4
-rw-r--r--src/commands/delete_files.rs61
-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.rs24
-rw-r--r--src/commands/open_file.rs53
-rw-r--r--src/commands/quit.rs2
-rw-r--r--src/config/config.rs2
-rw-r--r--src/config/keymap.rs91
-rw-r--r--src/config/mimetype.rs85
-rw-r--r--src/context.rs18
-rw-r--r--src/error.rs1
-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.rs3
-rw-r--r--src/preview.rs2
-rw-r--r--src/run.rs195
-rw-r--r--src/textfield.rs2
-rw-r--r--src/ui.rs50
-rw-r--r--src/unix.rs3
-rw-r--r--src/util/event.rs117
-rw-r--r--src/util/key_mapping.rs64
-rw-r--r--src/util/mod.rs2
-rw-r--r--src/window/panel.rs1
35 files changed, 1013 insertions, 981 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 438dd17..75f5304 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,17 +11,17 @@ edition = "2018"
alphanumeric-sort = "^1"
chrono = "^0"
dirs = "^1"
-fs_extra = "^1"
lazy_static = "^1"
-libc = "^0.2"
+libc = "^0"
notify = "^4"
-open = "1.2.2"
+open = "^1"
rand = "^0"
-rustyline = "^4.1"
+rustyline = "^4"
serde = "^1"
serde_derive = "^1"
-structopt = "0.2.14"
+structopt = "^0"
text_io = "^0"
+termion = "*"
toml = "^0"
unicode-width = "^0"
users = "^0"
diff --git a/config/keymap.toml b/config/keymap.toml
index a7d21d6..d2eee9c 100644
--- a/config/keymap.toml
+++ b/config/keymap.toml
@@ -2,210 +2,185 @@
# backspace = 263
[[mapcommand]]
-keys = [ 81 ]
-command = "force_quit"
+command = "new_tab"
+keys = [ "T" ]
[[mapcommand]]
-keys = [ 87 ]
+command = "new_tab"
+keys = [ "ctrl+t" ]
+[[mapcommand]]
command = "close_tab"
+keys = [ "W" ]
[[mapcommand]]
-keys = [ 113 ]
command = "close_tab"
-
+keys = [ "ctrl+w" ]
+[[mapcommand]]
+command = "close_tab"
+keys = [ "q" ]
+[[mapcommand]]
+command = "force_quit"
+keys = [ "Q" ]
[[mapcommand]]
-keys = [ 82 ]
command = "reload_dir_list"
+keys = [ "R" ]
+[[mapcommand]]
+command = "toggle_hidden"
+keys = [ "z", "h" ]
+[[mapcommand]]
+command = "tab_switch"
+args = [ "1" ]
+keys = [ "\t" ]
+[[mapcommand]]
+command = "tab_switch"
+args = [ "-1" ]
+keys = [ "shift+\t" ]
[[mapcommand]]
-keys = [ 259 ]
command = "cursor_move_up"
-
+keys = [ "up" ]
[[mapcommand]]
-keys = [ 107 ]
command = "cursor_move_up"
-
+keys = [ "k" ]
[[mapcommand]]
-keys = [ 258 ]
command = "cursor_move_down"
-
+keys = [ "down" ]
[[mapcommand]]
-keys = [ 106 ]
command = "cursor_move_down"
-
+keys = [ "j" ]
[[mapcommand]]
-keys = [ 260 ]
-command = "cd"
-args = [ ".." ]
-
-[[mapcommand]]
-keys = [ 104 ]
-command = "cd"
-args = [ ".." ]
-
-[[mapcommand]]
-keys = [ 261 ]
-command = "open_file"
-
-[[mapcommand]]
-keys = [ 108 ]
-command = "open_file"
-
+command = "cursor_move_end"
+keys = [ "end" ]
[[mapcommand]]
-keys = [ 10 ]
-command = "open_file"
-
-
+command = "cursor_move_end"
+keys = [ "G" ]
[[mapcommand]]
-keys = [ 262 ]
command = "cursor_move_home"
-
+keys = [ "home" ]
[[mapcommand]]
-keys = [ 103, 103 ]
command = "cursor_move_home"
-
+keys = [ "g", "g" ]
[[mapcommand]]
-keys = [ 360 ]
-command = "cursor_move_end"
-
-[[mapcommand]]
-keys = [ 71 ]
-command = "cursor_move_end"
-
-
+command = "cursor_move_page_up"
+keys = [ "page_up" ]
[[mapcommand]]
-keys = [ 338 ]
command = "cursor_move_page_down"
+keys = [ "page_down" ]
[[mapcommand]]
-keys = [ 339 ]
-command = "cursor_move_page_up"
-
-
+command = "open_file"
+keys = [ "right" ]
+[[mapcommand]]
+command = "open_file"
+keys = [ "l" ]
+[[mapcommand]]
+command = "open_file"
+keys = [ "\n" ]
[[mapcommand]]
-keys = [ 114 ]
command = "open_file_with"
-
+keys = [ "r" ]
[[mapcommand]]
-keys = [ 122, 104 ]
-command = "toggle_hidden"
-
+command = "cd"
+keys = [ "c", "d" ]
+[[mapcommand]]
+command = "cd"
+args = [ ".." ]
+keys = [ "left" ]
+[[mapcommand]]
+command = "cd"
+args = [ ".." ]
+keys = [ "h" ]
[[mapcommand]]
-keys = [ 100, 100 ]
command = "cut_files"
-
+keys = [ "d", "d" ]
[[mapcommand]]
-keys = [ 121, 121 ]
command = "copy_files"
-
+keys = [ "y", "y" ]
[[mapcommand]]
-keys = [ 112, 112 ]
command = "paste_files"
-
+keys = [ "p", "p" ]
[[mapcommand]]
-keys = [ 112, 111 ]
command = "paste_files"
args = [ "--overwrite" ]
-
-[[mapcommand]]
-keys = [ 97 ]
-command = "rename_append"
-
-[[mapcommand]]
-keys = [ 65 ]
-command = "rename_prepend"
-
+keys = [ "p", "o" ]
[[mapcommand]]
-keys = [ 99, 119 ]
-command = "console"
-args = [ "rename " ]
-
-[[mapcommand]]
-keys = [ 100, 68 ]
command = "delete_files"
-
+keys = [ "d", "D" ]
[[mapcommand]]
-keys = [ 330 ]
command = "delete_files"
+keys = [ "delete" ]
+[[mapcommand]]
+command = "rename_append"
+keys = [ "a" ]
+[[mapcommand]]
+command = "rename_prepend"
+keys = [ "A" ]
[[mapcommand]]
-keys = [ 32 ]
command = "select_files"
args = [ "--toggle" ]
-
+keys = [ " " ]
[[mapcommand]]
-keys = [ 116 ]
command = "select_files"
args = [ "--toggle", "--all" ]
+keys = [ "t" ]
[[mapcommand]]
-keys = [ 109, 107 ]
-command = "console"
-args = [ "mkdir " ]
-
+command = "search_next"
+keys = [ "n", "p" ]
[[mapcommand]]
-keys = [ 59 ]
-command = "console"
+command = "search_prev"
+keys = [ "n", "n" ]
[[mapcommand]]
-keys = [ 103, 104 ]
-command = "cd"
-
+command = "bulk_rename"
+keys = [ "b", "b" ]
[[mapcommand]]
-keys = [ 103, 114 ]
-command = "cd"
-args = [ "/" ]
+command = "set_mode"
+keys = [ "=" ]
[[mapcommand]]
-keys = [ 103, 101 ]
-command = "cd"
-args = [ "/etc" ]
-
+command = "console"
+keys = [ ";" ]
[[mapcommand]]
-keys = [ 47 ]
command = "console"
-args = [ "search " ]
-
+args = [ "mkdir " ]
+keys = [ "m", "k" ]
[[mapcommand]]
-keys = [ 78 ]
-command = "search_prev"
-
+command = "console"
+args = [ "rename " ]
+keys = [ "c", "w" ]
[[mapcommand]]
-keys = [ 110 ]
-command = "search_next"
-
+command = "console"
+args = [ "search " ]
+keys = [ "/" ]
[[mapcommand]]
-keys = [ 20 ]
-command = "new_tab"
-[[mapcommand]]
-keys = [ 84 ]
-command = "new_tab"
+command = "console"
+args = [ "search " ]
+keys = [ "/" ]
[[mapcommand]]
-keys = [ 23 ]
-command = "close_tab"
-
+command = "sort"
+args = [ "lexical" ]
+keys = [ "s", "l" ]
[[mapcommand]]
-keys = [ 9 ]
-command = "tab_switch"
-args = [ "1" ]
-
+command = "sort"
+args = [ "mtime" ]
+keys = [ "s", "m" ]
[[mapcommand]]
-keys = [ 353 ]
-command = "tab_switch"
-args = [ "-1" ]
+command = "sort"
+args = [ "natural" ]
+keys = [ "s", "n" ]
[[mapcommand]]
-keys = [ 45 ]
-command = "set_mode"
-
-## Features not yet implemented
-
+command = "cd"
+args = [ "/" ]
+keys = [ "g", "/" ]
[[mapcommand]]
-keys = [ 98, 98 ]
-command = "bulk_rename"
-
+command = "cd"
+args = [ "/etc" ]
+keys = [ "g", "e" ]
diff --git a/src/commands/bulk_rename.rs b/src/commands/bulk_rename.rs
index dab20c7..cd2dcf8 100644
--- a/src/commands/bulk_rename.rs
+++ b/src/commands/bulk_rename.rs
@@ -97,7 +97,7 @@ impl BulkRename {
for (p, q) in paths.iter().zip(paths_renamed.iter()) {
println!("{:?} -> {:?}", p, q);
}
- print!("Continue with rename? (y/N): ");
+ print!("Continue with rename? (Y/n): ");
std::io::stdout().flush()?;
let mut user_input = String::with_capacity(4);
@@ -105,7 +105,7 @@ impl BulkRename {
user_input = user_input.to_lowercase();
let user_input_trimmed = user_input.trim();
- if user_input_trimmed == "y" || user_input_trimmed == "yes" {
+ if user_input_trimmed != "n" || user_input_trimmed != "no" {
for (p, q) in paths.iter().zip(paths_renamed.iter()) {
let mut command = process::Command::new("mv");
command.arg("-iv");
diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs
index adaed07..8233ce2 100644
--- a/src/commands/delete_files.rs
+++ b/src/commands/delete_files.rs
@@ -5,6 +5,7 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable, ReloadDirList};
use crate::context::JoshutoContext;
use crate::error::JoshutoResult;
use crate::ui;
+use crate::util::event::Event;
use crate::window::JoshutoView;
use crate::KEYMAP_T;
@@ -35,32 +36,50 @@ 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",
+ ));
+ }
+
+ let mut ch = termion::event::Key::Char('n');
+ while let Ok(evt) = context.events.next() {
+ match evt {
+ Event::Input(key) => {
+ if key == termion::event::Key::Char('y') ||
+ key == termion::event::Key::Char('\n') {
+ if paths.len() > 1 {
+ ui::wprint_msg(&view.bot_win, "Are you sure? (y/N)");
+ ncurses::doupdate();
+ while let Ok(evt) = context.events.next() {
+ match evt {
+ Event::Input(key) => {
+ ch = key;
+ break;
+ }
+ _ => {}
+ }
+ }
+ } else {
+ ch = termion::event::Key::Char('y');
+ }
+ }
+ break;
+ }
+ _ => {}
}
}
+
+ if ch == termion::event::Key::Char('y') {
+ 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