From 52154153150d0014a76ef8b429b73c590cb7f755 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Thu, 20 Feb 2020 00:28:16 +0900 Subject: [PATCH] [completion] Allow users to customize fzf options via _fzf_comprun Related #1809 #1850 --- README.md | 15 ++++++++++++++ shell/completion.bash | 28 +++++++++++++++----------- shell/completion.zsh | 46 ++++++++++++++++++++++++++++++------------- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index e138ebb..b3c7cae 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,21 @@ _fzf_compgen_path() { _fzf_compgen_dir() { fd --type d --hidden --follow --exclude ".git" . "$1" } + +# (EXPERIMENTAL) Advanced customization of fzf options via _fzf_comprun function +# - The first argument to the function is the name of the command. +# - You should make sure to pass the rest of the arguments to fzf. +_fzf_comprun() { + local command=$1 + shift + + case "$command" in + cd) fzf "$@" --preview 'tree -C {} | head -200' ;; + export|unset) fzf "$@" --preview "eval 'echo \$'{}" "$@" ;; + ssh) fzf "$@" --preview 'dig {}' ;; + *) fzf "$@" ;; + esac +} ``` #### Supported commands diff --git a/shell/completion.bash b/shell/completion.bash index b8ad4e5..47d01e8 100644 --- a/shell/completion.bash +++ b/shell/completion.bash @@ -34,9 +34,16 @@ fi # To redraw line after fzf closes (printf '\e[5n') bind '"\e[0n": redraw-current-line' -__fzfcmd_complete() { - [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] && - echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf" +__fzf_comprun() { + if [ "$(type -t _fzf_comprun 2>&1)" = function ]; then + _fzf_comprun "$@" + elif [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ]; then + shift + fzf-tmux -d "${FZF_TMUX_HEIGHT:-40%}" "$@" + else + shift + fzf "$@" + fi } __fzf_orig_completion_filter() { @@ -142,8 +149,7 @@ _fzf_handle_dynamic_completion() { } __fzf_generic_path_completion() { - local cur base dir leftover matches trigger cmd fzf - fzf="$(__fzfcmd_complete)" + local cur base dir leftover matches trigger cmd cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}" COMPREPLY=() trigger=${FZF_COMPLETION_TRIGGER-'**'} @@ -159,7 +165,7 @@ __fzf_generic_path_completion() { leftover=${leftover/#\/} [ -z "$dir" ] && dir='.' [ "$dir" != "/" ] && dir="${dir/%\//}" - matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" $fzf $2 -q "$leftover" | while read -r item; do + matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do printf "%q$3 " "$item" done) matches=${matches% } @@ -184,10 +190,9 @@ __fzf_generic_path_completion() { } _fzf_complete() { - local cur selected trigger cmd fzf post + local cur selected trigger cmd post post="$(caller 0 | awk '{print $2}')_post" type -t "$post" > /dev/null 2>&1 || post=cat - fzf="$(__fzfcmd_complete)" cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}" trigger=${FZF_COMPLETION_TRIGGER-'**'} @@ -195,7 +200,7 @@ _fzf_complete() { if [[ "$cur" == *"$trigger" ]]; then cur=${cur:0:${#cur}-${#trigger}} - selected=$(cat | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" $fzf $1 -q "$cur" | $post | tr '\n' ' ') + selected=$(cat | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $1" __fzf_comprun "$2" -q "$cur" | $post | tr '\n' ' ') selected=${selected% } # Strip trailing space not to repeat "-o nospace" if [ -n "$selected" ]; then COMPREPLY=("$selected") @@ -226,9 +231,8 @@ _fzf_dir_completion() { _fzf_complete_kill() { [ -n "${COMP_WORDS[COMP_CWORD]}" ] && return 1 - local selected fzf - fzf="$(__fzfcmd_complete)" - selected=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS --preview 'echo {}' --preview-window down:3:wrap $FZF_COMPLETION_OPTS" $fzf -m | awk '{print $2}' | tr '\n' ' ') + local selected + selected=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS --preview 'echo {}' --preview-window down:3:wrap" __fzf_comprun "kill" -m | awk '{print $2}' | tr '\n' ' ') printf '\e[5n' if [ -n "$selected" ]; then diff --git a/shell/completion.zsh b/shell/completion.zsh index 6adb874..d5fccad 100644 --- a/shell/completion.zsh +++ b/shell/completion.zsh @@ -31,20 +31,40 @@ fi ########################################################### -__fzfcmd_complete() { - [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] && - echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf" +__fzf_comprun() { + if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then + _fzf_comprun "$@" + elif [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ]; then + shift + fzf-tmux -d "${FZF_TMUX_HEIGHT:-40%}" "$@" + else + shift + fzf "$@" + fi +} + +# Extract the name of the command. e.g. foo=1 bar baz** +__fzf_extract_command() { + local token tokens + tokens=(${(z)1}) + for token in $tokens; do + if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then + echo "$token" + return + fi + done + echo "${tokens[1]}" } __fzf_generic_path_completion() { - local base lbuf compgen fzf_opts suffix tail fzf dir leftover matches + local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches base=$1 lbuf=$2 + cmd=$(__fzf_extract_command "$lbuf") compgen=$3 fzf_opts=$4 suffix=$5 tail=$6 - fzf="$(__fzfcmd_complete)" setopt localoptions nonomatch eval "base=$base" @@ -55,7 +75,7 @@ __fzf_generic_path_completion() { leftover=${leftover/#\/} [ -z "$dir" ] && dir='.' [ "$dir" != "/" ] && dir="${dir/%\//}" - matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" ${(Q)${(Z+n+)fzf}} ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do + matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do echo -n "${(q)item}$suffix " done) matches=${matches% } @@ -87,17 +107,16 @@ _fzf_feed_fifo() ( ) _fzf_complete() { - local fifo fzf_opts lbuf fzf matches post + local fifo fzf_opts lbuf cmd matches post fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$" fzf_opts=$1 lbuf=$2 + cmd=$(__fzf_extract_command "$lbuf") post="${funcstack[2]}_post" type $post > /dev/null 2>&1 || post=cat - fzf="$(__fzfcmd_complete)" - _fzf_feed_fifo "$fifo" - matches=$(cat "$fifo" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" ${(Q)${(Z+n+)fzf}} ${(Q)${(Z+n+)fzf_opts}} -q "${(Q)prefix}" | $post | tr '\n' ' ') + matches=$(cat "$fifo" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "${(Q)prefix}" | $post | tr '\n' ' ') if [ -n "$matches" ]; then LBUFFER="$lbuf$matches" fi @@ -141,7 +160,7 @@ _fzf_complete_unalias() { } fzf-completion() { - local tokens cmd prefix trigger tail fzf matches lbuf d_cmds + local tokens cmd prefix trigger tail matches lbuf d_cmds setopt localoptions noshwordsplit noksh_arrays noposixbuiltins # http://zsh.sourceforge.net/FAQ/zshfaq03.html @@ -152,7 +171,7 @@ fzf-completion() { return fi - cmd=${tokens[1]} + cmd=$(__fzf_extract_command "$LBUFFER") # Explicitly allow for empty trigger. trigger=${FZF_COMPLETION_TRIGGER-'**'} @@ -167,8 +186,7 @@ fzf-completion() { tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))} # Kill completion (do not require trigger sequence) if [ $cmd = kill -a ${LBUFFER[-1]} = ' ' ]; then - fzf="$(__fzfcmd_complete)" - matches=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS --preview 'echo {}' --preview-window down:3:wrap $FZF_COMPLETION_OPTS" ${(Q)${(Z+n+)fzf}} -m | awk '{print $2}' | tr '\n' ' ') + matches=$(command ps -ef | sed 1d | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-50%} --min-height 15 --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS --preview 'echo {}' --preview-window down:3:wrap" __fzf_comprun "$cmd" -m | awk '{print $2}' | tr '\n' ' ') if [ -n "$matches" ]; then LBUFFER="$LBUFFER$matches" fi