Switch to clap-generated completions

The current completions required a lot of domain-specific knowledge
about each individual shell and their completion functionality. Much of
which is sparsely documented.

While clap does not generate perfect completions, since parameters like
`-e` are missing completions, it does a reasonable job while requiring
no work on writing these completions.

Since access to `cli.rs` isn't possible from the `build.rs`, these
completions aren't always generated on build. Instead a test verifies
that there has been no changes to these completions and provides a
simple code sample for re-generating them. This should provide a simple
solution with minimal overhead.
This commit is contained in:
Christian Duerr 2021-11-19 23:34:40 +00:00 committed by GitHub
parent b0da035e9e
commit c89939b5d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 349 additions and 206 deletions

1
Cargo.lock generated
View File

@ -21,6 +21,7 @@ dependencies = [
"alacritty_config_derive",
"alacritty_terminal",
"bitflags",
"clap",
"cocoa 0.24.0",
"copypasta",
"crossfont",

View File

@ -35,6 +35,9 @@ unicode-width = "0.1"
bitflags = "1"
dirs = "3.0.1"
[dev-dependencies]
clap = "2.33.3"
[build-dependencies]
gl_generator = "0.14.0"

View File

@ -246,6 +246,13 @@ pub enum SocketMessage {
mod tests {
use super::*;
#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::Read;
#[cfg(target_os = "linux")]
use clap::Shell;
use serde_yaml::mapping::Mapping;
#[test]
@ -334,4 +341,32 @@ mod tests {
let class = parse_class("one,two,three");
assert!(class.is_err());
}
#[cfg(target_os = "linux")]
#[test]
fn completions() {
let mut clap = Options::clap();
for (shell, file) in &[
(Shell::Bash, "alacritty.bash"),
(Shell::Fish, "alacritty.fish"),
(Shell::Zsh, "_alacritty"),
] {
let mut generated = Vec::new();
clap.gen_completions_to("alacritty", *shell, &mut generated);
let generated = String::from_utf8_lossy(&generated);
let mut completion = String::new();
let mut file = File::open(format!("../extra/completions/{}", file)).unwrap();
file.read_to_string(&mut completion).unwrap();
assert_eq!(generated, completion);
}
// NOTE: Use this to generate new completions.
//
// clap.gen_completions("alacritty", Shell::Bash, "../extra/completions/");
// clap.gen_completions("alacritty", Shell::Fish, "../extra/completions/");
// clap.gen_completions("alacritty", Shell::Zsh, "../extra/completions/");
}
}

View File

@ -1,62 +1,135 @@
#compdef alacritty
# Completions available for the first parameter.
_alacritty_first_param() {
# Main subcommands.
_describe "command" "(msg:'Available socket messages')"
autoload -U is-at-least
# Default options.
_alacritty_main
_alacritty() {
typeset -A opt_args
typeset -a _arguments_options
local ret=1
if is-at-least 5.2; then
_arguments_options=(-s -S -C)
else
_arguments_options=(-s -C)
fi
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" \
'-t+[Defines the window title \[default: Alacritty\]]' \
'--title=[Defines the window title \[default: Alacritty\]]' \
'--class=[Defines window class/app_id on X11/Wayland \[default: Alacritty\]]' \
'--embed=[Defines the X11 window ID (as a decimal integer) to embed Alacritty within]' \
'--working-directory=[Start the shell in the specified working directory]' \
'--config-file=[Specify alternative configuration file \[default: $XDG_CONFIG_HOME/alacritty/alacritty.yml\]]' \
'--socket=[Path for IPC socket creation]' \
'*-e+[Command and args to execute (must be last argument)]' \
'*--command=[Command and args to execute (must be last argument)]' \
'*-o+[Override configuration file options \[example: cursor.style=Beam\]]' \
'*--option=[Override configuration file options \[example: cursor.style=Beam\]]' \
'--print-events[Print all events to stdout]' \
'--ref-test[Generates ref test]' \
'--hold[Remain open after child process exits]' \
'(-v)*-q[Reduces the level of verbosity (the min level is -qq)]' \
'(-q)*-v[Increases the level of verbosity (the max level is -vvv)]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_alacritty_commands" \
"*::: :->alacritty" \
&& ret=0
case $state in
(alacritty)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:alacritty-command-$line[1]:"
case $line[1] in
(msg)
_arguments "${_arguments_options[@]}" \
'-s+[IPC socket connection path override]' \
'--socket=[IPC socket connection path override]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_alacritty__msg_commands" \
"*::: :->msg" \
&& ret=0
case $state in
(msg)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:alacritty-msg-command-$line[1]:"
case $line[1] in
(create-window)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
esac
;;
esac
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
esac
;;
esac
}
# Completions available for parameters after the first.
_alacritty_following_param() {
case $words[2] in
msg)
_alacritty_msg;;
*)
_alacritty_main;;
esac
(( $+functions[_alacritty_commands] )) ||
_alacritty_commands() {
local commands; commands=(
"msg:Available socket messages" \
"help:Prints this message or the help of the given subcommand(s)" \
)
_describe -t commands 'alacritty commands' commands "$@"
}
(( $+functions[_alacritty__msg__create-window_commands] )) ||
_alacritty__msg__create-window_commands() {
local commands; commands=(
)
_describe -t commands 'alacritty msg create-window commands' commands "$@"
}
(( $+functions[_alacritty__help_commands] )) ||
_alacritty__help_commands() {
local commands; commands=(
)
_describe -t commands 'alacritty help commands' commands "$@"
}
(( $+functions[_alacritty__msg__help_commands] )) ||
_alacritty__msg__help_commands() {
local commands; commands=(
)
_describe -t commands 'alacritty msg help commands' commands "$@"
}
(( $+functions[_alacritty__msg_commands] )) ||
_alacritty__msg_commands() {
local commands; commands=(
"create-window:Create a new window in the same Alacritty process" \
"help:Prints this message or the help of the given subcommand(s)" \
)
_describe -t commands 'alacritty msg commands' commands "$@"
}
# Completions for the main Alacritty executable.
_alacritty_main() {
# Limit some suggestions to the first option.
local ignore
(( $#words > 2 )) && ignore='!'
_arguments \
"$ignore(-)"{-h,--help}"[print help information]" \
"$ignore(-)"{-V,--version}"[print version information]" \
"--print-events[print all events to stdout]" \
'(-v)'{-q,-qq}"[reduce the level of verbosity (min is -qq)]" \
"--ref-test[generate ref test]" \
"--hold[remain open after child process exits]" \
'(-q)'{-v,-vv,-vvv}"[increase the level of verbosity (max is -vvv)]" \
"--class=[define the window class]:class" \
"--embed=[define the X11 window ID (as a decimal integer) to embed Alacritty within]:windowId" \
"(-e --command)"{-e,--command}"[execute command (must be last arg)]:program: _command_names -e:*::program arguments: _normal" \
"--config-file=[specify an alternative config file]:file:_files" \
"*"{-o=,--option=}"[override config file options]:option" \
"(-t --title)"{-t=,--title=}"[define the window title]:title" \
"--working-directory=[start shell in specified directory]:directory:_directories"\
"--socket=[Path for IPC socket creation]:file:_files"
}
# Completions for the `msg` subcommand.
_alacritty_msg() {
# Limit some suggestions to the first option.
local ignore
(( $#words > 3 )) && ignore='!'
_arguments \
"$ignore(-)"{-h,--help}"[print help information]" \
"$ignore(-)"{-V,--version}"[print version information]" \
"(-s --socket)"{-s=,--socket=}"[Path for IPC socket creation]:file:_files" \
"*: :((create-window:'Create a new window in the same Alacritty process'))"
}
# Handle arguments based on their position.
_arguments \
"1: :_alacritty_first_param" \
"*: :_alacritty_following_param"
_alacritty "$@"

View File

@ -1,55 +1,162 @@
#/usr/bin/env bash
# Load completion function
complete -F _alacritty alacritty
# Completion function
_alacritty()
{
local cur prev prevprev opts
_alacritty() {
local i cur prev opts cmds
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
prevprev="${COMP_WORDS[COMP_CWORD-2]}"
opts="-h --help -V --version --print-events -q -qq -v -vv -vvv --ref-test --hold -e --command --config-file -o --option -t --title --embed --class --working-directory --socket msg"
cmd=""
opts=""
# If `--command` or `-e` is used, stop completing
for i in "${!COMP_WORDS[@]}"; do
if [[ "${COMP_WORDS[i]}" == "--command" ]] \
|| [[ "${COMP_WORDS[i]}" == "-e" ]] \
&& [[ "${#COMP_WORDS[@]}" -gt "$(($i + 2))" ]]
then
return 0
fi
for i in ${COMP_WORDS[@]}
do
case "${i}" in
alacritty)
cmd="alacritty"
;;
create-window)
cmd+="__create__window"
;;
help)
cmd+="__help"
;;
msg)
cmd+="__msg"
;;
*)
;;
esac
done
# Match the previous word
case "${prev}" in
--command | -e)
# Complete all commands in $PATH
COMPREPLY=( $(compgen -c -- "${cur}") )
return 0;;
--config-file | --socket)
# File completion
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -f -- "${cur}") )
return 0;;
--class | --title | -t)
# Don't complete here
return 0;;
--working-directory)
# Directory completion
local IFS=$'\n'
compopt -o filenames
COMPREPLY=( $(compgen -d -- "${cur}") )
return 0;;
msg)
COMPREPLY=( $(compgen -W "-h --help -V --version -s --socket" -- "${cur}") )
return 0;;
case "${cmd}" in
alacritty)
opts=" -q -v -h -V -t -e -o --print-events --ref-test --hold --help --version --title --class --embed --working-directory --config-file --socket --command --option msg help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--title)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-t)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--class)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--embed)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--working-directory)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--config-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--socket)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--command)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-e)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--option)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-o)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__help)
opts=" -h -V --help --version "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__msg)
opts=" -h -V -s --help --version --socket create-window help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--socket)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-s)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__msg__create__window)
opts=" -h -V --help --version "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
alacritty__msg__help)
opts=" -h -V --help --version "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
# Show all flags if there was no previous word
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
}
complete -F _alacritty -o bashdefault -o default alacritty

