Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
install 5.98 KiB
#!/bin/bash

cd `dirname $BASH_SOURCE`
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

# System ruby is preferred
curses_check="begin; require 'curses'; rescue Exception; exit 1; end"
system_ruby=/usr/bin/ruby
if [ -x $system_ruby -a $system_ruby != "$ruby" ]; then
  $system_ruby --disable-gems -e "$curses_check" 2> /dev/null
  [ $? -eq 0 ] && ruby=$system_ruby
fi

echo "OK ($ruby)"

# Curses-support
echo -n "Checking Curses support ... "
"$ruby" -e "$curses_check"
if [ $? -eq 0 ]; then
  echo "OK"
else
  echo "Not found"
  echo "Installing 'curses' gem ... "
  /usr/bin/env gem install curses -v 1.0.0 --user-install
  if [ $? -ne 0 ]; then
    echo
    echo "Failed to install 'curses' gem."
    if [[ $(uname -r) =~ 'ARCH' ]]; then
      echo "Make sure that base-devel package group is installed."
    fi
    exit 1
  fi
fi

# Ruby version
echo -n "Checking Ruby version ... "
"$ruby" -e 'exit RUBY_VERSION >= "1.9"'
if [ $? -eq 0 ]; then
  echo ">= 1.9"
  "$ruby" --disable-gems -e "$curses_check"
  if [ $? -eq 0 ]; then
    fzf_cmd="$ruby --disable-gems $fzf_base/fzf"
  else
    fzf_cmd="$ruby $fzf_base/fzf"
  fi
else
  echo "< 1.9"
  fzf_cmd="$ruby $fzf_base/fzf"
fi

# Auto-completion
read -p "Do you want to add auto-completion support? ([y]/n) " -n 1 -r
echo
[[ ! $REPLY =~ ^[Nn]$ ]]
auto_completion=$?

# Key-bindings
read -p "Do you want to add key bindings? ([y]/n) " -n 1 -r
echo
[[ ! $REPLY =~ ^[Nn]$ ]]
key_bindings=$?

echo
for shell in bash zsh; do
  echo -n "Generate ~/.fzf.$shell ... "
  src=~/.fzf.${shell}

  fzf_completion="source $fzf_base/fzf-completion.${shell}"
  if [ $auto_completion -ne 0 ]; then
    fzf_completion="# $fzf_completion"
  fi

  cat > $src << EOF
# Setup fzf function
# ------------------
unalias fzf 2> /dev/null
fzf() {
  $fzf_cmd "\$@"
}
export -f fzf > /dev/null

# Auto-completion
# ---------------
$fzf_completion

EOF

  if [ $key_bindings -eq 0 ]; then
    if [ $shell = bash ]; then
      cat >> $src << "EOFZF"
# Key bindings
# ------------
if [[ $- =~ i ]]; then

read -r -d '' __fsel <<'EOF'
  find * -path '*/\.*' -prune \
    -o -type f -print \
    -o -type d -print \
    -o -type l -print 2> /dev/null | fzf -m | while read item; do
    printf '%q ' "$item"
  done
  echo
EOF

__fsel() {
  eval "$__fsel"
}

