Tools: Add bash completions for xkbcli

- Add bash completion script. It parses the commands help messages to
  provide the completions, thus any new subcommand or option will be
  supported, as long as it has its entry in the help messages. This
  should result in low maintenancei effort.
- Add installation entry in Meson. The path can be configured using
  the following options:
  - `enable-bash-completion` to enable the installation;
  - `bash-completion-path` to control the installation path. It will
    default to: `share/bash-completion/completions`.

TODO: completion for other shells, such as zsh?
master
Pierre Le Marre 2023-09-29 20:44:06 +02:00 committed by Wismill
parent 357c00b3a1
commit 1c1542d64f
5 changed files with 142 additions and 0 deletions

View File

@ -417,6 +417,7 @@ man_pages = []
# Tools
build_tools = get_option('enable-tools') and cc.has_header_symbol('getopt.h', 'getopt_long', prefix: '#define _GNU_SOURCE')
if build_tools
# Common resources
libxkbcommon_tools_internal_sources = [
'tools/tools-common.h',
'tools/tools-common.c',
@ -432,10 +433,27 @@ if build_tools
dependencies: dep_libxkbcommon,
)
# Tool: xkbcli
executable('xkbcli', 'tools/xkbcli.c',
dependencies: tools_dep, install: true)
install_man('tools/xkbcli.1')
if get_option('enable-bash-completion')
bash_completion_path = get_option('bash-completion-path')
if bash_completion_path == ''
bash_completion = dependency('bash-completion', required: false)
if bash_completion.found()
bash_completion_path = bash_completion.get_variable(pkgconfig: 'completionsdir')
else
bash_completion_path = get_option('datadir') / 'bash-completion/completions'
endif
endif
install_data('tools/xkbcli-bash-completion.sh',
rename: 'xkbcli',
install_dir: bash_completion_path)
endif
# Tool: compile-keymap
xkbcli_compile_keymap = executable('xkbcli-compile-keymap',
'tools/compile-keymap.c',
dependencies: tools_dep,
@ -450,12 +468,16 @@ if build_tools
c_args: ['-DENABLE_PRIVATE_APIS'],
include_directories: [include_directories('src', 'include')],
install: false)
# Tool: compose
executable('compose',
'tools/compose.c',
dependencies: tools_dep,
include_directories: [include_directories('src', 'include')],
install: false)
configh_data.set10('HAVE_XKBCLI_COMPILE_KEYMAP', true)
# Tool: how-to-type
executable('xkbcli-how-to-type',
'tools/how-to-type.c',
dependencies: tools_dep,
@ -463,6 +485,8 @@ if build_tools
install_dir: dir_libexec)
install_man('tools/xkbcli-how-to-type.1')
configh_data.set10('HAVE_XKBCLI_HOW_TO_TYPE', true)
# Tool: interactive-evdev
if cc.has_header('linux/input.h')
executable('xkbcli-interactive-evdev',
'tools/interactive-evdev.c',
@ -481,6 +505,8 @@ if build_tools
include_directories: [include_directories('src', 'include')],
install: false)
endif
# Tool: interactive-x11
if get_option('enable-x11')
x11_tools_dep = declare_dependency(
link_with: libxkbcommon_x11,
@ -498,6 +524,8 @@ if build_tools
install_man('tools/xkbcli-interactive-x11.1')
configh_data.set10('HAVE_XKBCLI_INTERACTIVE_X11', true)
endif
# Tool: interactive-wayland
if get_option('enable-wayland')
wayland_client_dep = dependency('wayland-client', version: '>=1.2.0', required: false)
wayland_protocols_dep = dependency('wayland-protocols', version: '>=1.12', required: false)
@ -534,6 +562,7 @@ You can disable the Wayland xkbcli programs with -Denable-wayland=false.''')
configh_data.set10('HAVE_XKBCLI_INTERACTIVE_WAYLAND', true)
endif
# Tool: list
if get_option('enable-xkbregistry')
configh_data.set10('HAVE_XKBCLI_LIST', true)
executable('xkbcli-list',
@ -544,6 +573,7 @@ You can disable the Wayland xkbcli programs with -Denable-wayland=false.''')
install_man('tools/xkbcli-list.1')
endif
# Tool: check-messages
executable('xkb-check-messages',
'tools/check-messages.c',
'tools/messages.c',

View File

@ -13,6 +13,11 @@ option(
type: 'string',
description: 'The X locale root [default=$datadir/X11/locale]',
)
option(
'bash-completion-path',
type: 'string',
description: 'Directory for bash completion scripts'
)
option(
'default-rules',
type: 'string',
@ -79,3 +84,9 @@ option(
value: true,
description: 'Enable building libxkbregistry',
)
option(
'enable-bash-completion',
type: 'boolean',
value: true,
description: 'Enable installing bash completion scripts',
)

View File

@ -58,6 +58,8 @@ usage(char **argv)
"Compile the given RMLVO to a keymap and print it\n"
"\n"
"Options:\n"
" --help\n"
" Print this help and exit\n"
" --verbose\n"
" Enable verbose debugging output\n"
#if ENABLE_PRIVATE_APIS

96
tools/xkbcli-bash-completion.sh Executable file
View File

@ -0,0 +1,96 @@
# bash completion support for xkbcli.
# See completion API documentation: https://github.com/scop/bash-completion
# NOTE: The script parses the commands help messages to provide the completions,
# thus any new subcommand or option will be supported, as long as it has its
# entry in the help messages. This should result in low maintenancei effort.
___xkbcli_main()
{
# Initialization: https://github.com/scop/bash-completion/blob/fdf4456186eb4548ef628e65fb1be73d8e4695e9/bash_completion.d/000_bash_completion_compat.bash#L205
local cur prev words cword cmd
_init_completion -s || return
# Find subcommand
local i=1
while [[ "$i" -lt "$COMP_CWORD" ]]; do
local s="${COMP_WORDS[i]}"
case "$s" in
-*) ;;
*)
cmd="$s"
break
;;
esac
(( i++ ))
done
# Parse available subcommands
local line
local is_command_list=false
local subcommands=()
while IFS='' read -r line; do
# Traverse subcommand list
if [[ "$is_command_list" == true ]]; then
# Check for subcommand based on the indentation
if [[ "$line" =~ ^[[:blank:]]{2}([[:alpha:]]([[:alnum:]]|-)+)$ ]]; then
subcommands+=("${BASH_REMATCH[1]}")
# Detect end of subcommand list based on indentation
elif [[ "$line" =~ ^[[:graph:]] ]]; then
is_command_list=false
fi
# Detect start of subcommand list
elif [[ "$line" == "Commands:" ]]; then
is_command_list=true
fi
# NOTE: <( COMMAND ) Bash construct is “process substitution”.
done < <(xkbcli --help)
# No previous subcommand or incomplete: completion for root xkbcli command
if [[ "$i" -eq "$COMP_CWORD" ]]; then
local opts
# Doc for _parse_help: https://github.com/scop/bash-completion/blob/fdf4456186eb4548ef628e65fb1be73d8e4695e9/bash_completion.d/000_bash_completion_compat.bash#L311
opts=$(_parse_help xkbcli)
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "${subcommands[*]} $opts" -- "$cur"))
return
fi
# Found a supported subcommand: proceed to completion
if [[ "${subcommands[*]}" =~ (^| )$cmd( |$) ]]; then
___xkbcli_subcommand "$cmd"
fi
}
___xkbcli_subcommand()
{
# Some special cases
case $1 in
compile-keymap | interactive-evdev)
case ${COMP_WORDS[COMP_CWORD-1]} in
--include | --keymap)
_filedir
return;;
esac
;;
list)
if [[ ${COMP_WORDS[COMP_CWORD]} != -* ]]; then
_filedir
return
fi
;;
esac
# Parse help to get command options
local opts
# Doc for _parse_usage and _parse_help:
# • https://github.com/scop/bash-completion/blob/fdf4456186eb4548ef628e65fb1be73d8e4695e9/bash_completion.d/000_bash_completion_compat.bash#L335
# • https://github.com/scop/bash-completion/blob/fdf4456186eb4548ef628e65fb1be73d8e4695e9/bash_completion.d/000_bash_completion_compat.bash#L311
# We need both as the current help messages adopt both GNU and BSD styles.
opts=$(_parse_usage xkbcli "$1 --help")
opts+=$(_parse_help xkbcli "$1 --help")
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
}
complete -F ___xkbcli_main xkbcli

View File

@ -37,6 +37,9 @@ usage(void)
"Global options:\n"
" -h, --help ...... show this help and exit\n"
" -V, --version ... show version information and exit\n"
/* WARNING: The following is parsed by the bash completion script.
* Any change to the format (in particular to the indentation)
* should kept in the script in sync. */
"Commands:\n"
#if HAVE_XKBCLI_LIST
" list\n"