Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/click/_bashcomplete.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:18:57 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d30785e31577 |
|---|---|
| 1 import copy | |
| 2 import os | |
| 3 import re | |
| 4 | |
| 5 from .core import Argument | |
| 6 from .core import MultiCommand | |
| 7 from .core import Option | |
| 8 from .parser import split_arg_string | |
| 9 from .types import Choice | |
| 10 from .utils import echo | |
| 11 | |
| 12 try: | |
| 13 from collections import abc | |
| 14 except ImportError: | |
| 15 import collections as abc | |
| 16 | |
| 17 WORDBREAK = "=" | |
| 18 | |
| 19 # Note, only BASH version 4.4 and later have the nosort option. | |
| 20 COMPLETION_SCRIPT_BASH = """ | |
| 21 %(complete_func)s() { | |
| 22 local IFS=$'\n' | |
| 23 COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ | |
| 24 COMP_CWORD=$COMP_CWORD \\ | |
| 25 %(autocomplete_var)s=complete $1 ) ) | |
| 26 return 0 | |
| 27 } | |
| 28 | |
| 29 %(complete_func)setup() { | |
| 30 local COMPLETION_OPTIONS="" | |
| 31 local BASH_VERSION_ARR=(${BASH_VERSION//./ }) | |
| 32 # Only BASH version 4.4 and later have the nosort option. | |
| 33 if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] \ | |
| 34 && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then | |
| 35 COMPLETION_OPTIONS="-o nosort" | |
| 36 fi | |
| 37 | |
| 38 complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s | |
| 39 } | |
| 40 | |
| 41 %(complete_func)setup | |
| 42 """ | |
| 43 | |
| 44 COMPLETION_SCRIPT_ZSH = """ | |
| 45 #compdef %(script_names)s | |
| 46 | |
| 47 %(complete_func)s() { | |
| 48 local -a completions | |
| 49 local -a completions_with_descriptions | |
| 50 local -a response | |
| 51 (( ! $+commands[%(script_names)s] )) && return 1 | |
| 52 | |
| 53 response=("${(@f)$( env COMP_WORDS=\"${words[*]}\" \\ | |
| 54 COMP_CWORD=$((CURRENT-1)) \\ | |
| 55 %(autocomplete_var)s=\"complete_zsh\" \\ | |
| 56 %(script_names)s )}") | |
| 57 | |
| 58 for key descr in ${(kv)response}; do | |
| 59 if [[ "$descr" == "_" ]]; then | |
| 60 completions+=("$key") | |
| 61 else | |
| 62 completions_with_descriptions+=("$key":"$descr") | |
| 63 fi | |
| 64 done | |
| 65 | |
| 66 if [ -n "$completions_with_descriptions" ]; then | |
| 67 _describe -V unsorted completions_with_descriptions -U | |
| 68 fi | |
| 69 | |
| 70 if [ -n "$completions" ]; then | |
| 71 compadd -U -V unsorted -a completions | |
| 72 fi | |
| 73 compstate[insert]="automenu" | |
| 74 } | |
| 75 | |
| 76 compdef %(complete_func)s %(script_names)s | |
| 77 """ | |
| 78 | |
| 79 COMPLETION_SCRIPT_FISH = ( | |
| 80 "complete --no-files --command %(script_names)s --arguments" | |
| 81 ' "(env %(autocomplete_var)s=complete_fish' | |
| 82 " COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t)" | |
| 83 ' %(script_names)s)"' | |
| 84 ) | |
| 85 | |
| 86 _completion_scripts = { | |
| 87 "bash": COMPLETION_SCRIPT_BASH, | |
| 88 "zsh": COMPLETION_SCRIPT_ZSH, | |
| 89 "fish": COMPLETION_SCRIPT_FISH, | |
| 90 } | |
| 91 | |
| 92 _invalid_ident_char_re = re.compile(r"[^a-zA-Z0-9_]") | |
| 93 | |
| 94 | |
| 95 def get_completion_script(prog_name, complete_var, shell): | |
| 96 cf_name = _invalid_ident_char_re.sub("", prog_name.replace("-", "_")) | |
| 97 script = _completion_scripts.get(shell, COMPLETION_SCRIPT_BASH) | |
| 98 return ( | |
| 99 script | |
| 100 % { | |
| 101 "complete_func": "_{}_completion".format(cf_name), | |
| 102 "script_names": prog_name, | |
| 103 "autocomplete_var": complete_var, | |
| 104 } | |
| 105 ).strip() + ";" | |
| 106 | |
| 107 | |
| 108 def resolve_ctx(cli, prog_name, args): | |
| 109 """Parse into a hierarchy of contexts. Contexts are connected | |
| 110 through the parent variable. | |
| 111 | |
| 112 :param cli: command definition | |
| 113 :param prog_name: the program that is running | |
| 114 :param args: full list of args | |
| 115 :return: the final context/command parsed | |
| 116 """ | |
| 117 ctx = cli.make_context(prog_name, args, resilient_parsing=True) | |
| 118 args = ctx.protected_args + ctx.args | |
| 119 while args: | |
| 120 if isinstance(ctx.command, MultiCommand): | |
| 121 if not ctx.command.chain: | |
| 122 cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) | |
| 123 if cmd is None: | |
| 124 return ctx | |
| 125 ctx = cmd.make_context( | |
| 126 cmd_name, args, parent=ctx, resilient_parsing=True | |
| 127 ) | |
| 128 args = ctx.protected_args + ctx.args | |
| 129 else: | |
| 130 # Walk chained subcommand contexts saving the last one. | |
| 131 while args: | |
| 132 cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) | |
| 133 if cmd is None: | |
| 134 return ctx | |
| 135 sub_ctx = cmd.make_context( | |
| 136 cmd_name, | |
| 137 args, | |
| 138 parent=ctx, | |
| 139 allow_extra_args=True, | |
| 140 allow_interspersed_args=False, | |
| 141 resilient_parsing=True, | |
| 142 ) | |
| 143 args = sub_ctx.args | |
| 144 ctx = sub_ctx | |
| 145 args = sub_ctx.protected_args + sub_ctx.args | |
| 146 else: | |
| 147 break | |
| 148 return ctx | |
| 149 | |
| 150 | |
| 151 def start_of_option(param_str): | |
| 152 """ | |
| 153 :param param_str: param_str to check | |
| 154 :return: whether or not this is the start of an option declaration | |
| 155 (i.e. starts "-" or "--") | |
| 156 """ | |
| 157 return param_str and param_str[:1] == "-" | |
| 158 | |
| 159 | |
| 160 def is_incomplete_option(all_args, cmd_param): | |
| 161 """ | |
| 162 :param all_args: the full original list of args supplied | |
| 163 :param cmd_param: the current command paramter | |
| 164 :return: whether or not the last option declaration (i.e. starts | |
| 165 "-" or "--") is incomplete and corresponds to this cmd_param. In | |
| 166 other words whether this cmd_param option can still accept | |
| 167 values | |
| 168 """ | |
| 169 if not isinstance(cmd_param, Option): | |
| 170 return False | |
| 171 if cmd_param.is_flag: | |
| 172 return False | |
| 173 last_option = None | |
| 174 for index, arg_str in enumerate( | |
| 175 reversed([arg for arg in all_args if arg != WORDBREAK]) | |
| 176 ): | |
| 177 if index + 1 > cmd_param.nargs: | |
| 178 break | |
| 179 if start_of_option(arg_str): | |
| 180 last_option = arg_str | |
| 181 | |
| 182 return True if last_option and last_option in cmd_param.opts else False | |
| 183 | |
| 184 | |
| 185 def is_incomplete_argument(current_params, cmd_param): | |
| 186 """ | |
| 187 :param current_params: the current params and values for this | |
| 188 argument as already entered | |
| 189 :param cmd_param: the current command parameter | |
| 190 :return: whether or not the last argument is incomplete and | |
| 191 corresponds to this cmd_param. In other words whether or not the | |
| 192 this cmd_param argument can still accept values | |
| 193 """ | |
| 194 if not isinstance(cmd_param, Argument): | |
| 195 return False | |
| 196 current_param_values = current_params[cmd_param.name] | |
| 197 if current_param_values is None: | |
| 198 return True | |
| 199 if cmd_param.nargs == -1: | |
| 200 return True | |
| 201 if ( | |
| 202 isinstance(current_param_values, abc.Iterable) | |
| 203 and cmd_param.nargs > 1 | |
| 204 and len(current_param_values) < cmd_param.nargs | |
| 205 ): | |
| 206 return True | |
| 207 return False | |
| 208 | |
| 209 | |
| 210 def get_user_autocompletions(ctx, args, incomplete, cmd_param): | |
| 211 """ | |
| 212 :param ctx: context associated with the parsed command | |
| 213 :param args: full list of args | |
| 214 :param incomplete: the incomplete text to autocomplete | |
| 215 :param cmd_param: command definition | |
| 216 :return: all the possible user-specified completions for the param | |
| 217 """ | |
| 218 results = [] | |
| 219 if isinstance(cmd_param.type, Choice): | |
| 220 # Choices don't support descriptions. | |
| 221 results = [ | |
| 222 (c, None) for c in cmd_param.type.choices if str(c).startswith(incomplete) | |
| 223 ] | |
| 224 elif cmd_param.autocompletion is not None: | |
| 225 dynamic_completions = cmd_param.autocompletion( | |
| 226 ctx=ctx, args=args, incomplete=incomplete | |
| 227 ) | |
| 228 results = [ | |
| 229 c if isinstance(c, tuple) else (c, None) for c in dynamic_completions | |
| 230 ] | |
| 231 return results | |
| 232 | |
| 233 | |
| 234 def get_visible_commands_starting_with(ctx, starts_with): | |
| 235 """ | |
| 236 :param ctx: context associated with the parsed command | |
| 237 :starts_with: string that visible commands must start with. | |
| 238 :return: all visible (not hidden) commands that start with starts_with. | |
| 239 """ | |
| 240 for c in ctx.command.list_commands(ctx): | |
| 241 if c.startswith(starts_with): | |
| 242 command = ctx.command.get_command(ctx, c) | |
| 243 if not command.hidden: | |
| 244 yield command | |
| 245 | |
| 246 | |
| 247 def add_subcommand_completions(ctx, incomplete, completions_out): | |
| 248 # Add subcommand completions. | |
| 249 if isinstance(ctx.command, MultiCommand): | |
| 250 completions_out.extend( | |
| 251 [ | |
| 252 (c.name, c.get_short_help_str()) | |
| 253 for c in get_visible_commands_starting_with(ctx, incomplete) | |
| 254 ] | |
| 255 ) | |
| 256 | |
| 257 # Walk up the context list and add any other completion | |
| 258 # possibilities from chained commands | |
| 259 while ctx.parent is not None: | |
| 260 ctx = ctx.parent | |
| 261 if isinstance(ctx.command, MultiCommand) and ctx.command.chain: | |
| 262 remaining_commands = [ | |
| 263 c | |
| 264 for c in get_visible_commands_starting_with(ctx, incomplete) | |
| 265 if c.name not in ctx.protected_args | |
| 266 ] | |
| 267 completions_out.extend( | |
| 268 [(c.name, c.get_short_help_str()) for c in remaining_commands] | |
| 269 ) | |
| 270 | |
| 271 | |
| 272 def get_choices(cli, prog_name, args, incomplete): | |
| 273 """ | |
| 274 :param cli: command definition | |
| 275 :param prog_name: the program that is running | |
| 276 :param args: full list of args | |
| 277 :param incomplete: the incomplete text to autocomplete | |
| 278 :return: all the possible completions for the incomplete | |
| 279 """ | |
| 280 all_args = copy.deepcopy(args) | |
| 281 | |
| 282 ctx = resolve_ctx(cli, prog_name, args) | |
| 283 if ctx is None: | |
| 284 return [] | |
| 285 | |
| 286 has_double_dash = "--" in all_args | |
| 287 | |
| 288 # In newer versions of bash long opts with '='s are partitioned, but | |
| 289 # it's easier to parse without the '=' | |
| 290 if start_of_option(incomplete) and WORDBREAK in incomplete: | |
| 291 partition_incomplete = incomplete.partition(WORDBREAK) | |
| 292 all_args.append(partition_incomplete[0]) | |
| 293 incomplete = partition_incomplete[2] | |
| 294 elif incomplete == WORDBREAK: | |
| 295 incomplete = "" | |
| 296 | |
| 297 completions = [] | |
| 298 if not has_double_dash and start_of_option(incomplete): | |
| 299 # completions for partial options | |
| 300 for param in ctx.command.params: | |
| 301 if isinstance(param, Option) and not param.hidden: | |
| 302 param_opts = [ | |
| 303 param_opt | |
| 304 for param_opt in param.opts + param.secondary_opts | |
| 305 if param_opt not in all_args or param.multiple | |
| 306 ] | |
| 307 completions.extend( | |
| 308 [(o, param.help) for o in param_opts if o.startswith(incomplete)] | |
| 309 ) | |
| 310 return completions | |
| 311 # completion for option values from user supplied values | |
| 312 for param in ctx.command.params: | |
| 313 if is_incomplete_option(all_args, param): | |
| 314 return get_user_autocompletions(ctx, all_args, incomplete, param) | |
| 315 # completion for argument values from user supplied values | |
| 316 for param in ctx.command.params: | |
| 317 if is_incomplete_argument(ctx.params, param): | |
| 318 return get_user_autocompletions(ctx, all_args, incomplete, param) | |
| 319 | |
| 320 add_subcommand_completions(ctx, incomplete, completions) | |
| 321 # Sort before returning so that proper ordering can be enforced in custom types. | |
| 322 return sorted(completions) | |
| 323 | |
| 324 | |
| 325 def do_complete(cli, prog_name, include_descriptions): | |
| 326 cwords = split_arg_string(os.environ["COMP_WORDS"]) | |
| 327 cword = int(os.environ["COMP_CWORD"]) | |
| 328 args = cwords[1:cword] | |
| 329 try: | |
| 330 incomplete = cwords[cword] | |
| 331 except IndexError: | |
| 332 incomplete = "" | |
| 333 | |
| 334 for item in get_choices(cli, prog_name, args, incomplete): | |
| 335 echo(item[0]) | |
| 336 if include_descriptions: | |
| 337 # ZSH has trouble dealing with empty array parameters when | |
| 338 # returned from commands, use '_' to indicate no description | |
| 339 # is present. | |
| 340 echo(item[1] if item[1] else "_") | |
| 341 | |
| 342 return True | |
| 343 | |
| 344 | |
| 345 def do_complete_fish(cli, prog_name): | |
| 346 cwords = split_arg_string(os.environ["COMP_WORDS"]) | |
| 347 incomplete = os.environ["COMP_CWORD"] | |
| 348 args = cwords[1:] | |
| 349 | |
| 350 for item in get_choices(cli, prog_name, args, incomplete): | |
| 351 if item[1]: | |
| 352 echo("{arg}\t{desc}".format(arg=item[0], desc=item[1])) | |
| 353 else: | |
| 354 echo(item[0]) | |
| 355 | |
| 356 return True | |
| 357 | |
| 358 | |
| 359 def bashcomplete(cli, prog_name, complete_var, complete_instr): | |
| 360 if "_" in complete_instr: | |
| 361 command, shell = complete_instr.split("_", 1) | |
| 362 else: | |
| 363 command = complete_instr | |
| 364 shell = "bash" | |
| 365 | |
| 366 if command == "source": | |
| 367 echo(get_completion_script(prog_name, complete_var, shell)) | |
| 368 return True | |
| 369 elif command == "complete": | |
| 370 if shell == "fish": | |
| 371 return do_complete_fish(cli, prog_name) | |
| 372 elif shell in {"bash", "zsh"}: | |
| 373 return do_complete(cli, prog_name, shell == "zsh") | |
| 374 | |
| 375 return False |
