From bf3005ed13af40d8839a9e48b176b3ceb0288b26 Mon Sep 17 00:00:00 2001 From: davidovski Date: Sat, 9 Oct 2021 22:34:01 +0100 Subject: added bash completion --- bash_aliases | 1 + bashrc | 72 +---- config/mpd/state | 4 +- config/vim/viminfo | Bin 159876 -> 159822 bytes deploy.sh | 1 + scripts/.scripts/fzf-bash-completion.sh | 470 ++++++++++++++++++++++++++++++++ 6 files changed, 475 insertions(+), 73 deletions(-) create mode 100644 scripts/.scripts/fzf-bash-completion.sh diff --git a/bash_aliases b/bash_aliases index 9d22389..41e62db 100644 --- a/bash_aliases +++ b/bash_aliases @@ -1,3 +1,4 @@ +alias l="ls -lah" alias cls="clear" alias cp="cp -v" alias gif-for-cli="gif-for-cli -l 0 -c █ --display-mode=truecolor" diff --git a/bashrc b/bashrc index dc55339..72262cd 100644 --- a/bashrc +++ b/bashrc @@ -22,60 +22,8 @@ shopt -s histappend HISTSIZE=1000 HISTFILESIZE=2000 -# check the window size after each command and, if necessary, -# update the values of LINES and COLUMNS. shopt -s checkwinsize -# If set, the pattern "**" used in a pathname expansion context will -# match all files and zero or more directories and subdirectories. -#shopt -s globstar - -# make less more friendly for non-text input files, see lesspipe(1) -#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" - -# set variable identifying the chroot you work in (used in the prompt below) -if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then - debian_chroot=$(cat /etc/debian_chroot) -fi - -# set a fancy prompt (non-color, unless we know we "want" color) -case "$TERM" in - xterm-color|*-256color) color_prompt=yes;; -esac - -# uncomment for a colored prompt, if the terminal has the capability; turned -# off by default to not distract the user: the focus in a terminal window -# should be on the output of commands, not on the prompt -#force_color_prompt=yes - -if [ -n "$force_color_prompt" ]; then - if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then - # We have color support; assume it's compliant with Ecma-48 - # (ISO/IEC-6429). (Lack of such support is extremely rare, and such - # a case would tend to support setf rather than setaf.) - color_prompt=yes - else - color_prompt= - fi -fi - -if [ "$color_prompt" = yes ]; then - PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' -else - PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' -fi -unset color_prompt force_color_prompt - -# If this is an xterm set the title to user@host:dir -case "$TERM" in -xterm*|rxvt*) - PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" - ;; -*) - ;; -esac - -# enable color support of ls and also add handy aliases if [ -x /usr/bin/dircolors ]; then test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" alias ls='ls --color=auto' @@ -87,25 +35,10 @@ if [ -x /usr/bin/dircolors ]; then alias egrep='egrep --color=auto' fi -# colored GCC warnings and errors -#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' - -# some more ls aliases -#alias ll='ls -l' -#alias la='ls -A' -alias l="ls -lah" -# Alias definitions. -# You may want to put all your additions into a separate file like -# ~/.bash_aliases, instead of adding them here directly. -# See /usr/share/doc/bash-doc/examples in the bash-doc package. - if [ -f ~/.bash_aliases ]; then . ~/.bash_aliases fi -# enable programmable completion features (you don't need to enable -# this, if it's already enabled in /etc/bash.bashrc and /etc/profile -# sources /etc/bash.bashrc). if ! shopt -oq posix; then if [ -f /usr/share/bash-completion/bash_completion ]; then . /usr/share/bash-completion/bash_completion @@ -161,14 +94,11 @@ function parse_git_dirty { fi } -source ~/.local/src/fzf-tab-completion/bash/fzf-bash-completion.sh +source ~/.scripts/fzf-bash-completion.sh bind -x '"\C-\t": fzf_bash_completion' -# Custom prompt -#export PS1="\[\033[38;5;6m\][\[$(tput sgr0)\]\[\033[38;5;2m\]\u\[$(tput sgr0)\]\[\033[38;5;10m\]@\[$(tput sgr0)\]\[\033[38;5;2m\]\h\[$(tput sgr0)\]\[\033[38;5;6m\]][\[$(tput sgr0)\]\[\033[38;5;2m\]\w\[$(tput sgr0)\]\[\033[38;5;6m\]]\[$(tput sgr0)\]\[$(tput sgr0)\]\[\033[38;5;159m\]\`parse_git_branch\`\[$(tput sgr0)\] " export PS1="\[\e[0;97m\]\w\[\e[0;37m\]\`parse_git_branch\` > \[\e[0;0m\]" -# ALWAYS start in the default location? cd PATH="/home/david/.mangadl-bash:${PATH}" diff --git a/config/mpd/state b/config/mpd/state index 7a2dffc..f052e7b 100644 --- a/config/mpd/state +++ b/config/mpd/state @@ -3,8 +3,8 @@ audio_device_state:1:My HTTP Stream audio_device_state:1:mpd audio_device_state:1:Visualizer feed state: play -current: 503 -time: 120.047000 +current: 1694 +time: 120.001000 random: 1 repeat: 1 single: 0 diff --git a/config/vim/viminfo b/config/vim/viminfo index e65fa82..5db687f 100644 Binary files a/config/vim/viminfo and b/config/vim/viminfo differ diff --git a/deploy.sh b/deploy.sh index c45efe6..983c4e6 100755 --- a/deploy.sh +++ b/deploy.sh @@ -21,6 +21,7 @@ save () { cp /usr/share/fonts/TTF/Meslo\ LG\ M\ Regular\ Nerd\ Font\ Complete\ Mono.ttf fonts/TTF/ mkdir -p fonts/noto-cjk cp -r /usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc fonts/noto-cjk + } load () { diff --git a/scripts/.scripts/fzf-bash-completion.sh b/scripts/.scripts/fzf-bash-completion.sh new file mode 100644 index 0000000..9a63e15 --- /dev/null +++ b/scripts/.scripts/fzf-bash-completion.sh @@ -0,0 +1,470 @@ +_FZF_COMPLETION_SEP=$'\x01' + +# shell parsing stuff +_fzf_bash_completion_egrep="$( { which rg || echo egrep; } 2>/dev/null)" +_fzf_bash_completion_awk="$( { which gawk || echo awk; } 2>/dev/null)" +_fzf_bash_completion_sed="$( { which gsed || echo sed; } 2>/dev/null)" + +_fzf_bash_completion_awk_escape() { + "$_fzf_bash_completion_sed" 's/\\/\\\\\\\\/g; s/[[*^$.]/\\\\&/g' <<<"$1" +} + +_fzf_bash_completion_shell_split() { + "$_fzf_bash_completion_egrep" -o \ + -e '[;(){}&\|:]' \ + -e '\|+|&+' \ + -e "(\\\\.|[^\"'[:space:];:(){}&\\|])+" \ + -e "\\\$'(\\\\.|[^'])*('|$)" \ + -e "'[^']*('|$)" \ + -e '"(\\.|\$($|[^(])|[^"$])*("|$)' \ + -e '".*' -e . +} + +_fzf_bash_completion_unbuffered_awk() { + # need to get awk to be unbuffered either by using -W interactive or system("") + "$_fzf_bash_completion_awk" -W interactive "${@:3}" "$1 { $2; print \$0; system(\"\") }" 2>/dev/null +} + +_fzf_bash_completion_flatten_subshells() { + ( + local count=0 buffer= + while IFS= read -r line; do + case "$line" in + \(|\{) (( count -- )) ;; + \)|\}) (( count ++ )) ;; + esac + + if (( count < 0 )); then + return + elif (( count > 0 )); then + buffer="$line$buffer" + else + printf '%s\n' "$line$buffer" + buffer= + fi + done < <(tac) + printf '%s\n' "$buffer" + ) | tac +} + +_fzf_bash_completion_find_matching_bracket() { + local count=0 + while IFS=: read num bracket; do + if [ "$bracket" = "$1" ]; then + (( count++ )) + if (( count > 0 )); then + printf '%s\n' "$num" + return 0 + fi + else + (( count -- )) + fi + done < <(fgrep $'(\n)' -n) + return 1 +} + +_fzf_bash_completion_parse_dq() { + local words="$(cat)" + local last="$(<<<"$words" tail -n1)" + + if [[ "$last" == \"* ]]; then + local shell="${last:1}" _shell joined + local word= + while true; do + # we are in a double quoted string + _shell="$(<<<"$shell" "$_fzf_bash_completion_sed" -r 's/^(\\.|[^"$])*\$\(//')" + + if [ "$shell" = "$_shell" ]; then + # no subshells + break + fi + + word+="${shell:0:-${#_shell}-2}" + shell="$_shell" + + # found a subshell + split="$(<<<"$shell" _fzf_bash_completion_shell_split)" + if ! split="$(_fzf_bash_completion_parse_dq <<<"$split")"; then + # bubble up + printf '%s\n' "$split" + return 1 + fi + if ! num="$(_fzf_bash_completion_find_matching_bracket ')' <<<"$split")"; then + # subshell not closed, this is it + printf '%s\n' "$split" + return 1 + fi + # subshell closed + joined="$(<<<"$split" head -n "$num" | tr -d \\n)" + word+=$'\n'"\$($joined"$'\n' + shell="${shell:${#joined}}" + done + fi + printf '%s\n' "$words" +} + +_fzf_bash_completion_parse_line() { + _fzf_bash_completion_shell_split \ + | _fzf_bash_completion_parse_dq \ + | _fzf_bash_completion_flatten_subshells \ + | tr \\n \\0 \ + | "$_fzf_bash_completion_sed" -r "$(cat <<'EOF' +s/\x00\s*\x00/\n/g; +s/\x00(\s*)$/\n\1/; +s/([^&\n\x00])&([^&\n\x00])/\1\n\&\n\2/g; +s/([\n\x00\z])([<>]+)([^\n\x00])/\1\2\n\3/g; +s/([<>][\n\x00])$/\1\n/; +s/^(.*[\x00\n])?(\[\[|case|do|done|elif|else|esac|fi|for|function|if|in|select|then|time|until|while|&|;|&&|\|[|&]?)[\x00\n]//; +s/^(\s*[\n\x00]|\w+=[^\n\x00]*[\n\x00])*// +EOF +)" \ + | tr \\0 \\n +} + +_fzf_bash_completion_compspec() { + complete -p -- "$1" || complete -p '' || printf '%s\n' 'complete -o filenames -F _fzf_bash_completion_fallback_completer' +} + +_fzf_bash_completion_fallback_completer() { + # fallback completion in case no compspecs loaded + if [[ "$1" == \~* && "$1" != */* ]]; then + # complete ~user directories + readarray -t COMPREPLY < <(compgen -P '~' -u -- "${1#\~}") + else + # complete files + readarray -t COMPREPLY < <(compgen -f -- "$1") + fi +} + +_fzf_bash_completion_loading_msg() { + echo 'Loading matches ...' +} + +fzf_bash_completion() { + printf '\r' + command tput sc 2>/dev/null || echo -ne "\0337" + printf '%s' "$(_fzf_bash_completion_loading_msg)" + command tput rc 2>/dev/null || echo -ne "\0338" + + local COMP_WORDS COMP_CWORD COMP_POINT COMP_LINE + local line="${READLINE_LINE:0:READLINE_POINT}" + readarray -t COMP_WORDS < <(_fzf_bash_completion_parse_line <<<"$line") + + if [[ "${#COMP_WORDS[@]}" = 0 || "$line" =~ .*[[:space:]]$ ]]; then + COMP_WORDS+=( '' ) + fi + COMP_CWORD="${#COMP_WORDS[@]}" + (( COMP_CWORD-- )) + + if [[ ${#COMP_WORDS[@]} -gt 1 ]]; then + _fzf_bash_completion_expand_alias "${COMP_WORDS[0]}" + fi + COMP_LINE="${COMP_WORDS[*]}" + COMP_POINT="${#COMP_LINE}" + + local cmd="${COMP_WORDS[0]}" + local prev + if [ "$COMP_CWORD" = 0 ]; then + prev= + else + prev="${COMP_WORDS[COMP_CWORD-1]}" + fi + local cur="${COMP_WORDS[COMP_CWORD]}" + + local COMPREPLY= + fzf_bash_completer "$cmd" "$cur" "$prev" + if [ -n "$COMPREPLY" ]; then + if [ -n "$cur" ]; then + line="${line::-${#cur}}" + fi + READLINE_LINE="${line}${COMPREPLY}${READLINE_LINE:$READLINE_POINT}" + (( READLINE_POINT+=${#COMPREPLY} - ${#cur} )) + fi + + printf '\r' + command tput el 2>/dev/null || echo -ne "\033[K" +} + +_fzf_bash_completion_selector() { + FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" \ + $(__fzfcmd 2>/dev/null || echo fzf) -1 -0 --prompt "> $line" --nth 2 -d "$_FZF_COMPLETION_SEP" \ + | tr -d "$_FZF_COMPLETION_SEP" +} + +_fzf_bash_completion_expand_alias() { + if alias "$1" &>/dev/null; then + value=( ${BASH_ALIASES[$1]} ) + if [ -n "${value[*]}" -a "${value[0]}" != "$1" ]; then + COMP_WORDS=( "${value[@]}" "${COMP_WORDS[@]:1}" ) + COMP_CWORD="$(( COMP_CWORD + ${#value[@]} - 1 ))" + fi + fi +} + +_fzf_bash_completion_get_results() { + local trigger="${FZF_COMPLETION_TRIGGER-**}" + if [[ "$2" =~ .*\$(\{?)([A-Za-z0-9_]*)$ ]]; then + # environment variables + local brace="${BASH_REMATCH[1]}" + local filter="${BASH_REMATCH[2]}" + if [ -n "$filter" ]; then + local prefix="${2:: -${#filter}}" + else + local prefix="$2" + fi + compgen -v -P "$prefix" -S "${brace:+\}}" -- "$filter" + elif [ "$COMP_CWORD" == 0 ]; then + # commands + printf '%s\n' compl_filenames=1 >&"${__evaled}" + compgen -abc -- "$2" | _fzf_bash_completion_dir_marker + elif [[ "$2" == *"$trigger" ]]; then + # replicate fzf ** trigger completion + local suffix="${2##*/}" + local prefix="${2::${#2}-${#suffix}}" + suffix="${suffix::${#suffix}-${#trigger}}" + + local flags=() + if [[ "$1" =~ cd|pushd|rmdir ]]; then + flags=( -type d ) + fi + + if [[ ! "$prefix" =~ (.?/).* ]]; then + prefix="./$prefix" + elif [ "${prefix::2}" = '~/' ]; then + prefix="${HOME}/${prefix:2}" + fi + + # smart case + if [ "${suffix,,}" = "${suffix}" ]; then + flags+=( -ipath "$prefix$suffix*" ) + else + flags+=( -path "$prefix$suffix*" ) + fi + + printf '%s\n' compl_filenames=1 >&"${__evaled}" + find -L "$prefix" -mindepth 1 "${flags[@]}" \( -type d -printf "%p/\n" , -type f -print \) 2>/dev/null | "$_fzf_bash_completion_sed" 's,^\./,,' + else + _fzf_bash_completion_complete "$@" + fi +} + +fzf_bash_completer() { + local value code + local compl_bashdefault compl_default compl_dirnames compl_filenames compl_noquote compl_nosort compl_nospace compl_plusdirs + + # preload completions in top shell + { complete -p -- "$1" || __load_completion "$1"; } &>/dev/null + + eval "$( + set -o pipefail + + # hack: hijack compopt + compopt() { _fzf_bash_completion_compopt "$@"; } + + local __unquoted="${2#[\"\']}" + exec {__evaled}>&1 + coproc ( + ( + _fzf_bash_completion_get_results "$@" + while (( $? == 124 )); do + _fzf_bash_completion_get_results "$@" + done + ) | _fzf_bash_completion_unbuffered_awk '$0!="" && !x[$0]++' '$0 = substr($0, 1, len) sep substr($0, len+1)' -vlen="${#__unquoted}" -vsep="$_FZF_COMPLETION_SEP" + ) + value="$(_fzf_bash_completion_selector "$1" "$__unquoted" "$3" <&"${COPROC[0]}")" + code="$?" + + printf 'COMPREPLY=%q\n' "$value" + printf 'code=%q\n' "$code" + kill 0 + )" + + if [ "$code" = 0 ]; then + readarray -t COMPREPLY < <( + if [ "$compl_noquote" != 1 -a "$compl_filenames" = 1 ]; then + while IFS= read -r line; do + if [ "$line" = "$2" ]; then + printf '%s\n' "$line" + # never quote the prefix + elif [ "${line::${#2}}" = "$2" ]; then + printf '%s%q\n' "$2" "${line:${#2}}" + elif [ "${line::1}" = '~' ]; then + printf '~%q\n' "${line:1}" + else + printf '%q\n' "$line" + fi + done + else + cat + fi <<<"$COMPREPLY" + ) + COMPREPLY="${COMPREPLY[*]}" + [ "$compl_nospace" != 1 ] && COMPREPLY="$COMPREPLY " + [[ "$compl_filenames" == *1* ]] && COMPREPLY="${COMPREPLY/%\/ //}" + fi +} + +_fzf_bash_completion_complete() { + local compgen_actions=() + local compspec="$(_fzf_bash_completion_compspec "$1" 2>/dev/null)" + + eval "compspec=( $compspec )" + set -- "${compspec[@]}" "$@" + shift + while [ "$#" -gt 4 ]; do + case "$1" in + -F) + local compl_function="$2" + shift ;; + -C) + local compl_command="$2" + shift ;; + -G) + local compl_globpat="$2" + shift ;; + -W) + local compl_wordlist="$2" + shift ;; + -X) + local compl_xfilter="$2" + shift ;; + -o) + _fzf_bash_completion_compopt -o "$2" + shift ;; + -A) + local compgen_opts+=( "$1" "$2" ) + shift ;; + -P) + local compl_prefix="$(_fzf_bash_completion_awk_escape "$2")" + shift ;; + -S) + local compl_suffix="$(_fzf_bash_completion_awk_escape "$2")" + shift ;; + -[a-z]) + compgen_actions+=( "$1" ) + ;; + esac + shift + done + shift + + COMPREPLY=() + if [ -n "$compl_function" ]; then + "$compl_function" "$@" >/dev/null + if [ "$?" = 124 ]; then + local newcompspec="$(_fzf_bash_completion_compspec "$1" 2>/dev/null)" + if [ "$newcompspec" != "$compspec" ]; then + return 124 + fi + "$compl_function" "$@" >/dev/null + fi + fi + + compl_filenames="${compl_filenames}${compl_plusdirs}${compl_dirnames}" + if [[ "$compl_filenames" == *1* ]]; then + local dir_marker=_fzf_bash_completion_dir_marker + else + local dir_marker=cat + fi + + printf 'compl_filenames=%q\n' "$compl_filenames" >&"${__evaled}" + printf 'compl_noquote=%q\n' "$compl_noquote" >&"${__evaled}" + printf 'compl_nospace=%q\n' "$compl_nospace" >&"${__evaled}" + + ( + ( + if [ -n "${compgen_actions[*]}" ]; then + compgen "${compgen_opts[@]}" -- "$2" + fi + + if [ -n "$compl_globpat" ]; then + printf %s\\n "$compl_globpat" + fi + + if [ -n "$compl_wordlist" ]; then + eval "printf '%s\\n' $compl_wordlist" + fi + + if [ -n "${COMPREPLY[*]}" ]; then + printf %s\\n "${COMPREPLY[@]}" + fi + + if [ -n "$compl_command" ]; then + ( + unset COMP_WORDS COMP_CWORD + export COMP_LINE="$COMP_LINE" COMP_POINT="$COMP_POINT" COMP_KEY="$COMP_KEY" COMP_TYPE="$COMP_TYPE" + eval "$compl_command" + ) + fi + + printf '%s\n' + ) | _fzf_bash_completion_apply_xfilter "$compl_xfilter" \ + | _fzf_bash_completion_unbuffered_awk '$0!=""' 'sub(find, replace)' -vfind='.*' -vreplace="$(printf %s "$compl_prefix" | "$_fzf_bash_completion_sed" 's/[&\]/\\&/g')&$(printf %s "$compl_suffix" | "$_fzf_bash_completion_sed" 's/[&\]/\\&/g')" \ + | if IFS= read -r line; then + printf '%s\n' "$line"; cat + else + local compgen_opts=() + [ "$compl_bashdefault" = 1 ] && compgen_opts+=( -o bashdefault ) + [ "$compl_default" = 1 ] && compgen_opts+=( -o default ) + [ "$compl_dirnames" = 1 ] && compgen_opts+=( -o dirnames ) + if [ -n "${compgen_opts[*]}" ]; then + compgen "${compgen_opts[@]}" -- "$2" + fi + fi + + if [ "$compl_plusdirs" = 1 ]; then + compgen -o dirnames -- "$2" + fi + ) \ + | _fzf_bash_completion_unbuffered_awk '' 'sub(find, replace)' -vfind="^$(_fzf_bash_completion_awk_escape "$2")" -vreplace="$("$_fzf_bash_completion_sed" -r 's/\\(.)/\1/g; s/[&\]/\\&/g' <<<"$2")" \ + | "$dir_marker" +} + +_fzf_bash_completion_apply_xfilter() { + if [ -z "$1" ]; then + cat + return + fi + + local pattern line word="$cur" + word="${word//\//\\/}" + word="${word//&/\\&}" + # replace any unescaped & with the word being completed + pattern="$("$_fzf_bash_completion_sed" 's/\(\(^\|[^\]\)\(\\\\\)*\)&/\1'"$word"'/g' <<<"${1:1}")" + + if [ "${1::1}" = ! ]; then + while IFS= read -r line; do [[ "$line" == $pattern ]] && printf '%s\n' "$line"; done + elif [ -n "$1" ]; then + while IFS= read -r line; do [[ "$line" != $pattern ]] && printf '%s\n' "$line"; done + fi +} + +_fzf_bash_completion_dir_marker() { + local line + while IFS= read -r line; do + # adapted from __expand_tilde_by_ref + if [[ "$line" == \~* ]]; then + eval "$(printf expanded=~%q "${line:1}")" + fi + [ -d "${expanded-"$line"}" ] && line="$line/" + printf '%s\n' "$line" + done +} + +_fzf_bash_completion_compopt() { + while [ "$#" -gt 0 ]; do + local val + if [ "$1" = -o ]; then + val=1 + elif [ "$1" = +o ]; then + val=0 + else + break + fi + + if [[ "$2" =~ bashdefault|default|dirnames|filenames|noquote|nosort|nospace|plusdirs ]]; then + eval "compl_$2=$val" + fi + shift 2 + done +} -- cgit v1.2.1