aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2018-12-02 18:03:15 +1300
committerTom Ryder <tom@sanctum.geek.nz>2018-12-02 18:03:15 +1300
commit693fc13bb98b17938f2208fbadaec1996822fc5e (patch)
tree4a9cc6179f521528c663c8ab5f3bbc59dc57eaa0
parentMerge branch 'release/v2.6.0' (diff)
parentBump VERSION (diff)
downloaddotfiles-693fc13bb98b17938f2208fbadaec1996822fc5e.tar.gz
dotfiles-693fc13bb98b17938f2208fbadaec1996822fc5e.zip
Merge branch 'release/v2.7.0'v2.7.0
* release/v2.7.0: (22 commits) Bump VERSION Make separate install-bash-completion target Overhaul Bash completion scripts Reduce ud() completion to just dirnames Upgrade uncap_ex.vim plugin to v0.3.0 Apply syntax fixes to last _text_filenames specs Rearrange _text_filenames completion a little Remove prompt() completion Throw away chgrp completion Throw away Git Bash completion Remove mysql(1) completion Use consistent temp names for shell subfile vars Overhaul pass(1) completion Adjust syntax of two more completion loads Remove ftp(1) completion Remove `kill` completion Use the positional parameter aliases for words Overhaul bd() completion again Remove unneeded -q option to shopt -s commands Don't include dotfiles in keep() names ...
-rw-r--r--Makefile6
-rw-r--r--README.md2
-rw-r--r--VERSION4
-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
-rw-r--r--ksh/kshrc6
m---------vim/bundle/uncap_ex0
-rw-r--r--zsh/zshrc8
40 files changed, 550 insertions, 828 deletions
diff --git a/Makefile b/Makefile
index f9ed21ac..882982c6 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@
install \
install-abook \
install-bash \
+ install-bash-completion \
install-bin \
install-bin-man \
install-curl \
@@ -359,11 +360,14 @@ install-abook:
cp -p -- abook/abookrc $(HOME)/.abook
install-bash: check-bash install-sh
- mkdir -p -- $(HOME)/.bashrc.d $(HOME)/.bash_completion.d $(HOME)/.config
+ mkdir -p -- $(HOME)/.bashrc.d
cp -p -- bash/bashrc $(HOME)/.bashrc
cp -p -- bash/bashrc.d/* $(HOME)/.bashrc.d
cp -p -- bash/bash_profile $(HOME)/.bash_profile
cp -p -- bash/bash_logout $(HOME)/.bash_logout
+
+install-bash-completion: install-bash
+ mkdir -p -- $(HOME)/.bash_completion.d $(HOME)/.config
cp -p -- bash/bash_completion $(HOME)/.config
cp -p -- bash/bash_completion.d/* $(HOME)/.bash_completion.d
diff --git a/README.md b/README.md
index ae9b4319..99bc9d7d 100644
--- a/README.md
+++ b/README.md
@@ -264,8 +264,6 @@ files, for things I really do get tired of typing repeatedly:
* Bash builtins: commands, help topics, shell options, variables, etc.
* `find(1)`'s more portable options
-* `ftp(1)` hostnames from `~/.netrc`
-* `git(1)` subcommands, remotes, branches, tags, and addable files
* `gpg(1)` long options
* `make(1)` targets read from a `Makefile`
* `man(1)` page titles
diff --git a/VERSION b/VERSION
index 8e55d49a..1f343c3f 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-tejr dotfiles v2.6.0
-Fri Nov 30 13:48:02 UTC 2018
+tejr dotfiles v2.7.0
+Sun Dec 2 05:03:14 UTC 2018
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[@]##*/}")
diff --git a/ksh/kshrc b/ksh/kshrc
index 43ac14da..1aea4b9d 100644
--- a/ksh/kshrc
+++ b/ksh/kshrc
@@ -20,7 +20,7 @@ set -o trackall
HISTFILE=$HOME/.ksh_history
# Load any supplementary scripts
-for kshrc in "$HOME"/.kshrc.d/*.ksh ; do
- [[ -e $kshrc ]] && . "$kshrc"
+for ksh in "$HOME"/.kshrc.d/*.ksh ; do
+ [[ -e $ksh ]] && . "$ksh"
done
-unset -v kshrc
+unset -v ksh
diff --git a/vim/bundle/uncap_ex b/vim/bundle/uncap_ex
-Subproject 8e50627fda3f774de4bafa064772566348da426
+Subproject 4e745cc73bc24ab0e0898f13040308cdc8a6075
diff --git a/zsh/zshrc b/zsh/zshrc
index 2977c4d9..0e111364 100644
--- a/zsh/zshrc
+++ b/zsh/zshrc
@@ -16,8 +16,8 @@ HISTFILE=$HOME/.zsh_history
SAVEHIST=$((1 << 12))
# Load Zsh-specific startup files
-for sh in "$HOME"/.zshrc.d/*.zsh ; do
- [[ -e $sh ]] || continue
- source "$sh"
+for zsh in "$HOME"/.zshrc.d/*.zsh ; do
+ [[ -e $zsh ]] || continue
+ source "$zsh"
done
-unset -v sh
+unset -v zsh