Last active
August 29, 2015 14:16
-
-
Save christierney/e84f44a1ca616fdbce65 to your computer and use it in GitHub Desktop.
nodeattr bash-completion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# nodeattr(1) completion | |
# | |
# Usage: add to ~/.bash_completion, or install in /etc/bash_completion.d/ | |
# Requires: _upvar _get_comp_words_by_ref _filedir from bash-completion | |
# Limitations: Successful completion of a query that opens with a quote causes | |
# a closing quote to be appended automatically, which is annoying if you want | |
# to add more attributes to the query. I think readline is doing this and I'm | |
# not sure how to stop it. | |
# List all nodes. | |
# | |
# Param: $1 Genders file | |
__list_nodes() | |
{ | |
nodeattr -f "$1" -s -A | |
} | |
# List all attributes that potentially complete the current word. | |
# | |
# Param: $1 Genders file | |
# Param: $2 Current word | |
__list_attrs() | |
{ | |
local cur="$2" | |
# as cur may be a complex nodeattr query in progress, strip off any | |
# leading query elements and just complete on the last portion. We'll | |
# add the leading query back later. | |
shopt -s extglob && local attr="${cur##*@(\(|\|\||&&|--|~)}" | |
local query=${cur%"$attr"} | |
local attrs=$( nodeattr -f "$1" -l ) | |
if [[ -z "$attr" ]]; then | |
# no word-in-progress: return all attrs, no values | |
echo ${attrs[@]} | |
else | |
# word-in-progress: return matching attrs and their potential values | |
local match matches=() | |
# find possible full expansions of $attr | |
for match in $( compgen -W "$attrs" -- "${attr%=*}" ); do | |
# find possible attribute values | |
local _vals=$( grep -o "$match=.*" "$1" | sort -u ) | |
matches+=( $( compgen -W "$match $_vals" -- "$attr" ) ) | |
# this is arguably more correct than grep but also super-slow: | |
# for val in $(nodeattr -f "$1" -UV $attr); do | |
# matches+=("$attr=$val") | |
# done | |
done | |
# restore the leading query, if any | |
echo "${matches[@]/#/$query}" | |
fi | |
} | |
# Returns the index where a string appears as a value in an array. | |
# | |
# Usage: __index_in_array "-f" i "${arr[@]}" | |
# Param: $1 Value to search for | |
# Param: $2 Name of variable to return result to | |
# Param: $3+ Array values | |
__index_in_array() | |
{ | |
local i arr=( "${@:3}" ) | |
for (( i=0; i < ${#arr[@]}; i++ )); do | |
[[ $1 == "${arr[$i]}" ]] && | |
local "$2" && _upvar $2 "$i" && | |
return | |
done | |
return 1 | |
} | |
# True is the word-to-complete starts with " or '. | |
# Param: $1 current word-to-complete | |
__is_quoted() | |
{ | |
[[ $1 == \"* ]] || [[ $1 == \'* ]] | |
} | |
# If the word-to-complete contains an equals (=), left-trim COMPREPLY items | |
# up to the equals. | |
# | |
# This works around the case where COMP_WORDBREAKS includes =, causing bash to | |
# insert an entire word instead of just completing it (i.e. attr= completes to | |
# attr=attr=val.) | |
# | |
# An alternate solution is to remove the = from COMP_WORDBREAKS in your | |
# .bashrc: | |
# # Remove equals (=) from list of word completion separators | |
# COMP_WORDBREAKS=${COMP_WORDBREAKS//=} | |
# | |
# See also: Bash FAQ - E13) Why does filename completion misbehave if a colon | |
# appears in the filename? - http://tiswww.case.edu/php/chet/bash/FAQ | |
# @param $1 current word to complete (cur) | |
# @modifies global array $COMPREPLY | |
# | |
__ltrim_equals_completions() { | |
# If word-to-complete contains an equals, | |
# and bash-version < 4, | |
# or bash-version >= 4 and COMP_WORDBREAKS contains a colon | |
if [[ | |
"$1" == *=* && ( | |
${BASH_VERSINFO[0]} -lt 4 || | |
(${BASH_VERSINFO[0]} -ge 4 && "$COMP_WORDBREAKS" == *=*) | |
) | |
]]; then | |
# Remove equals-word prefix from COMPREPLY items | |
local equals_word=${1%${1##*=}} | |
local i=${#COMPREPLY[*]} | |
while [ $((--i)) -ge 0 ]; do | |
COMPREPLY[$i]=${COMPREPLY[$i]#"$equals_word"} | |
done | |
fi | |
} # __ltrim_equals_completions() | |
_nodeattr() | |
{ | |
COMPREPLY=() | |
local opts="-f -q -c -n -s -X -A -v -Q -V -U -l -k -d --expand --compress" | |
local cur prev words cword && _get_comp_words_by_ref cur prev words cword | |
# when drawing completions from the genders db, use the file specified by | |
# the user, if any | |
local genders="/etc/genders" | |
#if [[ -n "${words[2]}" ]]; then | |
local i f && __index_in_array "-f" i "${words[@]}" && f="${words[$i+1]}" | |
if [[ -n "$f" ]]; then | |
# To correctly detect filenames with spaces we need to account for both | |
# escaped spaces, and quoted filenames: | |
local dequoted="$(dequote "$f")" | |
[[ -r "$dequoted" ]] && | |
genders="$dequoted" | |
fi | |
case $prev in | |
nodeattr) | |
COMPREPLY=( $( compgen -W "$opts" -- "$cur" ) ) | |
local attrs=$( __list_attrs "$genders" "$cur" ) | |
COMPREPLY+=( $attrs ) | |
__is_quoted $cur || __ltrim_equals_completions $cur | |
return | |
;; | |
-f|-d) | |
_filedir | |
return | |
;; | |
-l) | |
local nodes=$( __list_nodes "$genders" ) | |
COMPREPLY=( $( compgen -W "$nodes" -- "$cur" ) ) | |
return | |
;; | |
-A|-k|--expand|--compress) | |
return 1 | |
;; | |
-v|-Q) | |
local nodes=$( __list_nodes "$genders" ) | |
COMPREPLY=( $( compgen -W "$nodes" -- "$cur" ) ) | |
local attrs=$( __list_attrs "$genders" "$cur" ) | |
COMPREPLY+=( $attrs ) | |
__is_quoted $cur || __ltrim_equals_completions $cur | |
return | |
;; | |
-V|-U) | |
COMPREPLY=( $( compgen -W "-U -V" -- "$cur" ) ) | |
local attrs=$( __list_attrs "$genders" "$cur" ) | |
COMPREPLY+=( $attrs ) | |
__is_quoted $cur || __ltrim_equals_completions $cur | |
return | |
;; | |
esac | |
if [[ "$cur" == -* ]]; then | |
COMPREPLY=( $( compgen -W "$opts" -- "$cur" ) ) | |
else | |
local attrs=$( __list_attrs "$genders" "$cur" ) | |
COMPREPLY=( $attrs ) | |
__is_quoted $cur || __ltrim_equals_completions $cur | |
fi | |
} | |
complete -F _nodeattr -o nospace nodeattr |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment