From b3ab97c05e29bfabfbf6ec889f64e6144730b8d1 Mon Sep 17 00:00:00 2001 From: Paul Seyfert Date: Sat, 6 Mar 2021 00:06:35 +0100 Subject: [PATCH] rewrite zsh completion generation --- devscripts/zsh-completion.in | 24 +++-------------- devscripts/zsh-completion.py | 51 +++++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/devscripts/zsh-completion.in b/devscripts/zsh-completion.in index b394a1ae7..d073436f8 100644 --- a/devscripts/zsh-completion.in +++ b/devscripts/zsh-completion.in @@ -3,26 +3,8 @@ __youtube_dl() { local curcontext="$curcontext" fileopts diropts cur prev typeset -A opt_args - fileopts="{{fileopts}}" - diropts="{{diropts}}" - cur=$words[CURRENT] - case $cur in - :) - _arguments '*: :(::ytfavorites ::ytrecommended ::ytsubscriptions ::ytwatchlater ::ythistory)' - ;; - *) - prev=$words[CURRENT-1] - if [[ ${prev} =~ ${fileopts} ]]; then - _path_files - elif [[ ${prev} =~ ${diropts} ]]; then - _path_files -/ - elif [[ ${prev} == "--recode-video" ]]; then - _arguments '*: :(mp4 flv ogg webm mkv)' - else - _arguments '*: :({{flags}})' - fi - ;; - esac + _arguments {{args}} \ + '*: :(::ytfavorites ::ytrecommended ::ytsubscriptions ::ytwatchlater ::ythistory)' } -__youtube_dl \ No newline at end of file +__youtube_dl diff --git a/devscripts/zsh-completion.py b/devscripts/zsh-completion.py index 60aaf76cc..d0834beac 100755 --- a/devscripts/zsh-completion.py +++ b/devscripts/zsh-completion.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import os from os.path import dirname as dirn import sys +import optparse sys.path.insert(0, dirn(dirn((os.path.abspath(__file__))))) import youtube_dl @@ -15,31 +16,45 @@ ZSH_COMPLETION_TEMPLATE = "devscripts/zsh-completion.in" def build_completion(opt_parser): opts = [opt for group in opt_parser.option_groups for opt in group.option_list] - opts_file = [opt for opt in opts if opt.metavar == "FILE"] - opts_dir = [opt for opt in opts if opt.metavar == "DIR"] - fileopts = [] - for opt in opts_file: + # escaping is hard: + # - help may contain colons + # - metavar must have colons : escaped + # - single quotes must be removed + # - help must have square brackets [] escaped + def metaparse(opt): + if "--recode-video" == opt.get_opt_string(): + return ":{}:(mp4 flv ogg webm mkv)".format(opt.metavar) + if opt.metavar is None: + return "" + if opt.metavar == "FILE": + return ":FILE:_files" + if opt.metavar == "DIR": + return ":DIR:_directories" + else: + return ":{}:".format(opt.metavar.replace(":", "\\:")) + + def helpescape(opthelp): + if opthelp == optparse.SUPPRESS_HELP: + return "" + return "[{}]".format(opthelp.replace("'", "\"").replace("]", "\\]").replace("[", "\\[")) + + def optionexclude(opt): + # When an argument has a long and short version, the arguments entry shall be + # _arguments \ + # "(-t --thing)"{-t,--thing}"[do things]:WHAT_THING:" + # i.e. in parentheses with space the explanation of redundancy and in curly braces + # regular shell expansion to create two mostly identical entries. if opt._short_opts: - fileopts.extend(opt._short_opts) - if opt._long_opts: - fileopts.extend(opt._long_opts) + return "({0} {1})'{{{0},{1}}}'".format(opt._short_opts[0], opt.get_opt_string()) + return "{}".format(opt.get_opt_string()) - diropts = [] - for opt in opts_dir: - if opt._short_opts: - diropts.extend(opt._short_opts) - if opt._long_opts: - diropts.extend(opt._long_opts) - - flags = [opt.get_opt_string() for opt in opts] + mytest = ["'{}{}{}'".format(optionexclude(opt), helpescape(opt.help), metaparse(opt)) for opt in opts] with open(ZSH_COMPLETION_TEMPLATE) as f: template = f.read() - template = template.replace("{{fileopts}}", "|".join(fileopts)) - template = template.replace("{{diropts}}", "|".join(diropts)) - template = template.replace("{{flags}}", " ".join(flags)) + template = template.replace("{{args}}", " \\\n ".join(mytest)) with open(ZSH_COMPLETION_FILE, "w") as f: f.write(template)