diff --git a/README.md b/README.md index 567271c..5e3a1f0 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,14 @@ export FZF_COMPLETION_TRIGGER='~~' # Options to fzf command export FZF_COMPLETION_OPTS='+c -x' + +# Use ag instead of the default find command for listing candidates. +# - The first argument to the function is the base path to start traversal +# - Note that ag only lists files not directories +# - See the source code (completion.{bash,zsh}) for the details. +_fzf_compgen_paths() { + ag -g "" "$1" +} ``` #### Supported commands diff --git a/shell/completion.bash b/shell/completion.bash index ef3a965..9d84ce5 100644 --- a/shell/completion.bash +++ b/shell/completion.bash @@ -10,6 +10,26 @@ # - $FZF_COMPLETION_TRIGGER (default: '**') # - $FZF_COMPLETION_OPTS (default: empty) +# To use custom commands instead of find, override _fzf_compgen_{path,dir} +if ! declare -f _fzf_compgen_path > /dev/null; then + _fzf_compgen_path() { + echo "$1" + \find -L "$1" \ + -name .git -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \ + -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@' + } +fi + +if ! declare -f _fzf_compgen_dir > /dev/null; then + _fzf_compgen_dir() { + \find -L "$1" \ + -name .git -prune -o -name .svn -prune -o -type d \ + -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@' + } +fi + +########################################################### + _fzf_orig_completion_filter() { sed 's/^\(.*-F\) *\([^ ]*\).* \([^ ]*\)$/export _fzf_orig_completion_\3="\1 %s \3 #\2";/' | awk -F= '{gsub(/[^a-z0-9_= ;]/, "_", $1); print $1"="$2}' @@ -113,7 +133,7 @@ __fzf_generic_path_completion() { [ -z "$dir" ] && dir='.' [ "$dir" != "/" ] && dir="${dir/%\//}" tput sc - matches=$(\find -L "$dir" $1 -a -not -path "$dir" -print 2> /dev/null | sed 's@^\./@@' | $fzf $FZF_COMPLETION_OPTS $2 -q "$leftover" | while read item; do + matches=$(eval "$1 $(printf %q "$dir")" | $fzf $FZF_COMPLETION_OPTS $2 -q "$leftover" | while read item; do printf "%q$3 " "$item" done) matches=${matches% } @@ -171,21 +191,16 @@ _fzf_complete() { } _fzf_path_completion() { - __fzf_generic_path_completion \ - "-name .git -prune -o -name .svn -prune -o ( -type d -o -type f -o -type l )" \ - "-m" "" "$@" + __fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@" } +# Deprecated. No file only completion. _fzf_file_completion() { - __fzf_generic_path_completion \ - "-name .git -prune -o -name .svn -prune -o ( -type f -o -type l )" \ - "-m" "" "$@" + _fzf_path_completion "$@" } _fzf_dir_completion() { - __fzf_generic_path_completion \ - "-name .git -prune -o -name .svn -prune -o -type d" \ - "" "/" "$@" + __fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@" } _fzf_complete_kill() { @@ -239,13 +254,12 @@ _fzf_complete_unalias() { # fzf options complete -o default -F _fzf_opts_completion fzf -d_cmds="cd pushd rmdir" -f_cmds=" +d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}" +a_cmds=" awk cat diff diff3 emacs emacsclient ex file ftp g++ gcc gvim head hg java javac ld less more mvim nvim patch perl python ruby - sed sftp sort source tail tee uniq vi view vim wc xdg-open" -a_cmds=" + sed sftp sort source tail tee uniq vi view vim wc xdg-open basename bunzip2 bzip2 chmod chown curl cp dirname du find git grep gunzip gzip hg jar ln ls mv open rm rsync scp @@ -256,7 +270,7 @@ x_cmds="kill ssh telnet unset unalias export" if [ "$_fzf_completion_loaded" != '0.10.8' ]; then # Really wish I could use associative array but OSX comes with bash 3.2 :( eval $(complete | \grep '\-F' | \grep -v _fzf_ | - \grep -E " ($(echo $d_cmds $f_cmds $a_cmds $x_cmds | sed 's/ /|/g' | sed 's/+/\\+/g'))$" | _fzf_orig_completion_filter) + \grep -E " ($(echo $d_cmds $a_cmds $x_cmds | sed 's/ /|/g' | sed 's/+/\\+/g'))$" | _fzf_orig_completion_filter) export _fzf_completion_loaded=0.10.8 fi @@ -278,21 +292,16 @@ _fzf_defc() { fi } -# Directory -for cmd in $d_cmds; do - _fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o plusdirs" -done - -# File -for cmd in $f_cmds; do - _fzf_defc "$cmd" _fzf_file_completion "-o default -o bashdefault" -done - # Anything for cmd in $a_cmds; do _fzf_defc "$cmd" _fzf_path_completion "-o default -o bashdefault" done +# Directory +for cmd in $d_cmds; do + _fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o plusdirs" +done + unset _fzf_defc # Kill completion @@ -307,4 +316,4 @@ complete -F _fzf_complete_unset -o default -o bashdefault unset complete -F _fzf_complete_export -o default -o bashdefault export complete -F _fzf_complete_unalias -o default -o bashdefault unalias -unset cmd d_cmds f_cmds a_cmds x_cmds +unset cmd d_cmds a_cmds x_cmds diff --git a/shell/completion.zsh b/shell/completion.zsh index 6e2d932..c7dee7a 100644 --- a/shell/completion.zsh +++ b/shell/completion.zsh @@ -10,12 +10,32 @@ # - $FZF_COMPLETION_TRIGGER (default: '**') # - $FZF_COMPLETION_OPTS (default: empty) +# To use custom commands instead of find, override _fzf_compgen_{path,dir} +if ! declare -f _fzf_compgen_path > /dev/null; then + _fzf_compgen_path() { + echo "$1" + \find -L "$1" \ + -name .git -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \ + -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@' + } +fi + +if ! declare -f _fzf_compgen_dir > /dev/null; then + _fzf_compgen_dir() { + \find -L "$1" \ + -name .git -prune -o -name .svn -prune -o -type d \ + -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@' + } +fi + +########################################################### + __fzf_generic_path_completion() { - local base lbuf find_opts fzf_opts suffix tail fzf dir leftover matches nnm + local base lbuf compgen fzf_opts suffix tail fzf dir leftover matches nnm # (Q) flag removes a quoting level: "foo\ bar" => "foo bar" base=${(Q)1} lbuf=$2 - find_opts=$3 + compgen=$3 fzf_opts=$4 suffix=$5 tail=$6 @@ -33,7 +53,7 @@ __fzf_generic_path_completion() { [ -z "$dir" ] && dir='.' [ "$dir" != "/" ] && dir="${dir/%\//}" dir=${~dir} - matches=$(\find -L "$dir" ${=find_opts} -a -not -path "$dir" -print 2> /dev/null | sed 's@^\./@@' | ${=fzf} ${=FZF_COMPLETION_OPTS} ${=fzf_opts} -q "$leftover" | while read item; do + matches=$(eval "$compgen $(printf %q "$dir")" | ${=fzf} ${=FZF_COMPLETION_OPTS} ${=fzf_opts} -q "$leftover" | while read item; do printf "%q$suffix " "$item" done) matches=${matches% } @@ -50,14 +70,12 @@ __fzf_generic_path_completion() { } _fzf_path_completion() { - __fzf_generic_path_completion "$1" "$2" \ - "-name .git -prune -o -name .svn -prune -o ( -type d -o -type f -o -type l )" \ + __fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \ "-m" "" " " } _fzf_dir_completion() { - __fzf_generic_path_completion "$1" "$2" \ - "-name .git -prune -o -name .svn -prune -o -type d" \ + __fzf_generic_path_completion "$1" "$2" _fzf_compgen_dir \ "" "/" "" } @@ -145,7 +163,7 @@ fzf-completion() { zle redisplay # Trigger sequence given elif [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then - d_cmds=(cd pushd rmdir) + d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}) [ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}} [ -z "${tokens[-1]}" ] && lbuf=$LBUFFER || lbuf=${LBUFFER:0:-${#tokens[-1]}} diff --git a/test/test_go.rb b/test/test_go.rb index efbbf25..a8f75c6 100644 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -1269,7 +1269,7 @@ module CompletionTest tmux.send_keys 'C-u' tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab, pane: 0 tmux.until(1) { |lines| lines.item_count > 0 } - tmux.send_keys :Enter + tmux.send_keys 'C-K', :Enter tmux.until do |lines| tmux.send_keys 'C-L' lines[-1].end_with?('/tmp/fzf\ test/foobar') @@ -1339,6 +1339,20 @@ module CompletionTest tmux.send_keys 'C-L' lines[-1] == "kill #{pid}" end + + def test_custom_completion + tmux.send_keys '_fzf_compgen_path() { echo "\$1"; seq 10; }', :Enter + tmux.prepare + tmux.send_keys 'ls /tmp/**', :Tab, pane: 0 + tmux.until(1) { |lines| lines.item_count == 11 } + tmux.send_keys :BTab, :BTab, :BTab + tmux.until(1) { |lines| lines[-2].include? '(3)' } + tmux.send_keys :Enter + tmux.until do |lines| + tmux.send_keys 'C-L' + lines[-1] == "ls /tmp 1 2" + end + end ensure Process.kill 'KILL', pid.to_i rescue nil if pid end