Skip to content

Commit

Permalink
Tools: Add bash completions for xkbcli
Browse files Browse the repository at this point in the history
- 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 option `bash-completion-path`; it will default to:
  `share/bash-completion/completions`.

TODO: completion for other shells?
  • Loading branch information
wismill committed Oct 3, 2023
1 parent e329b3f commit adebb5a
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
32 changes: 32 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -432,10 +433,29 @@ if build_tools
dependencies: dep_libxkbcommon,
)

# Tool: xkbcli
executable('xkbcli', 'tools/xkbcli.c',
dependencies: tools_dep, install: true)
install_man('tools/xkbcli.1')

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
if bash_completion_path != 'no'
install_data(
'tools/xkbcli-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,
Expand All @@ -450,19 +470,25 @@ 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,
install: true,
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',
Expand All @@ -481,6 +507,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,
Expand All @@ -498,6 +526,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)
Expand Down Expand Up @@ -534,6 +564,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',
Expand All @@ -544,6 +575,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',
Expand Down
5 changes: 5 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ["no" disables]'
)
option(
'default-rules',
type: 'string',
Expand Down
2 changes: 2 additions & 0 deletions tools/compile-keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 97 additions & 0 deletions tools/xkbcli-completion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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
_init_completion -s || return

local i=1 cmd

# Find subcommand
while [[ "$i" -lt "$COMP_CWORD" ]]
do
local s="${COMP_WORDS[i]}"
case "$s" in
-*) ;;
*)
cmd="$s"
break
;;
esac
(( i++ ))
done

# Parse available subcommands
local help_output line is_command_list=false subcommands=()
help_output=$(xkbcli --help)
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
done <<< "$help_output"

# 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 & _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
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
3 changes: 3 additions & 0 deletions tools/xkbcli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit adebb5a

Please sign in to comment.