lf icon indicating copy to clipboard operation
lf copied to clipboard

How do I execute scripts that take a choice via stdin without needing to press <enter>?

Open UtkarshVerma opened this issue 11 months ago • 5 comments

I am trying to create a trash command that mimics the built-in delete command by doing the following:

  • Prompt the user: trash 'filename' ? [y/N]
  • User presses a key without needing to press <enter>.
  • Actions are performed accordingly.

So far, I have written this:

# Move the file/directory to trash.
cmd trash %{{
    set -f

    if ! command -v trash-put >/dev/null 2>&1; then 
        echo "trash-cli is not installed."
        exit 1
    fi

    file_count=$(echo "$fx" | wc -l)
	if [ $file_count -eq 1 ]; then
		printf "trash '%s'?" $fx
	else
		printf "trash %s items?" $file_count
	fi
	printf " [y/N] "
	
	choice=$(/usr/bin/env bash -c 'read -n 1 choice; echo $choice')

    # Clear the prompt.
    echo

    if [ $choice != "y" ]; then
        exit
    fi

    trash-put $fx
}}

The script behaves well if I execute in my dash shell interactively. However, the input requires me to press <enter> if I invoke the command via lf. What could be the issue here?

Secondly, is there any way I can suppress the ">" symbol that appears for shell-pipe commands for this command specifically. It's not a high priority thing, but would be nice to know if I could.

image

Thanks.

UtkarshVerma avatar Feb 29 '24 08:02 UtkarshVerma

Replace %{{ with ${{. Why do you even want a shell-pipe for such an interactive use case?

Btw, you could just read -r -n 1 choice instead of choice=$(.....).

MahouShoujoMivutilde avatar Mar 01 '24 00:03 MahouShoujoMivutilde

I want the script output to be shown at the bottom. Using ${{ completely occupies the whole screen. Also, I am using the choice=$(...) because /bin/sh points to dash which strictly conforms to the POSIX standard.

In POSIX, read -n 1 does not exist.

UtkarshVerma avatar Mar 01 '24 05:03 UtkarshVerma

I have a weird idea that could be a last resort if nobody has better ideas:

cmd delete :{{
	map y send_key_y
	map n send_key_n
	trashing_listener
}}

cmd send_key_y ${{
	pipe="/tmp/my_pipe_$id"
	echo 'y' > "$pipe"
}}

cmd send_key_n ${{
	pipe="/tmp/my_pipe_$id"
	echo 'n' > "$pipe"
}}

cmd trashing_listener &{{
	pipe="/tmp/my_pipe_$id"
	mkfifo "$pipe"

	cleanup() {
		rm -f "$pipe"
		lf -remote "send $id remap"
		lf -remote "send $id echo"
		exit 0
	}

	if ! command -v trash-put >/dev/null 2>&1; then
		lf -remote "send $id echo \"trash-cli is not installed.\""
		# exit 1
	fi

	file_count=$(echo "$fx" | wc -l)
	if [ $file_count -eq 1 ]; then
		lf -remote "send $id echo \"trash $fx? [y/N]\""
	else
		lf -remote "send $id echo \"trash $file_count items? [y/N]\""
	fi

	while true; do
		if read -r msg < "$pipe"; then
			if [ "$msg" = "y" ]; then
				notify-send "trash-put $fx"
			fi
			cleanup
		fi
	done
}}

cmd remap :{{
	map y copy
}}

delete calls a process that will listen for input and remaps y and n to provide that input trashing_listener creates a loop that listens for y remap is supposed to return key mappings to the original state

DusanLesan avatar Mar 01 '24 15:03 DusanLesan

Years ago in 2020 when I was configuring lf, I wanted the exact same thing you want. Back then, it was achievable by setting the shell to zsh and running read -q inside %{{ ... }}. See my dotfiles: https://github.com/randoragon/dotfiles/blob/cfcc10ff35e2f8d4633b035aa0bcf9bc7787c641/lf/.config/lf/lfrc#L34

However, a few years ago, I don't remember when, it stopped to work. Now read -q hangs inside %{{ }}, and only works properly inside ${{ }} which, as you pointed out, is inconsistent with :delete, because it occupies the entire screen.

I think this may be a bug, but I've been too lazy to investigate or open my own ticket :P

randoragon avatar May 29 '24 23:05 randoragon

@UtkarshVerma @randoragon If you are willing to deal with tmux flow, you can try to do it like:

cmd trash ${{
	split_height=$(( $(echo "$fx" | wc -l) + 2 ))
	[ $split_height -gt $lf_height ] && split_height=$lf_height
	tmux split-window -l $split_height '
		echo -n "'"$fx"'\n\nDelete files? [y/n] "
		stty -icanon -echo
		choice=$(dd bs=1 count=1 2>/dev/null)
		if [[ "$choice" == "y" ]]; then
			while IFS= read -r file; do
				notify-send "Moving file to trash..." ">$file<"
			done <<< "'"$fx"'"
			lf -remote "send '$id' load"
		fi
	'
}}

DusanLesan avatar Jun 23 '24 14:06 DusanLesan