From a6df46b189e229cfd3d5de1158aa72ada9a6f1eb Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:24:11 +0300 Subject: [PATCH 1/6] Improved Bash completions --- devscripts/bash-completion.in | 75 +++++++++++++++++++++-------------- devscripts/bash-completion.py | 20 +++++----- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index 994bb4e721..0c6b778d9c 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -1,33 +1,48 @@ -__yt_dlp() -{ - local cur prev opts fileopts diropts keywords - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="{{flags}}" - keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater :ythistory" - fileopts="-a|--batch-file|--download-archive|--cookies|--load-info-json" - diropts="--cache-dir" +_comp_cmd_yt-dlp() { + local cur prev + # Unused variables assigned by _comp_initialize. + # shellcheck disable=SC2034 + local words cword comp_args was_split - if [[ ${prev} =~ ${fileopts} ]]; then - local IFS=$'\n' - type compopt &>/dev/null && compopt -o filenames - COMPREPLY=( $(compgen -f -- ${cur}) ) - return 0 - elif [[ ${prev} =~ ${diropts} ]]; then - local IFS=$'\n' - type compopt &>/dev/null && compopt -o dirnames - COMPREPLY=( $(compgen -d -- ${cur}) ) - return 0 - fi + # ':' must be excluded from COMP_WORDBREAKS to handle :yt* keywords + _comp_initialize -n : -s -- "$@" || return - if [[ ${cur} =~ : ]]; then - COMPREPLY=( $(compgen -W "${keywords}" -- ${cur}) ) - return 0 - elif [[ ${cur} == * ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - fi -} + case "$prev" in + -a | --batch-file | --download-archive | --cookies | --load-info-json \ + | --config-locations | --netrc-location | --ffmpeg-location \ + | --client-certificate | --client-certificate-key ) + _comp_compgen_filedir + return + ;; -complete -F __yt_dlp yt-dlp + --plugin-dirs | --cache-dir ) + _comp_compgen_filedir -d + return + ;; + + * ) + case "$cur" in + :* ) + local -r keywords=( + :ytnotif{,ication}{,s} + :ytfav{,o{,u}rite}{,s} + :ytwatchlater + :ytrec{,ommended} + :ytsub{,scription}{,s} + :ythis{,tory} + ) + _comp_compgen -- -W "${keywords[*]}" + _comp_ltrim_colon_completions "$cur" + return + ;; + + * ) + local -r opts=( YT_DLP_FLAGS ) + _comp_compgen -- -W "${opts[*]}" + return + ;; + esac + ;; + esac +} \ + && complete -F _comp_cmd_yt-dlp yt-dlp diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index 3918ebde86..315cabbb7f 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -2,7 +2,9 @@ # Allow direct execution import os +import shlex import sys +from itertools import chain sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -13,19 +15,19 @@ BASH_COMPLETION_FILE = 'completions/bash/yt-dlp' BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in' -def build_completion(opt_parser): - opts_flag = [] - for group in opt_parser.option_groups: - for option in group.option_list: - # for every long flag - opts_flag.append(option.get_opt_string()) +def yt_dlp_flags(): + for opt in yt_dlp.parseOpts(ignore_config_files=True)[0]._get_all_options(): + for opt_str in chain(opt._short_opts, opt._long_opts): + yield shlex.quote(opt_str) + + +def build_completion(): with open(BASH_COMPLETION_TEMPLATE) as f: template = f.read() with open(BASH_COMPLETION_FILE, 'w') as f: # just using the special char - filled_template = template.replace('{{flags}}', ' '.join(opts_flag)) + filled_template = template.replace('YT_DLP_FLAGS', ' '.join(sorted(yt_dlp_flags()))) f.write(filled_template) -parser = yt_dlp.parseOpts(ignore_config_files=True)[0] -build_completion(parser) +build_completion() From 4d4f718b0a221003ca4c5d5301615a1ba323b3ef Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:19:07 +0300 Subject: [PATCH 2/6] Bash completion: sort short options before the long ones --- devscripts/bash-completion.in | 1 + devscripts/bash-completion.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index 0c6b778d9c..52a38f048c 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -38,6 +38,7 @@ _comp_cmd_yt-dlp() { * ) local -r opts=( YT_DLP_FLAGS ) + compopt -o nosort _comp_compgen -- -W "${opts[*]}" return ;; diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index 315cabbb7f..060c9ccffd 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -4,7 +4,6 @@ import os import shlex import sys -from itertools import chain sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -16,9 +15,18 @@ BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in' def yt_dlp_flags(): + opts, long_opts = [], [] + for opt in yt_dlp.parseOpts(ignore_config_files=True)[0]._get_all_options(): - for opt_str in chain(opt._short_opts, opt._long_opts): - yield shlex.quote(opt_str) + opts.extend(opt._short_opts) + long_opts.extend(opt._long_opts) + + opts.sort() + long_opts.sort() + opts.extend(long_opts) + + for o in opts: + yield shlex.quote(o) def build_completion(): @@ -26,7 +34,7 @@ def build_completion(): template = f.read() with open(BASH_COMPLETION_FILE, 'w') as f: # just using the special char - filled_template = template.replace('YT_DLP_FLAGS', ' '.join(sorted(yt_dlp_flags()))) + filled_template = template.replace('YT_DLP_FLAGS', ' '.join(yt_dlp_flags())) f.write(filled_template) From 8ed3b8ef1cde8cbc13695cb261e6ba212df65b41 Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:51:20 +0300 Subject: [PATCH 3/6] Fallback if bash-completion is not available --- devscripts/bash-completion.in | 96 +++++++++++++++++++++++------------ devscripts/bash-completion.py | 4 +- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index 52a38f048c..68b26e403a 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -1,49 +1,81 @@ _comp_cmd_yt-dlp() { - local cur prev - # Unused variables assigned by _comp_initialize. - # shellcheck disable=SC2034 - local words cword comp_args was_split + local have_bashcomp=0 cur="$2" prev="$3" - # ':' must be excluded from COMP_WORDBREAKS to handle :yt* keywords - _comp_initialize -n : -s -- "$@" || return + # Long options with equals and YouTube keyword completions + # do not work without bash-completion. + if declare -F _comp_initialize &> /dev/null; then + have_bashcomp=1 + # Unused variables assigned by _comp_initialize. + # shellcheck disable=SC2034 + local words cword comp_args was_split + # ':' must be excluded from COMP_WORDBREAKS to handle :yt* keywords + _comp_initialize -n : -s -- "$@" || return + fi + local filedir_action case "$prev" in -a | --batch-file | --download-archive | --cookies | --load-info-json \ | --config-locations | --netrc-location | --ffmpeg-location \ | --client-certificate | --client-certificate-key ) - _comp_compgen_filedir - return + filedir_action='file' ;; --plugin-dirs | --cache-dir ) - _comp_compgen_filedir -d - return + filedir_action='directory' ;; * ) - case "$cur" in - :* ) - local -r keywords=( - :ytnotif{,ication}{,s} - :ytfav{,o{,u}rite}{,s} - :ytwatchlater - :ytrec{,ommended} - :ytsub{,scription}{,s} - :ythis{,tory} - ) - _comp_compgen -- -W "${keywords[*]}" - _comp_ltrim_colon_completions "$cur" - return - ;; - - * ) - local -r opts=( YT_DLP_FLAGS ) - compopt -o nosort - _comp_compgen -- -W "${opts[*]}" - return - ;; - esac ;; esac + + local compreply_lines + if [[ "$filedir_action" ]]; then + if (( "$have_bashcomp" )); then + if [[ "$filedir_action" = directory ]]; then + _comp_compgen_filedir -d + else + _comp_compgen_filedir + fi + else + # macOS Bash 3.2.57 does not have compopt. + if type compopt &>/dev/null; then + compopt -o filenames + fi + compreply_lines="$(compgen -A "$filedir_action" -- "$cur")" + fi + elif [[ "$cur" = :* ]]; then + local -r keywords=( + :ytnotif{,ication}{,s} + :ytfav{,o{,u}rite}{,s} + :ytwatchlater + :ytrec{,ommended} + :ytsub{,scription}{,s} + :ythis{,tory} + ) + if (( "$have_bashcomp" )); then + _comp_compgen -- -W "${keywords[*]}" + _comp_ltrim_colon_completions "$cur" + else + compreply_lines="$(compgen -W "${keywords[*]}" -- "$cur")" + fi + else + local -r opts=( YT_DLP_OPTS ) + if type compopt &> /dev/null; then + compopt -o nosort + fi + if (( "$have_bashcomp" )); then + _comp_compgen -- -W "${opts[*]}" + else + compreply_lines="$(compgen -W "${opts[*]}" -- "$cur")" + fi + fi + + if [[ "$have_bashcomp" != 1 && "$compreply_lines" ]]; then + COMPREPLY=() + local line + while IFS='' read -r line; do + COMPREPLY+=("$line") + done <<< "$compreply_lines" + fi } \ && complete -F _comp_cmd_yt-dlp yt-dlp diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index 060c9ccffd..6f62d494b6 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -14,7 +14,7 @@ BASH_COMPLETION_FILE = 'completions/bash/yt-dlp' BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in' -def yt_dlp_flags(): +def yt_dlp_options(): opts, long_opts = [], [] for opt in yt_dlp.parseOpts(ignore_config_files=True)[0]._get_all_options(): @@ -34,7 +34,7 @@ def build_completion(): template = f.read() with open(BASH_COMPLETION_FILE, 'w') as f: # just using the special char - filled_template = template.replace('YT_DLP_FLAGS', ' '.join(yt_dlp_flags())) + filled_template = template.replace('YT_DLP_OPTS', ' '.join(yt_dlp_options())) f.write(filled_template) From 07cf8498c4f9922f23e2d461a4a832173946d169 Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:54:38 +0300 Subject: [PATCH 4/6] Partially workaround YouTube keywords Bash completions --- devscripts/bash-completion.in | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index 68b26e403a..41f05cdb17 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -1,5 +1,6 @@ _comp_cmd_yt-dlp() { - local have_bashcomp=0 cur="$2" prev="$3" + # Not using "$2" and "$3" to partially workaround YouTube keyword completions. + local have_bashcomp=0 cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD - 1]}" # Long options with equals and YouTube keyword completions # do not work without bash-completion. @@ -37,26 +38,30 @@ _comp_cmd_yt-dlp() { _comp_compgen_filedir fi else - # macOS Bash 3.2.57 does not have compopt. + # macOS Bash does not have compopt. if type compopt &>/dev/null; then compopt -o filenames fi compreply_lines="$(compgen -A "$filedir_action" -- "$cur")" fi elif [[ "$cur" = :* ]]; then - local -r keywords=( - :ytnotif{,ication}{,s} - :ytfav{,o{,u}rite}{,s} - :ytwatchlater - :ytrec{,ommended} - :ytsub{,scription}{,s} - :ythis{,tory} - ) + # Adding keywords one by one since macOS Bash + # can't initialise a local array from brace expansions. + local k keywords + for k in :ytnotif{,ication}{,s} \ + :ytfav{,o{,u}rite}{,s} \ + :ytwatchlater \ + :ytrec{,ommended} \ + :ytsub{,scription}{,s} \ + :ythis{,tory}; do + keywords+=("$k") + done if (( "$have_bashcomp" )); then _comp_compgen -- -W "${keywords[*]}" _comp_ltrim_colon_completions "$cur" else compreply_lines="$(compgen -W "${keywords[*]}" -- "$cur")" + compreply_lines="${compreply_lines//:/}" fi else local -r opts=( YT_DLP_OPTS ) From 1c7978b7a8094e926d38aabc0d9f37f43411dbfd Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:51:35 +0300 Subject: [PATCH 5/6] Generate file and directory Bash completions programmatically --- devscripts/bash-completion.in | 8 +++----- devscripts/bash-completion.py | 28 ++++++++++++++++++++++++---- yt_dlp/options.py | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/devscripts/bash-completion.in b/devscripts/bash-completion.in index 41f05cdb17..c58b9382b7 100644 --- a/devscripts/bash-completion.in +++ b/devscripts/bash-completion.in @@ -15,13 +15,11 @@ _comp_cmd_yt-dlp() { local filedir_action case "$prev" in - -a | --batch-file | --download-archive | --cookies | --load-info-json \ - | --config-locations | --netrc-location | --ffmpeg-location \ - | --client-certificate | --client-certificate-key ) + YT_DLP_FILE_OPTS_CASE ) filedir_action='file' ;; - --plugin-dirs | --cache-dir ) + YT_DLP_DIR_OPTS_CASE ) filedir_action='directory' ;; @@ -64,7 +62,7 @@ _comp_cmd_yt-dlp() { compreply_lines="${compreply_lines//:/}" fi else - local -r opts=( YT_DLP_OPTS ) + local -r opts=( YT_DLP_OPTS_ARRAY ) if type compopt &> /dev/null; then compopt -o nosort fi diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index 6f62d494b6..ed08ca037b 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -4,6 +4,7 @@ import os import shlex import sys +from dataclasses import dataclass sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -14,19 +15,32 @@ BASH_COMPLETION_FILE = 'completions/bash/yt-dlp' BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in' +@dataclass +class YtDlpOpts: + opts: tuple[str, ...] + file_opts: tuple[str, ...] + dir_opts: tuple[str, ...] + + def yt_dlp_options(): opts, long_opts = [], [] + file_opts, dir_opts = [], [] for opt in yt_dlp.parseOpts(ignore_config_files=True)[0]._get_all_options(): opts.extend(opt._short_opts) long_opts.extend(opt._long_opts) + if opt.metavar in {'FILE', 'PATH', 'CERTFILE', 'KEYFILE'}: + file_opts.extend(opt._short_opts) + file_opts.extend(opt._long_opts) + elif opt.metavar == 'DIR': + dir_opts.extend(opt._short_opts) + dir_opts.extend(opt._long_opts) opts.sort() long_opts.sort() opts.extend(long_opts) - for o in opts: - yield shlex.quote(o) + return YtDlpOpts(tuple(opts), tuple(file_opts), tuple(dir_opts)) def build_completion(): @@ -34,8 +48,14 @@ def build_completion(): template = f.read() with open(BASH_COMPLETION_FILE, 'w') as f: # just using the special char - filled_template = template.replace('YT_DLP_OPTS', ' '.join(yt_dlp_options())) - f.write(filled_template) + opts = yt_dlp_options() + f.write(template.replace( + 'YT_DLP_FILE_OPTS_CASE', ' | '.join(shlex.quote(o) for o in opts.file_opts), + ).replace( + 'YT_DLP_DIR_OPTS_CASE', ' | '.join(shlex.quote(o) for o in opts.dir_opts), + ).replace( + 'YT_DLP_OPTS_ARRAY', ' '.join(shlex.quote(o) for o in opts.opts), + )) build_completion() diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 14b582fc42..7f1c31daaf 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -1535,7 +1535,7 @@ def create_parser(): help='Netscape formatted file to read cookies from and dump cookie jar in') filesystem.add_option( '--no-cookies', - action='store_const', const=None, dest='cookiefile', metavar='FILE', + action='store_const', const=None, dest='cookiefile', help='Do not read/dump cookies from/to file (default)') filesystem.add_option( '--cookies-from-browser', From 1ef64f14a9c45895689bf7f68f1f0e4e2a424fbf Mon Sep 17 00:00:00 2001 From: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:35:57 +0300 Subject: [PATCH 6/6] Share option gathering between Bash and zsh completions --- devscripts/bash-completion.py | 53 ++++++++--------------------------- devscripts/utils.py | 35 +++++++++++++++++++++++ devscripts/zsh-completion.py | 35 ++++------------------- 3 files changed, 52 insertions(+), 71 deletions(-) diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index ed08ca037b..01dc94ff09 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -2,60 +2,31 @@ # Allow direct execution import os -import shlex import sys -from dataclasses import dataclass sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import yt_dlp +from devscripts.utils import gather_completion_opts BASH_COMPLETION_FILE = 'completions/bash/yt-dlp' BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in' -@dataclass -class YtDlpOpts: - opts: tuple[str, ...] - file_opts: tuple[str, ...] - dir_opts: tuple[str, ...] +def build_completion(opt_parser): + opts = gather_completion_opts(opt_parser) + with open(BASH_COMPLETION_TEMPLATE) as f: + template = f.read() -def yt_dlp_options(): - opts, long_opts = [], [] - file_opts, dir_opts = [], [] - - for opt in yt_dlp.parseOpts(ignore_config_files=True)[0]._get_all_options(): - opts.extend(opt._short_opts) - long_opts.extend(opt._long_opts) - if opt.metavar in {'FILE', 'PATH', 'CERTFILE', 'KEYFILE'}: - file_opts.extend(opt._short_opts) - file_opts.extend(opt._long_opts) - elif opt.metavar == 'DIR': - dir_opts.extend(opt._short_opts) - dir_opts.extend(opt._long_opts) - - opts.sort() - long_opts.sort() - opts.extend(long_opts) + template = template.replace('YT_DLP_FILE_OPTS_CASE', ' | '.join(opts.file_opts)) + template = template.replace('YT_DLP_DIR_OPTS_CASE', ' | '.join(opts.dir_opts)) + template = template.replace('YT_DLP_OPTS_ARRAY', ' '.join(opts.opts)) - return YtDlpOpts(tuple(opts), tuple(file_opts), tuple(dir_opts)) + with open(BASH_COMPLETION_FILE, 'w') as f: + f.write(template) -def build_completion(): - with open(BASH_COMPLETION_TEMPLATE) as f: - template = f.read() - with open(BASH_COMPLETION_FILE, 'w') as f: - # just using the special char - opts = yt_dlp_options() - f.write(template.replace( - 'YT_DLP_FILE_OPTS_CASE', ' | '.join(shlex.quote(o) for o in opts.file_opts), - ).replace( - 'YT_DLP_DIR_OPTS_CASE', ' | '.join(shlex.quote(o) for o in opts.dir_opts), - ).replace( - 'YT_DLP_OPTS_ARRAY', ' '.join(shlex.quote(o) for o in opts.opts), - )) - - -build_completion() +parser = yt_dlp.parseOpts(ignore_config_files=True)[0] +build_completion(parser) diff --git a/devscripts/utils.py b/devscripts/utils.py index b89d01e415..75a2423fdf 100644 --- a/devscripts/utils.py +++ b/devscripts/utils.py @@ -2,7 +2,9 @@ import argparse import datetime as dt import functools import re +import shlex import subprocess +from dataclasses import dataclass def read_file(fname): @@ -64,3 +66,36 @@ def run_process(*args, **kwargs): kwargs.setdefault('encoding', 'utf-8') kwargs.setdefault('errors', 'replace') return subprocess.run(args, **kwargs) + + +@dataclass +class YtDlpCompletionOpts: + opts: tuple[str, ...] + file_opts: tuple[str, ...] + dir_opts: tuple[str, ...] + + +def gather_completion_opts(yt_dlp_opt_parser): + opts, long_opts = [], [] + file_opts, dir_opts = [], [] + + for opt in yt_dlp_opt_parser._get_all_options(): + opts.extend(opt._short_opts) + long_opts.extend(opt._long_opts) + if opt.metavar in {'FILE', 'PATH', 'CERTFILE', 'KEYFILE'}: + file_opts.extend(opt._short_opts) + file_opts.extend(opt._long_opts) + elif opt.metavar == 'DIR': + dir_opts.extend(opt._short_opts) + dir_opts.extend(opt._long_opts) + + opts.sort() + long_opts.sort() + opts.extend(long_opts) + + # Escape after sorting. + return YtDlpCompletionOpts( + tuple(shlex.quote(o) for o in opts), + tuple(shlex.quote(o) for o in file_opts), + tuple(shlex.quote(o) for o in dir_opts), + ) diff --git a/devscripts/zsh-completion.py b/devscripts/zsh-completion.py index 046e9231f1..13318ef29b 100755 --- a/devscripts/zsh-completion.py +++ b/devscripts/zsh-completion.py @@ -8,46 +8,21 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import yt_dlp +from devscripts.utils import gather_completion_opts ZSH_COMPLETION_FILE = 'completions/zsh/_yt-dlp' 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'] - opts_path = [opt for opt in opts if opt.metavar == 'PATH'] - - fileopts = [] - for opt in opts_file: - if opt._short_opts: - fileopts.extend(opt._short_opts) - if opt._long_opts: - fileopts.extend(opt._long_opts) - - for opt in opts_path: - if opt._short_opts: - fileopts.extend(opt._short_opts) - if opt._long_opts: - fileopts.extend(opt._long_opts) - - 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] + opts = gather_completion_opts(opt_parser) 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('{{fileopts}}', '|'.join(opts.file_opts)) + template = template.replace('{{diropts}}', '|'.join(opts.dir_opts)) + template = template.replace('{{flags}}', ' '.join(opts.opts)) with open(ZSH_COMPLETION_FILE, 'w') as f: f.write(template)