Escape newline in selection list

pull/522/head
Arijit Basu 2 years ago committed by Arijit Basu
parent fbe6b2be10
commit bffe1d43ec

@ -1,7 +1,7 @@
# Environment Variables and Pipes # Environment Variables and Pipes
Alternative to `CallLua`, `CallLuaSilently` messages that call Lua functions, Alternative to `CallLua`, `CallLuaSilently` messages that call Lua functions,
there are `Call`, `CallSilently`, `BashExec`, `BashExecSilently` messages there are `Call0`, `CallSilently0`, `BashExec0`, `BashExecSilently0` messages
that call shell commands. that call shell commands.
However, unlike the Lua functions, these shell commands have to read the useful However, unlike the Lua functions, these shell commands have to read the useful
@ -16,12 +16,13 @@ To see the environment variables and pipes, invoke the shell by typing `:!` in d
mode and run the following command: mode and run the following command:
``` ```
env | grep ^XPLR_ env | grep ^XPLR
``` ```
You will see something like: You will see something like:
``` ```
XPLR=xplr
XPLR_FOCUS_INDEX=0 XPLR_FOCUS_INDEX=0
XPLR_MODE=action to XPLR_MODE=action to
XPLR_PIPE_SELECTION_OUT=/run/user/1000/xplr/session/122278/pipe/selection_out XPLR_PIPE_SELECTION_OUT=/run/user/1000/xplr/session/122278/pipe/selection_out
@ -43,6 +44,7 @@ called ["pipe"s][18].
The other variables are single-line variables containing simple information: The other variables are single-line variables containing simple information:
- [XPLR][38]
- [XPLR_APP_VERSION][30] - [XPLR_APP_VERSION][30]
- [XPLR_FOCUS_INDEX][31] - [XPLR_FOCUS_INDEX][31]
- [XPLR_FOCUS_PATH][32] - [XPLR_FOCUS_PATH][32]
@ -53,13 +55,18 @@ The other variables are single-line variables containing simple information:
### Environment variables ### Environment variables
#### XPLR
The binary path of xplr command.
#### XPLR_APP_VERSION #### XPLR_APP_VERSION
Self-explanatory. Self-explanatory.
#### XPLR_FOCUS_INDEX #### XPLR_FOCUS_INDEX
Contains the index of the currently focused item, as seen in [column-renderer/index][10]. Contains the index of the currently focused item, as seen in
[column-renderer/index][10].
#### XPLR_FOCUS_PATH #### XPLR_FOCUS_PATH
@ -67,7 +74,8 @@ Contains the full path of the currently focused node.
#### XPLR_INPUT_BUFFER #### XPLR_INPUT_BUFFER
The line currently in displaying in the xplr input buffer. For e.g. the search input while searching. See [Reading Input][37]. The line currently in displaying in the xplr input buffer. For e.g. the search
input while searching. See [Reading Input][37].
#### XPLR_MODE #### XPLR_MODE
@ -79,7 +87,8 @@ Contains the process ID of the current xplr process.
#### XPLR_SESSION_PATH #### XPLR_SESSION_PATH
Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/, you can find temporary files here, such as pipes. Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/,
you can find temporary files here, such as pipes.
### Pipes ### Pipes
@ -93,7 +102,10 @@ Currently there is only one input pipe.
`XPLR_PIPE_*_OUT` are the output pipes that contain data which cannot be `XPLR_PIPE_*_OUT` are the output pipes that contain data which cannot be
exposed directly via environment variables, like multi-line strings. exposed directly via environment variables, like multi-line strings.
These pipes can be accessed as plaintext files located in $XPLR_SESSION_PATH. These pipes can be accessed as plain text files located in $XPLR_SESSION_PATH.
Depending on the message (e.g. `Call` or `Call0`), each line will be separated
by newline or null character (`\n` or `\0`).
- [XPLR_PIPE_SELECTION_OUT][21] - [XPLR_PIPE_SELECTION_OUT][21]
- [XPLR_PIPE_GLOBAL_HELP_MENU_OUT][22] - [XPLR_PIPE_GLOBAL_HELP_MENU_OUT][22]
@ -104,13 +116,21 @@ These pipes can be accessed as plaintext files located in $XPLR_SESSION_PATH.
#### XPLR_PIPE_MSG_IN #### XPLR_PIPE_MSG_IN
Append new-line delimited messages to this pipe in [YAML][27] Append new messages to this pipe in [YAML][27] (or [JSON][7]) syntax. These
(or [JSON][7]) syntax. These messages will be read and messages will be read and handled by xplr after the command execution.
handled by xplr after the command execution.
Depending on the message (e.g. `Call` or `Call0`) you need to separate each
message using newline or null character (`\n` or `\0`).
> **_NOTE:_** Since version `v0.20.0`, it's recommended to avoid writing
> directly to this file, as safely escaping YAML strings is a lot of work. Use
> `xplr -m` / `xplr --pipe-msg-in` to pass messages to xplr in a safer way.
>
> Example: `"$XPLR" -m 'ChangeDirectory: %q' "${HOME:?}"`
#### XPLR_PIPE_SELECTION_OUT #### XPLR_PIPE_SELECTION_OUT
New-line delimited list of selected paths. List of selected paths.
#### XPLR_PIPE_GLOBAL_HELP_MENU_OUT #### XPLR_PIPE_GLOBAL_HELP_MENU_OUT
@ -118,20 +138,19 @@ The full help menu.
#### XPLR_PIPE_LOGS_OUT #### XPLR_PIPE_LOGS_OUT
New-line delimited list of logs. List of logs.
#### XPLR_PIPE_RESULT_OUT #### XPLR_PIPE_RESULT_OUT
New-line delimited result (selected paths if any, else the focused path) Result (selected paths if any, else the focused path)
#### XPLR_PIPE_HISTORY_OUT #### XPLR_PIPE_HISTORY_OUT
New-line delimited list of last visited paths (similar to jump list in vim). List of last visited paths (similar to jump list in vim).
#### XPLR_PIPE_DIRECTORY_NODES_OUT #### XPLR_PIPE_DIRECTORY_NODES_OUT
New-line delimited list of paths, filtered and sorted as displayed in the List of paths, filtered and sorted as displayed in the [files table][28].
[files table][28].
### Example: Using Environment Variables and Pipes ### Example: Using Environment Variables and Pipes
@ -140,14 +159,14 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = {
help = "ask name and greet", help = "ask name and greet",
messages = { messages = {
{ {
BashExec = [===[ BashExec0 = [===[
echo "What's your name?" echo "What's your name?"
read name read name
greeting="Hello $name!" greeting="Hello $name!"
message="$greeting You are inside $PWD" message="$greeting You are inside $PWD"
echo LogSuccess: '"'$message'"' >> "${XPLR_PIPE_MSG_IN:?}" "$XPLR" -m 'LogSuccess: %q' "$message"
]===] ]===]
} }
} }
@ -164,8 +183,8 @@ xplr.config.modes.builtin.default.key_bindings.on_key.X = {
help = "open", help = "open",
messages = { messages = {
{ {
BashExecSilently = [===[ BashExecSilently0 = [===[
xdg-open "${XPLR_FOCUS_PATH:?}" xdg-open "${XPLR_FOCUS_PATH:?}"
]===], ]===],
}, },
}, },
@ -194,3 +213,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.X = {
[35]: #xplr_pid [35]: #xplr_pid
[36]: #xplr_session_path [36]: #xplr_session_path
[37]: messages.md#reading-input [37]: messages.md#reading-input
[38]: #xplr

@ -570,7 +570,7 @@ Example:
Like `CallSilently0`, but it uses `\n` as the delimiter in input/output Like `CallSilently0`, but it uses `\n` as the delimiter in input/output
pipes, hence it cannot handle files with `\n` in the name. pipes, hence it cannot handle files with `\n` in the name.
You may want to use `Call0Silently` instead. You may want to use `CallSilently0` instead.
#### CallSilently0 #### CallSilently0
@ -606,14 +606,14 @@ Example:
Like `BashExecSilently0`, but it uses `\n` as the delimiter in Like `BashExecSilently0`, but it uses `\n` as the delimiter in
input/output pipes, hence it cannot handle files with `\n` in the name. input/output pipes, hence it cannot handle files with `\n` in the name.
You may want to use `BashExec0Silently` instead. You may want to use `BashExecSilently0` instead.
#### BashExecSilently0 #### BashExecSilently0
Like `BashExec0` but without the flicker. The stdin, stdout Like `BashExec0` but without the flicker. The stdin, stdout
stderr will be piped to null. So it's non-interactive. stderr will be piped to null. So it's non-interactive.
Type: { BashExec0Silently = "string" } Type: { BashExecSilently0 = "string" }
Example: Example:

@ -58,7 +58,8 @@ Finally, after publishing, don't hesitate to
`BashExec` and so on. File names may contain newline characters `BashExec` and so on. File names may contain newline characters
(e.g. `foo$'\n'bar`). (e.g. `foo$'\n'bar`).
- File names may also contain quotes. Use the syntax - File names may also contain quotes. Use the syntax
`printf 'Call0: "%s"' "${FOO_ESC}"` where `FOO_ESC=${FOO//\"/\\\"}`. `printf 'Call0: "%s"' "${FOO_ESC}"` where `FOO_ESC=${FOO//\"/\\\"}` and
`FOO_ESC=${FOO_ESC//$'\n'/\\n}`.
- Check for empty variables using the syntax `${FOO:?}` or use a default value - Check for empty variables using the syntax `${FOO:?}` or use a default value
`${FOO:-defaultvalue}`. `${FOO:-defaultvalue}`.

@ -1045,9 +1045,9 @@ xplr.config.modes.builtin.default = {
help = "global help menu", help = "global help menu",
messages = { messages = {
{ {
BashExec0 = [===[ BashExec = [===[
[ -z "$PAGER" ] && PAGER="less -+F" [ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | tr '\0' '\n' | ${PAGER:?} cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | ${PAGER:?}
]===], ]===],
}, },
}, },
@ -1157,6 +1157,7 @@ xplr.config.modes.builtin.default = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
NAME=$(basename "${XPLR_FOCUS_PATH:?}") NAME=$(basename "${XPLR_FOCUS_PATH:?}")
NAME_ESC=${NAME//\"/\\\"} NAME_ESC=${NAME//\"/\\\"}
NAME_ESC=${NAME_ESC//$'\n'/\\n}
printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}" printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===], ]===],
}, },
@ -1171,6 +1172,7 @@ xplr.config.modes.builtin.default = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
NAME=$(basename "${XPLR_FOCUS_PATH:?}") NAME=$(basename "${XPLR_FOCUS_PATH:?}")
NAME_ESC=${NAME//\"/\\\"} NAME_ESC=${NAME//\"/\\\"}
NAME_ESC=${NAME_ESC//$'\n'/\\n}
printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}" printf 'SetInputBuffer: "%s"\0' "${NAME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===], ]===],
}, },
@ -1208,6 +1210,7 @@ xplr.config.modes.builtin.default = {
{ {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
HOME_ESC=${HOME//\"/\\\"} HOME_ESC=${HOME//\"/\\\"}
HOME_ESC=${HOME_ESC//$'\n'/\\n}
printf 'ChangeDirectory: "%s"\0' "${HOME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}" printf 'ChangeDirectory: "%s"\0' "${HOME_ESC:?}" >> "${XPLR_PIPE_MSG_IN:?}"
]===], ]===],
}, },
@ -1304,8 +1307,8 @@ xplr.config.modes.builtin.debug_error = {
help = "open logs in editor", help = "open logs in editor",
messages = { messages = {
{ {
BashExec0 = [===[ BashExec = [===[
cat "${XPLR_PIPE_LOGS_OUT:?}" | tr '\0' '\n' | ${EDITOR:-vi} - cat "${XPLR_PIPE_LOGS_OUT:?}" | ${EDITOR:-vi} -
]===], ]===],
}, },
}, },
@ -1367,7 +1370,8 @@ xplr.config.modes.builtin.go_to_path = {
{ {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
PTH=${XPLR_INPUT_BUFFER} PTH=${XPLR_INPUT_BUFFER}
PTH_ESC=${XPLR_INPUT_BUFFER//\"/\\\"} PTH_ESC=${PTH//\"/\\\"}
PTH_ESC=${PTH_ESC//$'\n'/\\n}
if [ -d "$PTH" ]; then if [ -d "$PTH" ]; then
printf 'ChangeDirectory: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}" printf 'ChangeDirectory: "%s"\0' "$PTH_ESC" >> "${XPLR_PIPE_MSG_IN:?}"
elif [ -e "$PTH" ]; then elif [ -e "$PTH" ]; then
@ -1409,6 +1413,7 @@ xplr.config.modes.builtin.selection_ops = {
BashExec0 = [===[ BashExec0 = [===[
(while IFS= read -r -d '' LINE; do (while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"} LINE_ESC=${LINE//\"/\\\"}
LINE_ESC=${LINE_ESC//$'\n'/\\n}
if cp -vr -- "${LINE:?}" ./; then if cp -vr -- "${LINE:?}" ./; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC copied to ." >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogSuccess: "%s"\0' "$LINE_ESC copied to ." >> "${XPLR_PIPE_MSG_IN:?}"
else else
@ -1430,6 +1435,7 @@ xplr.config.modes.builtin.selection_ops = {
BashExec0 = [===[ BashExec0 = [===[
(while IFS= read -r -d '' LINE; do (while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"} LINE_ESC=${LINE//\"/\\\"}
LINE_ESC=${LINE_ESC//$'\n'/\\n}
if mv -v -- "${LINE:?}" ./; then if mv -v -- "${LINE:?}" ./; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC moved to ." >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogSuccess: "%s"\0' "$LINE_ESC moved to ." >> "${XPLR_PIPE_MSG_IN:?}"
else else
@ -1502,6 +1508,7 @@ xplr.config.modes.builtin.create_directory = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
PTH="$XPLR_INPUT_BUFFER" PTH="$XPLR_INPUT_BUFFER"
PTH_ESC=${PTH//\"/\\\"} PTH_ESC=${PTH//\"/\\\"}
PTH_ESC=${PTH_ESC//$'\n'/\\n}
if [ "$PTH" ]; then if [ "$PTH" ]; then
mkdir -p -- "$PTH" \ mkdir -p -- "$PTH" \
&& printf 'SetInputBuffer: ""\0' >> "${XPLR_PIPE_MSG_IN:?}" \ && printf 'SetInputBuffer: ""\0' >> "${XPLR_PIPE_MSG_IN:?}" \
@ -1545,6 +1552,7 @@ xplr.config.modes.builtin.create_file = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
PTH="$XPLR_INPUT_BUFFER" PTH="$XPLR_INPUT_BUFFER"
PTH_ESC=${PTH//\"/\\\"} PTH_ESC=${PTH//\"/\\\"}
PTH_ESC=${PTH_ESC//$'\n'/\\n}
if [ "$PTH" ]; then if [ "$PTH" ]; then
mkdir -p -- "$(dirname $PTH)" \ mkdir -p -- "$(dirname $PTH)" \
&& touch -- "$PTH" \ && touch -- "$PTH" \
@ -1693,8 +1701,10 @@ xplr.config.modes.builtin.rename = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
SRC="${XPLR_FOCUS_PATH:?}" SRC="${XPLR_FOCUS_PATH:?}"
SRC_ESC=${SRC//\"/\\\"} SRC_ESC=${SRC//\"/\\\"}
SRC_ESC=${SRC_ESC//$'\n'/\\n}
TARGET="${XPLR_INPUT_BUFFER:?}" TARGET="${XPLR_INPUT_BUFFER:?}"
TARGET_ESC=${TARGET//\"/\\\"} TARGET_ESC=${TARGET//\"/\\\"}
TARGET_ESC=${TARGET_ESC//$'\n'/\\n}
if [ -e "${TARGET:?}" ]; then if [ -e "${TARGET:?}" ]; then
printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}"
else else
@ -1737,8 +1747,10 @@ xplr.config.modes.builtin.duplicate_as = {
BashExecSilently0 = [===[ BashExecSilently0 = [===[
SRC="${XPLR_FOCUS_PATH:?}" SRC="${XPLR_FOCUS_PATH:?}"
SRC_ESC=${SRC//\"/\\\"} SRC_ESC=${SRC//\"/\\\"}
SRC_ESC=${SRC_ESC//$'\n'/\\n}
TARGET="${XPLR_INPUT_BUFFER:?}" TARGET="${XPLR_INPUT_BUFFER:?}"
TARGET_ESC=${TARGET//\"/\\\"} TARGET_ESC=${TARGET//\"/\\\"}
TARGET_ESC=${TARGET_ESC//$'\n'/\\n}
if [ -e "${TARGET:?}" ]; then if [ -e "${TARGET:?}" ]; then
printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogError: "%s"\0' "$TARGET_ESC already exists" >> "${XPLR_PIPE_MSG_IN:?}"
else else
@ -1775,6 +1787,7 @@ xplr.config.modes.builtin.delete = {
BashExec0 = [===[ BashExec0 = [===[
(while IFS= read -r -d '' LINE; do (while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"} LINE_ESC=${LINE//\"/\\\"}
LINE_ESC=${LINE_ESC//$'\n'/\\n}
if rm -rfv -- "${LINE:?}"; then if rm -rfv -- "${LINE:?}"; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}"
else else
@ -1795,6 +1808,7 @@ xplr.config.modes.builtin.delete = {
BashExec0 = [===[ BashExec0 = [===[
(while IFS= read -r -d '' LINE; do (while IFS= read -r -d '' LINE; do
LINE_ESC=${LINE//\"/\\\"} LINE_ESC=${LINE//\"/\\\"}
LINE_ESC=${LINE_ESC//$'\n'/\\n}
if [ -d "$LINE" ] && [ ! -L "$LINE" ]; then if [ -d "$LINE" ] && [ ! -L "$LINE" ]; then
if rmdir -v -- "${LINE:?}"; then if rmdir -v -- "${LINE:?}"; then
printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}" printf 'LogSuccess: "%s"\0' "$LINE_ESC deleted" >> "${XPLR_PIPE_MSG_IN:?}"
@ -1832,7 +1846,7 @@ xplr.config.modes.builtin.action = {
messages = { messages = {
{ Call0 = { command = "bash", args = { "-i" } } }, { Call0 = { command = "bash", args = { "-i" } } },
"ExplorePwdAsync", "ExplorePwdAsync",
"PopMode", "PopModeKeepingInputBuffer",
}, },
}, },
["c"] = { ["c"] = {
@ -1857,9 +1871,9 @@ xplr.config.modes.builtin.action = {
help = "logs", help = "logs",
messages = { messages = {
{ {
BashExec0 = [===[ BashExec = [===[
[ -z "$PAGER" ] && PAGER="less -+F" [ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_LOGS_OUT}" | tr '\0' '\n' | ${PAGER:?} cat -- "${XPLR_PIPE_LOGS_OUT}" | ${PAGER:?}
]===], ]===],
}, },
"PopMode", "PopMode",

@ -503,7 +503,7 @@ pub enum ExternalMsg {
/// Like `CallSilently0`, but it uses `\n` as the delimiter in input/output /// Like `CallSilently0`, but it uses `\n` as the delimiter in input/output
/// pipes, hence it cannot handle files with `\n` in the name. /// pipes, hence it cannot handle files with `\n` in the name.
/// You may want to use `Call0Silently` instead. /// You may want to use `CallSilently0` instead.
CallSilently(Command), CallSilently(Command),
/// Like `Call0` but without the flicker. The stdin, stdout /// Like `Call0` but without the flicker. The stdin, stdout
@ -535,13 +535,13 @@ pub enum ExternalMsg {
/// Like `BashExecSilently0`, but it uses `\n` as the delimiter in /// Like `BashExecSilently0`, but it uses `\n` as the delimiter in
/// input/output pipes, hence it cannot handle files with `\n` in the name. /// input/output pipes, hence it cannot handle files with `\n` in the name.
/// You may want to use `BashExec0Silently` instead. /// You may want to use `BashExecSilently0` instead.
BashExecSilently(String), BashExecSilently(String),
/// Like `BashExec0` but without the flicker. The stdin, stdout /// Like `BashExec0` but without the flicker. The stdin, stdout
/// stderr will be piped to null. So it's non-interactive. /// stderr will be piped to null. So it's non-interactive.
/// ///
/// Type: { BashExec0Silently = "string" } /// Type: { BashExecSilently0 = "string" }
/// ///
/// Example: /// Example:
/// ///

@ -727,7 +727,7 @@ fn draw_selection<B: Backend>(
.rev() .rev()
.take((layout_size.height.max(2) - 2).into()) .take((layout_size.height.max(2) - 2).into())
.rev() .rev()
.map(|n| n.absolute_path.to_owned()) .map(|n| n.absolute_path.replace('\\', "\\\\").replace('\n', "\\n"))
.map(ListItem::new) .map(ListItem::new)
.collect(); .collect();

Loading…
Cancel
Save