#!/usr/bin/env bash set -u [[ "$@" =~ --pre ]] && version=0.15.2 pre=1 || version=0.15.2 pre=0 auto_completion= key_bindings= update_config=2 binary_arch= allow_legacy= help() { cat << EOF usage: $0 [OPTIONS] --help Show this message --bin Download fzf binary only; Do not generate ~/.fzf.{bash,zsh} --all Download fzf binary and update configuration files to enable key bindings and fuzzy completion --[no-]key-bindings Enable/disable key bindings (CTRL-T, CTRL-R, ALT-C) --[no-]completion Enable/disable fuzzy completion (bash & zsh) --[no-]update-rc Whether or not to update shell configuration files --32 Download 32-bit binary --64 Download 64-bit binary EOF } for opt in "$@"; do case $opt in --help) help exit 0 ;; --all) auto_completion=1 key_bindings=1 update_config=1 allow_legacy=1 ;; --key-bindings) key_bindings=1 ;; --no-key-bindings) key_bindings=0 ;; --completion) auto_completion=1 ;; --no-completion) auto_completion=0 ;; --update-rc) update_config=1 ;; --no-update-rc) update_config=0 ;; --32) binary_arch=386 ;; --64) binary_arch=amd64 ;; --bin|--pre) ;; *) echo "unknown option: $opt" help exit 1 ;; esac done cd "$(dirname "${BASH_SOURCE[0]}")" fzf_base="$(pwd)" ask() { # If stdin is a tty, we are "interactive". # non-interactive shell: wait for a linefeed # interactive shell: continue after a single keypress read_n=$([ -t 0 ] && echo "-n 1") read -p "$1 ([y]/n) " $read_n -r echo [[ $REPLY =~ ^[Nn]$ ]] } check_binary() { echo -n " - Checking fzf executable ... " local output output=$("$fzf_base"/bin/fzf --version 2>&1) if [ $? -ne 0 ]; then echo "Error: $output" binary_error="Invalid binary" elif [ "$version" != "$output" ]; then echo "$output != $version" binary_error="Invalid version" else echo "$output" binary_error="" return 0 fi rm -f "$fzf_base"/bin/fzf return 1 } symlink() { echo " - Creating symlink: bin/$1 -> bin/fzf" (cd "$fzf_base"/bin && rm -f fzf && ln -sf $1 fzf) if [ $? -ne 0 ]; then binary_error="Failed to create symlink" return 1 fi } link_fzf_in_path() { if which_fzf="$(command -v fzf)"; then echo " - Found in \$PATH" echo " - Creating symlink: $which_fzf -> bin/fzf" (cd "$fzf_base"/bin && rm -f fzf && ln -sf "$which_fzf" fzf) check_binary && return fi return 1 } try_curl() { command -v curl > /dev/null && curl -fL $1 | tar -xz } try_wget() { command -v wget > /dev/null && wget -O - $1 | tar -xz } download() { echo "Downloading bin/fzf ..." if [ $pre = 0 ]; then if [ -x "$fzf_base"/bin/fzf ]; then echo " - Already exists" check_binary && return fi if [ -x "$fzf_base"/bin/$1 ]; then symlink $1 && check_binary && return fi link_fzf_in_path && return 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/$version/${1}.tgz set -o pipefail if ! (try_curl $url || try_wget $url); then set +o pipefail binary_error="Failed to download with curl and wget" return fi set +o pipefail if [ ! -f $1 ]; then binary_error="Failed to download ${1}" return fi chmod +x $1 && symlink $1 && check_binary } # Try to download binary executable archi=$(uname -sm) binary_available=1 binary_error="" case "$archi" in Darwin\ x86_64) download fzf-$version-darwin_${binary_arch:-amd64} ;; Darwin\ i*86) download fzf-$version-darwin_${binary_arch:-386} ;; Linux\ x86_64) download fzf-$version-linux_${binary_arch:-amd64} ;; Linux\ i*86) download fzf-$version-linux_${binary_arch:-386} ;; *) binary_available=0 binary_error=1 ;; esac install_ruby_fzf() { if [ -z "$allow_legacy" ]; then ask "Do you want to install legacy Ruby version instead?" && exit 1 fi echo "Installing legacy Ruby version ..." # ruby executable echo -n "Checking Ruby executable ... " ruby=$(command -v 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 ] && [ $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 # Create fzf script echo -n "Creating wrapper script for fzf ... " rm -f "$fzf_base"/bin/fzf echo "#!/bin/sh" > "$fzf_base"/bin/fzf echo "$fzf_cmd \"\$@\"" >> "$fzf_base"/bin/fzf chmod +x "$fzf_base"/bin/fzf echo "OK" } 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 if command -v go > /dev/null; then echo -n "Building binary (go get -u github.com/junegunn/fzf/src/fzf) ... " if [ -z "${GOPATH-}" ]; then export GOPATH="${TMPDIR:-/tmp}/fzf-gopath" mkdir -p "$GOPATH" fi if go get -u github.com/junegunn/fzf/src/fzf; then echo "OK" cp "$GOPATH/bin/fzf" "$fzf_base/bin/" else echo "Failed to build binary ..." install_ruby_fzf fi else echo "go executable not found. Cannot build binary ..." install_ruby_fzf fi fi [[ "$*" =~ "--bin" ]] && exit 0 # Auto-completion if [ -z "$auto_completion" ]; then ask "Do you want to enable fuzzy auto-completion?" auto_completion=$? fi # Key-bindings if [ -z "$key_bindings" ]; then ask "Do you want to enable key bindings?" key_bindings=$? fi echo has_zsh=$(command -v zsh > /dev/null && echo 1 || echo 0) shells=$([ $has_zsh -eq 1 ] && echo "bash zsh" || echo "bash") for shell in $shells; do echo -n "Generate ~/.fzf.$shell ... " src=~/.fzf.${shell} fzf_completion="[[ \$- == *i* ]] && source \"$fzf_base/shell/completion.${shell}\" 2> /dev/null" if [ $auto_completion -eq 0 ]; then fzf_completion="# $fzf_completion" fi fzf_key_bindings="source \"$fzf_base/shell/key-bindings.${shell}\"" if [ $key_bindings -eq 0 ]; then fzf_key_bindings="# $fzf_key_bindings" fi cat > $src << EOF # Setup fzf # --------- if [[ ! "\$PATH" == *$fzf_base/bin* ]]; then export PATH="\$PATH:$fzf_base/bin" fi # Man path # -------- if [[ ! "\$MANPATH" == *$fzf_base/man* && -d "$fzf_base/man" ]]; then export MANPATH="\$MANPATH:$fzf_base/man" fi # Auto-completion # --------------- $fzf_completion # Key bindings # ------------ $fzf_key_bindings EOF echo "OK" done # fish has_fish=$(command -v fish > /dev/null && echo 1 || echo 0) if [ $has_fish -eq 1 ]; then echo -n "Update fish_user_paths ... " fish << EOF echo \$fish_user_paths | grep $fzf_base/bin > /dev/null or set --universal fish_user_paths \$fish_user_paths $fzf_base/bin EOF [ $? -eq 0 ] && echo "OK" || echo "Failed" mkdir -p ~/.config/fish/functions if [ -e ~/.config/fish/functions/fzf.fish ]; then echo -n "Remove unnecessary ~/.config/fish/functions/fzf.fish ... " rm -f ~/.config/fish/functions/fzf.fish && echo "OK" || echo "Failed" fi fish_binding=~/.config/fish/functions/fzf_key_bindings.fish if [ $key_bindings -ne 0 ]; then echo -n "Symlink $fish_binding ... " ln -sf "$fzf_base/shell/key-bindings.fish" \ "$fish_binding" && echo "OK" || echo "Failed" else echo -n "Removing $fish_binding ... " rm -f "$fish_binding" echo "OK" fi fi append_line() { set -e local update line file pat lno update="$1" line="$2" file="$3" pat="${4:-}" echo "Update $file:" echo " - $line" [ -f "$file" ] || touch "$file" if [ $# -lt 4 ]; then lno=$(\grep -nF "$line" "$file" | sed 's/:.*//' | tr '\n' ' ') else lno=$(\grep -nF "$pat" "$file" | sed 's/:.*//' | tr '\n' ' ') fi if [ -n "$lno" ]; then echo " - Already exists: line #$lno" else if [ $update -eq 1 ]; then echo >> "$file" echo "$line" >> "$file" echo " + Added" else echo " ~ Skipped" fi fi echo set +e } if [ $update_config -eq 2 ]; then echo ask "Do you want to update your shell configuration files?" update_config=$? fi echo for shell in $shells; do [ $shell = zsh ] && dest=${ZDOTDIR:-~}/.zshrc || dest=~/.bashrc append_line $update_config "[ -f ~/.fzf.${shell} ] && source ~/.fzf.${shell}" "$dest" "~/.fzf.${shell}" done if [ $key_bindings -eq 1 ] && [ $has_fish -eq 1 ]; then bind_file=~/.config/fish/functions/fish_user_key_bindings.fish append_line $update_config "fzf_key_bindings" "$bind_file" fi if [ $update_config -eq 1 ]; then echo 'Finished. Restart your shell or reload config file.' echo ' source ~/.bashrc # bash' [ $has_zsh -eq 1 ] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh" [ $has_fish -eq 1 ] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish' echo echo 'Use uninstall script to remove fzf.' echo fi echo 'For more information, see: https://github.com/junegunn/fzf'