Rename messages

This commit is contained in:
Arijit Basu 2022-10-24 19:00:06 +05:30 committed by Arijit Basu
parent 7b8f38df5b
commit 91e3990df1
6 changed files with 370 additions and 280 deletions

View File

@ -547,54 +547,78 @@ Example:
#### Call
Like `Call0`, but it uses `\n` as the delimiter in input/output pipes,
hence it cannot handle files with `\n` in the name.
You may want to use `Call0` instead.
#### Call0
Call a shell command with the given arguments.
Note that the arguments will be shell-escaped.
So to read the variables, the `-c` option of the shell
can be used.
You may need to pass `ExplorePwd` depening on the expectation.
You may need to pass `ExplorePwd` depending on the expectation.
Type: { Call = { command = string, args = { "list", "of", "string" } }
Type: { Call0 = { command = "string", args = { "list", "of", "string" } }
Example:
- Lua: `{ Call = { command = "bash", args = { "-c", "read -p test" } } }`
- YAML: `Call: { command: bash, args: ["-c", "read -p test"] }`
- Lua: `{ Call0 = { command = "bash", args = { "-c", "read -p test" } } }`
- YAML: `Call0: { command: bash, args: ["-c", "read -p test"] }`
#### CallSilently
Like `Call` but without the flicker. The stdin, stdout
Like `CallSilently0`, but it uses `\n` as the delimiter in input/output
pipes, hence it cannot handle files with `\n` in the name.
You may want to use `Call0Silently` instead.
#### CallSilently0
Like `Call0` but without the flicker. The stdin, stdout
stderr will be piped to null. So it's non-interactive.
Type: { CallSilently = "string" }
Type: { CallSilently0 = { command = "string", args = {"list", "of", "string"} } }
Example:
- Lua: `{ CallSilently = { command = "tput", args = { "bell" } } }`
- YAML: `CallSilently: { command: tput, args: ["bell"] }`
- Lua: `{ CallSilently0 = { command = "tput", args = { "bell" } } }`
- YAML: `CallSilently0: { command: tput, args: ["bell"] }`
#### BashExec
Like `BashExec0`, but it uses `\n` as the delimiter in input/output
pipes, hence it cannot handle files with `\n` in the name.
You may want to use `BashExec0` instead.
#### BashExec0
An alias to `Call: {command: bash, args: ["-c", "{string}"], silent: false}`
where `{string}` is the given value.
Type: { BashExec = "string" }
Type: { BashExec0 = "string" }
Example:
- Lua: `{ BashExec = "read -p test" }`
- YAML: `BashExec: "read -p test"`
- Lua: `{ BashExec0 = "read -p test" }`
- YAML: `BashExec0: "read -p test"`
#### BashExecSilently
Like `BashExec` but without the flicker. The stdin, stdout
Like `BashExecSilently0`, but it uses `\n` as the delimiter in
input/output pipes, hence it cannot handle files with `\n` in the name.
You may want to use `BashExec0Silently` instead.
#### BashExecSilently0
Like `BashExec0` but without the flicker. The stdin, stdout
stderr will be piped to null. So it's non-interactive.
Type: { BashExecSilently = "string" }
Type: { BashExec0Silently = "string" }
Example:
- Lua: `{ BashExecSilently = "tput bell" }`
- YAML: `BashExecSilently: "tput bell"`
- Lua: `{ BashExecSilently0 = "tput bell" }`
- YAML: `BashExecSilently0: "tput bell"`
### Calling Lua Functions

View File

@ -15,7 +15,6 @@ pub use crate::msg::in_::ExternalMsg;
pub use crate::msg::in_::InternalMsg;
pub use crate::msg::in_::MsgIn;
pub use crate::msg::out::MsgOut;
use crate::newlines::unescape_string;
pub use crate::node::Node;
pub use crate::node::ResolvedNode;
pub use crate::pipe::Pipe;
@ -175,7 +174,6 @@ pub struct App {
pub mode: Mode,
pub layout: Layout,
pub input: InputBuffer,
pub input_unescaped: String,
pub pid: u32,
pub session_path: String,
pub pipe: Pipe,
@ -310,7 +308,6 @@ impl App {
mode,
layout,
input,
input_unescaped: Default::default(),
pid,
session_path: session_path.clone(),
pipe: Pipe::from_session_path(&session_path)?,
@ -452,9 +449,13 @@ impl App {
SwitchLayoutBuiltin(mode) => self.switch_layout_builtin(&mode),
SwitchLayoutCustom(mode) => self.switch_layout_custom(&mode),
Call(cmd) => self.call(cmd),
Call0(cmd) => self.call0(cmd),
CallSilently(cmd) => self.call_silently(cmd),
CallSilently0(cmd) => self.call_silently0(cmd),
BashExec(cmd) => self.bash_exec(cmd),
BashExec0(cmd) => self.bash_exec0(cmd),
BashExecSilently(cmd) => self.bash_exec_silently(cmd),
BashExecSilently0(cmd) => self.bash_exec_silently0(cmd),
CallLua(func) => self.call_lua(func),
CallLuaSilently(func) => self.call_lua_silently(func),
LuaEval(code) => self.lua_eval(code),
@ -557,14 +558,6 @@ impl App {
}
});
self.input_unescaped = unescape_string(
&self.input
.buffer
.as_ref()
.map(|i| i.value().to_string())
.unwrap_or_default(),
)?;
for msg in msgs {
self = self.enqueue(Task::new(MsgIn::External(msg), Some(key)));
}
@ -1108,12 +1101,24 @@ impl App {
Ok(self)
}
fn call0(mut self, command: Command) -> Result<Self> {
self.logs_hidden = true;
self.msg_out.push_back(MsgOut::Call0(command));
Ok(self)
}
fn call_silently(mut self, command: Command) -> Result<Self> {
self.logs_hidden = true;
self.msg_out.push_back(MsgOut::CallSilently(command));
Ok(self)
}
fn call_silently0(mut self, command: Command) -> Result<Self> {
self.logs_hidden = true;
self.msg_out.push_back(MsgOut::CallSilently0(command));
Ok(self)
}
fn bash_exec(self, script: String) -> Result<Self> {
self.call(Command {
command: "bash".into(),
@ -1121,6 +1126,13 @@ impl App {
})
}
fn bash_exec0(self, script: String) -> Result<Self> {
self.call0(Command {
command: "bash".into(),
args: vec!["-c".into(), script],
})
}
fn bash_exec_silently(self, script: String) -> Result<Self> {
self.call_silently(Command {
command: "bash".into(),
@ -1128,6 +1140,13 @@ impl App {
})
}
fn bash_exec_silently0(self, script: String) -> Result<Self> {
self.call_silently0(Command {
command: "bash".into(),
args: vec!["-c".into(), script],
})
}
fn call_lua(mut self, func: String) -> Result<Self> {
self.logs_hidden = true;
self.msg_out.push_back(MsgOut::CallLua(func));
@ -1560,48 +1579,48 @@ impl App {
}
}
pub fn directory_nodes_str(&self) -> String {
pub fn directory_nodes_str(&self, delimiter: char) -> String {
self.directory_buffer
.as_ref()
.map(|d| {
d.nodes
.iter()
.map(|n| format!("{}\n", n.absolute_path))
.map(|n| format!("{}{}", n.absolute_path, delimiter))
.collect::<Vec<String>>()
.join("")
})
.unwrap_or_default()
}
pub fn pwd_str(&self) -> String {
format!("{}\n", &self.pwd)
pub fn pwd_str(&self, delimiter: char) -> String {
format!("{}{}", &self.pwd, delimiter)
}
pub fn selection_str(&self) -> String {
pub fn selection_str(&self, delimiter: char) -> String {
self.selection
.iter()
.map(|n| format!("{}\n", n.absolute_path))
.map(|n| format!("{}{}", n.absolute_path, delimiter))
.collect::<Vec<String>>()
.join("")
}
pub fn result_str(&self) -> String {
pub fn result_str(&self, delimiter: char) -> String {
self.result()
.into_iter()
.map(|n| format!("{}\n", n.absolute_path))
.map(|n| format!("{}{}", n.absolute_path, delimiter))
.collect::<Vec<String>>()
.join("")
}
pub fn logs_str(&self) -> String {
pub fn logs_str(&self, delimiter: char) -> String {
self.logs
.iter()
.map(|l| format!("{}\n", l))
.map(|l| format!("{}{}", l, delimiter))
.collect::<Vec<String>>()
.join("")
}
pub fn global_help_menu_str(&self) -> String {
pub fn global_help_menu_str(&self, delimiter: char) -> String {
let builtin = self.config.modes.builtin.clone();
let custom = self.config.modes.custom.clone();
let read_only = self.config.general.read_only;
@ -1617,53 +1636,53 @@ impl App {
.help_menu()
.iter()
.map(|l| match l {
HelpMenuLine::Paragraph(p) => format!("\t{}\n", p),
HelpMenuLine::Paragraph(p) => format!("\t{}{}", p, delimiter),
HelpMenuLine::KeyMap(k, remaps, h) => {
let remaps = remaps.join(", ");
format!(" {:15} | {:25} | {}\n", k, remaps, h)
format!(" {:15} | {:25} | {}{}", k, remaps, h , delimiter)
}
})
.collect::<Vec<String>>()
.join("");
format!(
"### {}\n\n key | remaps | action\n --------------- | ------------------------- | ------\n{}\n",
name, help
"### {}{d}{d} key | remaps | action\n --------------- | ------------------------- | ------{d}{}{d}",
name, help, d = delimiter
)
})
.collect::<Vec<String>>()
.join("\n")
.join(&delimiter.to_string())
}
pub fn history_str(&self) -> String {
pub fn history_str(&self, delimiter: char) -> String {
self.history
.paths
.iter()
.map(|p| format!("{}\n", &p))
.map(|p| format!("{}{}", &p, delimiter))
.collect::<Vec<String>>()
.join("")
}
pub fn write_pipes(&self) -> Result<()> {
pub fn write_pipes(&self, delimiter: char) -> Result<()> {
fs::create_dir_all(self.pipe.path.clone())?;
fs::write(&self.pipe.msg_in, "")?;
let selection_str = self.selection_str();
let selection_str = self.selection_str(delimiter);
fs::write(&self.pipe.selection_out, selection_str)?;
let history_str = self.history_str();
let history_str = self.history_str(delimiter);
fs::write(&self.pipe.history_out, history_str)?;
let directory_nodes_str = self.directory_nodes_str();
let directory_nodes_str = self.directory_nodes_str(delimiter);
fs::write(&self.pipe.directory_nodes_out, directory_nodes_str)?;
let logs_str = self.logs_str();
let logs_str = self.logs_str(delimiter);
fs::write(&self.pipe.logs_out, logs_str)?;
let result_str = self.result_str();
let result_str = self.result_str(delimiter);
fs::write(&self.pipe.result_out, result_str)?;
let global_help_menu_str = self.global_help_menu_str();
let global_help_menu_str = self.global_help_menu_str(delimiter);
fs::write(&self.pipe.global_help_menu_out, global_help_menu_str)?;
Ok(())

View File

@ -1045,9 +1045,9 @@ xplr.config.modes.builtin.default = {
help = "global help menu",
messages = {
{
BashExec = [===[
BashExec0 = [===[
[ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | ${PAGER:?}
cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | tr '\0' '\n' | ${PAGER:?}
]===],
},
},
@ -1154,9 +1154,10 @@ xplr.config.modes.builtin.default = {
"PopMode",
{ SwitchModeBuiltin = "rename" },
{
BashExecSilently = [===[
BashExecSilently0 = [===[
NAME=$(basename "${XPLR_FOCUS_PATH:?}")
printf "SetInputBuffer: %s\0" "${NAME:?}" >> "${XPLR_PIPE_MSG_IN:?}"
NAME_ESC=${NAME//\"/\\\"}
printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===],
},
},
@ -1167,9 +1168,10 @@ xplr.config.modes.builtin.default = {
"PopMode",
{ SwitchModeBuiltin = "duplicate_as" },
{
BashExecSilently = [===[
BashExecSilently0 = [===[
NAME=$(basename "${XPLR_FOCUS_PATH:?}")
printf "SetInputBuffer: %s\0" "${NAME:?}" >> "${XPLR_PIPE_MSG_IN:?}"
NAME_ESC=${NAME//\"/\\\"}
printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===],
},
},
@ -1204,8 +1206,9 @@ xplr.config.modes.builtin.default = {
help = "go home",
messages = {
{
BashExecSilently = [===[
printf "ChangeDirectory: %s\0" "${HOME:?}" >> "${XPLR_PIPE_MSG_IN:?}"
BashExecSilently0 = [===[
HOME_ESC=${HOME//\"/\\\"}
printf 'ChangeDirectory: "%s"\0' "${HOME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===],
},
},
@ -1301,8 +1304,8 @@ xplr.config.modes.builtin.debug_error = {
help = "open logs in editor",
messages = {
{
BashExec = [===[
cat "${XPLR_PIPE_LOGS_OUT:?}" | ${EDITOR:-vi} -
BashExec0 = [===[
cat "${XPLR_PIPE_LOGS_OUT:?}" | tr '\0' '\n' | ${EDITOR:-vi} -
]===],
},
},
@ -1362,11 +1365,15 @@ xplr.config.modes.builtin.go_to_path = {
help = "submit",
messages = {
{
BashExecSilently = [===[
if [ -d "$XPLR_INPUT_BUFFER" ]; then
printf "ChangeDirectory: %s\0" "$XPLR_INPUT_BUFFER" >> "${XPLR_PIPE_MSG_IN:?}"
elif [ -e "$XPLR_INPUT_BUFFER" ]; then
printf "FocusPath: %s\0" "$XPLR_INPUT_BUFFER" >> "${XPLR_PIPE_MSG_IN:?}"
BashExecSilently0 = [===[
PTH=${XPLR_INPUT_BUFFER}
PTH_ESC=${XPLR_INPUT_BUFFER//\"/\\\"}
if [ -d "$PTH" ]; then
printf 'ChangeDirectory: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
elif [ -e "$PTH" ]; then
printf 'FocusPath: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "Could not find %s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
fi
]===],
},
@ -1399,16 +1406,17 @@ xplr.config.modes.builtin.selection_ops = {
help = "copy here",
messages = {
{
BashExec = [===[
(while IFS= read -r -d '' line; do
if cp -vr -- "${line:?}" ./; then
printf "LogSuccess: %s\0" "$line copied to $PWD" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "LogError: %s\0" "Failed to copy $line to $PWD" >> "${XPLR_PIPE_MSG_IN:?}"
fi
BashExec0 = [===[
(while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"}
if cp -vr -- "${LINE:?}" ./; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC copied to ." >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "%s"\0' "Failed to copy $LINE_ESC to ." >> "${XPLR_PIPE_MSG_IN:?}"
fi
done < "${XPLR_PIPE_SELECTION_OUT:?}")
printf "ExplorePwdAsync\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf "ClearSelection\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'ExplorePwdAsync\0' >> "${XPLR_PIPE_MSG_IN:?}"
printf 'ClearSelection\0' >> "${XPLR_PIPE_MSG_IN:?}"
read -p "[enter to continue]"
]===],
},
@ -1419,15 +1427,16 @@ xplr.config.modes.builtin.selection_ops = {
help = "move here",
messages = {
{
BashExec = [===[
(while IFS= read -r -d '' line; do
if mv -v -- "${line:?}" ./; then
printf "LogSuccess: %s\0" "$line moved to $PWD" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "LogError: %s\0" "Failed to move $line to $PWD" >> "${XPLR_PIPE_MSG_IN:?}"
fi
BashExec0 = [===[
(while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"}
if mv -v -- "${LINE:?}" ./; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC moved to ." >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "%s"\0' "Failed to move $LINE_ESC to ." >> "${XPLR_PIPE_MSG_IN:?}"
fi
done < "${XPLR_PIPE_SELECTION_OUT:?}")
printf "ExplorePwdAsync\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'ExplorePwdAsync\0' >> "${XPLR_PIPE_MSG_IN:?}"
read -p "[enter to continue]"
]===],
},
@ -1441,30 +1450,6 @@ xplr.config.modes.builtin.selection_ops = {
"PopMode",
},
},
["x"] = {
help = "open in gui",
messages = {
{
BashExecSilently = [===[
if [ -z "$OPENER" ]; then
if command -v xdg-open; then
OPENER=xdg-open
elif command -v open; then
OPENER=open
else
printf "LogError: %s\0" "$OPENER not found" >> "${XPLR_PIPE_MSG_IN:?}"
exit 1
fi
fi
(while IFS= read -r -d '' line; do
$OPENER "${line:?}" > /dev/null 2>&1
done < "${XPLR_PIPE_RESULT_OUT:?}")
]===],
},
"ClearScreen",
"PopMode",
},
},
},
},
}
@ -1514,16 +1499,17 @@ xplr.config.modes.builtin.create_directory = {
help = "submit",
messages = {
{
BashExecSilently = [===[
BashExecSilently0 = [===[
PTH="$XPLR_INPUT_BUFFER"
if [ "${PTH}" ]; then
mkdir -p -- "${PTH:?}" \
&& printf "SetInputBuffer: ''\0" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "ExplorePwd\0" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "LogSuccess: %s\0" "$PTH created" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "FocusPath: %s\0" "$PTH" >> "${XPLR_PIPE_MSG_IN:?}"
PTH_ESC=${PTH//\"/\\\"}
if [ "$PTH" ]; then
mkdir -p -- "$PTH" \
&& printf 'SetInputBuffer: ""\0' >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'ExplorePwd\0' >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'LogSuccess: "%s"\0' "$PTH_ESC created" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'FocusPath: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "PopMode\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'PopMode\0' >> "${XPLR_PIPE_MSG_IN:?}"
fi
]===],
},
@ -1556,8 +1542,9 @@ xplr.config.modes.builtin.create_file = {
help = "submit",
messages = {
{
BashExecSilently = [===[
BashExecSilently0 = [===[
PTH="$XPLR_INPUT_BUFFER"
PTH_ESC=${PTH//\"/\\\"}
if [ "$PTH" ]; then
mkdir -p -- "$(dirname $PTH)" \
&& touch -- "$PTH" \
@ -1566,7 +1553,7 @@ xplr.config.modes.builtin.create_file = {
&& printf 'ExplorePwd\0' >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'FocusPath: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "PopMode\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'PopMode\0' >> "${XPLR_PIPE_MSG_IN:?}"
fi
]===],
},
@ -1657,22 +1644,25 @@ xplr.config.modes.builtin.go_to = {
{ SetInputBuffer = "" },
},
},
["x"] = {
help = "open in gui",
messages = {
{
BashExecSilently = [===[
BashExecSilently0 = [===[
if [ -z "$OPENER" ]; then
if command -v xdg-open; then
OPENER=xdg-open
elif command -v open; then
OPENER=open
else
printf "LogError: %s\0" "$OPENER not found" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'LogError: "$OPENER not found"\0' >> "${XPLR_PIPE_MSG_IN:?}"
exit 1
fi
fi
$OPENER "${XPLR_FOCUS_PATH:?}" > /dev/null 2>&1
(while IFS= read -r -d '' LINE; do
$OPENER "${LINE:?}" > /dev/null 2>&1
done < "${XPLR_PIPE_RESULT_OUT:?}")
]===],
},
"ClearScreen",
@ -1700,16 +1690,18 @@ xplr.config.modes.builtin.rename = {
help = "submit",
messages = {
{
BashExecSilently = [===[
BashExecSilently0 = [===[
SRC="${XPLR_FOCUS_PATH:?}"
SRC_ESC=${SRC//\"/\\\"}
TARGET="${XPLR_INPUT_BUFFER:?}"
TARGET_ESC=${TARGET//\"/\\\"}
if [ -e "${TARGET:?}" ]; then
printf "LogError: %s\0" "$TARGET already exists" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}"
else
mv -- "${SRC:?}" "${TARGET:?}" \
&& printf "ExplorePwd\0" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "FocusPath: %s\0" "$TARGET" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "LogSuccess: %s\0" "$SRC renamed to $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
&& printf 'ExplorePwd\0' >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'FocusPath: "%s"\0' "$TARGET_ESC" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'LogSuccess: "%s"\0' "$SRC_ESC renamed to $TARGET_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
fi
]===],
},
@ -1742,16 +1734,18 @@ xplr.config.modes.builtin.duplicate_as = {
help = "submit",
messages = {
{
BashExecSilently = [===[
BashExecSilently0 = [===[
SRC="${XPLR_FOCUS_PATH:?}"
SRC_ESC=${SRC//\"/\\\"}
TARGET="${XPLR_INPUT_BUFFER:?}"
TARGET_ESC=${TARGET//\"/\\\"}
if [ -e "${TARGET:?}" ]; then
printf "LogError: %s\0" "$TARGET already exists" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}"
else
cp -r -- "${SRC:?}" "${TARGET:?}" \
&& printf "ExplorePwd\0" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "FocusPath: %s\0" "$TARGET" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf "LogSuccess: %s\0" "$SRC duplicated as $TARGET" >> "${XPLR_PIPE_MSG_IN:?}"
&& printf 'ExplorePwd\0' >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'FocusPath: "%s"\0' "$TARGET_ESC" >> "${XPLR_PIPE_MSG_IN:?}" \
&& printf 'LogSuccess: "%s"\0' "$SRC_ESC duplicated as $TARGET_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
fi
]===],
},
@ -1778,15 +1772,16 @@ xplr.config.modes.builtin.delete = {
help = "force delete",
messages = {
{
BashExec = [===[
(while IFS= read -r -d '' line; do
if rm -rfv -- "${line:?}"; then
printf "LogSuccess: %s\0" "$line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "LogError: %s\0" "Failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"
fi
BashExec0 = [===[
(while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"}
if rm -rfv -- "${LINE:?}"; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "%s"\0' "Failed to delete $LINE_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
fi
done < "${XPLR_PIPE_RESULT_OUT:?}")
printf "ExplorePwdAsync\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'ExplorePwdAsync\0' >> "${XPLR_PIPE_MSG_IN:?}"
read -p "[enter to continue]"
]===],
},
@ -1797,23 +1792,24 @@ xplr.config.modes.builtin.delete = {
help = "delete",
messages = {
{
BashExec = [===[
(while IFS= read -r -d '' line; do
if [ -d "$line" ] && [ ! -L "$line" ]; then
if rmdir -v -- "${line:?}"; then
printf "LogSuccess: %s\0" "$line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
BashExec0 = [===[
(while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"}
if [ -d "$LINE" ] && [ ! -L "$LINE" ]; then
if rmdir -v -- "${LINE:?}"; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "%s"\0' "Failed to delete $LINE_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
fi
else
printf "LogError: %s\0" "Failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"
if rm -v -- "${LINE:?}"; then
printf 'LogSuccess: "%s"\0' "$line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf 'LogError: "%s"\0' "Failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"
fi
fi
else
if rm -v -- "${line:?}"; then
printf "LogSuccess: %s\0" "$line deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else
printf "LogError: %s\0" "Failed to delete $line" >> "${XPLR_PIPE_MSG_IN:?}"
fi
fi
done < "${XPLR_PIPE_RESULT_OUT:?}")
printf "ExplorePwdAsync\0" >> "${XPLR_PIPE_MSG_IN:?}"
printf 'ExplorePwdAsync\0' >> "${XPLR_PIPE_MSG_IN:?}"
read -p "[enter to continue]"
]===],
},
@ -1834,7 +1830,7 @@ xplr.config.modes.builtin.action = {
["!"] = {
help = "shell",
messages = {
{ Call = { command = "bash", args = { "-i" } } },
{ Call0 = { command = "bash", args = { "-i" } } },
"ExplorePwdAsync",
"PopMode",
},
@ -1850,7 +1846,7 @@ xplr.config.modes.builtin.action = {
help = "open in editor",
messages = {
{
BashExec = [===[
BashExec0 = [===[
${EDITOR:-vi} "${XPLR_FOCUS_PATH:?}"
]===],
},
@ -1861,9 +1857,9 @@ xplr.config.modes.builtin.action = {
help = "logs",
messages = {
{
BashExec = [===[
BashExec0 = [===[
[ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_LOGS_OUT}" | ${PAGER:?}
cat -- "${XPLR_PIPE_LOGS_OUT}" | tr '\0' '\n' | ${PAGER:?}
]===],
},
"PopMode",
@ -2570,3 +2566,4 @@ end
-- You can also use nested tables such as
-- `xplr.fn.custom.my_plugin.my_function` to define custom functions.
xplr.fn.custom = {}

View File

@ -3,7 +3,6 @@ use indexmap::IndexSet;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use crate::newlines::escape_string;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum ExternalMsg {
@ -483,52 +482,72 @@ pub enum ExternalMsg {
/// ### Executing Commands ------------------------------------------------
/// Like `Call0`, but it uses `\n` as the delimiter in input/output pipes,
/// hence it cannot handle files with `\n` in the name.
/// You may want to use `Call0` instead.
Call(Command),
/// Call a shell command with the given arguments.
/// Note that the arguments will be shell-escaped.
/// So to read the variables, the `-c` option of the shell
/// can be used.
/// You may need to pass `ExplorePwd` depening on the expectation.
/// You may need to pass `ExplorePwd` depending on the expectation.
///
/// Type: { Call = { command = string, args = { "list", "of", "string" } }
/// Type: { Call0 = { command = "string", args = { "list", "of", "string" } }
///
/// Example:
///
/// - Lua: `{ Call = { command = "bash", args = { "-c", "read -p test" } } }`
/// - YAML: `Call: { command: bash, args: ["-c", "read -p test"] }`
Call(Command),
/// - Lua: `{ Call0 = { command = "bash", args = { "-c", "read -p test" } } }`
/// - YAML: `Call0: { command: bash, args: ["-c", "read -p test"] }`
Call0(Command),
/// Like `Call` but without the flicker. The stdin, stdout
/// Like `CallSilently0`, but it uses `\n` as the delimiter in input/output
/// pipes, hence it cannot handle files with `\n` in the name.
/// You may want to use `Call0Silently` instead.
CallSilently(Command),
/// Like `Call0` but without the flicker. The stdin, stdout
/// stderr will be piped to null. So it's non-interactive.
///
/// Type: { CallSilently = "string" }
/// Type: { CallSilently0 = { command = "string", args = {"list", "of", "string"} } }
///
/// Example:
///
/// - Lua: `{ CallSilently = { command = "tput", args = { "bell" } } }`
/// - YAML: `CallSilently: { command: tput, args: ["bell"] }`
CallSilently(Command),
/// - Lua: `{ CallSilently0 = { command = "tput", args = { "bell" } } }`
/// - YAML: `CallSilently0: { command: tput, args: ["bell"] }`
CallSilently0(Command),
/// Like `BashExec0`, but it uses `\n` as the delimiter in input/output
/// pipes, hence it cannot handle files with `\n` in the name.
/// You may want to use `BashExec0` instead.
BashExec(String),
/// An alias to `Call: {command: bash, args: ["-c", "{string}"], silent: false}`
/// where `{string}` is the given value.
///
/// Type: { BashExec = "string" }
/// Type: { BashExec0 = "string" }
///
/// Example:
///
/// - Lua: `{ BashExec = "read -p test" }`
/// - YAML: `BashExec: "read -p test"`
BashExec(String),
/// - Lua: `{ BashExec0 = "read -p test" }`
/// - YAML: `BashExec0: "read -p test"`
BashExec0(String),
/// Like `BashExec` but without the flicker. The stdin, stdout
/// Like `BashExecSilently0`, but it uses `\n` as the delimiter in
/// input/output pipes, hence it cannot handle files with `\n` in the name.
/// You may want to use `BashExec0Silently` instead.
BashExecSilently(String),
/// Like `BashExec0` but without the flicker. The stdin, stdout
/// stderr will be piped to null. So it's non-interactive.
///
/// Type: { BashExecSilently = "string" }
/// Type: { BashExec0Silently = "string" }
///
/// Example:
///
/// - Lua: `{ BashExecSilently = "tput bell" }`
/// - YAML: `BashExecSilently: "tput bell"`
BashExecSilently(String),
/// - Lua: `{ BashExecSilently0 = "tput bell" }`
/// - YAML: `BashExecSilently0: "tput bell"`
BashExecSilently0(String),
/// ### Calling Lua Functions ----------------------------------------------
@ -1071,7 +1090,7 @@ impl TryFrom<&str> for ExternalMsg {
if value.starts_with('!') {
serde_yaml::from_str(value)
} else if let Some((msg, args)) = value.split_once(' ') {
let msg = format!("!{} {}", msg.trim_end_matches(':'), escape_string(args));
let msg = format!("!{} {}", msg.trim_end_matches(':'), args);
serde_yaml::from_str(&msg)
} else {
serde_yaml::from_str(value)
@ -1086,11 +1105,11 @@ impl ExternalMsg {
Self::Call(_)
| Self::Call0(_)
| Self::CallSilently(_)
| Self::Call0Silently(_)
| Self::CallSilently0(_)
| Self::BashExec(_)
| Self::BashExec0(_)
| Self::BashExecSilently(_)
| Self::BashExec0Silently(_)
| Self::BashExecSilently0(_)
| Self::CallLua(_)
| Self::CallLuaSilently(_)
| Self::LuaEval(_)

View File

@ -10,7 +10,9 @@ pub enum MsgOut {
ClearScreen,
Debug(String),
Call(Command),
Call0(Command),
CallSilently(Command),
CallSilently0(Command),
CallLua(String),
CallLuaSilently(String),
LuaEval(String),

View File

@ -16,12 +16,14 @@ use crossterm::terminal as term;
use mlua::LuaSerdeExt;
use mlua::Value;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, ExitStatus, Stdio};
use std::process::{Command, Stdio};
use std::sync::mpsc;
use tui::backend::CrosstermBackend;
use tui::Terminal;
use tui_input::Input;
pub fn get_tty() -> Result<fs::File> {
let tty = "/dev/tty";
@ -60,7 +62,30 @@ fn call_lua_heavy(
lua::call(lua, func, arg)
}
fn call(app: &app::App, cmd: app::Command, silent: bool) -> Result<ExitStatus> {
fn call(
mut app: app::App,
cmd: app::Command,
silent: bool,
terminal: &mut Terminal<CrosstermBackend<File>>,
event_reader: &mut EventReader,
mouse_enabled: &mut bool,
delimiter: char,
) -> Result<app::App> {
if !silent {
if *mouse_enabled {
execute!(terminal.backend_mut(), event::DisableMouseCapture)
.unwrap_or_default();
}
event_reader.stop();
terminal.clear()?;
terminal.set_cursor(0, 0)?;
term::disable_raw_mode()?;
terminal.show_cursor()?;
}
app.write_pipes(delimiter)?;
let focus_index = app
.directory_buffer
.as_ref()
@ -74,10 +99,17 @@ fn call(app: &app::App, cmd: app::Command, silent: bool) -> Result<ExitStatus> {
(get_tty()?.into(), get_tty()?.into(), get_tty()?.into())
};
Command::new(cmd.command.clone())
let input_buffer = app
.input
.buffer
.as_ref()
.map(Input::to_string)
.unwrap_or_default();
let status = Command::new(cmd.command.clone())
.env("XPLR_APP_VERSION", app.version.clone())
.env("XPLR_PID", &app.pid.to_string())
.env("XPLR_INPUT_BUFFER", &app.input_unescaped)
.env("XPLR_INPUT_BUFFER", input_buffer)
.env("XPLR_FOCUS_PATH", app.focused_node_str())
.env("XPLR_FOCUS_INDEX", focus_index)
.env("XPLR_SESSION_PATH", &app.session_path)
@ -100,7 +132,49 @@ fn call(app: &app::App, cmd: app::Command, silent: bool) -> Result<ExitStatus> {
.stderr(stderr)
.args(cmd.args)
.status()
.map_err(Error::new)
.map(|s| {
if s.success() {
Ok(())
} else {
Err(format!("process exited with code {}", &s))
}
})
.unwrap_or_else(|e| Err(e.to_string()));
match pipe::read_all(&app.pipe.msg_in, delimiter) {
Ok(msgs) => {
app = app.handle_batch_external_msgs(msgs)?;
}
Err(err) => {
app = app.log_error(err.to_string())?;
}
};
app.cleanup_pipes()?;
if let Err(e) = status {
app = app.log_error(e)?;
};
if !silent {
terminal.clear()?;
term::enable_raw_mode()?;
terminal.hide_cursor()?;
event_reader.start();
if *mouse_enabled {
match execute!(terminal.backend_mut(), event::EnableMouseCapture) {
Ok(_) => {
*mouse_enabled = true;
}
Err(e) => {
app = app.log_error(e.to_string())?;
}
}
}
}
Ok(app)
}
fn start_fifo(path: &str, focus_path: &str) -> Result<fs::File> {
@ -122,6 +196,7 @@ pub struct Runner {
read_only: bool,
print_pwd_as_result: bool,
selection: Vec<PathBuf>,
delimiter: char,
}
impl Runner {
@ -158,6 +233,7 @@ impl Runner {
read_only: cli.read_only,
print_pwd_as_result: cli.print_pwd_as_result,
selection: paths.collect(),
delimiter: if cli.write0 { '\0' } else { '\n' },
})
}
@ -275,27 +351,27 @@ impl Runner {
}
PrintPwdAndQuit => {
result = Ok(Some(format!("{}\n", app.pwd)));
result = Ok(Some(app.pwd_str(self.delimiter)));
break 'outer;
}
PrintFocusPathAndQuit => {
result = Ok(app
.focused_node()
.map(|n| format!("{}\n", n.absolute_path)));
result = Ok(app.focused_node().map(|n| {
format!("{}{}", n.absolute_path, self.delimiter)
}));
break 'outer;
}
PrintSelectionAndQuit => {
result = Ok(Some(app.selection_str()));
result = Ok(Some(app.selection_str(self.delimiter)));
break 'outer;
}
PrintResultAndQuit => {
result = if self.print_pwd_as_result {
Ok(Some(app.pwd_str()))
Ok(Some(app.pwd_str(self.delimiter)))
} else {
Ok(Some(app.result_str()))
Ok(Some(app.result_str(self.delimiter)))
};
break 'outer;
@ -490,37 +566,6 @@ impl Runner {
};
}
CallSilently(cmd) => {
app.write_pipes()?;
let status = call(&app, cmd, true)
.map(|s| {
if s.success() {
Ok(())
} else {
Err(format!(
"process exited with code {}",
&s
))
}
})
.unwrap_or_else(|e| Err(e.to_string()));
match pipe::read_all(&app.pipe.msg_in) {
Ok(msgs) => {
app = app.handle_batch_external_msgs(msgs)?;
}
Err(err) => {
app = app.log_error(err.to_string())?;
}
};
app.cleanup_pipes()?;
if let Err(e) = status {
app = app.log_error(e.to_string())?;
};
}
CallLua(func) => {
execute!(
terminal.backend_mut(),
@ -684,67 +729,51 @@ impl Runner {
}
Call(cmd) => {
execute!(
terminal.backend_mut(),
event::DisableMouseCapture
)
.unwrap_or_default();
app = call(
app,
cmd,
false,
&mut terminal,
&mut event_reader,
&mut mouse_enabled,
'\n',
)?;
}
event_reader.stop();
Call0(cmd) => {
app = call(
app,
cmd,
false,
&mut terminal,
&mut event_reader,
&mut mouse_enabled,
'\0',
)?;
}
terminal.clear()?;
terminal.set_cursor(0, 0)?;
term::disable_raw_mode()?;
terminal.show_cursor()?;
CallSilently(cmd) => {
app = call(
app,
cmd,
true,
&mut terminal,
&mut event_reader,
&mut mouse_enabled,
'\n',
)?;
}
app.write_pipes()?;
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()));
// TODO remove duplicate segment
match pipe::read_all(&app.pipe.msg_in) {
Ok(msgs) => {
app = app.handle_batch_external_msgs(msgs)?;
}
Err(err) => {
app = app.log_error(err.to_string())?;
}
};
app.cleanup_pipes()?;
if let Err(e) = status {
app = app.log_error(e.to_string())?;
};
terminal.clear()?;
term::enable_raw_mode()?;
terminal.hide_cursor()?;
event_reader.start();
if mouse_enabled {
match execute!(
terminal.backend_mut(),
event::EnableMouseCapture
) {
Ok(_) => {
mouse_enabled = true;
}
Err(e) => {
app = app.log_error(e.to_string())?;
}
}
}
CallSilently0(cmd) => {
app = call(
app,
cmd,
true,
&mut terminal,
&mut event_reader,
&mut mouse_enabled,
'\0',
)?;
}
};
}