Skip to content

Instantly share code, notes, and snippets.

@MahouShoujoMivutilde
Last active October 26, 2023 13:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MahouShoujoMivutilde/b1fa83bc234b68dd164cb6e843965d75 to your computer and use it in GitHub Desktop.
Save MahouShoujoMivutilde/b1fa83bc234b68dd164cb6e843965d75 to your computer and use it in GitHub Desktop.
fzf as dmenu replacement

fzf as dmenu replacement

Why? ...Because it's faster.

So you'll need:

  1. terminal that launches fast and supports custom class or window name. St fits the bill perfectly.

  2. window manager with an option to automatically put windows in center based on class or name. Most seem to have it.

#!/usr/bin/env bash
# fzfmenu - fzf as dmenu replacement
input=$(mktemp -u --suffix .fzfmenu.input)
output=$(mktemp -u --suffix .fzfmenu.output)
mkfifo $input
mkfifo $output
chmod 600 $input $output
# it's better to use st here (starts a lot faster than pretty much everything else)
# the ugly printf | sed thing is here to make args with quotes work.
# (e.g. --preview='echo {1}')
# sadly we can't use "$@" here directly because we are inside sh -c "..." call
# already
st -c fzfmenu -n fzfmenu -e sh -c "cat $input | fzf $(printf -- " '%s'" "$@" | sed "s/^ ''$//") | tee $output" & disown
# handle ctrl+c outside child terminal window
trap "kill $! 2>/dev/null; rm -f $input $output" EXIT
cat > $input
cat $output
@eylles
Copy link

eylles commented Oct 11, 2022

i took a look into this and made a couple changes to try and make it a bit more compatible with existing dmenu scripts.

EDIT: i just made it a little better, now with a config file and the dracula theme by default cuz why not?

#!/usr/bin/env bash
# fzfmenu - fzf as dmenu replacement

CONFIG="${XDG_CONFIG_HOME:-~/.config}/fzfmenu/fzfrc"
if [ -f "$CONFIG" ]; then
    # load config
    . "$CONFIG"
fi

awkVal() {
    awk -v Val="$1" 'BEGIN{FS=OFS=" "} {for(i=1;i<=NF;i++){if($i==Val){print $(i+1);next}}}'
}

input=$(mktemp -u --suffix .fzfmenu.input)
output=$(mktemp -u --suffix .fzfmenu.output)
mkfifo "$input"
mkfifo "$output"
chmod 600 $input $output

while getopts "fbip:l:w:" opt; do case "${opt}" in
    i) FZF_OPTS="$FZF_OPTS -$opt"  ;;
    l) FZF_OPTS="$FZF_OPTS --height $OPTARG" ;;
    p) FZF_OPTS="$FZF_OPTS --header $OPTARG" ;;
    w) TERM_OPTS="-into $OPTARG" ;;
    f|b) printf '%s\n' "${0##*/}: option $opt ignored" ;;
    *) printf '%s\n' "${0##*/}: invalid option $opt" >&2 ; exit 1 ;;
esac done
shift $(( OPTIND -1 ))

# add options from config and replace others if necesary
runHeight=$(printf '%s\n' $FZF_OPTS | awkVal "height")
if [ -z "$runHeight" ] || [ "${runHeight%\%}" -le 50 ]; then
    FZF_OPTS="$FZF_OPTS $FZF_HEIGHT"
fi

runPrompt=$(printf '%s\n' $FZF_OPTS | awkVal "heder")
if [ -z "$runPrompt" ]; then
    FZF_OPTS="$FZF_OPTS $FZF_HEADER"
fi

FZF_OPTS="$FZF_OPTS $FZF_MENU_OPTS"
FZF_OPTS="$FZF_OPTS $FZF_COLORS"

export FZF_OPTS

if [ -z "$FLOATING_TERMINAL" ]; then
    FLOATING_TERMINAL='st -c fzfmenu -n fzfmenu '
fi
if [ -z "$EXEC_FLAG" ]; then
    EXEC_FLAG=' -e '
fi

$FLOATING_TERMINAL $TERM_OPTS $EXEC_FLAG sh -c "cat $input | fzf $FZF_OPTS | tee $output" & disown

