Ability to call commands silently

Some commands doesn't require to capture stdout and stderr.
They can be called without needing to reset the screen.

Add `CallSilently` and `BashExecSilently` to execute those commands
faster.

Also, some optimization.
pull/44/head
Arijit Basu 3 years ago committed by Arijit Basu
parent be2911e073
commit 9f78a1fcff

@ -5,7 +5,6 @@ use anyhow::{bail, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
@ -528,12 +527,24 @@ pub enum ExternalMsg {
/// Example: `Call: {command: bash, args: ["-c", "read -p test"]}`
Call(Command),
/// An alias to `Call: {command: bash, args: ["-c", "${command}"]}`
/// Like `Call` but without the flicker. The stdin, stdout
/// stderr will be piped to null. So it's non-interactive.
///
/// Example: `CallSilently: {command: tput, args: ["bell"]}`
CallSilently(Command),
/// An alias to `Call: {command: bash, args: ["-c", "${command}"], silent: false}`
/// where ${command} is the given value.
///
/// Example: `BashExec: "read -p test"`
BashExec(String),
/// Like `BashExec` but without the flicker. The stdin, stdout
/// stderr will be piped to null. So it's non-interactive.
///
/// Example: `BashExecSilently: "tput bell"`
BashExecSilently(String),
/// Select the focused node.
Select,
@ -626,6 +637,8 @@ pub enum MsgOut {
PrintAppStateAndQuit,
Debug(String),
Call(Command),
CallSilently(Command),
Enque(Task),
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
@ -727,7 +740,6 @@ pub struct App {
config: Config,
pwd: String,
directory_buffers: HashMap<String, DirectoryBuffer>,
tasks: BinaryHeap<Task>,
selection: Vec<Node>,
msg_out: VecDeque<MsgOut>,
mode: Mode,
@ -795,7 +807,6 @@ impl App {
config,
pwd: pwd.to_string_lossy().to_string(),
directory_buffers: Default::default(),
tasks: Default::default(),
selection: Default::default(),
msg_out: Default::default(),
mode,
@ -819,7 +830,7 @@ impl App {
}
pub fn enqueue(mut self, task: Task) -> Self {
self.tasks.push(task);
self.msg_out.push_back(MsgOut::Enque(task));
self
}
@ -871,7 +882,9 @@ impl App {
ExternalMsg::ResetInputBuffer => self.reset_input_buffer(),
ExternalMsg::SwitchMode(mode) => self.switch_mode(&mode),
ExternalMsg::Call(cmd) => self.call(cmd),
ExternalMsg::CallSilently(cmd) => self.call_silently(cmd),
ExternalMsg::BashExec(cmd) => self.bash_exec(cmd),
ExternalMsg::BashExecSilently(cmd) => self.bash_exec_silently(cmd),
ExternalMsg::Select => self.select(),
ExternalMsg::UnSelect => self.un_select(),
ExternalMsg::ToggleSelection => self.toggle_selection(),
@ -1093,10 +1106,10 @@ impl App {
self.change_directory(&parent.to_string_lossy().to_string())?
.focus_by_file_name(&filename.to_string_lossy().to_string())
} else {
Ok(self)
bail!("invalid path {}", path)
}
} else {
Ok(self)
self.change_directory("/")
}
}
@ -1122,6 +1135,11 @@ impl App {
Ok(self)
}
fn call_silently(mut self, command: Command) -> Result<Self> {
self.msg_out.push_back(MsgOut::CallSilently(command));
Ok(self)
}
fn bash_exec(self, script: String) -> Result<Self> {
self.call(Command {
command: "bash".into(),
@ -1129,6 +1147,13 @@ impl App {
})
}
fn bash_exec_silently(self, script: String) -> Result<Self> {
self.call_silently(Command {
command: "bash".into(),
args: vec!["-c".into(), script],
})
}
fn add_directory(mut self, parent: String, dir: DirectoryBuffer) -> Result<Self> {
self.directory_buffers.insert(parent, dir);
self.msg_out.push_back(MsgOut::Refresh);
@ -1421,9 +1446,4 @@ impl App {
pub fn version(&self) -> &String {
&self.version
}
/// Get a reference to the app's tasks.
pub fn pop_task_out(&mut self) -> Option<Task> {
self.tasks.pop()
}
}

@ -333,7 +333,7 @@ impl Default for KeyBindings {
help: rename
messages:
- SwitchMode: rename
- BashExec: |
- BashExecSilently: |
echo "SetInputBuffer: $(basename ${XPLR_FOCUS_PATH})" >> "${XPLR_PIPE_MSG_IN:?}"
".":
@ -586,7 +586,7 @@ impl Default for Config {
x:
help: open in gui
messages:
- BashExec: |
- BashExecSilently: |
OPENER="$(which xdg-open)"
${OPENER:-open} "${XPLR_FOCUS_PATH:?}" &> /dev/null
- SwitchMode: default
@ -809,11 +809,12 @@ impl Default for Config {
enter:
help: create file
messages:
- BashExec: |
- BashExecSilently: |
PTH="${XPLR_INPUT_BUFFER:?}"
if touch "${PTH:?}"; then
echo "LogSuccess: $PTH created" >> "${XPLR_PIPE_MSG_IN:?}"
echo Explore >> "${XPLR_PIPE_MSG_IN:?}"
echo "LogSuccess: $PTH created" >> "${XPLR_PIPE_MSG_IN:?}"
echo "FocusPath: $PTH" >> "${XPLR_PIPE_MSG_IN:?}"
else
echo "LogError: failed to create $PTH" >> "${XPLR_PIPE_MSG_IN:?}"
echo Refresh >> "${XPLR_PIPE_MSG_IN:?}"
@ -850,11 +851,12 @@ impl Default for Config {
enter:
help: create directory
messages:
- BashExec: |
- BashExecSilently: |
PTH="${XPLR_INPUT_BUFFER:?}"
if mkdir -p "$PTH"; then
echo Explore >> "${XPLR_PIPE_MSG_IN:?}"
echo "LogSuccess: $PTH created" >> "${XPLR_PIPE_MSG_IN:?}"
echo "FocusPath: $PTH" >> "${XPLR_PIPE_MSG_IN:?}"
else
echo "LogError: failed to create $PTH" >> "${XPLR_PIPE_MSG_IN:?}"
fi
@ -890,12 +892,13 @@ impl Default for Config {
enter:
help: rename
messages:
- BashExec: |
- BashExecSilently: |
SRC="${XPLR_FOCUS_PATH:?}"
TARGET="${XPLR_INPUT_BUFFER:?}"
if mv -v "${SRC:?}" "${TARGET:?}"; then
echo "LogSuccess: $SRC renamed to $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
echo Explore >> "${XPLR_PIPE_MSG_IN:?}"
echo "LogSuccess: $SRC renamed to $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
echo "FocusPath: $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
else
echo "LogError: failed to rename $SRC to $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
fi
@ -941,6 +944,7 @@ impl Default for Config {
fi
else
if rm -v "${line:?}"; then
echo "FocusNext" >> "${XPLR_PIPE_MSG_IN:?}"
echo "LogSuccess: $line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
echo "LogError: failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"
@ -957,6 +961,7 @@ impl Default for Config {
- BashExec: |
(while IFS= read -r line; do
if rm -rfv "${line:?}"; then
echo "FocusNext" >> "${XPLR_PIPE_MSG_IN:?}"
echo "LogSuccess: $line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
echo "LogError: failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"

@ -6,8 +6,10 @@ use crossterm::terminal as term;
use handlebars::Handlebars;
use std::env;
use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio};
use std::sync::mpsc;
use termion::get_tty;
use tui::backend::CrosstermBackend;
@ -20,6 +22,55 @@ use xplr::pipe_reader;
use xplr::pwd_watcher;
use xplr::ui;
fn call(app: &app::App, cmd: app::Command, silent: bool) -> io::Result<ExitStatus> {
let input_buffer = app.input_buffer().unwrap_or_default();
let focus_index = app
.directory_buffer()
.map(|d| d.focus)
.unwrap_or_default()
.to_string();
let pipe_msg_in = app.pipe().msg_in.clone();
let pipe_mode_out = app.pipe().mode_out.clone();
let pipe_focus_out = app.pipe().focus_out.clone();
let pipe_selection_out = app.pipe().selection_out.clone();
let pipe_result_out = app.pipe().result_out.clone();
let pipe_directory_nodes_out = app.pipe().directory_nodes_out.clone();
let pipe_global_help_menu_out = app.pipe().global_help_menu_out.clone();
let pipe_logs_out = app.pipe().logs_out.clone();
let session_path = app.session_path();
let (stdin, stdout, stderr) = if silent {
(Stdio::null(), Stdio::null(), Stdio::null())
} else {
(Stdio::inherit(), Stdio::inherit(), Stdio::inherit())
};
Command::new(cmd.command.clone())
.current_dir(app.pwd())
.env("XPLR_APP_VERSION", app.version())
.env("XPLR_CONFIG_VERSION", &app.config().version)
.env("XPLR_PID", &app.pid().to_string())
.env("XPLR_INPUT_BUFFER", input_buffer)
.env("XPLR_FOCUS_PATH", app.focused_node_str())
.env("XPLR_FOCUS_INDEX", focus_index)
.env("XPLR_SESSION_PATH", session_path)
.env("XPLR_PIPE_MSG_IN", pipe_msg_in)
.env("XPLR_PIPE_SELECTION_OUT", pipe_selection_out)
.env("XPLR_PIPE_FOCUS_OUT", pipe_focus_out)
.env("XPLR_PIPE_MODE_OUT", pipe_mode_out)
.env("XPLR_PIPE_RESULT_OUT", pipe_result_out)
.env("XPLR_PIPE_GLOBAL_HELP_MENU_OUT", pipe_global_help_menu_out)
.env("XPLR_PIPE_DIRECTORY_NODES_OUT", pipe_directory_nodes_out)
.env("XPLR_PIPE_LOGS_OUT", pipe_logs_out)
.stdin(stdin)
.stdout(stdout)
.stderr(stderr)
.args(cmd.args)
.status()
}
fn main() -> Result<()> {
let (tx_msg_in, rx_msg_in) = mpsc::channel();
let (tx_event_reader, rx_event_reader) = mpsc::channel();
@ -114,6 +165,10 @@ fn main() -> Result<()> {
while let Some(msg) = app.pop_msg_out() {
match msg {
app::MsgOut::Enque(task) => {
tx_msg_in.send(task)?;
}
app::MsgOut::PrintResultAndQuit => {
output = Some(app.result_str());
break 'outer;
@ -158,50 +213,36 @@ fn main() -> Result<()> {
terminal.draw(|f| ui::draw(f, &app, &hb))?;
}
app::MsgOut::CallSilently(cmd) => {
tx_event_reader.send(true)?;
let status = call(&app, cmd, false)
.map(|s| {
if s.success() {
Ok(())
} else {
Err(format!("process exited with code {}", &s))
}
})
.unwrap_or_else(|e| Err(e.to_string()));
if let Err(e) = status {
let msg = app::MsgIn::External(app::ExternalMsg::LogError(e));
tx_msg_in.send(app::Task::new(1, msg, None))?;
};
tx_event_reader.send(false)?;
}
app::MsgOut::Call(cmd) => {
tx_event_reader.send(true)?;
terminal.clear()?;
term::disable_raw_mode()?;
terminal.set_cursor(0, 0)?;
terminal.show_cursor()?;
let input_buffer = app.input_buffer().unwrap_or_default();
let focus_index = app
.directory_buffer()
.map(|d| d.focus)
.unwrap_or_default()
.to_string();
let pipe_msg_in = app.pipe().msg_in.clone();
let pipe_mode_out = app.pipe().mode_out.clone();
let pipe_focus_out = app.pipe().focus_out.clone();
let pipe_selection_out = app.pipe().selection_out.clone();
let pipe_result_out = app.pipe().result_out.clone();
let pipe_directory_nodes_out = app.pipe().directory_nodes_out.clone();
let pipe_global_help_menu_out = app.pipe().global_help_menu_out.clone();
let pipe_logs_out = app.pipe().logs_out.clone();
let session_path = app.session_path();
let status = std::process::Command::new(cmd.command.clone())
.current_dir(app.pwd())
.env("XPLR_APP_VERSION", app.version())
.env("XPLR_CONFIG_VERSION", &app.config().version)
.env("XPLR_PID", &app.pid().to_string())
.env("XPLR_INPUT_BUFFER", input_buffer)
.env("XPLR_FOCUS_PATH", app.focused_node_str())
.env("XPLR_FOCUS_INDEX", focus_index)
.env("XPLR_SESSION_PATH", session_path)
.env("XPLR_PIPE_MSG_IN", pipe_msg_in)
.env("XPLR_PIPE_SELECTION_OUT", pipe_selection_out)
.env("XPLR_PIPE_FOCUS_OUT", pipe_focus_out)
.env("XPLR_PIPE_MODE_OUT", pipe_mode_out)
.env("XPLR_PIPE_RESULT_OUT", pipe_result_out)
.env("XPLR_PIPE_GLOBAL_HELP_MENU_OUT", pipe_global_help_menu_out)
.env("XPLR_PIPE_DIRECTORY_NODES_OUT", pipe_directory_nodes_out)
.env("XPLR_PIPE_LOGS_OUT", pipe_logs_out)
.args(cmd.args.clone())
.status()
let status = call(&app, cmd, false)
.map(|s| {
if s.success() {
Ok(())
@ -224,10 +265,6 @@ fn main() -> Result<()> {
};
}
while let Some(task) = app.pop_task_out() {
tx_msg_in.send(task)?;
}
if app.focused_node() != last_app.focused_node() {
fs::write(&app.pipe().focus_out, app.focused_node_str())?;
};
@ -254,6 +291,7 @@ fn main() -> Result<()> {
}
terminal.clear()?;
terminal.set_cursor(0, 0)?;
term::disable_raw_mode()?;
execute!(terminal.backend_mut(), term::LeaveAlternateScreen)?;
terminal.show_cursor()?;

Loading…
Cancel
Save