mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Copy feature in ui; new keybinding <C>
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
Released on ??
|
Released on 21/12/2020
|
||||||
|
|
||||||
> The Bookmarks Update
|
> The Bookmarks Update
|
||||||
|
|
||||||
@@ -24,6 +24,9 @@ Released on ??
|
|||||||
- **Text Editor**
|
- **Text Editor**
|
||||||
- Added text editor feature to explorer view
|
- Added text editor feature to explorer view
|
||||||
- Added `o` to keybindings to open a text file
|
- Added `o` to keybindings to open a text file
|
||||||
|
- Keybindings:
|
||||||
|
- `C`: Copy file/directory
|
||||||
|
- `O`: Open text file in editor
|
||||||
- Enhancements:
|
- Enhancements:
|
||||||
- User interface
|
- User interface
|
||||||
- Collpased borders to make everything more *aesthetic*
|
- Collpased borders to make everything more *aesthetic*
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ Text editor is automatically found using this [awesome crate](https://github.com
|
|||||||
| `<PGDOWN>` | Move down in selected list by 8 rows |
|
| `<PGDOWN>` | Move down in selected list by 8 rows |
|
||||||
| `<ENTER>` | Enter directory |
|
| `<ENTER>` | Enter directory |
|
||||||
| `<SPACE>` | Upload / download selected file |
|
| `<SPACE>` | Upload / download selected file |
|
||||||
|
| `<C>` | Copy file/directory |
|
||||||
| `<D>` | Make directory |
|
| `<D>` | Make directory |
|
||||||
| `<E>` | Delete file (Same as `CANC`) |
|
| `<E>` | Delete file (Same as `CANC`) |
|
||||||
| `<G>` | Go to supplied path |
|
| `<G>` | Go to supplied path |
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ mod tests {
|
|||||||
|
|
||||||
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_host_copy_file() {
|
fn test_host_copy_file_absolute() {
|
||||||
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||||
// Create file in tmpdir
|
// Create file in tmpdir
|
||||||
let mut file1_path: PathBuf = PathBuf::from(tmpdir.path());
|
let mut file1_path: PathBuf = PathBuf::from(tmpdir.path());
|
||||||
@@ -851,6 +851,28 @@ mod tests {
|
|||||||
assert_eq!(host.files.len(), 2);
|
assert_eq!(host.files.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
||||||
|
#[test]
|
||||||
|
fn test_host_copy_file_relative() {
|
||||||
|
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||||
|
// Create file in tmpdir
|
||||||
|
let mut file1_path: PathBuf = PathBuf::from(tmpdir.path());
|
||||||
|
file1_path.push("foo.txt");
|
||||||
|
// Write file 1
|
||||||
|
let mut file1: File = File::create(file1_path.as_path()).ok().unwrap();
|
||||||
|
assert!(file1.write_all(b"Hello world!\n").is_ok());
|
||||||
|
// Get file 2 path
|
||||||
|
let file2_path: PathBuf = PathBuf::from("bar.txt");
|
||||||
|
// Create host
|
||||||
|
let mut host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
|
||||||
|
let file1_entry: FsEntry = host.files.get(0).unwrap().clone();
|
||||||
|
assert_eq!(file1_entry.get_name(), String::from("foo.txt"));
|
||||||
|
// Copy
|
||||||
|
assert!(host.copy(&file1_entry, file2_path.as_path()).is_ok());
|
||||||
|
// Verify host has two files
|
||||||
|
assert_eq!(host.files.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hop_copy_directory_absolute() {
|
fn test_hop_copy_directory_absolute() {
|
||||||
|
|||||||
@@ -62,6 +62,77 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ### callback_copy
|
||||||
|
///
|
||||||
|
/// Callback for COPY command (both from local and remote)
|
||||||
|
pub(super) fn callback_copy(&mut self, input: String) {
|
||||||
|
let dest_path: PathBuf = PathBuf::from(input);
|
||||||
|
match self.tab {
|
||||||
|
FileExplorerTab::Local => {
|
||||||
|
// Get selected entry
|
||||||
|
if self.local.files.get(self.local.index).is_some() {
|
||||||
|
let entry: FsEntry = self.local.files.get(self.local.index).unwrap().clone();
|
||||||
|
if let Some(ctx) = self.context.as_mut() {
|
||||||
|
match ctx.local.copy(&entry, dest_path.as_path()) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Copied \"{}\" to \"{}\"",
|
||||||
|
entry.get_abs_path().display(),
|
||||||
|
dest_path.display()
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
// Reload entries
|
||||||
|
let wrkdir: PathBuf = self.local.wrkdir.clone();
|
||||||
|
self.local_scan(wrkdir.as_path());
|
||||||
|
}
|
||||||
|
Err(err) => self.log_and_alert(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!(
|
||||||
|
"Could not copy \"{}\" to \"{}\": {}",
|
||||||
|
entry.get_abs_path().display(),
|
||||||
|
dest_path.display(),
|
||||||
|
err
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileExplorerTab::Remote => {
|
||||||
|
// Get selected entry
|
||||||
|
if self.remote.files.get(self.remote.index).is_some() {
|
||||||
|
let entry: FsEntry = self.remote.files.get(self.remote.index).unwrap().clone();
|
||||||
|
match self.client.as_mut().copy(&entry, dest_path.as_path()) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.log(
|
||||||
|
LogLevel::Info,
|
||||||
|
format!(
|
||||||
|
"Copied \"{}\" to \"{}\"",
|
||||||
|
entry.get_abs_path().display(),
|
||||||
|
dest_path.display()
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
self.reload_remote_dir();
|
||||||
|
}
|
||||||
|
Err(err) => self.log_and_alert(
|
||||||
|
LogLevel::Error,
|
||||||
|
format!(
|
||||||
|
"Could not copy \"{}\" to \"{}\": {}",
|
||||||
|
entry.get_abs_path().display(),
|
||||||
|
dest_path.display(),
|
||||||
|
err
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ### callback_mkdir
|
/// ### callback_mkdir
|
||||||
///
|
///
|
||||||
/// Callback for MKDIR command (supports both local and remote)
|
/// Callback for MKDIR command (supports both local and remote)
|
||||||
|
|||||||
@@ -177,6 +177,20 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char(ch) => match ch {
|
KeyCode::Char(ch) => match ch {
|
||||||
|
'c' | 'C' => {
|
||||||
|
// Copy
|
||||||
|
self.input_mode = InputMode::Popup(PopupType::Input(
|
||||||
|
String::from("Insert destination name"),
|
||||||
|
FileTransferActivity::callback_copy,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
'd' | 'D' => {
|
||||||
|
// Make directory
|
||||||
|
self.input_mode = InputMode::Popup(PopupType::Input(
|
||||||
|
String::from("Insert directory name"),
|
||||||
|
FileTransferActivity::callback_mkdir,
|
||||||
|
));
|
||||||
|
}
|
||||||
'e' | 'E' => {
|
'e' | 'E' => {
|
||||||
// Get file at index
|
// Get file at index
|
||||||
if let Some(entry) = self.local.files.get(self.local.index) {
|
if let Some(entry) = self.local.files.get(self.local.index) {
|
||||||
@@ -201,13 +215,6 @@ impl FileTransferActivity {
|
|||||||
FileTransferActivity::callback_change_directory,
|
FileTransferActivity::callback_change_directory,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
'd' | 'D' => {
|
|
||||||
// Make directory
|
|
||||||
self.input_mode = InputMode::Popup(PopupType::Input(
|
|
||||||
String::from("Insert directory name"),
|
|
||||||
FileTransferActivity::callback_mkdir,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
'h' | 'H' => {
|
'h' | 'H' => {
|
||||||
// Show help
|
// Show help
|
||||||
self.input_mode = InputMode::Popup(PopupType::Help);
|
self.input_mode = InputMode::Popup(PopupType::Help);
|
||||||
@@ -389,6 +396,20 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char(ch) => match ch {
|
KeyCode::Char(ch) => match ch {
|
||||||
|
'c' | 'C' => {
|
||||||
|
// Copy
|
||||||
|
self.input_mode = InputMode::Popup(PopupType::Input(
|
||||||
|
String::from("Insert destination name"),
|
||||||
|
FileTransferActivity::callback_copy,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
'd' | 'D' => {
|
||||||
|
// Make directory
|
||||||
|
self.input_mode = InputMode::Popup(PopupType::Input(
|
||||||
|
String::from("Insert directory name"),
|
||||||
|
FileTransferActivity::callback_mkdir,
|
||||||
|
));
|
||||||
|
}
|
||||||
'e' | 'E' => {
|
'e' | 'E' => {
|
||||||
// Get file at index
|
// Get file at index
|
||||||
if let Some(entry) = self.remote.files.get(self.remote.index) {
|
if let Some(entry) = self.remote.files.get(self.remote.index) {
|
||||||
@@ -405,13 +426,6 @@ impl FileTransferActivity {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'd' | 'D' => {
|
|
||||||
// Make directory
|
|
||||||
self.input_mode = InputMode::Popup(PopupType::Input(
|
|
||||||
String::from("Insert directory name"),
|
|
||||||
FileTransferActivity::callback_mkdir,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
'g' | 'G' => {
|
'g' | 'G' => {
|
||||||
// Goto
|
// Goto
|
||||||
// Show input popup
|
// Show input popup
|
||||||
|
|||||||
@@ -716,6 +716,16 @@ impl FileTransferActivity {
|
|||||||
Span::raw(" "),
|
Span::raw(" "),
|
||||||
Span::raw("Delete file"),
|
Span::raw("Delete file"),
|
||||||
])),
|
])),
|
||||||
|
ListItem::new(Spans::from(vec![
|
||||||
|
Span::styled(
|
||||||
|
"<C>",
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Cyan)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
Span::raw(" "),
|
||||||
|
Span::raw("Copy"),
|
||||||
|
])),
|
||||||
ListItem::new(Spans::from(vec![
|
ListItem::new(Spans::from(vec![
|
||||||
Span::styled(
|
Span::styled(
|
||||||
"<D>",
|
"<D>",
|
||||||
|
|||||||
Reference in New Issue
Block a user