termPID=$!

# consume stdin into named pipe
cat > "$input"

for arg in "$@"; do
    # if it's a pipe then drain it to input
    [ -p "$arg" ] && { cat "$arg" > "$input"; };
done

# handle ctrl+c outside child terminal window
cleanup() {
    if kill -0 "$termPID" 2>/dev/null; then
        kill $termPID 2>/dev/null
    fi
    rm -f "$input $output"
}
trap cleanup EXIT

cat "$output"

config:

# vim: ft=sh

FZF_HEADER="--header 'fzf Menu'"
FZF_HEIGHT="--height 100%"

# default options, same as set inside the script when not set here
FZF_MENU_OPTS="--layout=reverse \
     --cycle --preview-window sharp --preview-window 70% \
     --prompt='filter: ' \
     --bind alt-k:preview-up \
     --bind alt-j:preview-down"

# colorscheme for fzf, by default we ship dracula
FZF_COLORS="--color=fg:#f8f8f2,bg:#282a36,hl:#bd93f9,fg+:#f8f8f2,bg+:#44475a \
--color=hl+:#bd93f9,info:#ffb86c,prompt:#50fa7b,pointer:#ff79c6,marker:#ff79c6 \
--color=spinner:#ffb86c,header:#6272a4"

# the terminal to use as a floating terminal, you must set this var along all
# the necessary arguments to set the correct name and/or class for your
# terminal, remember to set the name and or classname to fzfmenu for
# consistency, for an example we are using xterm here
#FLOATING_TERMINAL='xterm -name fzfmenu -T fzfmenu '
#       WM_CLASS name flag  ^      ^     ^    ^   ^
#          WM_CLASS name to set ---|     |    |   |
#                 terminal title flag ---|    |   |
#                    terminal title to set ---|   |
#                 this has to end with a space ---|

# the terminal's exec flag, yes it must be surrounded by spaces
#EXEC_FLAG=' -e '

@RayZ0rr
Copy link

RayZ0rr commented Sep 29, 2023

@eylles

alongwith this script I have been using another one like this:

fzfmenu

#!/usr/bin/env bash

export FZF_DEFAULT_OPTS="--height=100% --layout=reverse --border --no-sort --prompt=\"~ \" --color=dark,hl:red:regular,fg+:white:regular,hl+:red:regular:reverse,query:white:regular,info:gray:regular,prompt:red:bold,pointer:red:bold"

exec floating-terminal bash -c "fzf-tmux -m $* < /proc/$$/fd/0 | awk '{print}' > /proc/$$/fd/1"

Here, floating-terminal is the floating-terminal from Seirdy but I modified to take optional class name for the terminal

#!/usr/bin/env bash

class_name="Fzf-Menu"
if [[ "$1" == "class" ]]; then
    class_name="$2"
fi

# This is the terminal program that term-dmenu runs. Some examples:
# alacritty --class $class_name -e "$@"
# kitty --class $class_name "$@"
# These examples give the window the "$class_name" class so you can make your window
# manager float anything with class "$class_name"

if [[ -z "$FLOATING_TERMINAL" ]]; then
  # find_alt() prints the first parameter recognized by `command -v`
  find_alt() {
    for i; do
      command -v "${i%% *}" >/dev/null && {
	echo "$i"
	      return 0
	    }
    done
    return 1
  }
  FLOATING_TERMINAL=$(find_alt "foot -a $class_name" "havoc" "alacritty --class=$class_name -e" "kitty --class=$class_name" "konsole" "gnome-terminal" "termite" "st -c $class_name" "xterm" "wezterm start --class=$class_name")
fi

if [[ "$1" == "class" ]]; then
    $FLOATING_TERMINAL "${@:2}"
else
    $FLOATING_TERMINAL "${@}"
fi

# vim:ft=sh

I use it as : <input args> | FzfMenu | <use output>. Eg:

find . -type f | fzfmenu | xargs -I{} nvim {}
dmenu_path | fzfmenu | bash

@eylles
Copy link

eylles commented Sep 29, 2023

@RayZ0rr interesting, i have a repo that sees occasional commits https://github.com/eylles/fzfmenu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment