mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-22 04:45:14 +00:00
82156d34cc
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.
471 lines
11 KiB
Bash
Executable File
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
|
|
|