From fa5617e0764e41ea90189f2c26e8389839e2df2f Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 20 Nov 2013 01:29:36 +0900 Subject: [PATCH] Implement bash auto-completion with fzf --- README.md | 69 ++++++++++++++++++++++++-------- fzf-completion.bash | 95 +++++++++++++++++++++++++++++++++++++++++++++ fzf-completion.zsh | 9 +++++ install | 71 +++++++++++++++++++++++++++++++-- 4 files changed, 224 insertions(+), 20 deletions(-) create mode 100644 fzf-completion.bash create mode 100644 fzf-completion.zsh diff --git a/README.md b/README.md index 430e82f..f20dd1a 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,24 @@ fzf requires Ruby (>= 1.8.5). Installation ------------ -Download [fzf executable](https://raw.github.com/junegunn/fzf/master/fzf) and -put it somewhere in your search $PATH. +### Using install script + +Clone this repository and run +[install](https://github.com/junegunn/fzf/blob/master/install) script. + +```sh +git clone https://github.com/junegunn/fzf.git ~/.fzf +~/.fzf/install +``` + +The script will add an alias to fzf and auto-completion support to your +`.bashrc` and `.zshrc`. + +### Manual installation + +Or you can just download +[fzf executable](https://raw.github.com/junegunn/fzf/master/fzf) and put it +somewhere in your search $PATH. ```sh mkdir -p ~/bin @@ -25,20 +41,6 @@ wget https://raw.github.com/junegunn/fzf/master/fzf -O ~/bin/fzf chmod +x ~/bin/fzf ``` -Or you can just clone this repository and run -[install](https://github.com/junegunn/fzf/blob/master/install) script. - -```sh -git clone https://github.com/junegunn/fzf.git -fzf/install -``` - -Make sure that ~/bin is included in $PATH. - -```sh -export PATH=$PATH:~/bin -``` - ### Install as Ruby gem fzf can be installed as a Ruby gem @@ -215,7 +217,7 @@ zsh widgets ----------- ```sh -# CTRL-T - Paste the selected file(s) path into the command line +# CTRL-T - Paste the selected file path(s) into the command line fzf-file-widget() { local FILES local IFS=" @@ -250,6 +252,36 @@ zle -N fzf-history-widget bindkey '^R' fzf-history-widget ``` +Auto-completion (experimental) +------------------------------ + +Disclaimer: *Auto-completion feature is currently experimental, it can change +over time* + +### bash + +fuzzy-finder-completion can be triggered if you type in a directory name +followed by the trigger sequence which is by default `**`. + +```sh +vim ** +vim ..** +vim ~/** + +cd ** +cd ../ +``` + +You can use different trigger sequence by setting `FZF_COMPLETION_TRIGGER` + +```sh +export FZF_COMPLETION_TRIGGER='~~' +``` + +### zsh + +TODO + Tips ---- @@ -269,6 +301,9 @@ Define fzf alias with the option as follows: alias fzf='ruby --disable-gems ~/bin/fzf' ``` +This is automatically set up in your .bashrc and .zshrc if you use the bundled +[install](https://github.com/junegunn/fzf/blob/master/install) script. + ### Incorrect display on Ruby 1.8 It is reported that the output of fzf can become unreadable on some terminals diff --git a/fzf-completion.bash b/fzf-completion.bash new file mode 100644 index 0000000..fff7f63 --- /dev/null +++ b/fzf-completion.bash @@ -0,0 +1,95 @@ +#!/bin/bash +# ____ ____ +# / __/___ / __/ +# / /_/_ / / /_ +# / __/ / /_/ __/ +# /_/ /___/_/-completion.bash +# +# - $FZF_COMPLETION_TRIGGER (default '**') +# - $FZF_COMPLETION_OPTS + +_fzf_opts_completion() { + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + opts="-m --multi -x --extended -s --sort +s +i +c --no-color" + + case "${prev}" in + --sort|-s) + COMPREPLY=( $(compgen -W "$(seq 2000 1000 10000)" -- ${cur}) ) + return 0 + ;; + esac + + if [[ ${cur} =~ ^-|\+ ]]; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + return 0 +} + +_fzf_generic_completion() { + local cur prev opts base matches ignore + COMPREPLY=() + FZF_COMPLETION_TRIGGER=${FZF_COMPLETION_TRIGGER:-**} + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + if [[ ${cur} == *"$FZF_COMPLETION_TRIGGER" ]]; then + base=${cur:0:${#cur}-${#FZF_COMPLETION_TRIGGER}} + base=${base%/} + eval base=$base + + ignore=${FZF_COMPLETION_IGNORE:-*/.git/*} + find_opts="-name .git -prune -o -name .svn -prune -o" + if [ -z "$base" -o -d "$base" ]; then + matches=$(find ${base:-*} $1 2> /dev/null | fzf $FZF_COMPLETION_OPTS $2 | while read item; do + if [[ ${item} =~ \ ]]; then + echo -n "\"$item\" " + else + echo -n "$item " + fi + done) + matches=${matches% } + if [ -n "$matches" ]; then + COMPREPLY=( "$matches" ) + return 0 + fi + fi + fi +} + +_fzf_all_completion() { + _fzf_generic_completion \ + "-name .git -prune -o -name .svn -prune -o -type f -print -o -type l -print" \ + "-m" +} + +_fzf_dir_completion() { + _fzf_generic_completion \ + "-name .git -prune -o -name .svn -prune -o -type d -print" \ + "" +} + +complete -F _fzf_opts_completion fzf + +for cmd in "cd pushd rmdir"; do + complete -F _fzf_dir_completion -o default $cmd +done + +FZF_COMPLETION_COMMANDS=${FZF_COMPLETION_COMMANDS:- + awk basename bunzip2 bzip2 curl diff diff3 dirname du emacs ex file find ftp + g++ gcc git grep gunzip gvim gzip head hg jar java javac jps ld less ls more + mvim open patch perl python rsync ruby scp sed sftp sort svn tail tar tee + uniq unzip vi view vim wc zip +} + +for cmd in $FZF_COMPLETION_COMMANDS; do + complete -F _fzf_all_completion -o default $cmd +done + +bind '"\e\e": complete' +bind '"\er": redraw-current-line' +bind '"\C-i": "\e\e\er"' + diff --git a/fzf-completion.zsh b/fzf-completion.zsh new file mode 100644 index 0000000..5e2db28 --- /dev/null +++ b/fzf-completion.zsh @@ -0,0 +1,9 @@ +#!/bin/zsh +# ____ ____ +# / __/___ / __/ +# / /_/_ / / /_ +# / __/ / /_/ __/ +# /_/ /___/_/-completion.zsh +# + +# TODO diff --git a/install b/install index 22353ee..54ca03b 100755 --- a/install +++ b/install @@ -1,7 +1,72 @@ #!/bin/bash cd `dirname $BASH_SOURCE` -mkdir -p ~/bin -ln -sf `pwd`/fzf ~/bin/fzf -chmod +x ~/bin/fzf +FZF_BASE=`pwd` + +# ruby executable +echo -n "Checking Ruby executable ... " +RUBY=`which ruby` +if [ $? -ne 0 ]; then + echo "ruby executable not found!" + exit 1 +fi +echo "OK" + +# Curses-support +echo -n "Checking Curses support ... " +/usr/bin/env ruby -e "begin; require 'curses'; rescue Exception; exit 1; end" +if [ $? -ne 0 ]; then + echo "Your ruby does not support 'curses'" + exit 1 +fi +echo "OK" + +# Ruby version +echo -n "Checking Ruby version ... " +/usr/bin/env ruby -e 'exit RUBY_VERSION >= "1.9"' +if [ $? -eq 0 ]; then + echo ">= 1.9" + FZF_ALIAS="alias fzf='$RUBY --disable-gems $FZF_BASE/fzf'" +else + echo "< 1.9" + FZF_ALIAS="alias fzf='$RUBY $FZF_BASE/fzf' # fzf" +fi + +# Auto-completion +read -p "Do you want to add auto-completion support? (y/n) " -n 1 -r +echo +[[ ! $REPLY =~ ^[Nn]$ ]] +AUTO_COMPLETION=$? + +echo +for shell in bash zsh; do + rc=~/.${shell}rc + echo "Update $rc:" + + # Install fzf alias + echo "- Add fzf alias:" + echo " - $FZF_ALIAS" + if [ $(grep "alias fzf=" $rc | wc -l) -gt 0 ]; then + echo " - (X) fzf alias already exists" + else + echo $FZF_ALIAS >> $rc + echo " - Added." + fi + + # Install auto-completion support + if [ $AUTO_COMPLETION -eq 0 ]; then + FZF_COMPLETION="source $FZF_BASE/fzf-completion.${shell}" + echo "- Add auto-completion support" + echo " - $FZF_COMPLETION" + if [ $(grep "source.*fzf-completion" $rc | wc -l) -gt 0 ]; then + echo " - (X) fzf-completion.${shell} already being sourced" + else + echo $FZF_COMPLETION >> $rc + echo " - Added." + fi + fi + echo +done + +echo "Finished. Remove the added lines to uninstall fzf."