aboutsummaryrefslogtreecommitdiff
path: root/bash
diff options
context:
space:
mode:
Diffstat (limited to 'bash')
-rw-r--r--bash/bash_completion.d/_abook_addresses.bash31
-rw-r--r--bash/bash_completion.d/_completion_ignore_case.bash12
-rw-r--r--bash/bash_completion.d/_ssh_config_hosts.bash33
-rw-r--r--bash/bash_completion.d/_text_filenames.bash30
-rw-r--r--bash/bash_completion.d/bd.bash54
-rw-r--r--bash/bash_completion.d/chgrp.bash14
-rw-r--r--bash/bash_completion.d/eds.bash52
-rw-r--r--bash/bash_completion.d/find.bash70
-rw-r--r--bash/bash_completion.d/ftp.bash33
-rw-r--r--bash/bash_completion.d/git.bash198
-rw-r--r--bash/bash_completion.d/gpg.bash26
-rw-r--r--bash/bash_completion.d/keep.bash45
-rw-r--r--bash/bash_completion.d/kill.bash16
-rw-r--r--bash/bash_completion.d/mail.bash4
-rw-r--r--bash/bash_completion.d/make.bash93
-rw-r--r--bash/bash_completion.d/man.bash101
-rw-r--r--bash/bash_completion.d/mex.bash60
-rw-r--r--bash/bash_completion.d/mysql.bash42
-rw-r--r--bash/bash_completion.d/openssl.bash44
-rw-r--r--bash/bash_completion.d/pass.bash76
-rw-r--r--bash/bash_completion.d/path.bash103
-rw-r--r--bash/bash_completion.d/prompt.bash2
-rw-r--r--bash/bash_completion.d/sd.bash60
-rw-r--r--bash/bash_completion.d/sed.bash3
-rw-r--r--bash/bash_completion.d/source.bash3
-rw-r--r--bash/bash_completion.d/tail.bash3
-rw-r--r--bash/bash_completion.d/td.bash51
-rw-r--r--bash/bash_completion.d/ud.bash37
-rw-r--r--bash/bash_completion.d/vi.bash3
-rw-r--r--bash/bash_completion.d/view.bash3
-rw-r--r--bash/bash_completion.d/vim.bash3
-rw-r--r--bash/bashrc8
-rw-r--r--bash/bashrc.d/completion.bash37
-rw-r--r--bash/bashrc.d/keep.bash2
34 files changed, 536 insertions, 816 deletions
diff --git a/bash/bash_completion.d/_abook_addresses.bash b/bash/bash_completion.d/_abook_addresses.bash
index e79eef42..6f7e226f 100644
--- a/bash/bash_completion.d/_abook_addresses.bash
+++ b/bash/bash_completion.d/_abook_addresses.bash
@@ -1,32 +1,29 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Email addresses from abook(1)
_abook_addresses() {
# Needs abook(1)
hash abook 2>/dev/null || return
- # Iterate through words produced by subshell
- local word
- while read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
+ # Iterate through completions produced by subshell
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- # Set case-insensitive matching if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocasematch 2>/dev/null
- break
- ;;
- esac
- done < <(bind -v)
+ # Make matches behave appropriately
+ if _completion_ignore_case ; then
+ shopt -s nocasematch 2>/dev/null
+ fi
# Generate list of email addresses from abook(1)
while IFS=$'\t' read -r address _ ; do
case $address in
- ("${COMP_WORDS[COMP_CWORD]}"*)
- printf '%s\n' "$address"
- ;;
+ ("$2"*) printf '%s\n' "$address" ;;
esac
done < <(abook --mutt-query \@)
)
diff --git a/bash/bash_completion.d/_completion_ignore_case.bash b/bash/bash_completion.d/_completion_ignore_case.bash
new file mode 100644
index 00000000..fe8208fc
--- /dev/null
+++ b/bash/bash_completion.d/_completion_ignore_case.bash
@@ -0,0 +1,12 @@
+# Return whether to ignore case for filename completion
+_completion_ignore_case() {
+
+ # Check Readline settings for case-insensitive matching
+ while read -r _ set ; do
+ [[ $set == 'completion-ignore-case on' ]] || continue
+ return 0
+ done < <(bind -v)
+
+ # Didn't find it, stay case-sensitive
+ return 1
+}
diff --git a/bash/bash_completion.d/_ssh_config_hosts.bash b/bash/bash_completion.d/_ssh_config_hosts.bash
index c26457cf..3f937a2a 100644
--- a/bash/bash_completion.d/_ssh_config_hosts.bash
+++ b/bash/bash_completion.d/_ssh_config_hosts.bash
@@ -1,45 +1,28 @@
# Complete ssh_config(5) hostnames
_ssh_config_hosts() {
- # Don't complete anything that wouldn't be in a valid hostname
- case ${COMP_WORDS[COMP_CWORD]} in
- *[!a-zA-Z0-9.-]*) return 1 ;;
- esac
-
# Iterate through words from a subshell
- while read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- # Check bind settings to see if we should match case insensitively
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -qs nocasematch 2>/dev/null
- break
- ;;
- esac
- done < <(bind -v)
-
# Iterate through SSH client config paths
for config in "$HOME"/.ssh/config /etc/ssh/ssh_config ; do
[[ -e $config ]] || continue
- # Read Host options and their first value from file
+ # Read 'Host' options and their first value from file
while read -r option value _ ; do
[[ $option == Host ]] || continue
# Check host value
case $value in
-
- # Don't complete with wildcard characters
+ # No empties
+ ('') ;;
+ # No wildcards
(*'*'*) ;;
-
# Found a match; print it
- ("${COMP_WORDS[COMP_CWORD]}"*)
- printf '%s\n' "$value"
- ;;
+ ("$2"*) printf '%s\n' "$value" ;;
esac
done < "$config"
diff --git a/bash/bash_completion.d/_text_filenames.bash b/bash/bash_completion.d/_text_filenames.bash
index 9cc1c722..b6e035ad 100644
--- a/bash/bash_completion.d/_text_filenames.bash
+++ b/bash/bash_completion.d/_text_filenames.bash
@@ -8,34 +8,36 @@
# the thing I want, and I want it to stay fast.
#
_text_filenames() {
- local item
- while IFS= read -r item ; do
+
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -r comp ; do
# Exclude blanks
- [[ -n $item ]] || continue
+ [[ -n $comp ]] || continue
# Exclude nonexistent (some sort of error)
- [[ -e $item ]] || continue
+ [[ -e $comp ]] || continue
# Exclude files with block, character, pipe, or socket type
- ! [[ -b $item ]] || continue
- ! [[ -c $item ]] || continue
- ! [[ -p $item ]] || continue
- ! [[ -S $item ]] || continue
+ ! [[ -b $comp ]] || continue
+ ! [[ -c $comp ]] || continue
+ ! [[ -p $comp ]] || continue
+ ! [[ -S $comp ]] || continue
# Accept directories
- if [[ -d $item ]] ; then
- COMPREPLY[${#COMPREPLY[@]}]=$item
+ if [[ -d $comp ]] ; then
+ COMPREPLY[ci++]=$comp
continue
fi
# Check the filename extension to know what to exclude
(
# Case-insensitive matching available since 3.1-alpha
- shopt -qs nocasematch 2>/dev/null
+ shopt -s nocasematch 2>/dev/null
# Match against known binary patterns
- case $item in
+ case $comp in
# Archives
(*.7z) ;;
@@ -153,7 +155,7 @@ _text_filenames() {
) || continue
# Complete everything else; some of it will still be binary
- COMPREPLY[${#COMPREPLY[@]}]=$item
+ COMPREPLY[ci++]=$comp
- done < <(compgen -A file -- "${COMP_WORDS[COMP_CWORD]}")
+ done < <(compgen -A file -- "$2")
}
diff --git a/bash/bash_completion.d/bd.bash b/bash/bash_completion.d/bd.bash
index e67cdd09..09134e6a 100644
--- a/bash/bash_completion.d/bd.bash
+++ b/bash/bash_completion.d/bd.bash
@@ -1,25 +1,45 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Completion setup for bd()
_bd() {
- # Only makes sense for the first argument
- ((COMP_CWORD == 1)) || return
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -d / -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
+
+ # Build an array of path nodes, leaf to root
+ path=$PWD
+ while [[ -n $path ]] ; do
+ node=${path##*/}
+ path=${path%/*}
+ [[ -n $node ]] || continue
+ nodes[ni++]=$node
+ done
+
+ # Continue if we have at least two nodes, counting the leaf
+ ((${#nodes[@]} > 1)) || return
- # Build a list of dirnames in $PWD
- local -a dirnames
- IFS=/ read -rd '' -a dirnames < <(printf '%s\0' "${PWD#/}")
+ # Shift off the leaf, since it is not meaningful to go "back to" the
+ # current directory
+ nodes=("${nodes[@]:1}")
- # Remove the last element in the array (the current directory)
- ((${#dirnames[@]})) || return
- dirnames=("${dirnames[@]:0:${#dirnames[@]}-1}")
+ # Make matching behave appropriately
+ if _completion_ignore_case ; then
+ shopt -s nocasematch 2>/dev/null
+ fi
- # Add the matching dirnames to the reply
- local dirname
- for dirname in "${dirnames[@]}" ; do
- case $dirname in
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$(printf %q "$dirname")
- ;;
- esac
- done
+ # Iterate through the nodes and print the ones that match the word
+ # being completed, with a trailing slash as terminator
+ for node in "${nodes[@]}" ; do
+ case $node in
+ ("$2"*) printf '%s/' "$node" ;;
+ esac
+ done
+ )
}
complete -F _bd bd
diff --git a/bash/bash_completion.d/chgrp.bash b/bash/bash_completion.d/chgrp.bash
deleted file mode 100644
index 5e93ccee..00000000
--- a/bash/bash_completion.d/chgrp.bash
+++ /dev/null
@@ -1,14 +0,0 @@
-# Complete group names for first non-option chgrp(1) argument
-_chgrp() {
- local i
- for ((i = 1; i < COMP_CWORD; i++)) ; do
- case ${COMP_WORDS[i]} in
- -*) ;;
- *) return 1 ;;
- esac
- done
- while read -r group ; do
- COMPREPLY[${#COMPREPLY[@]}]=$group
- done < <(compgen -A group -- "${COMP_WORDS[COMP_CWORD]}")
-}
-complete -F _chgrp -o bashdefault -o default chgrp
diff --git a/bash/bash_completion.d/eds.bash b/bash/bash_completion.d/eds.bash
index 1c5b2aa2..371962ca 100644
--- a/bash/bash_completion.d/eds.bash
+++ b/bash/bash_completion.d/eds.bash
@@ -1,38 +1,34 @@
-# Complete args to eds(1df) with existing executables in $EDSPATH, defaulting
-# to ~/.local/bin
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
+# Complete args to eds(1df)
_eds() {
- local edspath
- edspath=${EDSPATH:-"$HOME"/.local/bin}
- [[ -d $edspath ]] || return
- local executable
- while IFS= read -rd '' executable ; do
- [[ -n $executable ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$executable
+
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -d / -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- shopt -s dotglob nullglob
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
+ # Make globs expand appropriately
+ shopt -u dotglob
+ shopt -s nullglob
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
- declare -a files
- files=("${EDSPATH:-"$HOME"/.local/bin}"/"${COMP_WORDS[COMP_CWORD]}"*)
- declare -a executables
- for file in "${files[@]}" ; do
+ # Iterate through files in local binaries directory
+ edspath=${EDSPATH:-"$HOME"/.local/bin}
+ for file in "$edspath"/"$2"* ; do
+ # Skip directories
! [[ -d $file ]] || continue
- [[ -e $file ]] || continue
+ # Skip non-executable files
[[ -x $file ]] || continue
- executables[${#executables[@]}]=${file##*/}
+ # Print entry, null-terminated
+ printf '%q\0' "${file##*/}"
done
-
- # Print quoted entries, null-delimited
- printf '%q\0' "${executables[@]}"
)
}
complete -F _eds eds
diff --git a/bash/bash_completion.d/find.bash b/bash/bash_completion.d/find.bash
index 7e2ae9c3..f87029e7 100644
--- a/bash/bash_completion.d/find.bash
+++ b/bash/bash_completion.d/find.bash
@@ -1,42 +1,14 @@
-# compopt requires Bash >=4.0, and I don't think it's worth making a compatible
-# version
-((BASH_VERSINFO[0] >= 4)) || return
-
# Semi-intelligent completion for find(1); nothing too crazy
_find() {
- # Backtrack through words so far; if none of them look like options, we're
- # still completing directory names
- local i
- local -i opts
- for ((i = COMP_CWORD; i >= 0; i--)) ; do
- case ${COMP_WORDS[i]} in
- -*)
- opts=1
- break
- ;;
- esac
- done
- if ! ((opts)) ; then
- compopt -o dirnames
- return
- fi
-
- # For the rest of this, if we end up with an empty COMPREPLY, we should
- # just do what Bash would normally do
- compopt -o bashdefault -o default
-
- # Iterate through whatever the subshell gives us; don't add blank items, though
- local item
- while read -r item ; do
- [[ -n $item ]] || continue
- COMPREPLY+=("$item")
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- # If the word being completed starts with a dash, just complete it as
- # an option; crude, but simple, and will be right the vast majority of
- # the time
- case ${COMP_WORDS[COMP_CWORD]} in
+ # Complete POSIX-specified options
+ case $2 in
(-*)
compgen -W '
-atime
@@ -58,34 +30,28 @@ _find() {
-type
-user
-xdev
- ' -- "${COMP_WORDS[COMP_CWORD]}"
+ ' -- "$2"
+ return
;;
esac
- # Otherwise, look at the word *before* this one to figure out what to
+ # Look at the word *before* this one to figure out what to
# complete
- case ${COMP_WORDS[COMP_CWORD-1]} in
+ case $3 in
# Args to -exec and -execdir should be commands
- (-exec|-execdir)
- compgen -A command -- "${COMP_WORDS[COMP_CWORD]}"
- ;;
-
- # Args to -group should complete group names
- (-group)
- compgen -A group -- "${COMP_WORDS[COMP_CWORD]}"
- ;;
+ (-exec|-execdir) compgen -A command -- "$2" ;;
# Legal POSIX flags for -type
- (-type)
- compgen -W 'b c d f l p s' -- "${COMP_WORDS[COMP_CWORD]}"
- ;;
+ (-type) compgen -W 'b c d f l p s' -- "$2" ;;
+
+ # Args to -group should complete group names
+ (-group) compgen -A group -- "$2" ;;
# Args to -user should complete usernames
- (-user)
- compgen -A user -- "${COMP_WORDS[COMP_CWORD]}"
- ;;
+ (-user) compgen -A user -- "$2" ;;
+
esac
)
}
-complete -F _find find
+complete -F _find -o bashdefault -o default find
diff --git a/bash/bash_completion.d/ftp.bash b/bash/bash_completion.d/ftp.bash
deleted file mode 100644
index a584dd81..00000000
--- a/bash/bash_completion.d/ftp.bash
+++ /dev/null
@@ -1,33 +0,0 @@
-# Completion for ftp(1) with .netrc machines
-_ftp() {
-
- # Bail if the .netrc file is illegible
- local netrc
- netrc=$HOME/.netrc
- [[ -r $netrc ]] || return
-
- # 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 bashdefault -o default ftp
diff --git a/bash/bash_completion.d/git.bash b/bash/bash_completion.d/git.bash
deleted file mode 100644
index a2edb468..00000000
--- a/bash/bash_completion.d/git.bash
+++ /dev/null
@@ -1,198 +0,0 @@
-# Some simple completion for Git
-_git() {
-
- # Subcommands for this function to stack words onto COMPREPLY; if the first
- # argument is not given, the rest of the function is reached
- case $1 in
-
- # No argument; continue normal completion
- '') ;;
-
- # Symbolic references, remote or local
- refs)
- local ref
- while IFS= read -r ref ; do
- ref=${ref#refs/*/}
- case $ref in
- '') continue ;;
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$ref
- ;;
- esac
- done < <(git for-each-ref --format '%(refname)' 2>/dev/null)
- return
- ;;
-
- # Remote names
- remotes)
- local remote
- while IFS= read -r remote ; do
- case $remote in
- '') continue ;;
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$remote
- ;;
- esac
- done < <(git remote 2>/dev/null)
- return
- ;;
-
- # Git aliases
- aliases)
- local alias
- while IFS= read -r alias ; do
- alias=${alias#alias.}
- alias=${alias%% *}
- case $alias in
- '') continue ;;
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$alias
- ;;
- esac
- done < <(git config --get-regexp '^alias\.' 2>/dev/null)
- return
- ;;
-
- # Git subcommands
- subcommands)
- local execpath
- execpath=$(git --exec-path) || return
- local path
- for path in "$execpath"/git-"${COMP_WORDS[COMP_CWORD]}"* ; do
- ! [[ -d $path ]] || continue
- [[ -e $path ]] || continue
- [[ -x $path ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=${path#"$execpath"/git-}
- done
- return
- ;;
- esac
-
- # Try to find the index of the Git subcommand
- local -i sci i
- for ((i = 1; !sci && i <= COMP_CWORD; i++)) ; do
- case ${COMP_WORDS[i]} in
-
- # Skip --option=value
- --*=*) ;;
-
- # These ones have arguments, so bump the index up one more
- -C|-c|--exec-path|--git-dir|--work-tree|--namespace) ((i++)) ;;
-
- # Skip --option
- --?*) ;;
-
- # We have hopefully found our subcommand
- *) ((sci = i)) ;;
- esac
- done
-
- # Complete initial subcommand or alias
- if ((sci == COMP_CWORD)) ; then
- "${FUNCNAME[0]}" subcommands
- "${FUNCNAME[0]}" aliases
- return
- fi
-
- # Test subcommand to choose completions
- case ${COMP_WORDS[sci]} in
-
- # Help on real subcommands (not aliases)
- help)
- "${FUNCNAME[0]}" subcommands
- return
- ;;
-
- # Complete with remote subcommands and then remote names
- remote)
- if ((COMP_CWORD == 2)) ; then
- local word
- while IFS= read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
- done < <(compgen -W '
- add
- get-url
- prune
- remove
- rename
- set-branches
- set-head
- set-url
- show
- update
- ' -- "${COMP_WORDS[COMP_CWORD]}")
- else
- "${FUNCNAME[0]}" remotes
- fi
- return
- ;;
-
- # Complete with stash subcommands
- stash)
- ((COMP_CWORD == 2)) || return
- local word
- while IFS= read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
- done < <(compgen -W '
- apply
- branch
- clear
- create
- drop
- list
- pop
- save
- show
- store
- ' -- "${COMP_WORDS[COMP_CWORD]}")
- return
- ;;
-
- # Complete with submodule subcommands
- submodule)
- ((COMP_CWORD == 2)) || return
- local word
- while IFS= read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
- done < <(compgen -W '
- add
- deinit
- foreach
- init
- status
- summary
- sync
- update
- ' -- "${COMP_WORDS[COMP_CWORD]}")
- return
- ;;
-
- # Complete with remotes and then refs
- fetch|pull|push)
- if ((COMP_CWORD == 2)) ; then
- "${FUNCNAME[0]}" remotes
- else
- "${FUNCNAME[0]}" refs
- fi
- ;;
-
- # Commands for which I'm likely to want a ref
- branch|checkout|merge|rebase|tag)
- "${FUNCNAME[0]}" refs
- ;;
-
- # I normally only want a refspec for "reset" if I'm using the --hard or
- # --soft option; otherwise, files are fine
- reset)
- case ${COMP_WORDS[COMP_CWORD-1]} in
- --hard|--soft)
- "${FUNCNAME[0]}" refs
- ;;
- esac
- ;;
- esac
-}
-complete -F _git -o bashdefault -o default git
diff --git a/bash/bash_completion.d/gpg.bash b/bash/bash_completion.d/gpg.bash
index 697e4a65..c6f92676 100644
--- a/bash/bash_completion.d/gpg.bash
+++ b/bash/bash_completion.d/gpg.bash
@@ -1,22 +1,26 @@
# Completion for gpg(1) with long options
_gpg() {
- # Bail if no gpg(1)
+ # Needs gpg(1)
hash gpg 2>/dev/null || return
# Bail if not completing an option
- case ${COMP_WORDS[COMP_CWORD]} in
- --*) return 1 ;;
+ case $2 in
+ --*) ;;
+ *) return 1 ;;
esac
# Generate completion reply from gpg(1) options
- local option
- while read -r option ; do
- case $option in
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$option
- ;;
- esac
- done < <(gpg --dump-options 2>/dev/null)
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
+ gpg --dump-options 2>/dev/null |
+ while read -r option ; do
+ case $option in
+ ("$2"*) printf '%s\n' "$option" ;;
+ esac
+ done
+ )
}
complete -F _gpg -o bashdefault -o default gpg
diff --git a/bash/bash_completion.d/keep.bash b/bash/bash_completion.d/keep.bash
index 00b1469e..c7144684 100644
--- a/bash/bash_completion.d/keep.bash
+++ b/bash/bash_completion.d/keep.bash
@@ -1,3 +1,8 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Complete calls to keep() with variables and functions, or if -d is given with
# stuff that's already kept
_keep() {
@@ -7,16 +12,17 @@ _keep() {
mode=keep
if ((COMP_CWORD > 1)) ; then
case ${COMP_WORDS[1]} in
+ # Help; no completion
-h) return 1 ;;
+ # Deleting; change mode
-d) mode=delete ;;
esac
fi
# Collect words from an appropriate type of completion
- local word
- while read -r word ; do
- [[ -n $word ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$word
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
# Switch on second word; is it a -d option?
@@ -24,32 +30,27 @@ _keep() {
# Keepable names: all functions and variables
(keep)
- compgen -A function -A variable \
- -- "${COMP_WORDS[COMP_CWORD]}"
+ compgen -A function -A variable -- "$2"
;;
# Kept names: .bash-suffixed names in keep dir
(delete)
+
# Make globs behave correctly
+ shopt -u dotglob
shopt -s nullglob
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
# Build list of kept names
- dir=${BASHKEEP:-"$HOME"/.bashkeep.d}
- cword=${COMP_WORDS[COMP_CWORD]}
- kept=("$dir"/"$cword"*.bash)
- kept=("${kept[@]##*/}")
- kept=("${kept[@]%.bash}")
-
- # Print kept names
- printf '%s\n' "${kept[@]}"
+ bashkeep=${BASHKEEP:-"$HOME"/.bashkeep.d}
+ for keep in "$bashkeep"/"$2"*.bash ; do
+ ! [[ -d $keep ]] || continue
+ keep=${keep##*/}
+ keep=${keep%.bash}
+ printf '%s\n' "$keep"
+ done
;;
esac
)
diff --git a/bash/bash_completion.d/kill.bash b/bash/bash_completion.d/kill.bash
deleted file mode 100644
index dccc926b..00000000
--- a/bash/bash_completion.d/kill.bash
+++ /dev/null
@@ -1,16 +0,0 @@
-# Complete kill builtin with jobspecs (prefixed with % so it will accept them)
-# and this user's PIDs (requires pgrep(1))
-_kill() {
- local pid
- while read -r pid ; do
- case $pid in
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$pid
- ;;
- esac
- done < <( {
- compgen -A job -P%
- pgrep -u "$USER" .
- } 2>/dev/null )
-}
-complete -F _kill kill
diff --git a/bash/bash_completion.d/mail.bash b/bash/bash_completion.d/mail.bash
index 5d1cdec0..0f6d60d4 100644
--- a/bash/bash_completion.d/mail.bash
+++ b/bash/bash_completion.d/mail.bash
@@ -1,5 +1,5 @@
# Completion for mail(1) with abook(1) email addresses
-if ! declare -F _text_filenames >/dev/null ; then
- source "$HOME"/.bash_completion.d/_text_filenames.bash
+if ! declare -F _abook_addresses >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_abook_addresses.bash
fi
complete -F _abook_addresses -o bashdefault -o default mail
diff --git a/bash/bash_completion.d/make.bash b/bash/bash_completion.d/make.bash
index 0f39ef4b..2527d145 100644
--- a/bash/bash_completion.d/make.bash
+++ b/bash/bash_completion.d/make.bash
@@ -1,3 +1,8 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Completion setup for Make, completing targets
_make() {
@@ -5,48 +10,56 @@ _make() {
# first, then "Makefile"). You may want to add "GNU-makefile" after this.
local mf
for mf in makefile Makefile '' ; do
- [[ -e $mf ]] || continue
- break
+ ! [[ -e $mf ]] || break
done
[[ -n $mf ]] || return
- # Iterate through the Makefile, line by line
- local 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 -rd '' -a targets < \
- <(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 < "$mf"
+ # Iterate through completions produced by subshell
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
+ while IFS= read -r line ; do
+
+ # Match expected format
+ case $line in
+ # Has no equals sign anywhere
+ (*=*) continue ;;
+ # First char not a tab
+ ($'\t'*) continue ;;
+ # Has a colon on the line
+ (*:*) ;;
+ # Skip anything else
+ (*) continue ;;
+ esac
+
+ # Break the target up with space delimiters
+ local -a targets
+ IFS=' ' read -a targets -r \
+ < <(printf '%s\n' "${line%%:*}")
+
+ # Short-circuit if there are no targets
+ ((${#targets[@]})) || exit
+
+ # Make matches behave correctly
+ if _completion_ignore_case ; then
+ shopt -s nocasematch 2>/dev/null
+ fi
+
+ # Examine each target for completion suitability
+ local target
+ for target in "${targets[@]}" ; do
+ case $target in
+ # Not .PHONY, .POSIX etc
+ (.*) ;;
+ # Nothing with metacharacters
+ (*[^[:word:]./-]*) ;;
+ # Match!
+ ("$2"*) printf '%s\n' "$target" ;;
+ esac
+ done
+
+ done < "$mf"
+ )
}
complete -F _make -o bashdefault -o default make
diff --git a/bash/bash_completion.d/man.bash b/bash/bash_completion.d/man.bash
index ffef48ec..274f663a 100644
--- a/bash/bash_completion.d/man.bash
+++ b/bash/bash_completion.d/man.bash
@@ -1,43 +1,32 @@
# Autocompletion for man(1)
_man() {
- # Don't even bother if we don't have manpath(1)
- hash manpath 2>/dev/null || return
-
- # Snarf the word
- local word
- word=${COMP_WORDS[COMP_CWORD]}
-
- # Don't bother if the word has slashes in it, the user is probably trying
- # to complete an actual path
- case $word in
+ # Don't interfere with a user typing a path
+ case $2 in
*/*) return 1 ;;
esac
- # 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)) ; then
- case ${COMP_WORDS[COMP_CWORD-1]} in
- [0-9]*)
- section=${COMP_WORDS[COMP_CWORD-1]}
- subdir=man${section%%[^0-9]*}
- ;;
- esac
- fi
+ # If previous word started with a number, we'll assume that's a section to
+ # search
+ case $3 in
+ [0-9]*) sec=$3 ;;
+ esac
+
+ # Cut completion short if we have neither section nor word; there will
+ # probably be too many results
+ [[ -n $sec ]] || [[ -n $2 ]] || return
# Read completion results from a subshell and add them to the COMPREPLY
# array individually
- local page
- while IFS= read -rd '' page ; do
- [[ -n $page ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$page
+ local ci comp
+ while IFS= read -d '' -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
# Do not return dotfiles, give us extended globbing, and expand empty
# globs to just nothing
shopt -u dotglob
- shopt -s extglob nullglob
+ shopt -s nullglob
# Make globbing case-insensitive if appropriate
while read -r _ setting ; do
@@ -49,34 +38,54 @@ _man() {
esac
done < <(bind -v)
- # Break manpath(1) output into an array of paths
- declare -a manpaths
- IFS=: read -a manpaths -r < <(manpath 2>/dev/null)
-
- # Iterate through the manual page paths and add every manual page we find
- declare -a pages
- for manpath in "${manpaths[@]}" ; do
- [[ -n $manpath ]] || continue
- if [[ -n $section ]] ; then
- for page in \
- "$manpath"/"$subdir"/"$word"*."$section"?(.[glx]z|.bz2|.lzma|.Z)
- do
- pages[${#pages[@]}]=$page
+ # Figure out the manual paths to search
+ if hash amanpath 2>/dev/null ; then
+
+ # manpath(1) exists, run it to find what to search
+ IFS=: read -a manpaths -r \
+ < <(manpath 2>/dev/null)
+ else
+
+ # Fall back on some typical paths
+ manpaths=( \
+ "$HOME"/.local/man \
+ "$HOME"/.local/share/man \
+ /usr/man \
+ /usr/share/man \
+ /usr/local/man \
+ /usr/local/share/man \
+ )
+ fi
+
+ # Add pages from each manual directory
+ local pages pi
+ for mp in "${manpaths[@]}" ; do
+ [[ -n $mp ]] || continue
+
+ # Which pattern? Depends on section specification
+ if [[ -n $sec ]] ; then
+
+ # Section requested; quoted value in glob
+ for page in "$mp"/man"${sec%%[!0-9]*}"/"$2"*."$sec"* ; do
+ pages[pi++]=${page##*/}
done
else
- for page in "$manpath"/man[0-9]*/"$word"*.* ; do
- pages[${#pages[@]}]=$page
+
+ # No section;
+ for page in "$mp"/man[0-9]*/"$2"*.[0-9]* ; do
+ pages[pi++]=${page##*/}
done
fi
done
- # Strip paths, .gz suffixes, and finally .<section> suffixes
- pages=("${pages[@]##*/}")
- pages=("${pages[@]%.@([glx]z|bz2|lzma|Z)}")
+ # Bail if there are no pages
+ ((pi)) || exit
+
+ # Strip section suffixes
pages=("${pages[@]%.[0-9]*}")
- # Print quoted entries, null-delimited
- printf '%q\0' "${pages[@]}"
+ # Print entries, null-delimited
+ printf '%s\0' "${pages[@]}"
)
}
complete -F _man -o bashdefault -o default man
diff --git a/bash/bash_completion.d/mex.bash b/bash/bash_completion.d/mex.bash
index bc3d2c7b..b1e0e1a7 100644
--- a/bash/bash_completion.d/mex.bash
+++ b/bash/bash_completion.d/mex.bash
@@ -1,16 +1,54 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Completion setup for mex(1df), completing non-executable files in $PATH
_mex() {
- local -a path
- IFS=: read -ra path < <(printf '%s\n' "$PATH")
- local dir name
- for dir in "${path[@]}" ; do
- [[ -d $dir ]] || continue
- for name in "$dir"/* ; do
- [[ -e $name ]] || continue
- ! [[ -d $name ]] || continue
- ! [[ -x $name ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=${name##*/}
+
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -d / -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
+
+ # Make globs expand appropriately
+ shopt -u dotglob
+ shopt -s nullglob
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
+
+ # Break $PATH up into an array
+ declare -a paths
+ IFS=: read -a paths -r \
+ < <(printf '%s\n' "$PATH")
+
+ # Iterate through each path, collecting non-executable filenames
+ for path in "${paths[@]}" ; do
+ for name in "$path"/"$2"* ; do
+
+ # Skip anything that is not a plain file
+ [[ -f $name ]] || continue
+ # Skip files that are already executable
+ ! [[ -x $name ]] || continue
+
+ # Chop off leading path
+ name=${name##*/}
+
+ # Skip certain filename patterns
+ case $name in
+ # DOS batch file
+ (*.bat) continue ;;
+ # README files
+ (README*) continue ;;
+ esac
+
+ # Print name of the file
+ printf '%s/' "${name##*/}"
+
+ done
done
- done
+ )
}
complete -F _mex mex
diff --git a/bash/bash_completion.d/mysql.bash b/bash/bash_completion.d/mysql.bash
deleted file mode 100644
index 3ff97090..00000000
--- a/bash/bash_completion.d/mysql.bash
+++ /dev/null
@@ -1,42 +0,0 @@
-# Completion setup for MySQL for configured databases
-_mysql() {
-
- # Only makes sense for first argument
- ((COMP_CWORD == 1)) || return
-
- # Bail if directory doesn't exist
- local dirname
- dirname=$HOME/.mysql
- [[ -d $dirname ]] || return
-
- # Return the names of the .cnf files sans prefix as completions
- local db
- while IFS= read -rd '' db ; do
- [[ -n $db ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$db
- done < <(
-
- # Set options so that globs expand correctly
- shopt -s dotglob nullglob
-
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
-
- # 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}")
-
- # Print quoted entries, null-delimited
- printf '%q\0' "${cnfs[@]}"
- )
-}
-complete -F _mysql -o bashdefault -o default mysql
diff --git a/bash/bash_completion.d/openssl.bash b/bash/bash_completion.d/openssl.bash
index 86650770..1cb4bd07 100644
--- a/bash/bash_completion.d/openssl.bash
+++ b/bash/bash_completion.d/openssl.bash
@@ -1,26 +1,32 @@
# Some simple completion for openssl(1ssl)
_openssl() {
+ # Needs openssl(1ssl)
+ hash openssl 2>/dev/null || return
+
# Only complete the first word: OpenSSL subcommands
- case $COMP_CWORD in
- 1)
- while read -r subcmd ; do
- case $subcmd in
- '') ;;
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$subcmd
- ;;
- esac
- done < <(
- for arg in \
- list-cipher-commands \
- list-standard-commands \
- list-message-digest-commands ; do
- printf '%s\n' "$arg"
- openssl "$arg"
+ ((COMP_CWORD == 1)) || return
+
+ # Iterate through completions produced by subshell
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
+
+ # Run each of the command-listing commands; read each line into an
+ # array of subcommands (they are printed as a table)
+ for list in commands digest-commands cipher-commands ; do
+ openssl list -"$list"
+ done | {
+ declare -a subcmds
+ while read -a subcmds -r ; do
+ for subcmd in "${subcmds[@]}" ; do
+ case $subcmd in
+ ("$2"*) printf '%s\n' "$subcmd" ;;
+ esac
done
- )
- ;;
- esac
+ done
+ }
+ )
}
complete -F _openssl -o bashdefault -o default openssl
diff --git a/bash/bash_completion.d/pass.bash b/bash/bash_completion.d/pass.bash
index 176886dc..5a6e0b6c 100644
--- a/bash/bash_completion.d/pass.bash
+++ b/bash/bash_completion.d/pass.bash
@@ -1,47 +1,55 @@
-# Requires Bash >= 4.0 for globstar
-((BASH_VERSINFO[0] >= 4)) || return
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
# 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
+_pass() {
- # 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
- [[ -n $entry ]] || continue
- COMPREPLY+=("$entry")
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -d '' -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- # Set shell options to expand globs the way we expect
+ # Make globs expand appropriately
shopt -u dotglob
- shopt -s globstar nullglob
+ shopt -s nullglob
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
+ # Set password store path
+ pass_dir=${PASSWORD_STORE_DIR:-"$HOME"/.password-store}
- # 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}")
+ # Gather the entries
+ for entry in "$pass_dir"/"$2"*.gpg ; do
+ entries[ei++]=$entry
+ done
- # Print quoted entries, null-delimited
- printf '%q\0' "${entries[@]}"
+ # Try to iterate into subdirs, use depth search with ** if available
+ if shopt -s globstar 2>/dev/null ; then
+ for entry in "$pass_dir"/"$2"**/*.gpg ; do
+ entries[ei++]=$entry
+ done
+ else
+ for entry in "$pass_dir"/"$2"*/*.gpg ; do
+ entries[ei++]=$entry
+ done
+ fi
+
+ # Iterate through entries
+ for entry in "${entries[@]}" ; do
+ # Skip directories
+ ! [[ -d $entry ]] || continue
+ # Strip leading path
+ entry=${entry#"$pass_dir"/}
+ # Strip .gpg suffix
+ entry=${entry%.gpg}
+ # Print shell-quoted entry, null terminated
+ printf '%q\0' "$entry"
+ done
)
}
complete -F _pass pass
diff --git a/bash/bash_completion.d/path.bash b/bash/bash_completion.d/path.bash
index 7143b448..9234f132 100644
--- a/bash/bash_completion.d/path.bash
+++ b/bash/bash_completion.d/path.bash
@@ -1,3 +1,8 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Completion for path
_path() {
@@ -5,9 +10,9 @@ _path() {
if ((COMP_CWORD == 1)) ; then
# Complete operation as first word
- local cmd
- while read -r cmd ; do
- COMPREPLY[${#COMPREPLY[@]}]=$cmd
+ local ci comp
+ while read -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(compgen -W '
append
check
@@ -17,63 +22,61 @@ _path() {
pop
remove
shift
- ' -- "${COMP_WORDS[COMP_CWORD]}")
+ ' -- "$2")
+ return
+ fi
# Complete with either directories or $PATH entries as all other words
- else
- case ${COMP_WORDS[1]} in
+ case ${COMP_WORDS[1]} in
- # Complete with a directory
- insert|append|check)
- local dirname
- while IFS= read -rd '' dirname ; do
- [[ -n $dirname ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$dirname
- done < <(
+ # Complete with a directory
+ insert|append|check)
+ local ci comp
+ while IFS= read -d '' -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
- # Set options to glob correctly
- shopt -s dotglob nullglob
+ # Make globs expand appropriately
+ shopt -s dotglob nullglob
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
+ # Print shell-quoted matching directories, null-terminated
+ for dir in "$2"*/ ; do
+ printf '%q\0' "${dir%/}"
+ done
+ )
+ ;;
- # Collect directory names, strip trailing slash
- local -a dirnames
- dirnames=("${COMP_WORDS[COMP_CWORD]}"*/)
- dirnames=("${dirnames[@]%/}")
+ # Complete with directories from PATH
+ remove)
+ local ci comp
+ while IFS= read -d '' -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
- # Print quoted entries, null-delimited
- printf '%q\0' "${dirnames[@]}"
- )
- ;;
+ # Make matches work appropriately
+ if _completion_ignore_case ; then
+ shopt -s nocasematch 2>/dev/null
+ fi
- # Complete with directories from PATH
- remove)
- local -a promptarr
- IFS=: read -rd '' -a promptarr < \
- <(printf '%s\0' "$PATH")
- local part
- for part in "${promptarr[@]}" ; do
- case $part in
- "${COMP_WORDS[COMP_CWORD]}"*)
- COMPREPLY[${#COMPREPLY[@]}]=$(printf '%q' "$part")
- ;;
+ # Break PATH into parts
+ declare -a paths
+ IFS=: read -a paths -d '' -r \
+ < <(printf '%s\0' "$PATH")
+
+ # Print shell-quoted matching parts, null-terminated
+ for path in "${paths[@]}" ; do
+ case $path in
+ ("$2"*) printf '%q\0' "$path" ;;
esac
done
- ;;
+ )
+ ;;
- # No completion
- *)
- return 1
- ;;
- esac
- fi
+ # No completion
+ *) return 1 ;;
+ esac
}
complete -F _path path
diff --git a/bash/bash_completion.d/prompt.bash b/bash/bash_completion.d/prompt.bash
deleted file mode 100644
index d73d77ec..00000000
--- a/bash/bash_completion.d/prompt.bash
+++ /dev/null
@@ -1,2 +0,0 @@
-# Complete subcommands for the prompt wrapper
-complete -W 'on off git svn vcs ret job' prompt
diff --git a/bash/bash_completion.d/sd.bash b/bash/bash_completion.d/sd.bash
index e7e82f80..66dea73b 100644
--- a/bash/bash_completion.d/sd.bash
+++ b/bash/bash_completion.d/sd.bash
@@ -1,52 +1,32 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Completion function for sd; any sibling directories, excluding the self
_sd() {
- # Only makes sense for the first argument
- ((COMP_CWORD == 1)) || return
-
- # Current directory can't be root directory
- case $PWD in
- /) return 1 ;;
- esac
-
# Build list of matching sibling directories
- local dirname
- while IFS= read -rd '' dirname ; do
- [[ -n $dirname ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$dirname
+ local ci comp
+ while IFS= read -d / -r comp ; do
+ COMPREPLY[ci++]=$comp
done < <(
- # Set options to glob correctly
+ # Make globs expand appropriately
shopt -s dotglob nullglob
-
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
-
- # 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
- case $dirname in
- "${PWD##*/}") ;;
- *) sibs[${#sibs[@]}]=$dirname ;;
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
+
+ # Print matching sibling dirs that are not the current dir
+ for sibling in ../"$2"*/ ; do
+ sibling=${sibling%/}
+ sibling=${sibling#../}
+ case $sibling in
+ ("${PWD##*/}") ;;
+ (*) printf '%q/' "${sibling}" ;;
esac
done
-
- # Print quoted sibling directories, null-delimited
- printf '%q\0' "${sibs[@]}"
)
}
complete -F _sd sd
diff --git a/bash/bash_completion.d/sed.bash b/bash/bash_completion.d/sed.bash
index 7957ebe2..3137412e 100644
--- a/bash/bash_completion.d/sed.bash
+++ b/bash/bash_completion.d/sed.bash
@@ -1,4 +1,5 @@
# Completion for sed(1) with files that look editable
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames sed
diff --git a/bash/bash_completion.d/source.bash b/bash/bash_completion.d/source.bash
index de608813..8f40e9e2 100644
--- a/bash/bash_completion.d/source.bash
+++ b/bash/bash_completion.d/source.bash
@@ -1,4 +1,5 @@
# Completion for `source` with files that look like plain text
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames source
diff --git a/bash/bash_completion.d/tail.bash b/bash/bash_completion.d/tail.bash
index e80f40a5..6fe56e29 100644
--- a/bash/bash_completion.d/tail.bash
+++ b/bash/bash_completion.d/tail.bash
@@ -1,4 +1,5 @@
# Completion for tail(1) with files that look editable
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames tail
diff --git a/bash/bash_completion.d/td.bash b/bash/bash_completion.d/td.bash
index 92927c28..f3735691 100644
--- a/bash/bash_completion.d/td.bash
+++ b/bash/bash_completion.d/td.bash
@@ -1,36 +1,31 @@
+# Load _completion_ignore_case helper function
+if ! declare -F _completion_ignore_case >/dev/null ; then
+ source "$HOME"/.bash_completion.d/_completion_ignore_case.bash
+fi
+
# Complete filenames for td(1df)
_td() {
- local dir
- dir=${TODO_DIR:-"$HOME"/Todo}
- local fn
- while IFS= read -rd '' fn ; do
- [[ -n $fn ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$fn
- done < <(
- shopt -s extglob nullglob
- shopt -u dotglob
- # Make globbing case-insensitive if appropriate; is there a cleaner way
- # to find this value?
- while read -r _ option value ; do
- case $option in
- (completion-ignore-case)
- case $value in
- (on)
- shopt -s nocaseglob
- break
- ;;
- esac
- ;;
- esac
- done < <(bind -v)
+ # Iterate through completions produced by subshell
+ local ci comp
+ while IFS= read -d / -r comp ; do
+ COMPREPLY[ci++]=$comp
+ done < <(
- declare -a fns
- fns=("$dir"/"${COMP_WORDS[COMP_CWORD]}"*)
- fns=("${fns[@]#"$dir"/}")
+ # Make globs expand appropriately
+ shopt -u dotglob
+ shopt -s nullglob
+ if _completion_ignore_case ; then
+ shopt -s nocaseglob
+ fi
- # Print quoted entries, null-delimited
- printf '%q\0' "${fns[@]}"
+ # Find and print matching file entries
+ for list in "${TODO_DIR:-"$HOME"/Todo}"/"$2"* ; do
+ # Skip directories
+ ! [[ -d $list ]] || continue
+ # Print entry, slash-terminated
+ printf '%q/' "${list##*/}"
+ done
)
}
complete -F _td td
diff --git a/bash/bash_completion.d/ud.bash b/bash/bash_completion.d/ud.bash
index c7dee582..e6c79716 100644
--- a/bash/bash_completion.d/ud.bash
+++ b/bash/bash_completion.d/ud.bash
@@ -1,35 +1,2 @@
-# Completion setup for ud
-_ud() {
-
- # Only makes sense for the second argument
- ((COMP_CWORD == 2)) || return
-
- # Iterate through directories, null-separated, add them to completions
- local dirname
- while IFS= read -rd '' dirname ; do
- [[ -n "$dirname" ]] || continue
- COMPREPLY[${#COMPREPLY[@]}]=$dirname
- done < <(
-
- # Set options to glob correctly
- shopt -s dotglob nullglob
-
- # Make globbing case-insensitive if appropriate
- while read -r _ setting ; do
- case $setting in
- ('completion-ignore-case on')
- shopt -s nocaseglob
- break
- ;;
- esac
- done < <(bind -v)
-
- # Collect directory names, strip trailing slashes
- dirnames=("${COMP_WORDS[COMP_CWORD]}"*/)
- dirnames=("${dirnames[@]%/}")
-
- # Print results null-delimited
- printf '%s\0' "${dirnames[@]}"
- )
-}
-complete -F _ud -o filenames ud
+# Completie ud() with directory names
+complete -A directory ud
diff --git a/bash/bash_completion.d/vi.bash b/bash/bash_completion.d/vi.bash
index 728be438..5dd35dc1 100644
--- a/bash/bash_completion.d/vi.bash
+++ b/bash/bash_completion.d/vi.bash
@@ -1,4 +1,5 @@
# Completion for vi(1) with files that look editable
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames vi
diff --git a/bash/bash_completion.d/view.bash b/bash/bash_completion.d/view.bash
index e228500f..7709b068 100644
--- a/bash/bash_completion.d/view.bash
+++ b/bash/bash_completion.d/view.bash
@@ -1,4 +1,5 @@
# Completion for view(1) with files that look editable
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames view
diff --git a/bash/bash_completion.d/vim.bash b/bash/bash_completion.d/vim.bash
index 02d085d1..7b9ba027 100644
--- a/bash/bash_completion.d/vim.bash
+++ b/bash/bash_completion.d/vim.bash
@@ -1,4 +1,5 @@
# Completion for vim(1) with files that look editable
-declare -F _text_filenames >/dev/null ||
+if ! declare -F _text_filenames >/dev/null ; then
source "$HOME"/.bash_completion.d/_text_filenames.bash
+fi
complete -F _text_filenames -o filenames vim
diff --git a/bash/bashrc b/bash/bashrc
index 3b4c91bd..a05526f2 100644
--- a/bash/bashrc
+++ b/bash/bashrc
@@ -95,8 +95,8 @@ if ((BASH_VERSINFO[0] >= 4)) ; then
fi
# Load Bash-specific startup files
-for sh in "$HOME"/.bashrc.d/*.bash ; do
- [[ -e $sh ]] || continue
- source "$sh"
+for bash in "$HOME"/.bashrc.d/*.bash ; do
+ [[ -e $bash ]] || continue
+ source "$bash"
done
-unset -v sh
+unset -v bash
diff --git a/bash/bashrc.d/completion.bash b/bash/bashrc.d/completion.bash
index d938275c..5161a0bf 100644
--- a/bash/bashrc.d/completion.bash
+++ b/bash/bashrc.d/completion.bash
@@ -79,7 +79,8 @@ complete -A helptopic \
complete -P '%' -A job \
'disown' \
'fg' \
- 'jobs'
+ 'jobs' \
+ 'kill'
complete -P '%' -A stopped \
'bg'
@@ -102,24 +103,42 @@ if ((BASH_VERSINFO[0] >= 4)) ; then
'readarray'
fi
-# If we have dynamic completion loading (Bash>=4.0), use it
+# If we have dynamic completion loading (Bash >= 4.0), use it
if ((BASH_VERSINFO[0] >= 4)) ; then
# Handler tries to load appropriate completion for commands
_completion_loader() {
- [[ -n $1 ]] || return
+
+ # Check completed command for validity
+ case $1 in
+ # Not empty
+ '') return 1 ;;
+ # Not starting with an underscore
+ _*) return 1 ;;
+ esac
+
+ # Build expected path for the command completion
local compspec
compspec=$HOME/.bash_completion.d/$1.bash
- [[ -f $compspec ]] || return
- source "$compspec" >/dev/null 2>&1 && return 124
+
+ # Skip directories and nonexistent files
+ [[ -e $compspec ]] || return
+ ! [[ -d $compspec ]] || return
+
+ # Try to read the file, return 124 if it worked
+ if source "$compspec" ; then
+ return 124
+ fi
}
+
+ # Set completion loader to use the above function
complete -D -F _completion_loader -o bashdefault -o default
# If not, load all of the completions up now
else
- for sh in "$HOME"/.bash_completion.d/*.bash ; do
- [[ -e $sh ]] || continue
- source "$sh"
+ for bash in "$HOME"/.bash_completion.d/[^_]*.bash ; do
+ [[ -e $bash ]] || continue
+ source "$bash"
done
- unset -v sh
+ unset -v bash
fi
diff --git a/bash/bashrc.d/keep.bash b/bash/bashrc.d/keep.bash
index ab89288e..a39d2fa7 100644
--- a/bash/bashrc.d/keep.bash
+++ b/bash/bashrc.d/keep.bash
@@ -131,7 +131,7 @@ EOF
# Otherwise the user must want us to print all the NAMEs kept
(
- shopt -s dotglob nullglob
+ shopt -s nullglob
declare -a keeps
keeps=("$bashkeep"/*.bash)
keeps=("${keeps[@]##*/}")