From: Jörg Behrmann Date: Sun, 16 Jun 2024 13:11:14 +0000 (+0200) Subject: completion: add zsh X-Git-Tag: v24~7^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2104f7431e95addf51aaae8bbe00c859c2517439;p=thirdparty%2Fmkosi.git completion: add zsh This completion of verbs is based on _timedatectl in systemd repo. Completion for short options doesn't work. It also doesn't work for timedatectl, so this needs to be fixed in both places. Co-authored-by: Zbigniew Jędrzejewski-Szmek --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index b610f25d6..9146bd6a8 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -51,6 +51,7 @@ from mkosi.config import ( collect_completion_arguments, finalize_completion_bash, finalize_completion_fish, + finalize_completion_zsh, format_bytes, parse_config, summary, @@ -4425,6 +4426,8 @@ def print_completion(args: Args, *, resources: Path) -> None: print(finalize_completion_bash(completion_args, resources)) elif args.shell_completion == ShellCompletion.fish: print(finalize_completion_fish(completion_args, resources)) + elif args.shell_completion == ShellCompletion.zsh: + print(finalize_completion_zsh(completion_args, resources)) def expand_specifier(s: str) -> str: diff --git a/mkosi/config.py b/mkosi/config.py index c39d882c7..761ef14b6 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -244,6 +244,7 @@ class ShellCompletion(StrEnum): none = enum.auto() bash = enum.auto() fish = enum.auto() + zsh = enum.auto() def __bool__(self) -> bool: return self != ShellCompletion.none @@ -1205,6 +1206,15 @@ class CompGen(StrEnum): else: return "-f" + def to_zsh(self) -> str: + if self == CompGen.files: + return ":path:_files -/" + elif self == CompGen.dirs: + return ":directory:_files -f" + else: + return "" + + @dataclasses.dataclass(frozen=True) class ConfigSetting: dest: str @@ -4601,3 +4611,51 @@ def finalize_completion_fish(options: list[CompletionItem], resources: Path) -> c.write(option.compgen.to_fish()) c.write("\n") return c.getvalue() + + +def finalize_completion_zsh(options: list[CompletionItem], resources: Path) -> str: + def to_zsh_array(name: str, entries: Iterable[str]) -> str: + return f"declare -a {name.replace('-', '_')}=(" + " ".join(shlex.quote(str(e)) for e in entries) + ")" + + completion = resources / "completion.zsh" + + with io.StringIO() as c: + c.write("#compdef mkosi\n") + c.write("# SPDX-License-Identifier: LGPL-2.1-or-later\n\n") + + c.write(to_zsh_array("_mkosi_verbs", [str(v) for v in Verb])) + c.write("\n\n") + + + c.write(completion.read_text()) + c.write("\n") + + c.write("_arguments -s \\\n") + c.write(" '(- *)'{-h,--help}'[Show this help]' \\\n") + c.write(" '(- *)--version[Show package version]' \\\n") + + for option in options: + if not option.short and not option.long: + continue + + posix = option.help and "'" in option.help + open_quote = "$'" if posix else "'" + if option.short and option.long: + c.write(f" '({option.short} {option.long})'{{{option.short},{option.long}}}{open_quote}") + else: + c.write(f" {open_quote}{option.short or option.long}") + + if option.help: + help = option.help.replace("'", r"\'") + c.write(f"[{help}]") + if option.choices: + # TODO: maybe use metavar here? At least for me it's not shown, though + c.write(":arg:(") + c.write(" ".join(option.choices)) + c.write(")") + c.write(option.compgen.to_zsh()) + c.write("' \\\n") + + c.write(" '*::mkosi verb:_mkosi_verb'\n\n") + + return c.getvalue() diff --git a/mkosi/resources/completion.zsh b/mkosi/resources/completion.zsh new file mode 100644 index 000000000..e9cd3b58e --- /dev/null +++ b/mkosi/resources/completion.zsh @@ -0,0 +1,17 @@ +_mkosi_verb(){ + if (( CURRENT == 1 )); then + _describe -t commands 'mkosi verb' _mkosi_verbs + else + local curcontext="$curcontext" + cmd="${${_mkosi_verbs[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + if (( $+functions[_mkosi_$cmd] )); then + _mkosi_$cmd + else + _message "no more options" + fi + else + _message "unknown mkosi verb: $words[1]" + fi + fi +}