diff options
author | rabite0 <rabite@posteo.de> | 2019-04-27 19:11:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-27 19:11:51 +0200 |
commit | 939697d919796ac48415a394654a0251a37f084d (patch) | |
tree | ca1029b96ec0a99266d3b023a9f7aba1bccb4228 /src | |
parent | 103ac52a5ef07b154c28d6a985a86ff6ca8c2d50 (diff) |
Select files and enter directories by calling external program (#27)
* select file/dir with external program
* multi-file select with external prog
* last adjustions and README update
* separated cd/selection into their own functions
* update README
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 14 | ||||
-rw-r--r-- | src/file_browser.rs | 182 |
2 files changed, 194 insertions, 2 deletions
diff --git a/src/config.rs b/src/config.rs index 4801286..5b0f8dc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,8 @@ use crate::fail::{HError, HResult, ErrorLog}; pub struct Config { pub animation: bool, pub show_hidden: bool, + pub select_cmd: String, + pub cd_cmd: String } @@ -12,7 +14,9 @@ impl Config { pub fn new() -> Config { Config { animation: true, - show_hidden: false + show_hidden: false, + select_cmd: "find -type f | fzf -m".to_string(), + cd_cmd: "find -type d | fzf".to_string() } } @@ -31,6 +35,14 @@ impl Config { Ok(("animation", "off")) => { config.animation = false; }, Ok(("show_hidden", "on")) => { config.show_hidden = true; }, Ok(("show_hidden", "off")) => { config.show_hidden = false; }, + Ok(("select_cmd", cmd)) => { + let cmd = cmd.to_string(); + config.select_cmd = cmd; + } + Ok(("cd_cmd", cmd)) => { + let cmd = cmd.to_string(); + config.cd_cmd = cmd; + } _ => { HError::config_error::<Config>(line.to_string()).log(); } } config diff --git a/src/file_browser.rs b/src/file_browser.rs index 408b0c7..1ca9046 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -1,10 +1,12 @@ use termion::event::Key; use pathbuftools::PathBufTools; +use osstrtools::OsStrTools; use std::io::Write; use std::sync::{Arc, Mutex, RwLock}; use std::path::PathBuf; use std::ffi::OsString; +use std::os::unix::ffi::OsStringExt; use std::collections::HashSet; use crate::files::{File, Files}; @@ -437,6 +439,19 @@ impl FileBrowser { Ok(()) } + pub fn main_widget_goto_wait(&mut self, dir :&File) -> HResult<()> { + self.main_widget_goto(&dir)?; + + // replace this with on_ready_mut() later + let pause = std::time::Duration::from_millis(10); + while self.main_widget().is_err() { + self.main_async_widget_mut()?.refresh().log(); + std::thread::sleep(pause); + } + + Ok(()) + } + pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> { self.cache_files().log(); @@ -825,6 +840,170 @@ impl FileBrowser { Ok(()) } + fn external_select(&mut self) -> HResult<()> { + let shell = std::env::var("SHELL").unwrap_or("bash".into()); + let cmd = self.core + .config.read()? + .get()? + .select_cmd + .clone(); + + self.core.get_sender().send(Events::InputEnabled(false))?; + self.core.screen.drop_screen(); + self.preview_widget().map(|preview| preview.cancel_animation()).log(); + + let cmd_result = std::process::Command::new(shell) + .arg("-c") + .arg(&cmd) + .stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::inherit()) + .output(); + + self.core.screen.reset_screen().log(); + self.clear().log(); + self.core.get_sender().send(Events::InputEnabled(true))?; + + match cmd_result { + Ok(cmd_result) => { + if cmd_result.status.success() { + let cwd = &self.cwd.path; + + let paths = OsString::from_vec(cmd_result.stdout) + .split_lines() + .iter() + .map(|output| { + let path = PathBuf::from(output); + if path.is_absolute() { + path + } else { + cwd.join(path) + } + }) + .collect::<Vec<PathBuf>>(); + + if paths.len() == 1 { + let path = &paths[0]; + if path.exists() { + if path.is_dir() { + let dir = File::new_from_path(&path, None)?; + + self.main_widget_goto(&dir).log(); + } else if path.is_file() { + let file = File::new_from_path(&path, None)?; + let dir = file.parent_as_file()?; + + self.main_widget_goto_wait(&dir).log(); + + self.main_widget_mut()?.select_file(&file); + } + } else { + let msg = format!("Can't access path: {}!", + path.to_string_lossy()); + self.show_status(&msg).log(); + } + } else { + let mut last_file = None; + for file_path in paths { + if !file_path.exists() { + let msg = format!("Can't find: {}", + file_path .to_string_lossy()); + self.show_status(&msg).log(); + continue; + } + + let dir_path = file_path.parent()?; + if self.cwd.path != dir_path { + let file_dir = File::new_from_path(&dir_path, None); + + self.main_widget_goto_wait(&file_dir?).log(); + } + + self.main_widget_mut()? + .content + .find_file_with_path(&file_path) + .map(|file| { + file.toggle_selection(); + last_file = Some(file.clone()); + }); + } + + self.main_widget_mut().map(|w| { + last_file.map(|f| w.select_file(&f)); + w.content.set_dirty(); + }).log(); + } + } else { + self.show_status("External program failed!").log(); + } + } + Err(_) => self.show_status("Can't run external program!").log() + } + + Ok(()) + } + + fn external_cd(&mut self) -> HResult<()> { + let shell = std::env::var("SHELL").unwrap_or("bash".into()); + let cmd = self.core + .config.read()? + .get()? + .cd_cmd + .clone(); + + self.core.get_sender().send(Events::InputEnabled(false))?; + self.core.screen.drop_screen(); + self.preview_widget().map(|preview| preview.cancel_animation()).log(); + + let cmd_result = std::process::Command::new(shell) + .arg("-c") + .arg(cmd) + .stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::inherit()) + .output(); + + self.core.screen.reset_screen().log(); + self.clear().log(); + self.core.get_sender().send(Events::InputEnabled(true))?; + + match cmd_result { + Ok(cmd_result) => { + if cmd_result.status.success() { + let cwd = &self.cwd.path; + + let path_string = OsString::from_vec(cmd_result.stdout); + let path_string = path_string.trim_end_newlines(); + let path = PathBuf::from(path_string); + let path = if path.is_absolute() { + path + } else { + cwd.join(path) + }; + + if path.exists() { + if path.is_dir() { + let dir = File::new_from_path(&path, None)?; + self.main_widget_goto(&dir).log(); + } + else { + let msg = format!("Can't access path: {}!", + path.to_string_lossy()); + self.show_status(&msg).log(); + } + + } else { + self.show_status("External program failed!").log(); + } + } + } + Err(_) => self.show_status("Can't run external program!").log() + } + + Ok(()) + } + + fn exec_cmd(&mut self, tab_dirs: Vec<File>, tab_files: Vec<Vec<File>>) -> HResult<()> { @@ -1031,6 +1210,8 @@ impl Widget for FileBrowser { fn on_key(&mut self, key: Key) -> HResult<()> { match key { + Key::Alt(' ') => self.external_select()?, + Key::Alt('/') => self.external_cd()?, Key::Char('/') => { self.turbo_cd()?; }, Key::Char('q') => HError::quit()?, Key::Char('Q') => { self.quit_with_dir()?; }, @@ -1040,7 +1221,6 @@ impl Widget for FileBrowser { Key::Char('-') => { self.goto_prev_cwd()?; }, Key::Char('`') => { self.goto_bookmark()?; }, Key::Char('m') => { self.add_bookmark()?; }, - Key::Char('w') => { self.show_procview()?; }, Key::Char('g') => self.show_log()?, Key::Char('z') => self.run_subshell()?, |