summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Spangler <paul.spangler@ni.com>2021-08-09 13:27:39 -0500
committerDavid Lord <davidism@gmail.com>2022-03-28 08:05:27 -0700
commitf2e579ab187ca8fdfbe6ce86de08f0e9f62fe4ae (patch)
tree69c832d1b503191116c1fa42038d6790badaa151 /src
parentd251cb0abc9b0dbda2402d4d831c18718cfb51bf (diff)
downloadclick-f2e579ab187ca8fdfbe6ce86de08f0e9f62fe4ae.tar.gz
shell completion prioritizes option values over new options
Allow option values that start with an option prefix to complete rather than treating them like new options. Don't treat count options as needing a value to complete.
Diffstat (limited to 'src')
-rw-r--r--src/click/core.py3
-rw-r--r--src/click/shell_completion.py17
2 files changed, 11 insertions, 9 deletions
diff --git a/src/click/core.py b/src/click/core.py
index 263a5ff..939ad81 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -292,6 +292,8 @@ class Context:
#: must be never propagated to another arguments. This is used
#: to implement nested parsing.
self.protected_args: t.List[str] = []
+ #: the collected prefixes of the command's options.
+ self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set()
if obj is None and parent is not None:
obj = parent.obj
@@ -1385,6 +1387,7 @@ class Command(BaseCommand):
)
ctx.args = args
+ ctx._opt_prefixes.update(parser._opt_prefixes)
return args
def invoke(self, ctx: Context) -> t.Any:
diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py
index 1e9df6f..c17a8e6 100644
--- a/src/click/shell_completion.py
+++ b/src/click/shell_completion.py
@@ -448,17 +448,16 @@ def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
)
-def _start_of_option(value: str) -> bool:
+def _start_of_option(ctx: Context, value: str) -> bool:
"""Check if the value looks like the start of an option."""
if not value:
return False
c = value[0]
- # Allow "/" since that starts a path.
- return not c.isalnum() and c != "/"
+ return c in ctx._opt_prefixes
-def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
+def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool:
"""Determine if the given parameter is an option that needs a value.
:param args: List of complete args before the incomplete value.
@@ -467,7 +466,7 @@ def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
if not isinstance(param, Option):
return False
- if param.is_flag:
+ if param.is_flag or param.count:
return False
last_option = None
@@ -476,7 +475,7 @@ def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool:
if index + 1 > param.nargs:
break
- if _start_of_option(arg):
+ if _start_of_option(ctx, arg):
last_option = arg
return last_option is not None and last_option in param.opts
@@ -551,7 +550,7 @@ def _resolve_incomplete(
# split and discard the "=" to make completion easier.
if incomplete == "=":
incomplete = ""
- elif "=" in incomplete and _start_of_option(incomplete):
+ elif "=" in incomplete and _start_of_option(ctx, incomplete):
name, _, incomplete = incomplete.partition("=")
args.append(name)
@@ -559,7 +558,7 @@ def _resolve_incomplete(
# even if they start with the option character. If it hasn't been
# given and the incomplete arg looks like an option, the current
# command will provide option name completions.
- if "--" not in args and _start_of_option(incomplete):
+ if "--" not in args and _start_of_option(ctx, incomplete):
return ctx.command, incomplete
params = ctx.command.get_params(ctx)
@@ -567,7 +566,7 @@ def _resolve_incomplete(
# If the last complete arg is an option name with an incomplete
# value, the option will provide value completions.
for param in params:
- if _is_incomplete_option(args, param):
+ if _is_incomplete_option(ctx, args, param):
return param, incomplete
# It's not an option name or value. The first argument without a