__fsel_tmux() {
  local height lines
  height=${FZF_TMUX_HEIGHT:-40%}
  lines=${LINES:-40}
  if [[ $height =~ %$ ]]; then
    height=${height:0:${#height}-1}
    height=$(( height * lines / 100 ))
  fi
  tmux split-window -l $height "tmux send-keys -t $TMUX_PANE \"\$($__fsel)\""
}

__fcd() {
  local dir
  dir=$(find ${1:-*} -path '*/\.*' -prune -o -type d -print 2> /dev/null | fzf +m) && printf 'cd %q' "$dir"
}

__use_tmux=0
[ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ] && __use_tmux=1

if [ -z "$(set -o | grep '^vi.*on')" ]; then
  # Required to refresh the prompt after fzf
  bind '"\er": redraw-current-line'

  # CTRL-T - Paste the selected file path into the command line
  if [ $__use_tmux -eq 1 ]; then
    bind '"\C-t": " \C-u \C-a\C-k$(__fsel_tmux)\e\C-e\C-y\C-a\C-d\C-y\ey\C-h"'
  else
    bind '"\C-t": " \C-u \C-a\C-k$(__fsel)\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er"'
  fi

  # CTRL-R - Paste the selected command from history into the command line
  bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s | sed \"s/ *[0-9]* *//\")\e\C-e\er"'

  # ALT-C - cd into the selected directory
  bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
else
  bind '"\C-x\C-e": shell-expand-line'
  bind '"\C-x\C-r": redraw-current-line'

  # CTRL-T - Paste the selected file path into the command line
  # - FIXME: Selected items are attached to the end regardless of cursor position
  if [ $__use_tmux -eq 1 ]; then
    bind '"\C-t": "\e$a \eddi$(__fsel_tmux)\C-x\C-e\e0P$xa"'
  else
    bind '"\C-t": "\e$a \eddi$(__fsel)\C-x\C-e\e0Px$a \C-x\C-r"'
  fi

  # CTRL-R - Paste the selected command from history into the command line
  bind '"\C-r": "\eddi$(HISTTIMEFORMAT= history | fzf +s | sed \"s/ *[0-9]* *//\")\C-x\C-e\e$a\C-x\C-r"'

  # ALT-C - cd into the selected directory
  bind '"\ec": "\eddi$(__fcd)\C-x\C-e\C-x\C-r\C-m"'
fi

unset __use_tmux

fi
EOFZF
    else
      cat >> $src << "EOFZF"
# Key bindings
# ------------
# CTRL-T - Paste the selected file path(s) into the command line
read -r -d '' __fsel <<'EOF'
  find * -path '*/\.*' -prune \
    -o -type f -print \
    -o -type d -print \
    -o -type l -print 2> /dev/null | fzf -m | while read item; do
    printf '%q ' "$item"
  done
  echo
EOF

if [ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ]; then
  fzf-file-widget() {
    local height lines
    height=${FZF_TMUX_HEIGHT:-40%}
    lines=${LINES:-40}
    if [[ $height =~ %$ ]]; then
      height=${height:0:${#height}-1}
      height=$(( height * lines / 100 ))
    fi
    tmux split-window -l $height "tmux send-keys -t $TMUX_PANE \"\$($__fsel)\""
  }
else
  fzf-file-widget() {
    LBUFFER="${LBUFFER%% #}$(eval "$__fsel")"
    zle redisplay
  }
fi
zle     -N   fzf-file-widget
bindkey '^T' fzf-file-widget

# ALT-C - cd into the selected directory
fzf-cd-widget() {
  cd "${$(find * -path '*/\.*' -prune \
          -o -type d -print 2> /dev/null | fzf):-.}"
  zle reset-prompt
}
zle     -N    fzf-cd-widget
bindkey '\ec' fzf-cd-widget

# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
  LBUFFER=$(fc -l 1 | fzf +s | sed "s/ *[0-9]* *//")
  zle redisplay
}
zle     -N   fzf-history-widget
bindkey '^R' fzf-history-widget

EOFZF
    fi
  fi

  echo "OK"
done

echo
for shell in bash zsh; do
  rc=~/.${shell}rc
  src="source ~/.fzf.${shell}"

  echo "Update $rc:"
  echo "  - $src"
  if [ $(grep -F "$src" $rc | wc -l) -gt 0 ]; then
    echo "    - Not added (already being sourced)"
  else
    echo $src >> $rc
    echo "    - Added"
  fi
  echo
done

cat << EOF
Finished. Reload your .bashrc or .zshrc.
   source ~/.bashrc  # bash
   source ~/.zshrc   # zsh

To uninstall fzf, simply remove the added line.
EOF