mirror of https://github.com/bobwen-dev/hunter
use osstring for filenames/paths
This commit is contained in:
parent
161ba5ac3f
commit
e36ddf34b4
|
@ -7,8 +7,9 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
|||
use std::time::Duration;
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{OsString, OsStr};
|
||||
|
||||
use crate::files::{File, Files, ShortPaths};
|
||||
use crate::files::{File, Files, PathBufExt, OsStrTools};
|
||||
use crate::listview::ListView;
|
||||
use crate::miller_columns::MillerColumns;
|
||||
use crate::widget::Widget;
|
||||
|
@ -131,13 +132,15 @@ impl Tabbable for TabView<FileBrowser> {
|
|||
Key::Char('!') => {
|
||||
let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let selected_files = self.widgets.iter().fold(HashMap::new(),
|
||||
|mut f, w| {
|
||||
let dir = w.cwd().unwrap().clone();
|
||||
let selected_files = w.selected_files().unwrap();
|
||||
f.insert(dir, selected_files);
|
||||
f
|
||||
});
|
||||
let selected_files = self
|
||||
.widgets
|
||||
.iter()
|
||||
.map(|w| {
|
||||
w.selected_files()
|
||||
.map_err(|_| Vec::<Files>::new())
|
||||
.unwrap()
|
||||
}).collect();
|
||||
|
||||
self.widgets[self.active].exec_cmd(tab_dirs, selected_files)
|
||||
}
|
||||
_ => { self.active_tab_mut().on_key(key) }
|
||||
|
@ -633,39 +636,55 @@ impl FileBrowser {
|
|||
|
||||
fn exec_cmd(&mut self,
|
||||
tab_dirs: Vec<File>,
|
||||
tab_files: HashMap<File, Vec<File>>) -> HResult<()> {
|
||||
tab_files: Vec<Vec<File>>) -> HResult<()> {
|
||||
let cwd = self.cwd()?;
|
||||
let filename = self.selected_file()?.name.clone();
|
||||
let filename = self.selected_file()?.path.quoted_file_name()?;
|
||||
let selected_files = self.selected_files()?;
|
||||
|
||||
let file_names
|
||||
= selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>();
|
||||
let files = selected_files.iter()
|
||||
.map(|f| f.path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
let cmd = self.minibuffer("exec")?;
|
||||
let cmd = self.minibuffer("exec")?.trim_start().to_string();
|
||||
|
||||
self.show_status(&format!("Running: \"{}\"", &cmd)).log();
|
||||
let cmd = OsString::from(cmd);
|
||||
let space = OsString::from(" ");
|
||||
|
||||
let mut cmd = if file_names.len() == 0 {
|
||||
cmd.replace("$s", &format!("{}", &filename))
|
||||
let mut cmd = if files.len() == 0 {
|
||||
cmd.replace(&OsString::from("$s"), &filename)
|
||||
} else {
|
||||
let args = file_names.iter().map(|f| {
|
||||
format!(" \"{}\" ", f)
|
||||
}).collect::<String>();
|
||||
cmd.replace("$s", &args)
|
||||
let args = files.iter()
|
||||
.fold(OsString::new(), |mut args, file| {
|
||||
if let Some(name) = file.quoted_file_name() {
|
||||
args.push(name);
|
||||
args.push(space.clone());
|
||||
}
|
||||
args
|
||||
});
|
||||
let args = args.trim_last_space();
|
||||
|
||||
cmd.replace(&OsString::from("$s"), &args)
|
||||
};
|
||||
|
||||
for (i, tab_dir) in tab_dirs.iter().enumerate() {
|
||||
if let Some(tab_files) = tab_files.get(tab_dir) {
|
||||
let tab_file_identifier = format!("${}s", i);
|
||||
let args = tab_files.iter().map(|f| {
|
||||
let file_path = f.strip_prefix(&cwd);
|
||||
format!(" \"{}\" ", file_path.to_string_lossy())
|
||||
}).collect::<String>();
|
||||
if let Some(tab_files) = tab_files.get(i) {
|
||||
let tab_file_identifier = OsString::from(format!("${}s", i));
|
||||
|
||||
let args = tab_files.iter()
|
||||
.fold(OsString::new(), |mut args, file| {
|
||||
let file_path = file.strip_prefix(&cwd);
|
||||
let name = file_path.quoted_path();
|
||||
args.push(name);
|
||||
args.push(space.clone());
|
||||
|
||||
args
|
||||
});
|
||||
|
||||
cmd = cmd.replace(&tab_file_identifier, &args);
|
||||
}
|
||||
|
||||
let tab_identifier = format!("${}", i);
|
||||
let tab_path = tab_dir.path.to_string_lossy();
|
||||
let tab_identifier = OsString::from(format!("${}", i));
|
||||
let tab_path = tab_dir.path().into_os_string();
|
||||
cmd = cmd.replace(&tab_identifier, &tab_path);
|
||||
}
|
||||
|
||||
|
|
131
src/files.rs
131
src/files.rs
|
@ -4,6 +4,8 @@ use std::os::unix::fs::MetadataExt;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::unix::ffi::{OsStringExt, OsStrExt};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
use lscolors::LsColors;
|
||||
use mime_detective;
|
||||
|
@ -609,25 +611,144 @@ impl File {
|
|||
Some(time.format("%F %R").to_string())
|
||||
}
|
||||
|
||||
pub fn short_path(&self) -> PathBuf {
|
||||
self.path.short_path()
|
||||
}
|
||||
|
||||
pub fn short_string(&self) -> String {
|
||||
self.path.short_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait ShortPaths {
|
||||
pub trait PathBufExt {
|
||||
fn short_path(&self) -> PathBuf;
|
||||
fn short_string(&self) -> String;
|
||||
fn name_starts_with(&self, pat: &str) -> bool;
|
||||
fn quoted_file_name(&self) -> Option<OsString>;
|
||||
fn quoted_path(&self) -> OsString;
|
||||
}
|
||||
|
||||
impl ShortPaths for PathBuf {
|
||||
fn short_string(&self) -> String {
|
||||
impl PathBufExt for PathBuf {
|
||||
fn short_path(&self) -> PathBuf {
|
||||
if let Ok(home) = crate::paths::home_path() {
|
||||
if let Ok(short) = self.strip_prefix(home) {
|
||||
let mut path = PathBuf::from("~");
|
||||
path.push(short);
|
||||
return path.to_string_lossy().to_string();
|
||||
return path
|
||||
}
|
||||
}
|
||||
return self.to_string_lossy().to_string();
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
fn short_string(&self) -> String {
|
||||
self.short_path().to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
fn name_starts_with(&self, pat: &str) -> bool {
|
||||
if let Some(name) = self.file_name() {
|
||||
let nbytes = name.as_bytes();
|
||||
let pbytes = pat.as_bytes();
|
||||
|
||||
if nbytes.starts_with(pbytes) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn quoted_file_name(&self) -> Option<OsString> {
|
||||
if let Some(name) = self.file_name() {
|
||||
let mut name = name.as_bytes().to_vec();
|
||||
let mut quote = "\"".as_bytes().to_vec();
|
||||
//let mut quote_after = "\"".as_bytes().to_vec();
|
||||
let mut quoted = vec![];
|
||||
quoted.append(&mut quote.clone());
|
||||
quoted.append(&mut name);
|
||||
quoted.append(&mut quote);
|
||||
|
||||
let quoted_name = OsStr::from_bytes("ed).to_os_string();
|
||||
return Some(quoted_name);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn quoted_path(&self) -> OsString {
|
||||
let mut path = self.clone().into_os_string().into_vec();
|
||||
let mut quote = "\"".as_bytes().to_vec();
|
||||
|
||||
let mut quoted = vec![];
|
||||
quoted.append(&mut quote.clone());
|
||||
quoted.append(&mut path);
|
||||
quoted.append(&mut quote);
|
||||
|
||||
OsString::from_vec(quoted)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OsStrTools {
|
||||
fn replace(&self, from: &OsStr, to: &OsStr) -> OsString;
|
||||
fn trim_last_space(&self) -> OsString;
|
||||
fn contains_osstr(&self, pat: &OsStr) -> bool;
|
||||
fn position(&self, pat: &OsStr) -> Option<usize>;
|
||||
}
|
||||
|
||||
impl OsStrTools for OsStr {
|
||||
fn replace(&self, from: &OsStr, to: &OsStr) -> OsString {
|
||||
let orig_string = self.as_bytes().to_vec();
|
||||
let from = from.as_bytes();
|
||||
let to = to.as_bytes().to_vec();
|
||||
let from_len = from.len();
|
||||
|
||||
let new_string = orig_string
|
||||
.windows(from_len)
|
||||
.enumerate()
|
||||
.fold(Vec::new(), |mut pos, (i, chars)| {
|
||||
if chars == from {
|
||||
pos.push(i);
|
||||
}
|
||||
pos
|
||||
}).iter().rev().fold(orig_string.to_vec(), |mut string, pos| {
|
||||
let pos = *pos;
|
||||
string.splice(pos..pos+from_len, to.clone());
|
||||
string
|
||||
});
|
||||
|
||||
OsString::from_vec(new_string)
|
||||
}
|
||||
|
||||
fn trim_last_space(&self) -> OsString {
|
||||
let string = self.as_bytes();
|
||||
let len = string.len();
|
||||
|
||||
if len > 0 {
|
||||
OsString::from_vec(string[..len-1].to_vec())
|
||||
} else {
|
||||
self.to_os_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_osstr(&self, pat: &OsStr) -> bool {
|
||||
let string = self.as_bytes();
|
||||
let pat = pat.as_bytes();
|
||||
let pat_len = pat.len();
|
||||
|
||||
string.windows(pat_len)
|
||||
.find(|chars|
|
||||
chars == &pat
|
||||
).is_some()
|
||||
}
|
||||
|
||||
fn position(&self, pat: &OsStr) -> Option<usize> {
|
||||
let string = self.as_bytes();
|
||||
let pat = pat.as_bytes();
|
||||
let pat_len = pat.len();
|
||||
|
||||
string.windows(pat_len)
|
||||
.position(|chars|
|
||||
chars == pat
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,6 +300,7 @@ impl ListView<Files>
|
|||
let file = self.selected_file_mut();
|
||||
file.toggle_selection();
|
||||
self.move_down();
|
||||
self.core.set_dirty();
|
||||
self.refresh().log();
|
||||
}
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ impl Previewer {
|
|||
-> Result<Box<dyn Widget + Send>, HError> {
|
||||
let process =
|
||||
std::process::Command::new("scope.sh")
|
||||
.arg(&file.name)
|
||||
.arg(&file.path)
|
||||
.arg("10".to_string())
|
||||
.arg("10".to_string())
|
||||
.arg("".to_string())
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::sync::mpsc::Sender;
|
|||
use std::process::Child;
|
||||
use std::os::unix::process::{CommandExt, ExitStatusExt};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use termion::event::Key;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
@ -15,6 +16,7 @@ use crate::hbox::HBox;
|
|||
use crate::preview::WillBeWidget;
|
||||
use crate::fail::{HResult, HError, ErrorLog};
|
||||
use crate::term;
|
||||
use crate::files::OsStrTools;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Process {
|
||||
|
@ -114,8 +116,13 @@ impl Listable for ListView<Vec<Process>> {
|
|||
}
|
||||
|
||||
impl ListView<Vec<Process>> {
|
||||
fn run_proc(&mut self, cmd: &str) -> HResult<()> {
|
||||
fn run_proc(&mut self, cmd: &OsString) -> HResult<()> {
|
||||
let shell = std::env::var("SHELL").unwrap_or("sh".into());
|
||||
let home = crate::paths::home_path()?.into_os_string();
|
||||
let short = OsString::from("~");
|
||||
let short_cmd = cmd.replace(&home, &short).to_string_lossy().to_string();
|
||||
|
||||
self.show_status(&format!("Running: {}", &short_cmd)).log();
|
||||
|
||||
let handle = std::process::Command::new(shell)
|
||||
.arg("-c")
|
||||
|
@ -125,7 +132,7 @@ impl ListView<Vec<Process>> {
|
|||
.before_exec(|| unsafe { libc::dup2(1, 2); Ok(()) })
|
||||
.spawn()?;
|
||||
let mut proc = Process {
|
||||
cmd: cmd.to_string(),
|
||||
cmd: short_cmd,
|
||||
handle: Arc::new(Mutex::new(handle)),
|
||||
output: Arc::new(Mutex::new(String::new())),
|
||||
status: Arc::new(Mutex::new(None)),
|
||||
|
@ -278,7 +285,7 @@ impl ProcView {
|
|||
self.hbox.get_textview()
|
||||
}
|
||||
|
||||
pub fn run_proc(&mut self, cmd: &str) -> HResult<()> {
|
||||
pub fn run_proc(&mut self, cmd: &OsString) -> HResult<()> {
|
||||
self.get_listview_mut().run_proc(cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue