diff --git a/meson.build b/meson.build index 91d0b36..7d5802b 100644 --- a/meson.build +++ b/meson.build @@ -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', diff --git a/meson_options.txt b/meson_options.txt index 924d1f7..d52cc37 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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', +) diff --git a/tools/compile-keymap.c b/tools/compile-keymap.c index d762624..f49aa3c 100644 --- a/tools/compile-keymap.c +++ b/tools/compile-keymap.c @@ -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 diff --git a/tools/xkbcli-bash-completion.sh b/tools/xkbcli-bash-completion.sh new file mode 100755 index 0000000..fdab116 --- /dev/null +++ b/tools/xkbcli-bash-completion.sh @@ -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 diff --git a/tools/xkbcli.c b/tools/xkbcli.c index 75f72fe..3602fb9 100644 --- a/tools/xkbcli.c +++ b/tools/xkbcli.c @@ -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"