diff --git a/docs/en/src/awesome-hacks.md b/docs/en/src/awesome-hacks.md index 62f11fb..cade125 100644 --- a/docs/en/src/awesome-hacks.md +++ b/docs/en/src/awesome-hacks.md @@ -87,12 +87,13 @@ xplr.config.modes.builtin.default.key_bindings.on_key.m = { help = "bookmark", messages = { { - BashExecSilently = [===[ + BashExecSilently0 = [===[ PTH="${XPLR_FOCUS_PATH:?}" + PTH_ESC=$(printf %q "$PTH") if echo "${PTH:?}" >> "${XPLR_SESSION_PATH:?}/bookmarks"; then - echo "LogSuccess: ${PTH:?} added to bookmarks" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks" else - echo "LogError: Failed to bookmark ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC" fi ]===], }, @@ -103,10 +104,11 @@ xplr.config.modes.builtin.default.key_bindings.on_key["`"] = { help = "go to bookmark", messages = { { - BashExec = [===[ + BashExec0 = [===[ PTH=$(cat "${XPLR_SESSION_PATH:?}/bookmarks" | fzf --no-sort) + PTH_ESC=$(printf %q "$PTH") if [ "$PTH" ]; then - echo FocusPath: "'"${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'FocusPath: %q' "$PTH" fi ]===], }, @@ -138,17 +140,18 @@ xplr.config.modes.custom.bookmark = { m = { help = "bookmark dir", messages = { - { BashExecSilently = [[ + { BashExecSilently0 = [[ PTH="${XPLR_FOCUS_PATH:?}" if [ -d "${PTH}" ]; then PTH="${PTH}" elif [ -f "${PTH}" ]; then PTH="$(dirname "${PTH}")" fi + PTH_ESC=$(printf %q "$PTH") if echo "${PTH:?}" >> "${XPLR_BOOKMARK_FILE:?}"; then - echo "LogSuccess: ${PTH:?} added to bookmarks" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks" else - echo "LogError: Failed to bookmark ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC" fi ]] }, @@ -158,10 +161,10 @@ xplr.config.modes.custom.bookmark = { help = "go to bookmark", messages = { { - BashExec = [===[ + BashExec0 = [===[ PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort) if [ "$PTH" ]; then - echo FocusPath: "'"${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'FocusPath: %q' "$PTH" fi ]===] }, @@ -170,7 +173,7 @@ xplr.config.modes.custom.bookmark = { d = { help = "delete bookmark", messages = { - { BashExec = [[ + { BashExec0 = [[ PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort) sd "$PTH\n" "" "${XPLR_BOOKMARK_FILE:?}" ]] @@ -206,7 +209,7 @@ xplr.config.modes.builtin.go_to.key_bindings.on_key.b = { help = "bookmark jump", messages = { "PopMode", - { BashExec = [===[ + { BashExec0 = [===[ field='\(\S\+\s*\)' esc=$(printf '\033') N="${esc}[0m" @@ -223,7 +226,7 @@ xplr.config.modes.builtin.go_to.key_bindings.on_key.b = { --preview-window="right:50%" \ | sed 's#.*-> ##') if [ "$PTH" ]; then - echo ChangeDirectory: "'"${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'ChangeDirectory: %q' "$PTH" fi ]===] }, @@ -250,10 +253,10 @@ xplr.config.modes.builtin.go_to.key_bindings.on_key.h = { messages = { "PopMode", { - BashExec = [===[ - PTH=$(cat "${XPLR_PIPE_HISTORY_OUT:?}" | sort -u | fzf --no-sort) + BashExec0 = [===[ + PTH=$(cat "${XPLR_PIPE_HISTORY_OUT:?}" | sort -z -u | fzf --read0) if [ "$PTH" ]; then - echo ChangeDirectory: "'"${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m 'ChangeDirectory: %q' "$PTH" fi ]===], }, @@ -286,7 +289,7 @@ xplr.config.modes.builtin.default.key_bindings.on_key.R = { NODES=${SELECTION:-$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}")} if [ "$NODES" ]; then echo -e "$NODES" | renamer - echo ExplorePwdAsync >> "${XPLR_PIPE_MSG_IN:?}" + "$XPLR" -m ExplorePwdAsync fi ]===], }, @@ -312,7 +315,7 @@ xplr.config.modes.builtin.default.key_bindings.on_key.S = { help = "serve $PWD", messages = { { - BashExec = [===[ + BashExec0 = [===[ IP=$(ip addr | grep -w inet | cut -d/ -f1 | grep -Eo '[0-9]{1,3}\.[0-9]{ 1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | fzf --prompt 'Select IP > ') echo "IP: ${IP:?}" read -p "Port (default 5000): " PORT @@ -344,16 +347,16 @@ xplr.config.modes.builtin.default.key_bindings.on_key.P = { help = "preview", messages = { { - BashExecSilently = [===[ + BashExecSilently0 = [===[ FIFO_PATH="/tmp/xplr.fifo" if [ -e "$FIFO_PATH" ]; then - echo StopFifo >> "$XPLR_PIPE_MSG_IN" + "$XPLR" -m StopFifo rm -f -- "$FIFO_PATH" else mkfifo "$FIFO_PATH" "$HOME/.local/bin/imv-open.sh" "$FIFO_PATH" "$XPLR_FOCUS_PATH" & - echo "StartFifo: '$FIFO_PATH'" >> "$XPLR_PIPE_MSG_IN" + "$XPLR" -m 'StartFifo: %q' "$FIFO_PATH" fi ]===], }, @@ -407,27 +410,33 @@ local function stat(node) return node.mime_essence end -local function read(path, lines) - local out = "" +local function read(path, height) local p = io.open(path) if p == nil then - return stat(path) + return nil end local i = 0 + local res = "" for line in p:lines() do - out = out .. line .. "\n" - if i == lines then + if line:match("[^ -~\n\t]") then + p:close() + return + end + + res = res .. line .. "\n" + if i == height then break end i = i + 1 end p:close() - return out + return res end + xplr.config.layouts.builtin.default = { Horizontal = { config = { @@ -485,7 +494,7 @@ Navigate using the [tere][19] file explorer (defaults to type-to-nav system). xplr.config.modes.builtin.default.key_bindings.on_key.T = { help = "tere nav", messages = { - { BashExec = [[echo ChangeDirectory: "'"$(tere)"'" >> "$XPLR_PIPE_MSG_IN"]] }, + { BashExec0 = [[xplr -m 'ChangeDirectory: %q' "$(tere)"]] }, }, } ``` diff --git a/docs/en/src/configure-key-bindings.md b/docs/en/src/configure-key-bindings.md index 277eec0..a637d50 100644 --- a/docs/en/src/configure-key-bindings.md +++ b/docs/en/src/configure-key-bindings.md @@ -155,12 +155,12 @@ xplr.config.modes.custom.fzxplr = { messages = { { BashExec = [===[ - PTH=$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}" | awk -F/ '{print $NF}' | fzf) - if [ -d "$PTH" ]; then - echo ChangeDirectory: "'"${PWD:?}/${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" - else - echo FocusPath: "'"${PWD:?}/${PTH:?}"'" >> "${XPLR_PIPE_MSG_IN:?}" - fi + PTH=$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}" | awk -F/ '{print $NF}' | fzf) + if [ -d "$PTH" ]; then + "$XPLR" -m 'ChangeDirectory: %q' "$PTH" + else + "$XPLR" -m 'FocusPath: %q' "$PTH" + fi ]===] }, "PopMode", @@ -180,12 +180,10 @@ As you can see, the key `F` in mode `fzxplr` (the name can be anything) executes a script in `bash`. `BashExec`, `PopMode`, `SwitchModeBuiltin`, `ChangeDirectory` and `FocusPath` -are [messages][18], `$XPLR_PIPE_MSG_IN`, -`$XPLR_PIPE_DIRECTORY_NODES_OUT` are -[environment variables][22] exported by `xplr` -before executing the command. They contain the path to the -[input][23] and [output][24] pipes that -allows external tools to interact with `xplr`. +are [messages][18], `$XPLR`, `$XPLR_PIPE_DIRECTORY_NODES_OUT` are +[environment variables][22] exported by `xplr` before executing the command. +They contain the path to the [input][23] and [output][24] pipes that allows +external tools to interact with `xplr`. Now that we have our new mode ready, let's add an entry point to this mode via the `default` mode. diff --git a/docs/en/src/install.md b/docs/en/src/install.md index 4ce55e1..5fb143f 100644 --- a/docs/en/src/install.md +++ b/docs/en/src/install.md @@ -24,8 +24,7 @@ version of xplr, but they have one common drawback - the user will need to keep an eye on the releases, and manually upgrade xplr when a new version is available. -One way to keep an eye on the releases is to -[watch the repository][4]. +One way to keep an eye on the releases is to [watch the repository][4]. ## Community Maintained Repositories diff --git a/docs/en/src/post-install.md b/docs/en/src/post-install.md index 96da3c4..6e41df2 100644 --- a/docs/en/src/post-install.md +++ b/docs/en/src/post-install.md @@ -7,11 +7,9 @@ Once [installed][1], use the following steps to setup and run xplr. ```bash mkdir -p ~/.config/xplr -version="$(xplr | grep ^version: | cut -d' ' -f 2)" +version="$(xplr --version | awk '{print $2}')" -# When the app loads, press `#` - -echo version = '"'${version:?}'"' > ~/.config/xplr/init.lua +echo "version = '${version:?}'" > ~/.config/xplr/init.lua ``` Then diff --git a/src/bin/xplr.rs b/src/bin/xplr.rs index bb8a900..bf6cdc6 100644 --- a/src/bin/xplr.rs +++ b/src/bin/xplr.rs @@ -20,8 +20,11 @@ fn main() { -- Denotes the end of command-line flags and options --force-focus Focuses on the given , even if it is a directory -h, --help Prints help information - -m, --pipe-msg-in Helps passing messages to the active xplr session - --print-pwd-as-result Prints the present working directory when quitting + -m, --pipe-msg-in Helps safely passing messages to the active xplr + session, use %%, %s and %q as the placeholders + -M, --print-msg-in Like --pipe-msg-in, but prints the message instead of + passing to the active xplr session + --print-pwd-as-result Prints the present working directory when quitting with `PrintResultAndQuit` --read-only Enables read-only mode (config.general.read_only) --read0 Reads paths separated using the null character (\0) @@ -59,6 +62,11 @@ fn main() { eprintln!("error: {}", err); std::process::exit(1); } + } else if !cli.print_msg_in.is_empty() { + if let Err(err) = cli::print_msg_in(cli.print_msg_in) { + eprintln!("error: {}", err); + std::process::exit(1); + } } else { match runner::from_cli(cli).and_then(|a| a.run()) { Ok(Some(out)) => { diff --git a/src/cli.rs b/src/cli.rs index 6c7bb4a..ff67e79 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -22,6 +22,7 @@ pub struct Cli { pub extra_config: Vec, pub on_load: Vec, pub pipe_msg_in: Vec, + pub print_msg_in: Vec, pub paths: Vec, } @@ -120,9 +121,11 @@ impl Cli { } "-m" | "--pipe-msg-in" => { - for arg in args.by_ref() { - cli.pipe_msg_in.push(arg); - } + cli.pipe_msg_in.extend(args.by_ref()); + } + + "-M" | "--print-msg-in" => { + cli.print_msg_in.extend(args.by_ref()); } // path @@ -137,15 +140,39 @@ impl Cli { } pub fn pipe_msg_in(args: Vec) -> Result<()> { - let mut args = args.into_iter(); + let mut msg = fmt_msg_in(args)?; - let format = args.next().context("usage: xplr -m FORMAT [ARGUMENT]...")?; - let mut msg = "".to_string(); + if let Ok(path) = std::env::var("XPLR_PIPE_MSG_IN") { + let delimiter = fs::read(&path)? + .first() + .cloned() + .context("failed to detect delimmiter")?; + + msg.push(delimiter.try_into()?); + File::options() + .append(true) + .open(&path)? + .write_all(msg.as_bytes())?; + } else { + println!("{}", msg); + }; + Ok(()) +} + +pub fn print_msg_in(args: Vec) -> Result<()> { + let msg = fmt_msg_in(args)?; + print!("{}", msg); + Ok(()) +} + +fn fmt_msg_in(args: Vec) -> Result { + let mut args = args.into_iter(); + let format = args.next().context("usage: FORMAT [ARGUMENT]...")?; + let mut msg = "".to_string(); let mut last_char = None; - let chars = format.chars(); - for ch in chars { + for ch in format.chars() { match (ch, last_char) { ('%', Some('%')) => { msg.push(ch); @@ -183,22 +210,6 @@ pub fn pipe_msg_in(args: Vec) -> Result<()> { } // Validate - let mut msg = json::to_string(&ExternalMsg::try_from(msg.as_str())?)?; - - if let Ok(path) = std::env::var("XPLR_PIPE_MSG_IN") { - let delimiter = fs::read(&path)? - .first() - .cloned() - .context("failed to detect delimmiter")?; - - msg.push(delimiter.try_into()?); - File::options() - .append(true) - .open(&path)? - .write_all(msg.as_bytes())?; - } else { - println!("{}", msg); - } - - Ok(()) + let msg = json::to_string(&ExternalMsg::try_from(msg.as_str())?)?; + Ok(msg) } diff --git a/src/ui.rs b/src/ui.rs index ac3382b..331edb0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -678,7 +678,7 @@ fn draw_table( config, format!( " {} ({}) ", - app.pwd, + app.pwd.replace('\\', "\\\\").replace('\n', "\\n"), app.directory_buffer .as_ref() .map(|d| d.total)