From 4cbbd121c012b3962f12fdff0f1820c3b8636a44 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sat, 30 Jul 2016 01:17:09 +1200 Subject: Move bash completion setup into separate dir --- bash/bash_completion.d/bash.bash | 38 +++++++++++++++++++++++ bash/bash_completion.d/bd.bash | 22 +++++++++++++ bash/bash_completion.d/ftp.bash | 33 ++++++++++++++++++++ bash/bash_completion.d/git.bash | 27 ++++++++++++++++ bash/bash_completion.d/gpg.bash | 17 ++++++++++ bash/bash_completion.d/keep.bash | 2 ++ bash/bash_completion.d/make.bash | 44 ++++++++++++++++++++++++++ bash/bash_completion.d/man.bash | 63 ++++++++++++++++++++++++++++++++++++++ bash/bash_completion.d/mkcd.bash | 1 + bash/bash_completion.d/mysql.bash | 34 ++++++++++++++++++++ bash/bash_completion.d/pass.bash | 39 +++++++++++++++++++++++ bash/bash_completion.d/path.bash | 60 ++++++++++++++++++++++++++++++++++++ bash/bash_completion.d/prompt.bash | 2 ++ bash/bash_completion.d/sd.bash | 39 +++++++++++++++++++++++ bash/bash_completion.d/ssh.bash | 22 +++++++++++++ bash/bash_completion.d/td.bash | 18 +++++++++++ bash/bash_completion.d/ud.bash | 28 +++++++++++++++++ bash/bash_completion.d/vared.bash | 1 + bash/bash_completion.d/vis.bash | 22 +++++++++++++ bash/bash_completion.d/vr.bash | 1 + 20 files changed, 513 insertions(+) create mode 100644 bash/bash_completion.d/bash.bash create mode 100644 bash/bash_completion.d/bd.bash create mode 100644 bash/bash_completion.d/ftp.bash create mode 100644 bash/bash_completion.d/git.bash create mode 100644 bash/bash_completion.d/gpg.bash create mode 100644 bash/bash_completion.d/keep.bash create mode 100644 bash/bash_completion.d/make.bash create mode 100644 bash/bash_completion.d/man.bash create mode 100644 bash/bash_completion.d/mkcd.bash create mode 100644 bash/bash_completion.d/mysql.bash create mode 100644 bash/bash_completion.d/pass.bash create mode 100644 bash/bash_completion.d/path.bash create mode 100644 bash/bash_completion.d/prompt.bash create mode 100644 bash/bash_completion.d/sd.bash create mode 100644 bash/bash_completion.d/ssh.bash create mode 100644 bash/bash_completion.d/td.bash create mode 100644 bash/bash_completion.d/ud.bash create mode 100644 bash/bash_completion.d/vared.bash create mode 100644 bash/bash_completion.d/vis.bash create mode 100644 bash/bash_completion.d/vr.bash (limited to 'bash/bash_completion.d') diff --git a/bash/bash_completion.d/bash.bash b/bash/bash_completion.d/bash.bash new file mode 100644 index 00000000..5d944b9b --- /dev/null +++ b/bash/bash_completion.d/bash.bash @@ -0,0 +1,38 @@ +# Various easy completions + +# Bash builtins +complete -A builtin builtin + +# Bash options +complete -A setopt set + +# Commands +complete -A command command complete coproc exec hash type + +# Directories +complete -A directory cd pushd mkdir rmdir + +# Functions +complete -A function function + +# Help topics +complete -A helptopic help + +# Jobspecs +complete -A job disown fg jobs +complete -A stopped bg + +# Readline bindings +complete -A binding bind + +# Shell options +complete -A shopt shopt + +# Signal names +complete -A signal trap + +# Variables +complete -A variable declare export readonly typeset + +# Both functions and variables +complete -A function -A variable unset diff --git a/bash/bash_completion.d/bd.bash b/bash/bash_completion.d/bd.bash new file mode 100644 index 00000000..68589dff --- /dev/null +++ b/bash/bash_completion.d/bd.bash @@ -0,0 +1,22 @@ +# Completion setup for bd +_bd() { + + # Only makes sense for the first argument + ((COMP_CWORD == 1)) || return 1 + + # Build a list of dirnames in $PWD + local -a dirnames + IFS=/ read -d '' -a dirnames < <(printf '%s\0' "${PWD#/}") + + # Remove the last element in the array (the current directory) + ((${#dirnames[@]})) || return 1 + dirnames=("${dirnames[@]:0:"$((${#dirnames[@]}-1))"}") + + # Add the matching dirnames to the reply + local dirname + for dirname in "${dirnames[@]}" ; do + [[ $dirname == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY=("${COMPREPLY[@]}" "$(printf %q "$dirname")") + done +} +complete -F _bd bd diff --git a/bash/bash_completion.d/ftp.bash b/bash/bash_completion.d/ftp.bash new file mode 100644 index 00000000..5770f137 --- /dev/null +++ b/bash/bash_completion.d/ftp.bash @@ -0,0 +1,33 @@ +# Completion for ftp with .netrc machines +_ftp() { + + # Bail if the .netrc file is illegible + local netrc + netrc=$HOME/.netrc + [[ -r $netrc ]] || return 1 + + # Tokenize the file + local -a tokens + read -a tokens -d '' -r < "$netrc" + + # Iterate through tokens and collect machine names + local -a machines + local -i nxm + local token + for token in "${tokens[@]}" ; do + if ((nxm)) ; then + machines[${#machines[@]}]=$token + nxm=0 + elif [[ $token == machine ]] ; then + nxm=1 + fi + done + + # Generate completion reply + local machine + for machine in "${machines[@]}" ; do + [[ $machine == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$machine + done +} +complete -F _ftp -o default ftp diff --git a/bash/bash_completion.d/git.bash b/bash/bash_completion.d/git.bash new file mode 100644 index 00000000..496712ae --- /dev/null +++ b/bash/bash_completion.d/git.bash @@ -0,0 +1,27 @@ +# Completion for git local branch names +_git() { + + # Bail if not a git repo (or no git!) + git rev-parse --git-dir >/dev/null 2>&1 || return 1 + + # Switch on the previous word + case ${COMP_WORDS[1]} in + + # If the first word is appropriate, complete with branch/tag names + checkout|merge|rebase) + local branch + while read -r _ _ branch ; do + branch=${branch##*/} + [[ $branch == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$branch + done < <(git for-each-ref refs/heads refs/tags 2>/dev/null) + return + ;; + + # Bail if it isn't + *) + return 1 + ;; + esac +} +complete -F _git -o default git diff --git a/bash/bash_completion.d/gpg.bash b/bash/bash_completion.d/gpg.bash new file mode 100644 index 00000000..c2f08415 --- /dev/null +++ b/bash/bash_completion.d/gpg.bash @@ -0,0 +1,17 @@ +# Completion for gpg with long options +_gpg() { + + # Bail if no gpg(1) + hash gpg 2>/dev/null || return 1 + + # Bail if not completing an option + [[ ${COMP_WORDS[COMP_CWORD]} == --* ]] || return 1 + + # Generate completion reply from gpg(1) options + local option + while read -r option ; do + [[ $option == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$option + done < <(gpg --dump-options 2>/dev/null) +} +complete -F _gpg -o default gpg diff --git a/bash/bash_completion.d/keep.bash b/bash/bash_completion.d/keep.bash new file mode 100644 index 00000000..bfe5dd2f --- /dev/null +++ b/bash/bash_completion.d/keep.bash @@ -0,0 +1,2 @@ +# Complete calls to keep with existing function names and variable names +complete -A function -A variable keep diff --git a/bash/bash_completion.d/make.bash b/bash/bash_completion.d/make.bash new file mode 100644 index 00000000..ca209e8e --- /dev/null +++ b/bash/bash_completion.d/make.bash @@ -0,0 +1,44 @@ +# Completion setup for Make, completing targets +_make() { + + # Bail if no legible Makefile + [[ -r Makefile ]] || return 1 + + # Iterate through the Makefile, line by line + while IFS= read -r line ; do + case $line in + + # We're looking for targets but not variable assignments + $'\t'*) ;; + *:=*) ;; + *:*) + + # Break the target up with space delimiters + local -a targets + IFS=' ' read -a targets -d '' < \ + <(printf '%s\0' "${line%%:*}") + + # Iterate through the targets and add suitable ones + local target + for target in "${targets[@]}" ; do + case $target in + + # Don't complete special targets beginning with a + # period + .*) ;; + + # Don't complete targets with names that have + # characters outside of the POSIX spec (plus slashes) + *[^[:word:]./-]*) ;; + + # Add targets that match what we're completing + ${COMP_WORDS[COMP_CWORD]}*) + COMPREPLY[${#COMPREPLY[@]}]=$target + ;; + esac + done + ;; + esac + done < Makefile +} +complete -F _make -o default make diff --git a/bash/bash_completion.d/man.bash b/bash/bash_completion.d/man.bash new file mode 100644 index 00000000..f1762a01 --- /dev/null +++ b/bash/bash_completion.d/man.bash @@ -0,0 +1,63 @@ +# Autocompletion for man(1) +_man() { + + # Don't even bother if we don't have manpath(1) + hash manpath || return 1 + + # Snarf the word + local word + word=${COMP_WORDS[COMP_CWORD]} + + # If this is the second word, and the previous word started with a number, + # we'll assume that's the section to search + local section subdir + if ((COMP_CWORD > 1)) && [[ ${COMP_WORDS[COMP_CWORD-1]} == [0-9]* ]] ; then + section=${COMP_WORDS[COMP_CWORD-1]} + subdir=man${section%%[^0-9]*} + fi + + # Read completion results from a subshell and add them to the COMPREPLY + # array individually + while IFS= read -rd '' page ; do + COMPREPLY[${#COMPREPLY[@]}]=$page + done < <( + + # Do not return dotfiles, give us extended globbing, and expand empty + # globs to just nothing + shopt -u dotglob + shopt -s extglob nullglob + + # Start an array of pages + declare -a pages + + # Break manpath(1) output into an array of paths + IFS=: read -a manpaths -r < <(manpath 2>/dev/null) + + # Iterate through the manual page paths and add every manual page we find + for manpath in "${manpaths[@]}" ; do + [[ $manpath ]] || continue + if [[ $section ]] ; then + for page in "$manpath"/"$subdir"/"$word"*."$section"?(.[glx]z|.bz2|.lzma|.Z) ; do + pages[${#pages[@]}]=$page + done + else + for page in "$manpath"/man[0-9]*/"$word"*.* ; do + pages[${#pages[@]}]=$page + done + fi + done + + # Strip paths, .gz suffixes, and finally .
suffixes + pages=("${pages[@]##*/}") + pages=("${pages[@]%.@([glx]z|bz2|lzma|Z)}") + pages=("${pages[@]%.[0-9]*}") + + # Bail out if we ended up with no pages somehow to prevent us from + # printing + ((${#pages[@]})) || exit 1 + + # Print the pages array to stdout, quoted and null-delimited + printf '%q\0' "${pages[@]}" + ) +} +complete -F _man -o default man diff --git a/bash/bash_completion.d/mkcd.bash b/bash/bash_completion.d/mkcd.bash new file mode 100644 index 00000000..0db967d8 --- /dev/null +++ b/bash/bash_completion.d/mkcd.bash @@ -0,0 +1 @@ +complete -A directory mkcd diff --git a/bash/bash_completion.d/mysql.bash b/bash/bash_completion.d/mysql.bash new file mode 100644 index 00000000..f64b6f32 --- /dev/null +++ b/bash/bash_completion.d/mysql.bash @@ -0,0 +1,34 @@ +# Completion setup for MySQL for configured databases +_mysql() { + + # Only makes sense for first argument + ((COMP_CWORD == 1)) || return 1 + + # Bail if directory doesn't exist + local dirname + dirname=$HOME/.mysql + [[ -d $dirname ]] || return 1 + + # Return the names of the .cnf files sans prefix as completions + local db + while IFS= read -rd '' db ; do + COMPREPLY[${#COMPREPLY[@]}]=$db + done < <( + + # Set options so that globs expand correctly + shopt -s dotglob nullglob + + # Collect all the config file names, strip off leading path and .cnf + local -a cnfs + cnfs=("$dirname"/"${COMP_WORDS[COMP_CWORD]}"*.cnf) + cnfs=("${cnfs[@]#"$dirname"/}") + cnfs=("${cnfs[@]%.cnf}") + + # Bail if no files to prevent empty output + ((${#cnfs[@]})) || exit 1 + + # Print the conf names, null-delimited + printf '%q\0' "${cnfs[@]}" + ) +} +complete -F _mysql -o default mysql diff --git a/bash/bash_completion.d/pass.bash b/bash/bash_completion.d/pass.bash new file mode 100644 index 00000000..28941952 --- /dev/null +++ b/bash/bash_completion.d/pass.bash @@ -0,0 +1,39 @@ +# Requires Bash >= 4.0 for globstar +((BASH_VERSINFO[0] >= 4)) || return + +# Custom completion for pass(1), because I don't like the one included with the +# distribution +_pass() +{ + # If we can't read the password directory, just bail + local passdir + passdir=${PASSWORD_STORE_DIR:-"$HOME"/.password-store} + [[ -r $passdir ]] || return 1 + + # Iterate through list of .gpg paths, extension stripped, null-delimited, + # and filter them down to the ones matching the completing word (compgen + # doesn't seem to do this properly with a null delimiter) + local entry + while IFS= read -rd '' entry ; do + COMPREPLY[${#COMPREPLY[@]}]=$entry + done < <( + + # Set shell options to expand globs the way we expect + shopt -u dotglob + shopt -s globstar nullglob + + # Gather the entries and remove their .gpg suffix + declare -a entries + entries=("$passdir"/"${COMP_WORDS[COMP_CWORD]}"*/**/*.gpg \ + "$passdir"/"${COMP_WORDS[COMP_CWORD]}"*.gpg) + entries=("${entries[@]#"$passdir"/}") + entries=("${entries[@]%.gpg}") + + # Bail if no entries to prevent empty output + ((${#entries[@]})) || exit 1 + + # Print all the entries, null-delimited + printf '%q\0' "${entries[@]}" + ) +} +complete -F _pass pass diff --git a/bash/bash_completion.d/path.bash b/bash/bash_completion.d/path.bash new file mode 100644 index 00000000..fd94b7c4 --- /dev/null +++ b/bash/bash_completion.d/path.bash @@ -0,0 +1,60 @@ +# Completion for path +_path() { + + # What to do depends on which word we're completing + if ((COMP_CWORD == 1)) ; then + + # Complete operation as first word + local cmd + for cmd in help list insert append remove set check ; do + [[ $cmd == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$cmd + done + + # Complete with either directories or $PATH entries as all other words + else + case ${COMP_WORDS[1]} in + + # Complete with a directory + insert|i|append|add|a|check|c|set|s) + local dirname + while IFS= read -rd '' dirname ; do + COMPREPLY[${#COMPREPLY[@]}]=$dirname + done < <( + + # Set options to glob correctly + shopt -s dotglob nullglob + + # Collect directory names, strip trailing slash + local -a dirnames + dirnames=("${COMP_WORDS[COMP_CWORD]}"*/) + dirnames=("${dirnames[@]%/}") + + # Bail if no results to prevent empty output + ((${#dirnames[@]})) || exit 1 + + # Print results, quoted and null-delimited + printf '%q\0' "${dirnames[@]}" + ) + ;; + + # Complete with directories from PATH + remove|rm|r) + local -a promptarr + IFS=: read -d '' -a promptarr < <(printf '%s\0' "$PATH") + local part + for part in "${promptarr[@]}" ; do + [[ $part == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$(printf '%q\0' "$part") + done + ;; + + # No completion + *) + return 1 + ;; + esac + fi +} + +complete -F _path path diff --git a/bash/bash_completion.d/prompt.bash b/bash/bash_completion.d/prompt.bash new file mode 100644 index 00000000..b114b7bb --- /dev/null +++ b/bash/bash_completion.d/prompt.bash @@ -0,0 +1,2 @@ +# Complete words +complete -W 'on off git hg svn vcs ret job' prompt diff --git a/bash/bash_completion.d/sd.bash b/bash/bash_completion.d/sd.bash new file mode 100644 index 00000000..f8017591 --- /dev/null +++ b/bash/bash_completion.d/sd.bash @@ -0,0 +1,39 @@ +# Completion function for sd; any sibling directories, excluding the self +_sd() { + + # Only makes sense for the first argument + ((COMP_CWORD == 1)) || return 1 + + # Current directory can't be root directory + [[ $PWD != / ]] || return 1 + + # Build list of matching sibiling directories + while IFS= read -rd '' dirname ; do + COMPREPLY[${#COMPREPLY[@]}]=$dirname + done < <( + + # Set options to glob correctly + shopt -s dotglob nullglob + + # Collect directory names, strip leading ../ and trailing / + local -a dirnames + dirnames=(../"${COMP_WORDS[COMP_CWORD]}"*/) + dirnames=("${dirnames[@]#../}") + dirnames=("${dirnames[@]%/}") + + # Iterate again, but exclude the current directory this time + local -a sibs + local dirname + for dirname in "${dirnames[@]}" ; do + [[ $dirname != "${PWD##*/}" ]] || continue + sibs[${#sibs[@]}]=$dirname + done + + # Bail if no results to prevent empty output + ((${#sibs[@]})) || exit 1 + + # Print results, null-delimited + printf '%q\0' "${sibs[@]}" + ) +} +complete -F _sd sd diff --git a/bash/bash_completion.d/ssh.bash b/bash/bash_completion.d/ssh.bash new file mode 100644 index 00000000..bbb9b246 --- /dev/null +++ b/bash/bash_completion.d/ssh.bash @@ -0,0 +1,22 @@ +# Completion for ssh/sftp/ssh-copy-id with config hostnames +_ssh() { + + # Read hostnames from existent config files, no asterisks + local -a hosts + local config option value + for config in "$HOME"/.ssh/config /etc/ssh/ssh_config ; do + [[ -e $config ]] || continue + while read -r option value _ ; do + [[ $option == Host ]] || continue + [[ $value != *'*'* ]] || continue + hosts[${#hosts[@]}]=$value + done < "$config" + done + + # Generate completion reply + for host in "${hosts[@]}" ; do + [[ $host == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$host + done +} +complete -F _ssh -o default ssh sftp ssh-copy-id diff --git a/bash/bash_completion.d/td.bash b/bash/bash_completion.d/td.bash new file mode 100644 index 00000000..ffb9c973 --- /dev/null +++ b/bash/bash_completion.d/td.bash @@ -0,0 +1,18 @@ +# Complete filenames for td(1) +_td() { + local dir + dir=${TODO_DIR:-"$HOME"/Todo} + while IFS= read -rd '' fn ; do + COMPREPLY[${#COMPREPLY[@]}]=$fn + done < <( + shopt -s extglob nullglob + shopt -u dotglob + local -a fns + fns=("$dir"/"${COMP_WORDS[COMP_CWORD]}"*) + fns=("${fns[@]#"$dir"/}") + ((${#fns[@]})) || exit 1 + printf '%s\0' "${fns[@]##"$dir"/}" + ) + return +} +complete -F _td td diff --git a/bash/bash_completion.d/ud.bash b/bash/bash_completion.d/ud.bash new file mode 100644 index 00000000..47171b78 --- /dev/null +++ b/bash/bash_completion.d/ud.bash @@ -0,0 +1,28 @@ +# Completion setup for ud +_ud() { + + # Only makes sense for the second argument + ((COMP_CWORD == 2)) || return 1 + + # Iterate through directories, null-separated, add them to completions + local dirname + while IFS= read -rd '' dirname ; do + COMPREPLY[${#COMPREPLY[@]}]=$dirname + done < <( + + # Set options to glob correctly + shopt -s dotglob nullglob + + # Collect directory names, strip trailing slashes + local -a dirnames + dirnames=("${COMP_WORDS[COMP_CWORD]}"*/) + dirnames=("${dirnames[@]%/}") + + # Bail if no results to prevent empty output + ((${#dirnames[@]})) || exit 1 + + # Print results null-delimited + printf '%s\0' "${dirnames[@]}" + ) +} +complete -F _ud -o filenames ud diff --git a/bash/bash_completion.d/vared.bash b/bash/bash_completion.d/vared.bash new file mode 100644 index 00000000..ea8cefd1 --- /dev/null +++ b/bash/bash_completion.d/vared.bash @@ -0,0 +1 @@ +complete -A variable vared diff --git a/bash/bash_completion.d/vis.bash b/bash/bash_completion.d/vis.bash new file mode 100644 index 00000000..f84cb702 --- /dev/null +++ b/bash/bash_completion.d/vis.bash @@ -0,0 +1,22 @@ +# Complete args to vis(1) with existing executables in $VISPATH, defaulting to +# ~/.local/bin +_vis() { + local vispath + vispath=${VISPATH:-"$HOME"/.local/bin} + [[ -d $vispath ]] || return + while IFS= read -rd '' executable ; do + COMPREPLY[${#COMPREPLY[@]}]=$executable + done < <( + shopt -s dotglob nullglob + declare -a files + files=("${VISPATH:-"$HOME"/.local/bin}"/"${COMP_WORDS[COMP_CWORD]}"*) + declare -a executables + for file in "${files[@]}" ; do + [[ -f $file && -x $file ]] || continue + executables[${#executables[@]}]=${file##*/} + done + ((${#executables[@]})) || exit 1 + printf '%q\0' "${executables[@]}" + ) +} +complete -F _vis vis diff --git a/bash/bash_completion.d/vr.bash b/bash/bash_completion.d/vr.bash new file mode 100644 index 00000000..2d5120f0 --- /dev/null +++ b/bash/bash_completion.d/vr.bash @@ -0,0 +1 @@ +complete -A directory vr -- cgit v1.2.3