From 761fd7247a77f550a5083ca8cbd430909f773bd5 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Thu, 26 Nov 2015 18:15:58 +1300 Subject: Experimental man(1) completion --- bash/bashrc.d/man.bash | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 bash/bashrc.d/man.bash diff --git a/bash/bashrc.d/man.bash b/bash/bashrc.d/man.bash new file mode 100644 index 00000000..4994c379 --- /dev/null +++ b/bash/bashrc.d/man.bash @@ -0,0 +1,65 @@ +# Autocompletion for man(1) +_man() { + + # Don't even bother if we don't have manpath(1) + hash manpath || return 1 + + # Snarf the word + local word + word=${COMP_WORDS[COMP_CWORD]} + + # If this is the second word, and the previous word was a number, we'll + # assume that's the section to search + local section + if ((COMP_CWORD > 1)) && [[ ${COMP_WORDS[COMP_CWORD-1]} != [^0-9] ]] ; then + section='man'${COMP_WORDS[COMP_CWORD-1]} + fi + + # + # Read newline-separated output from a subshell into the COMPREPLY array. + # + # This is subtly wrong. Given that it's just a path, there's theoretically + # no reason that the name of a man(1) page couldn't contain a newline. If + # one of them does, then this method will include some junk manual page + # names. But who on earth makes a manual page with a newline in the name? + # + # Using null separators doesn't work, because read prioritises reading the + # end of the line before it does field-splitting, and filling COMPREPLY + # entry-by-entry is *really* slow, so this is the least-wrong solution I + # could come up with that allows me to use a subshell to elegantly set + # globbing shell options. + # + IFS=$'\n' read -a COMPREPLY -d '' -r < <( + + # Don't return dotfiles, and expand empty globs to just nothing + shopt -u dotglob + shopt -s nullglob + + # Start an array of pages + declare -a pages + + # Break manpath(1) output into an array of paths + IFS=: read -a manpaths -r < <(manpath) + + # Iterate through the manual page paths and add every manual page we find + for manpath in "${manpaths[@]}" ; do + [[ $manpath ]] || continue + pages=("${pages[@]}" "$manpath"/"$section"*/"$word"*) + done + + # Strip paths, .gz suffixes, and finally .
suffixes + pages=("${pages[@]##*/}") + pages=("${pages[@]%.gz}") + pages=("${pages[@]%.*}") + + # Bail out if we ended up with no pages somehow to prevent us from + # printing + ((${#pages[@]})) || exit 1 + + # Print the pages array to stdout, newline-separated; see above + # explanation + (IFS=$'\n' ; printf %s "${pages[*]}") + ) +} +complete -F _man -o default man + -- cgit v1.2.3