#!/usr/bin/env bash # We don't need return codes for "$(command)", only stdout is needed. # Allow `[[ -n "$(command)" ]]`, `func "$(command)"`, pipes, etc. # shellcheck disable=SC2312 set -uo pipefail # global-scope vars REQUIRED_NVIM_VERSION=0.10.0 REQUIRED_NVIM_VERSION_LEGACY=0.9.0 USE_SSH=1 CLONE_ATTR=("--progress") DEST_DIR="${HOME}/.config/nvim" BACKUP_DIR="${DEST_DIR}_backup-$(date +%Y%m%dT%H%M%S)" abort() { printf "%s\n" "$@" >&2 exit 1 } # Fail fast with a concise message when not using bash # Single brackets are needed here for POSIX compatibility # shellcheck disable=SC2292 if [ -z "${BASH_VERSION:-}" ]; then abort "Bash is required to interpret this script." fi # Check if script is run with force-interactive mode in CI if [[ -n "${CI-}" && -n "${INTERACTIVE-}" ]]; then abort "Cannot run force-interactive mode in CI." fi # String formatters if [[ -t 1 ]]; then tty_escape() { printf "\033[%sm" "$1"; } else tty_escape() { :; } fi tty_mkbold() { tty_escape "1;$1"; } tty_underline="$(tty_escape "4;39")" tty_yellow="$(tty_escape "0;33")" tty_blue="$(tty_mkbold 34)" tty_red="$(tty_mkbold 31)" tty_bold="$(tty_mkbold 39)" tty_reset="$(tty_escape 0)" shell_join() { local arg printf "%s" "$1" shift for arg in "$@"; do printf " " printf "%s" "${arg// /\ }" done } execute() { if ! "$@"; then abort "$(printf "Failed during: %s" "$(shell_join "$@")")" fi } major_minor() { echo "${1%%.*}.$( x="${1#*.}" echo "${x%%.*}" )" } chomp() { printf "%s" "${1/"$'\n'"/}" } info() { printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" } info_ext() { printf "${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" } warn() { printf "${tty_yellow}Warning:${tty_reset} %s\n" "$(chomp "$1")" } warn_ext() { printf " %s\n" "$(chomp "$1")" } getc() { local save_state save_state="$(/bin/stty -g)" /bin/stty raw -echo IFS='' read -r -n 1 -d '' "$@" /bin/stty "${save_state}" } ring_bell() { # Use the shell's audible bell if [[ -t 1 ]]; then printf "\a" fi } wait_for_user() { local c printf "\n" echo "Press ${tty_bold}RETURN${tty_reset}/${tty_bold}ENTER${tty_reset} to continue or any other key to abort..." getc c # we test for \r and \n because some stuff does \r instead if ! [[ "$c" == $'\r' || "$c" == $'\n' ]]; then abort "${tty_red}Aborted.${tty_reset}" fi } version_ge() { [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -ge "${2#*.}" ]] } prompt_confirm() { local choice while true; do read -r -p "$1 [Y/n]: " choice case "$choice" in [yY][eE][sS] | [yY]) return 1 ;; [nN][oO] | [nN]) return 0 ;; *) if [[ -z "$choice" ]]; then return 1 fi printf "${tty_red}%s\n\n${tty_reset}" "Input invalid! Please enter one of the following: '[y/yes]' or '[n/no]'." ;; esac done } check_ssh() { info "Validating SSH connection..." ssh -T git@github.com &>/dev/null if ! [ $? -eq 1 ]; then info "We'll use HTTPS to fetch and update plugins." return 0 else prompt_confirm "Do you prefer to use SSH to fetch and update plugins? (otherwise HTTPS)" return $? fi } clone_pref() { info "Checking 'git clone' preferences..." if ! prompt_confirm "Would you like to perform a shallow clone ('--depth=1')?"; then CLONE_ATTR+=("--depth=1") fi } check_nvim_version() { local nvim_version nvim_version="$(nvim --version | head -n1 | sed -e 's|^[^0-9]*||' -e 's| .*||')" if version_ge "$(major_minor "${nvim_version##* }")" "$(major_minor "$1")"; then return 0 else return 1 fi } clone_repo() { if check_nvim_version "${REQUIRED_NVIM_VERSION}"; then execute "git" "clone" "-b" "main" "${CLONE_ATTR[@]}" "$1" "${DEST_DIR}" elif check_nvim_version "${REQUIRED_NVIM_VERSION_LEGACY}"; then warn "You have outdated Nvim installed (< ${REQUIRED_NVIM_VERSION})." info "Automatically redirecting you to the latest compatible version..." execute "git" "clone" "-b" "0.9" "${CLONE_ATTR[@]}" "$1" "${DEST_DIR}" else warn "You have outdated Nvim installed (< ${REQUIRED_NVIM_VERSION_LEGACY})." abort "$( cat </dev/null; then abort "$( cat </dev/null; then abort "$( cat </dev/null; then abort "$( cat <