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 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?
  • Loading branch information
wismill committed Oct 5, 2023
1 parent 357c00b commit 1c1542d
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 0 deletions.
30 changes: 30 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,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,
Expand All @@ -450,19 +468,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 +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,
Expand All @@ -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)
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand Down
11 changes: 11 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'
)
option(
'default-rules',
type: 'string',
Expand Down Expand Up @@ -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',
)
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
96 changes: 96 additions & 0 deletions tools/xkbcli-bash-completion.sh
Original file line number Diff line number Diff line change
@@ -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
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 1c1542d

Please sign in to comment.