Text editor input and session

This commit is contained in:
ChristianVisintin
2020-12-20 15:05:22 +01:00
parent 47f9b39630
commit 3f6f03af33
2 changed files with 157 additions and 29 deletions

View File

@@ -19,9 +19,11 @@
*
*/
extern crate tempfile;
use super::{
DialogCallback, DialogYesNoOption, FileExplorerTab, FileTransferActivity, FsEntry, InputEvent,
InputField, InputMode, OnInputSubmitCallback, PopupType,
InputField, InputMode, LogLevel, OnInputSubmitCallback, PopupType,
};
use crossterm::event::{KeyCode, KeyModifiers};
@@ -175,10 +177,6 @@ impl FileTransferActivity {
}
}
KeyCode::Char(ch) => match ch {
'q' | 'Q' => {
// Create quit prompt dialog
self.input_mode = self.create_quit_popup();
}
'e' | 'E' => {
// Get file at index
if let Some(entry) = self.local.files.get(self.local.index) {
@@ -223,6 +221,38 @@ impl FileTransferActivity {
let pwd: PathBuf = self.local.wrkdir.clone();
self.local_scan(pwd.as_path());
}
'o' | 'O' => {
// Edit local file
if self.local.files.get(self.local.index).is_some() {
// Clone entry due to mutable stuff...
let fsentry: FsEntry =
self.local.files.get(self.local.index).unwrap().clone();
// Check if file
if fsentry.is_file() {
self.log(
LogLevel::Info,
format!(
"Opening file \"{}\"...",
fsentry.get_abs_path().display()
)
.as_str(),
);
// Edit file
match self.edit_local_file(fsentry.get_abs_path().as_path()) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.local.wrkdir.clone();
self.local_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
}
}
}
'q' | 'Q' => {
// Create quit prompt dialog
self.input_mode = self.create_quit_popup();
}
'r' | 'R' => {
// Rename
self.input_mode = InputMode::Popup(PopupType::Input(
@@ -402,6 +432,33 @@ impl FileTransferActivity {
// Reload file entries
self.reload_remote_dir();
}
'o' | 'O' => {
// Edit remote file
if self.remote.files.get(self.remote.index).is_some() {
// Clone entry due to mutable stuff...
let fsentry: FsEntry =
self.remote.files.get(self.remote.index).unwrap().clone();
// Check if file
if let FsEntry::File(file) = fsentry {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", file.abs_path.display())
.as_str(),
);
// Edit file
match self.edit_remote_file(&file) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.remote.wrkdir.clone();
self.remote_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
// Put input mode back to normal
self.input_mode = InputMode::Explorer;
}
}
}
'q' | 'Q' => {
// Create quit prompt dialog
self.input_mode = self.create_quit_popup();

View File

@@ -28,7 +28,7 @@ extern crate tempfile;
// Locals
use super::{FileTransferActivity, InputMode, LogLevel, PopupType};
use crate::fs::{FsEntry, FsFile};
use crate::utils::fmt_millis;
use crate::utils::{fmt_millis, hash_sha256_file};
// Ext
use bytesize::ByteSize;
@@ -546,12 +546,7 @@ impl FileTransferActivity {
// Apply file mode to file
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
if let Some(pex) = remote.unix_pex {
if let Err(err) = self
.context
.as_ref()
.unwrap()
.local
.chmod(local, pex)
if let Err(err) = self.context.as_ref().unwrap().local.chmod(local, pex)
{
self.log(
LogLevel::Error,
@@ -723,10 +718,10 @@ impl FileTransferActivity {
}
}
/// ### edit_file
/// ### edit_local_file
///
/// Edit a file on localhost
pub(super) fn edit_file(&mut self, path: &Path) {
pub(super) fn edit_local_file(&mut self, path: &Path) -> Result<(), String> {
// Read first 2048 bytes or less from file to check if it is textual
match OpenOptions::new().read(true).open(path) {
Ok(mut f) => {
@@ -735,25 +730,16 @@ impl FileTransferActivity {
match f.read(&mut buff) {
Ok(size) => {
if content_inspector::inspect(&buff[0..size]).is_binary() {
self.log_and_alert(
LogLevel::Error,
format!("Could not open file in editor: file is binary"),
);
return;
return Err(format!("Could not open file in editor: file is binary"));
}
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not read file: {}", err),
);
return;
return Err(format!("Could not read file: {}", err));
}
}
}
Err(err) => {
self.log_and_alert(LogLevel::Error, format!("Could not read file: {}", err));
return;
return Err(format!("Could not read file: {}", err));
}
}
// Put input mode back to normal
@@ -772,9 +758,7 @@ impl FileTransferActivity {
)
.as_str(),
),
Err(err) => {
self.log_and_alert(LogLevel::Error, format!("Could not open editor: {}", err))
}
Err(err) => return Err(format!("Could not open editor: {}", err)),
}
if let Some(ctx) = self.context.as_mut() {
// Clear screen
@@ -784,5 +768,92 @@ impl FileTransferActivity {
}
// Re-enable raw mode
let _ = enable_raw_mode();
Ok(())
}
/// ### edit_remote_file
///
/// Edit file on remote host
pub(super) fn edit_remote_file(&mut self, file: &FsFile) -> Result<(), String> {
// Create temp file
let tmpfile: tempfile::NamedTempFile = match tempfile::NamedTempFile::new() {
Ok(f) => f,
Err(err) => {
return Err(format!("Could not create temporary file: {}", err));
}
};
// Download file
if let Err(err) = self.filetransfer_recv_file(tmpfile.path(), file) {
return Err(err);
}
// Get current file hash
let prev_hash: String = match hash_sha256_file(tmpfile.path()) {
Ok(s) => s,
Err(err) => {
return Err(format!(
"Could not get sha256 for \"{}\": {}",
file.abs_path.display(),
err
))
}
};
// Edit file
if let Err(err) = self.edit_local_file(tmpfile.path()) {
return Err(err);
}
// Check if file has changed
let new_hash: String = match hash_sha256_file(tmpfile.path()) {
Ok(s) => s,
Err(err) => {
return Err(format!(
"Could not get sha256 for \"{}\": {}",
file.abs_path.display(),
err
))
}
};
// If hash is different, write changes
match new_hash != prev_hash {
true => {
self.log(
LogLevel::Info,
format!(
"File \"{}\" has changed; writing changes to remote",
file.abs_path.display()
)
.as_ref(),
);
// Get local fs entry
let tmpfile_entry: FsEntry =
match self.context.as_ref().unwrap().local.stat(tmpfile.path()) {
Ok(e) => e,
Err(err) => {
return Err(format!(
"Could not stat \"{}\": {}",
tmpfile.path().display(),
err
))
}
};
// Write file
let tmpfile_entry: &FsFile = match &tmpfile_entry {
FsEntry::Directory(_) => panic!("tempfile is a directory for some reason"),
FsEntry::File(f) => f,
};
// Send file
if let Err(err) =
self.filetransfer_send_file(tmpfile_entry, file.abs_path.as_path())
{
return Err(err);
}
}
false => {
self.log(
LogLevel::Info,
format!("File \"{}\" hasn't changed", file.abs_path.display()).as_ref(),
);
}
}
Ok(())
}
}