View File

@ -1,104 +1,28 @@
# Available subcommands
set -l commands msg help
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-a "msg help"
# Meta
complete -c alacritty \
-n "not __fish_seen_subcommand_from help" \
-s "v" \
-l "version" \
-d "Prints version information"
complete -c alacritty \
-n "not __fish_seen_subcommand_from help" \
-s "h" \
-l "help" \
-d "Prints help information"
# Config
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-f \
-l "config-file" \
-d "Specify an alternative config file"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "t" \
-l "title" \
-d "Defines the window title"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "class" \
-d "Defines the window class"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "embed" \
-d "Defines the X11 window ID (as a decimal integer) to embed Alacritty within"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-x \
-a '(__fish_complete_directories (commandline -ct))' \
-l "working-directory" \
-d "Start shell in specified directory"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "hold" \
-d "Remain open after child process exits"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "o" \
-l "option" \
-d "Override config file options"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "socket" \
-d "Path for IPC socket creation"
# Output
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "print-events" \
-d "Print all events to stdout"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "q" \
-d "Reduces the level of verbosity (min is -qq)"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "qq" \
-d "Reduces the level of verbosity"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "v" \
-d "Increases the level of verbosity"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "vv" \
-d "Increases the level of verbosity"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "vvv" \
-d "Increases the level of verbosity"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-l "ref-test" \
-d "Generates ref test"
complete -c alacritty \
-n "not __fish_seen_subcommand_from $commands" \
-s "e" \
-l "command" \
-d "Execute command (must be last arg)"
# Subcommand `msg`
complete -c alacritty \
-n "__fish_seen_subcommand_from msg" \
-s "s" \
-l "socket" \
-d "Socket path override"
complete -c alacritty \
-n "__fish_seen_subcommand_from msg" \
-a "create-window help"
complete -c alacritty -n "__fish_use_subcommand" -s t -l title -d 'Defines the window title [default: Alacritty]'
complete -c alacritty -n "__fish_use_subcommand" -l class -d 'Defines window class/app_id on X11/Wayland [default: Alacritty]'
complete -c alacritty -n "__fish_use_subcommand" -l embed -d 'Defines the X11 window ID (as a decimal integer) to embed Alacritty within'
complete -c alacritty -n "__fish_use_subcommand" -l working-directory -d 'Start the shell in the specified working directory'
complete -c alacritty -n "__fish_use_subcommand" -l config-file -d 'Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]'
complete -c alacritty -n "__fish_use_subcommand" -l socket -d 'Path for IPC socket creation'
complete -c alacritty -n "__fish_use_subcommand" -s e -l command -d 'Command and args to execute (must be last argument)'
complete -c alacritty -n "__fish_use_subcommand" -s o -l option -d 'Override configuration file options [example: cursor.style=Beam]'
complete -c alacritty -n "__fish_use_subcommand" -l print-events -d 'Print all events to stdout'
complete -c alacritty -n "__fish_use_subcommand" -l ref-test -d 'Generates ref test'
complete -c alacritty -n "__fish_use_subcommand" -l hold -d 'Remain open after child process exits'
complete -c alacritty -n "__fish_use_subcommand" -s q -d 'Reduces the level of verbosity (the min level is -qq)'
complete -c alacritty -n "__fish_use_subcommand" -s v -d 'Increases the level of verbosity (the max level is -vvv)'
complete -c alacritty -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
complete -c alacritty -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
complete -c alacritty -n "__fish_use_subcommand" -f -a "msg" -d 'Available socket messages'
complete -c alacritty -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from msg" -s s -l socket -d 'IPC socket connection path override'
complete -c alacritty -n "__fish_seen_subcommand_from msg" -s h -l help -d 'Prints help information'
complete -c alacritty -n "__fish_seen_subcommand_from msg" -s V -l version -d 'Prints version information'
complete -c alacritty -n "__fish_seen_subcommand_from msg" -f -a "create-window" -d 'Create a new window in the same Alacritty process'
complete -c alacritty -n "__fish_seen_subcommand_from msg" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -s h -l help -d 'Prints help information'
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -s V -l version -d 'Prints version information'
complete -c alacritty -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
complete -c alacritty -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'
complete -c alacritty -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
complete -c alacritty -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information'