use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use std::process::{Child, Command};
use std::os::unix::process::{CommandExt, ExitStatusExt};
use std::io::{BufRead, BufReader};
use std::ffi::{OsString, OsStr};
use std::os::unix::ffi::{OsStringExt, OsStrExt};
use termion::event::Key;
use unicode_width::UnicodeWidthStr;
use osstrtools::OsStrTools;
use async_value::Stale;
use crate::listview::{Listable, ListView};
use crate::textview::TextView;
use crate::widget::{Widget, Events, WidgetCore};
use crate::coordinates::Coordinates;
use crate::preview::AsyncWidget;
use crate::dirty::Dirtyable;
use crate::hbox::HBox;
use crate::fail::{HResult, HError, ErrorLog};
use crate::term::{self, ScreenExt};
use crate::files::File;
#[derive(Debug)]
struct Process {
cmd: String,
handle: Arc<Mutex<Child>>,
output: Arc<Mutex<String>>,
status: Arc<Mutex<Option<i32>>>,
success: Arc<Mutex<Option<bool>>>,
sender: Sender<Events>
}
pub struct Cmd {
pub cmd: OsString,
pub args: Option<Vec<OsString>>,
pub vars: Option<Vec<(OsString, OsString)>>,
pub short_cmd: Option<String>,
pub cwd: File,
pub cwd_files: Option<Vec<File>>,
pub tab_files: Option<Vec<Vec<File>>>,
pub tab_paths: Option<Vec<File>>,
}
impl Cmd {
fn process(&mut self) -> Vec<OsString> {
let cmd = self.cmd.clone().split(&OsString::from(" "));
let cmd = self.substitute_cwd_files(cmd);
let cmd = self.substitute_tab_files(cmd);
let cmd = self.substitute_tab_paths(cmd);
cmd
}
fn substitute_cwd_files(&mut self, cmd: Vec<OsString>) -> Vec<OsString> {
if self.cwd_files.is_none() { return cmd; }
let cwd_pat = OsString::from("$s");
let cwd_files = self.cwd_files
.take()
.unwrap() // There always is a file, even if just place holder
.iter()
.map(|file|
// strip out the cwd part to make path shorter
file.strip_prefix(&self.cwd)
.into_os_string()
// escape single quotes so file names with them work
.escape_single_quote())
.collect::<Vec<OsString>>();
cmd.iter()
.map(|part| part.splice_quoted_single(&cwd_pat,
cwd_files.clone()))
.flatten().collect()
}
fn substitute_tab_files(&mut self, cmd: Vec<OsString>) -> Vec<OsString> {
if self.tab_files.is_none() { return cmd; }
let tab_files = self.tab_files.take().unwrap();
tab_files.into_iter()
.enumerate()
.fold(cmd, |cmd, (i, tab_files)| {
let tab_files_pat = OsString::from(format!("${}s", i));
let tab_file_paths = tab_files.iter()
.map(|file|
// strip out the cwd part to make path shorter
file.strip_prefix(&self.cwd)
.into_os_string()
// escape single quotes so file names with them work
.escape_single_quote())
.collect::<Vec<OsString>>();
cmd.iter().map(|part| {
part.splice_quoted_single(&tab_files_pat,
tab_file_paths.clone())
}).flatten().collect()
})
}
fn substitute_tab_paths(&mut self, cmd: Vec<OsString>) -> Vec<OsString> {
if self.tab_paths.is_none() { return cmd; }
let tab_paths = self.tab_paths.take().unwrap();
tab_paths.into_iter()
.enumerate()
.fold(cmd, |cmd, (i, tab_path)| {
let tab_path_pat = OsString::from(format!("${}", i));
let tab_path = tab_path.strip_prefix(&self.cwd)
.into_os_string()
// escape single quotes so file names with them work
.escape_single_quote();
cmd.iter().map(|part| {
part.splice_quoted_single(&tab_path_pat,
vec![tab_path.clone()])
}).flatten().collect()
})
}
}
impl PartialEq for Process {
fn eq(&self, other: &Process) -> bool {
self.cmd == other.cmd
}
}
impl Process {
fn read_proc(&mut self) -> HResult<()> {
let handle = self.handle.clone();
let output = self.output.clone();
let status = self.status.clone();
let success = self.success.clone();
let sender = self.sender.clone();
let cmd = self.cmd.clone();
let pid = self.handle.lock()?.id();
std::thread::spawn(move || -> HResult<()> {
let stdout = handle.