fzf/install
Junegunn Choi 82156d34cc Update Makefile and install script
fzf may not run correctly on some OS even when the binary the platform
is successfully downloaded. The install script is updated to check if
the system has no problem running the executable and fall back to Ruby
version when necessary.
2015-01-05 12:21:26 +09:00

471 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
version=0.9.0
cd $(dirname $BASH_SOURCE)
fzf_base=$(pwd)
ask() {
read -p "$1 ([y]/n) " -n 1 -r
echo
[[ ! $REPLY =~ ^[Nn]$ ]]
}
check_binary() {
echo "- Checking fzf executable"
echo -n " - "
if ! "$fzf_base"/bin/fzf --version; then
binary_error="Error occurred"
fi
}
download() {
echo "Downloading fzf executable ($1) ..."
if [ -x "$fzf_base"/bin/fzf ]; then
if ! ask "- fzf already exists. Download it again?"; then
check_binary
return
fi
fi
mkdir -p "$fzf_base"/bin && cd "$fzf_base"/bin
if [ $? -ne 0 ]; then
binary_error="Failed to create bin directory"
return
fi
local url=https://github.com/junegunn/fzf-bin/releases/download/snapshot/${1}.tgz
if which curl > /dev/null; then
curl -fL $url | tar -xz
elif which wget > /dev/null; then
wget -O - $url | tar -xz
else
binary_error="curl or wget not found"
return
fi
if [ ! -f $1 ]; then
binary_error="Failed to download ${1}"
return
fi
mv $1 fzf && chmod +x fzf && check_binary
}
# Try to download binary executable
archi=$(uname -sm)
binary_available=1
binary_error=""
if [ "$archi" = "Darwin x86_64" ]; then
download fzf-$version-darwin_amd64
elif [ "$archi" = "Linux x86_64" ]; then
download fzf-$version-linux_amd64
else
binary_available=0
fi
cd "$fzf_base"
if [ -n "$binary_error" ]; then
if [ $binary_available -eq 0 ]; then
echo "No prebuilt binary for $archi ... "
else
echo " - $binary_error ... "
fi
echo "Installing legacy Ruby version ..."
# 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
system_ruby=/usr/bin/ruby
if [ -x $system_ruby -a $system_ruby != "$ruby" ]; then
$system_ruby --disable-gems -rcurses -e0 2> /dev/null
[ $? -eq 0 ] && ruby=$system_ruby
fi
echo "OK ($ruby)"
# Curses-support
echo -n "Checking Curses support ... "
"$ruby" -rcurses -e0 2> /dev/null
if [ $? -eq 0 ]; then
echo "OK"
else
echo "Not found"
echo "Installing 'curses' gem ... "
if (( EUID )); then
/usr/bin/env gem install curses --user-install
else
/usr/bin/env gem install curses
fi
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 -rcurses -e0 2> /dev/null
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
fi
# Auto-completion
ask "Do you want to add auto-completion support?"
auto_completion=$?
# Key-bindings
ask "Do you want to add key bindings?"
key_bindings=$?
echo
for shell in bash zsh; do
echo -n "Generate ~/.fzf.$shell ... "
src=~/.fzf.${shell}
fzf_completion="[[ \$- =~ i ]] && 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
unset fzf 2> /dev/null
if [ -x "$fzf_base/bin/fzf" ]; then
if [[ ! "\$PATH" =~ "$fzf_base/bin" ]]; then
export PATH="$fzf_base/bin:\$PATH"
fi
else
fzf() {
$fzf_cmd "\$@"
}
export -f fzf > /dev/null
fi
# Auto-completion
# ---------------
$fzf_completion
EOF
if [ $key_bindings -eq 0 ]; then
if [ $shell = bash ]; then
cat >> $src << "EOFZF"
# Key bindings
# ------------
__fsel() {
command 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
}
if [[ $- =~ i ]]; then
__fsel_tmux() {
local height
height=${FZF_TMUX_HEIGHT:-40%}
if [[ $height =~ %$ ]]; then
height="-p ${height%\%}"
else
height="-l $height"
fi
tmux split-window $height "bash -c 'source ~/.fzf.bash; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
}
__fcd() {
local dir
dir=$(command find -L ${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 \C-h"'
fi
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s +m -n2..,.. | 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\exa "'
fi
bind -m vi-command '"\C-t": "i\C-t"'
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": "\eddi$(HISTTIMEFORMAT= history | fzf +s +m -n2..,.. | sed \"s/ *[0-9]* *//\")\C-x\C-e\e$a\C-x\C-r"'
bind -m vi-command '"\C-r": "i\C-r"'
# ALT-C - cd into the selected directory
bind '"\ec": "\eddi$(__fcd)\C-x\C-e\C-x\C-r\C-m"'
bind -m vi-command '"\ec": "i\ec"'
fi
unset __use_tmux
fi
EOFZF
else
cat >> $src << "EOFZF"
# Key bindings
# ------------
# CTRL-T - Paste the selected file path(s) into the command line
__fsel() {
set -o nonomatch
command 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
}
if [[ $- =~ i ]]; then
if [ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ]; then
fzf-file-widget() {
local height
height=${FZF_TMUX_HEIGHT:-40%}
if [[ $height =~ %$ ]]; then
height="-p ${height%\%}"
else
height="-l $height"
fi
tmux split-window $height "zsh -c 'source ~/.fzf.zsh; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
}
else
fzf-file-widget() {
LBUFFER="${LBUFFER}$(__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 "${$(set -o nonomatch; command find -L * -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 +m -n2..,.. | sed "s/ *[0-9*]* *//")
zle redisplay
}
zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
fi
EOFZF
fi
fi
echo "OK"
done
# fish
has_fish=0
if [ -n "$(which fish)" ]; then
has_fish=1
echo -n "Generate ~/.config/fish/functions/fzf.fish ... "
mkdir -p ~/.config/fish/functions
if [ -n "$binary_error" ]; then
cat > ~/.config/fish/functions/fzf.fish << EOFZF
function fzf
$fzf_cmd \$argv
end
EOFZF
else
cat > ~/.config/fish/functions/fzf.fish << EOFZF
function fzf
$fzf_base/bin/fzf \$argv
end
EOFZF
fi
echo "OK"
if [ $key_bindings -eq 0 ]; then
echo -n "Generate ~/.config/fish/functions/fzf_key_bindings.fish ... "
cat > ~/.config/fish/functions/fzf_key_bindings.fish << "EOFZF"
function fzf_key_bindings
# Due to a bug of fish, we cannot use command substitution,
# so we use temporary file instead
if [ -z "$TMPDIR" ]
set -g TMPDIR /tmp
end
function __fzf_list
command find * -path '*/\.*' -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null
end
function __fzf_list_dir
command find -L * -path '*/\.*' -prune -o -type d -print 2> /dev/null
end
function __fzf_escape
while read item
echo -n (echo -n "$item" | sed -E 's/([ "$~'\''([{<>})])/\\\\\\1/g')' '
end
end
function __fzf_ctrl_t
if [ -n "$TMUX_PANE" -a "$FZF_TMUX" != "0" ]
tmux split-window (__fzf_tmux_height) "fish -c 'fzf_key_bindings; __fzf_ctrl_t_tmux \\$TMUX_PANE'"
else
__fzf_list | fzf -m > $TMPDIR/fzf.result
and commandline -i (cat $TMPDIR/fzf.result | __fzf_escape)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
end
function __fzf_ctrl_t_tmux
__fzf_list | fzf -m > $TMPDIR/fzf.result
and tmux send-keys -t $argv[1] (cat $TMPDIR/fzf.result | __fzf_escape)
rm -f $TMPDIR/fzf.result
end
function __fzf_reverse
if which tac > /dev/null
tac $argv
else
tail -r $argv
end
end
function __fzf_ctrl_r
history | __fzf_reverse | fzf +s +m > $TMPDIR/fzf.result
and commandline (cat $TMPDIR/fzf.result)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
function __fzf_alt_c
# Fish hangs if the command before pipe redirects (2> /dev/null)
__fzf_list_dir | fzf +m > $TMPDIR/fzf.result
[ (cat $TMPDIR/fzf.result | wc -l) -gt 0 ]
and cd (cat $TMPDIR/fzf.result)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
function __fzf_tmux_height
if set -q FZF_TMUX_HEIGHT
set height $FZF_TMUX_HEIGHT
else
set height 40%
end
if echo $height | \grep -q -E '%$'
echo "-p "(echo $height | sed 's/%$//')
else
echo "-l $height"
end
set -e height
end
bind \ct '__fzf_ctrl_t'
bind \cr '__fzf_ctrl_r'
bind \ec '__fzf_alt_c'
end
EOFZF
echo "OK"
fi
fi
append_line() {
echo "Update $2:"
echo " - $1"
[ -f "$2" ] || touch "$2"
if [ $# -lt 3 ]; then
line=$(\grep -nF "$1" "$2" | sed 's/:.*//' | tr '\n' ' ')
else
line=$(\grep -nF "$3" "$2" | sed 's/:.*//' | tr '\n' ' ')
fi
if [ -n "$line" ]; then
echo " - Already exists: line #$line"
else
echo "$1" >> "$2"
echo " + Added"
fi
echo
}
echo
for shell in bash zsh; do
append_line "[ -f ~/.fzf.${shell} ] && source ~/.fzf.${shell}" ~/.${shell}rc "~/.fzf.${shell}"
done
if [ $key_bindings -eq 0 -a $has_fish -eq 1 ]; then
bind_file=~/.config/fish/functions/fish_user_key_bindings.fish
append_line "fzf_key_bindings" "$bind_file"
echo ' * Due to a known bug of fish, you may have issues running fzf on fish.'
echo ' * If that happens, try the following:'
echo ' - Remove ~/.config/fish/functions/fzf.fish'
echo ' - Place fzf executable in a directory included in $PATH'
echo
fi
cat << EOF
Finished. Restart your shell or reload config file.
source ~/.bashrc # bash
source ~/.zshrc # zsh
EOF
[ $has_fish -eq 1 ] && echo " fzf_key_bindings # fish"; cat << EOF
Use uninstall script to remove fzf.
For more information, see: https://github.com/junegunn/fzf
EOF