1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
_I='COMP_CWORD'
_H='COMP_WORDS'
_G='fish'
_F='zsh'
_E='bash'
_D='_'
_C=False
_B=None
_A=True
import copy,os,re
from collections import abc
from .core import Argument
from .core import MultiCommand
from .core import Option
from .parser import split_arg_string
from .types import Choice
from .utils import echo
WORDBREAK='='
COMPLETION_SCRIPT_BASH='\n%(complete_func)s() {\n local IFS=$\'\n\'\n COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\\n COMP_CWORD=$COMP_CWORD \\\n %(autocomplete_var)s=complete $1 ) )\n return 0\n}\n\n%(complete_func)setup() {\n local COMPLETION_OPTIONS=""\n local BASH_VERSION_ARR=(${BASH_VERSION//./ })\n # Only BASH version 4.4 and later have the nosort option.\n if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then\n COMPLETION_OPTIONS="-o nosort"\n fi\n\n complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s\n}\n\n%(complete_func)setup\n'
COMPLETION_SCRIPT_ZSH='\n#compdef %(script_names)s\n\n%(complete_func)s() {\n local -a completions\n local -a completions_with_descriptions\n local -a response\n (( ! $+commands[%(script_names)s] )) && return 1\n\n response=("${(@f)$( env COMP_WORDS="${words[*]}" \\\n COMP_CWORD=$((CURRENT-1)) \\\n %(autocomplete_var)s="complete_zsh" \\\n %(script_names)s )}")\n\n for key descr in ${(kv)response}; do\n if [[ "$descr" == "_" ]]; then\n completions+=("$key")\n else\n completions_with_descriptions+=("$key":"$descr")\n fi\n done\n\n if [ -n "$completions_with_descriptions" ]; then\n _describe -V unsorted completions_with_descriptions -U\n fi\n\n if [ -n "$completions" ]; then\n compadd -U -V unsorted -a completions\n fi\n compstate[insert]="automenu"\n}\n\ncompdef %(complete_func)s %(script_names)s\n'
COMPLETION_SCRIPT_FISH='complete --no-files --command %(script_names)s --arguments "(env %(autocomplete_var)s=complete_fish COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) %(script_names)s)"'
_completion_scripts={_E:COMPLETION_SCRIPT_BASH,_F:COMPLETION_SCRIPT_ZSH,_G:COMPLETION_SCRIPT_FISH}
_invalid_ident_char_re=re.compile('[^a-zA-Z0-9_]')
def get_completion_script(prog_name,complete_var,shell):A=prog_name;B=_invalid_ident_char_re.sub('',A.replace('-',_D));C=_completion_scripts.get(shell,COMPLETION_SCRIPT_BASH);return (C%{'complete_func':f"_{B}_completion",'script_names':A,'autocomplete_var':complete_var}).strip()+';'
def resolve_ctx(cli,prog_name,args):
B=args;A=cli.make_context(prog_name,B,resilient_parsing=_A);B=A.protected_args+A.args
while B:
if isinstance(A.command,MultiCommand):
if not A.command.chain:
E,C,B=A.command.resolve_command(A,B)
if C is _B:return A
A=C.make_context(E,B,parent=A,resilient_parsing=_A);B=A.protected_args+A.args
else:
while B:
E,C,B=A.command.resolve_command(A,B)
if C is _B:return A
D=C.make_context(E,B,parent=A,allow_extra_args=_A,allow_interspersed_args=_C,resilient_parsing=_A);B=D.args
A=D;B=D.protected_args+D.args
else:break
return A
def start_of_option(param_str):A=param_str;return A and A[:1]=='-'
def is_incomplete_option(all_args,cmd_param):
A=cmd_param
if not isinstance(A,Option):return _C
if A.is_flag:return _C
B=_B
for (D,C) in enumerate(reversed([A for A in all_args if A!=WORDBREAK])):
if D+1>A.nargs:break
if start_of_option(C):B=C
return _A if B and B in A.opts else _C
def is_incomplete_argument(current_params,cmd_param):
A=cmd_param
if not isinstance(A,Argument):return _C
B=current_params[A.name]
if B is _B:return _A
if A.nargs==-1:return _A
if isinstance(B,abc.Iterable)and A.nargs>1 and len(B)<A.nargs:return _A
return _C
def get_user_autocompletions(ctx,args,incomplete,cmd_param):
C=incomplete;A=cmd_param;B=[]
if isinstance(A.type,Choice):B=[(B,_B)for B in A.type.choices if str(B).startswith(C)]
elif A.autocompletion is not _B:D=A.autocompletion(ctx=ctx,args=args,incomplete=C);B=[A if isinstance(A,tuple)else(A,_B)for A in D]
return B
def get_visible_commands_starting_with(ctx,starts_with):
A=ctx
for B in A.command.list_commands(A):
if B.startswith(starts_with):
C=A.command.get_command(A,B)
if not C.hidden:yield C
def add_subcommand_completions(ctx,incomplete,completions_out):
C=completions_out;B=incomplete;A=ctx
if isinstance(A.command,MultiCommand):C.extend([(C.name,C.get_short_help_str())for C in get_visible_commands_starting_with(A,B)])
while A.parent is not _B:
A=A.parent
if isinstance(A.command,MultiCommand)and A.command.chain:D=[C for C in get_visible_commands_starting_with(A,B)if C.name not in A.protected_args];C.extend([(A.name,A.get_short_help_str())for A in D])
def get_choices(cli,prog_name,args,incomplete):
B=incomplete;D=copy.deepcopy(args);C=resolve_ctx(cli,prog_name,args)
if C is _B:return[]
G='--'in D
if start_of_option(B)and WORDBREAK in B:F=B.partition(WORDBREAK);D.append(F[0]);B=F[2]
elif B==WORDBREAK:B=''
E=[]
if not G and start_of_option(B):
for A in C.command.params:
if isinstance(A,Option)and not A.hidden:H=[B for B in A.opts+A.secondary_opts if B not in D or A.multiple];E.extend([(C,A.help)for C in H if C.startswith(B)])
return E
for A in C.command.params:
if is_incomplete_option(D,A):return get_user_autocompletions(C,D,B,A)
for A in C.command.params:
if is_incomplete_argument(C.params,A):return get_user_autocompletions(C,D,B,A)
add_subcommand_completions(C,B,E);return sorted(E)
def do_complete(cli,prog_name,include_descriptions):
B=split_arg_string(os.environ[_H]);C=int(os.environ[_I]);E=B[1:C]
try:D=B[C]
except IndexError:D=''
for A in get_choices(cli,prog_name,E,D):
echo(A[0])
if include_descriptions:echo(A[1]if A[1]else _D)
return _A
def do_complete_fish(cli,prog_name):
B=split_arg_string(os.environ[_H]);C=os.environ[_I];D=B[1:]
for A in get_choices(cli,prog_name,D,C):
if A[1]:echo(f"{A[0]}\t{A[1]}")
else:echo(A[0])
return _A
def bashcomplete(cli,prog_name,complete_var,complete_instr):
C=complete_instr;B=prog_name
if _D in C:D,A=C.split(_D,1)
else:D=C;A=_E
if D=='source':echo(get_completion_script(B,complete_var,A));return _A
elif D=='complete':
if A==_G:return do_complete_fish(cli,B)
elif A in{_E,_F}:return do_complete(cli,B,A==_F)
return _C
|