tools/gstreamer-completion: Support gst-inspect, and gst-launch element properties

Completes options like "--gst-debug-level" and the values of some of
those options; completes gst-launch pipeline element names, property
names, and even property values (for enum or boolean properties only).

Doesn't complete all caps specifications, nor element names specified
earlier in the pipeline with "name=...".

The GStreamer version number is hard-coded into the completion script:
This patch is off the master branch and has the version hard-coded as
"1.0"; it needs to be updated if backported to the 0.10 branch. You
could always create a "gstreamer-completion.in" that has the appropriate
version inserted by "configure", but I'd rather not do that. The
hard-coded version is consistent with the previous implementation of
gstreamer-completion, which had the registry path hard-coded as
~/.gstreamer-1.0/registry.xml.

Note that GStreamer 0.10 installs "gst-inspect" and "gst-inspect-0.10".
"gst-inspect --help" only prints 4 flags (--help, --print, --gst-mm,
gst-list-mm) whereas "gst-inspect-0.10 --help-all" prints the full list
of flags. The same applies to "gst-launch" and "gst-launch-0.10".
GStreamer 1.0 only installs "gst-inspect-1.0", not "gst-inspect".

Requires bash 4; only tested with bash 4.2. Requires "bash-completion"
(which you install with your system's package manager).

Put this in /etc/bash_completion.d/ or in `pkg-config
--variable=compatdir bash-completion`, where it will be loaded at the
beginning of every new terminal session;
or in `pgk-config --variable=completionsdir bash-completion`, renamed to
match the name of the command it completes (e.g. "gst-launch-1.0", with
an additional symlink named "gst-inspect-1.0"), where it will be
autoloaded when needed.

test-gstreamer-completion.sh is (for now) in tests/misc -- it might be
worth creating "tests/check/tools", with all the necessary automake
boilerplate, and moving test-gstreamer-completion.sh there, and have it
run automatically with "make check".

IF YOU'RE NEW TO BASH COMPLETION SCRIPTS
----------------------------------------

"complete -F _gst_launch gst-launch-1.0" means that bash will run the
function "_gst_launch" to generate possible completions for the command
"gst-launch-1.0".

"_gst_launch" must return the possible completions in the array variable
COMPREPLY. (Note on bash syntax: "V=(a b c)" assigns three elements to
the array "V").

"compgen" prints a list of possible completions to standard output. Try
it:

    compgen -W "abc1 abc2 def" -- "a"
    compgen -f -- "/"

The last argument is the word currently being completed; compgen uses it
to filter out the non-matching completions. We put "--" first, in case
the word currently being completed starts with "-" or "--", so that it
isn't treated as a flag to compgen.

For the documentation of COMP_WORDS, COMP_CWORD, etc see
http://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-COMP_005fCWORD-180

See also:
* http://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html
* http://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html

The bash-completion package provides the helper function
"_init_completion" which populates variables "cur", "prev", and "words".
See
http://anonscm.debian.org/gitweb/?p=bash-completion/bash-completion.git;a=blob;f=bash_completion;h=870811b4;hb=HEAD#l634

Note that by default, bash appends a space to the completed word. When
the completion is "property=" we don't want a trailing space; calling
"compopt -o nospace" modifies the currently-executing completion
accordingly. See
http://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#index-compopt
This commit is contained in:
David Rothlisberger 2012-12-19 10:46:50 +00:00 committed by Stefan Sauer
parent 271c707c45
commit 020dd3bbf3
2 changed files with 283 additions and 6 deletions

View file

@ -0,0 +1,127 @@
#!/usr/bin/env bash
bashcomp=$(pkg-config --variable=prefix bash-completion
)/share/bash-completion/bash_completion
[ -f $bashcomp ] && . $bashcomp ||
{ [ -f /etc/bash_completion ] && . /etc/bash_completion; }
. $(dirname "$0")/../../tools/gstreamer-completion
ret=0
test_gst_inspect_completion() {
local expected
COMP_WORDS=(gst-inspect)
while [[ "$1" != -- ]]; do COMP_WORDS+=("$1"); shift; done; shift
COMP_CWORD=$(( ${#COMP_WORDS[*]} - 1 ))
COMP_LINE="${COMP_WORDS[*]}"
COMP_POINT=${#COMP_LINE}
while [[ -n "$1" ]]; do expected+=("$1"); shift; done
printf "test_gst_inspect_completion: '${COMP_WORDS[*]}'... "
_gst_inspect
_assert_expected && echo OK
}
_assert_expected() {
for x in "${expected[@]}"; do
grep -w -q -- "$x" <(echo "${COMPREPLY[*]}") &>/dev/null || {
ret=1
echo FAIL
echo "Expected: '$x'. Got:"
for r in "${COMPREPLY[@]}"; do echo $r; done | head
echo ""
return 1
}
done
return 0
}
# test_gst_inspect_completion <command line to complete> -- <expected completions>
test_gst_inspect_completion '' -- --version --gst-debug-level= coreelements fakesrc
test_gst_inspect_completion --ver -- --version
test_gst_inspect_completion --gst-debug-le -- --gst-debug-level=
test_gst_inspect_completion --gst-debug-level= -- 0 1 2 3 4 5
test_gst_inspect_completion coreel -- coreelements
test_gst_inspect_completion fake -- fakesrc fakesink
test_gst_inspect_completion --version --gst-debug-level = 2 fake -- fakesrc fakesink
test_gst_launch_completion() {
local expected
COMP_WORDS=(gst-launch)
while [[ "$1" != -- ]]; do COMP_WORDS+=("$1"); shift; done; shift
COMP_CWORD=$(( ${#COMP_WORDS[*]} - 1 ))
COMP_LINE="${COMP_WORDS[*]}"
COMP_POINT=${#COMP_LINE}
while [[ -n "$1" ]]; do expected+=("$1"); shift; done
printf "test_gst_launch_completion: '${COMP_WORDS[*]}'... "
_gst_launch
_assert_expected &&
echo OK
}
# test_gst_launch_completion <command line to complete> -- <expected completions>
test_gst_launch_completion '' -- --eos-on-shutdown --gst-debug-level= fakesrc fakesink
test_gst_launch_completion --mes -- --messages
test_gst_launch_completion --gst-debug-le -- --gst-debug-level=
test_gst_launch_completion --gst-debug-level -- --gst-debug-level=
test_gst_launch_completion --gst-debug-level = -- 0 1 2 3 4 5
test_gst_launch_completion fak -- fakesrc fakesink
test_gst_launch_completion --messages fak -- fakesrc fakesink
test_gst_launch_completion --messages --eos-on-shutdown fak -- fakesrc
test_gst_launch_completion fakesrc '' -- name= is-live= format= !
test_gst_launch_completion fakesrc is-live -- is-live=
test_gst_launch_completion fakesrc is-live = -- true false
test_gst_launch_completion fakesrc format = -- bytes time buffers percent
test_gst_launch_parse() {
local cur cword words curtype option element property
words=(gst-launch)
while [[ "$1" != -- ]]; do words+=("$1"); shift; done; shift
cword=$(( ${#words[*]} - 1 ))
local xcurtype="$1" xoption="$2" xelement="$3" xproperty="$4"
printf "test_gst_launch_parse: '${words[*]}'... "
_gst_launch_parse
_assert curtype "$curtype" "$xcurtype" &&
_assert option "$option" "$xoption" &&
_assert element "$element" "$xelement" &&
_assert property "$property" "$xproperty" &&
echo OK
}
_assert() {
local name="$1" got="$2" expected="$3"
[[ -z "$expected" || "$got" == "$expected" ]] || {
ret=1
echo "FAIL"
echo "Expected $name: '$expected'. Got: '$got'."
echo ""
false
}
}
test_gst_launch_parse '' -- option-or-element '' '' ''
test_gst_launch_parse --mes -- option '' '' ''
test_gst_launch_parse --messages -- option '' '' ''
test_gst_launch_parse --gst-debug-level = -- optionval --gst-debug-level '' ''
test_gst_launch_parse fak -- element '' '' ''
test_gst_launch_parse --messages fak -- element '' '' ''
test_gst_launch_parse --gst-debug-level = 5 fak -- element '' '' ''
test_gst_launch_parse fakesrc '' -- property '' fakesrc ''
test_gst_launch_parse fakesrc is-l -- property '' fakesrc ''
test_gst_launch_parse fakesrc is-live = -- propertyval '' fakesrc is-live
test_gst_launch_parse fakesrc is-live = true form -- property '' 'fakesrc' ''
test_gst_launch_parse fakesrc is-live = true ! -- ! '' '' ''
test_gst_launch_parse fakesrc is-live = true ! fakesi -- element '' '' ''
test_gst_launch_parse fakesrc is-live = true ! fakesink '' -- property '' fakesink ''
exit $ret

View file

@ -1,13 +1,163 @@
#
# Bash tab-completion for GStreamer. -*- shell-script -*-
# Put this in /etc/bash_completion.d/
#
_gst_version=1.0
_gst_inspect() {
local cur prev words cword split
_init_completion -n : -s || return
_gst_common_options || return
COMPREPLY=( $(compgen \
-W "$(_parse_help gst-inspect-$_gst_version --help-all) \
$(_gst_plugins) $(_gst_elements)" \
-- "$cur") )
[[ $COMPREPLY == *= ]] && compopt -o nospace 2>/dev/null
} &&
complete -F _gst_inspect gst-inspect-$_gst_version
_gst_launch() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=( $(compgen -W "$(_gst_elements)" -- "$cur") )
local cur cword prev words
_init_completion -n : || return
local curtype option element property
_gst_launch_parse
_gst_common_options || return
COMPREPLY=( $(_gst_launch_compgen) )
[[ $COMPREPLY == *= ]] && compopt -o nospace 2>/dev/null
} &&
complete -F _gst_launch -o default gst-launch-1.0
complete -o default -F _gst_launch gst-launch-$_gst_version
_gst_common_options() {
if [[ -v curtype && -v option ]]; then # Called from _gst_launch
[[ $curtype == optionval ]] || return 0
else # Called from _gst_inspect
local option="$prev"
fi
case "$option" in
--gst-debug-level)
COMPREPLY=( $(compgen -W "0 1 2 3 4 5" -- "$cur") );;
--gst-debug) # TODO: comma-separated list of category_name:level pairs.
;;
--gst-plugin-path) # TODO: support multiple (colon-separated) paths.
COMPREPLY=( $(compgen -d -- "$cur") );;
--gst-plugin-load) # TODO: comma-separated list of plugins (files?).
;;
*) return 0;;
esac
return 1 # No need to attempt further completions.
}
_gst_launch_compgen() {
case $curtype in
option|option-or-element)
compgen \
-W "$(_parse_help gst-launch-$_gst_version --help-all)" \
-- "$cur"
;;& # test next pattern too.
element|option-or-element)
compgen -W "$(_gst_elements)" -- "$cur" ;;
optionval)
case "$option" in
-o|--output) compgen -f -- "$cur" ;;
--exclude) ;; # TODO: comma-separated list of status information types.
esac ;;
\!)
compgen -W '!' -- "$cur" ;;
property)
compgen -W "$(_gst_properties $element) ! " -- "$cur" ;;
propertyval)
compgen -W "$(_gst_property_values $element $property)" -- "$cur" ;;
esac
}
_gst_plugins() {
gst-inspect-$_gst_version 2>/dev/null |
grep -v 'Total count' |
awk -F': +' '{print $1}' |
uniq
}
_gst_elements() {
gst-inspect-1.0 | grep -v 'Total count' | awk -F': +' '{print $2}'
gst-inspect-$_gst_version 2>/dev/null |
grep -v 'Total count' |
awk -F': +' '{print $2}'
}
_gst_properties() {
local element="$1"
gst-inspect-$_gst_version "$element" 2>/dev/null |
sed -n '/^Element Properties:$/,$ p' |
awk '/^ [a-z]/ { print $1 "=" }'
}
_gst_property_values() {
local element=$1 property=$2
gst-inspect-$_gst_version $element 2>/dev/null |
awk "
/^Element Properties:\$/ { inproperties = 1; next; }
inproperties && /^ $property / { inproperty = 1; next; }
inproperty && /^ *Boolean/ { printf \"true\nfalse\n\"; exit; }
inproperty && /^ *Enum/ { inenum = 1; next; }
inenum && /^ *\([0-9]+\): / { print \$2; next; }
inproperty && /^ [a-z]/ { exit; }"
}
# Walks over $words, sets $curtype to the string:
#
# 'option' if $cur is an option or flag like "-a" or "--abc".
# 'optionval' if $cur is the value of an option
# (which will be set in $option).
# 'element' if $cur is a GStreamer element name.
# '!' if $cur is '!'.
# 'property' if $cur is the name of a property of a GStreamer element
# (which will be set in $element).
# 'propertyval' if $cur is the value of an element's property
# (which will be set in $element and $property, respectively).
#
# ($cur is the word currently being completed.)
#
# Before calling this function make sure that $curtype, $option, $element and
# $property are local, and that $cur, $cword and $words have been initialised
# by calling _init_completion.
#
# See test cases in tests/misc/test-gstreamer-completion.sh in the
# gstreamer source repository.
#
_gst_launch_parse() {
local i next state
curtype= i=1 state=start
while [[ $i -le $cword ]]; do
next="${words[i]}"
# Note that COMP_WORDBREAKS by default includes "=" and ":".
case "$state,$next" in
start,-*) curtype=option option="$next" state=option;;
start,) curtype=option-or-element;;
start,*) curtype=element element="$next" state=element;;
option,=) curtype=optionval state=option=;;
option,*) _gst_takes_arg "$option" &&
curtype=optionval state=start ||
# re-evaluate without incrementing i:
{ curtype= state=start; continue; }
;;
option=,*) curtype=optionval state=start;;
element,\!) curtype='!' state='!';;
\!,*) curtype=element element="$next" state=element;;
element,*) curtype=property property="$next" state=property;;
property,=) curtype=propertyval state=property=;;
property=,*) curtype=propertyval state=element;;
esac
i=$((i + 1))
done
[[ "$cur" == "=" ]] && cur=""
}
_gst_takes_arg() {
case "$1" in
-o|--output|--gst-debug-level|--gst-debug) true;;
--gst-plugin-path|--gst-plugin-load|--exclude) true;;
*) false;;
esac
}