summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-06-07 13:55:46 -0400
committerTristan Van Berkom <tristan.vanberkom@codethink.co.uk>2018-06-07 16:14:11 -0400
commit8234e9c7d7f989ba86e4da4acecd92f8732b003c (patch)
tree804bae7bf328283bfb9f806d7e838be0b13effdf
parentb25c31ea358986fdc5f95cf607fc75f00fb5fb78 (diff)
downloadbuildstream-8234e9c7d7f989ba86e4da4acecd92f8732b003c.tar.gz
_frontend/cli.py: Allow specifying commands in `bst help`
o This supports deeply nested commands as well as shallow commands o Automated support for bash completions included
-rw-r--r--buildstream/_frontend/cli.py71
-rw-r--r--buildstream/_frontend/complete.py9
2 files changed, 67 insertions, 13 deletions
diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index 9cd0a871c..143d59aa4 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -12,6 +12,45 @@ from .complete import main_bashcomplete, complete_path, CompleteUnhandled
# Override of click's main entry point #
##################################################################
+# search_command()
+#
+# Helper function to get a command and context object
+# for a given command.
+#
+# Args:
+# commands (list): A list of command words following `bst` invocation
+# context (click.Context): An existing toplevel context, or None
+#
+# Returns:
+# context (click.Context): The context of the associated command, or None
+#
+def search_command(args, *, context=None):
+ if context is None:
+ context = cli.make_context('bst', args, resilient_parsing=True)
+
+ # Loop into the deepest command
+ command = cli
+ command_ctx = context
+ for cmd in args:
+ command = command_ctx.command.get_command(command_ctx, cmd)
+ if command is None:
+ return None
+ command_ctx = command.make_context(command.name, [command.name],
+ parent=command_ctx,
+ resilient_parsing=True)
+
+ return command_ctx
+
+
+# Completion for completing command names as help arguments
+def complete_commands(cmd, args, incomplete):
+ command_ctx = search_command(args[1:])
+ if command_ctx and command_ctx.command and isinstance(command_ctx.command, click.MultiCommand):
+ return [subcommand + " " for subcommand in command_ctx.command.list_commands(command_ctx)]
+
+ return []
+
+
# Special completion for completing the bst elements in a project dir
def complete_target(args, incomplete):
"""
@@ -73,7 +112,7 @@ def complete_target(args, incomplete):
return complete_path("File", incomplete, base_directory=base_directory)
-def override_completions(cmd_param, args, incomplete):
+def override_completions(cmd, cmd_param, args, incomplete):
"""
:param cmd_param: command definition
:param args: full list of args typed before the incomplete arg
@@ -81,6 +120,9 @@ def override_completions(cmd_param, args, incomplete):
:return: all the possible user-specified completions for the param
"""
+ if cmd.name == 'help':
+ return complete_commands(cmd, args, incomplete)
+
# We can't easily extend click's data structures without
# modifying click itself, so just do some weak special casing
# right here and select which parameters we want to handle specially.
@@ -195,15 +237,26 @@ def cli(context, **kwargs):
##################################################################
@cli.command(name="help", short_help="Print usage information",
context_settings={"help_option_names": []})
-@click.argument("arg", nargs=-1)
+@click.argument("command", nargs=-1, metavar='COMMAND')
@click.pass_context
-def help_command(ctx, **kwargs):
- click.echo(ctx.parent.get_help(), err=True)
- # TODO support bst help <command> but currently
- # seems non obvious how to do this with click.
- if kwargs["arg"]:
- click.echo("\n{} {} --help for more usage on a specific command\n".format(
- ctx.parent.info_name, " ".join(kwargs["arg"])), err=True)
+def help_command(ctx, command):
+ """Print usage information about a given command
+ """
+ command_ctx = search_command(command, context=ctx.parent)
+ if not command_ctx:
+ click.echo("Not a valid command: '{} {}'"
+ .format(ctx.parent.info_name, " ".join(command)), err=True)
+ sys.exit(-1)
+
+ click.echo(command_ctx.command.get_help(command_ctx), err=True)
+
+ # Hint about available sub commands
+ if isinstance(command_ctx.command, click.MultiCommand):
+ detail = " "
+ if command:
+ detail = " {} ".format(" ".join(command))
+ click.echo("\nFor usage on a specific command: {} help{}COMMAND"
+ .format(ctx.parent.info_name, detail), err=True)
##################################################################
diff --git a/buildstream/_frontend/complete.py b/buildstream/_frontend/complete.py
index fa986ee6b..4a367e62a 100644
--- a/buildstream/_frontend/complete.py
+++ b/buildstream/_frontend/complete.py
@@ -209,7 +209,7 @@ def is_incomplete_argument(current_params, cmd_param):
return False
-def get_user_autocompletions(args, incomplete, cmd_param, override):
+def get_user_autocompletions(args, incomplete, cmd, cmd_param, override):
"""
:param args: full list of args typed before the incomplete arg
:param incomplete: the incomplete text of the arg to autocomplete
@@ -222,7 +222,8 @@ def get_user_autocompletions(args, incomplete, cmd_param, override):
# Use the type specific default completions unless it was overridden
try:
- return override(cmd_param=cmd_param,
+ return override(cmd=cmd,
+ cmd_param=cmd_param,
args=args,
incomplete=incomplete)
except CompleteUnhandled:
@@ -268,14 +269,14 @@ def get_choices(cli, prog_name, args, incomplete, override):
# completion for option values by choices
for cmd_param in ctx.command.params:
if isinstance(cmd_param, Option) and is_incomplete_option(all_args, cmd_param):
- choices.extend(get_user_autocompletions(all_args, incomplete, cmd_param, override))
+ choices.extend(get_user_autocompletions(all_args, incomplete, ctx.command, cmd_param, override))
found_param = True
break
if not found_param:
# completion for argument values by choices
for cmd_param in ctx.command.params:
if isinstance(cmd_param, Argument) and is_incomplete_argument(ctx.params, cmd_param):
- choices.extend(get_user_autocompletions(all_args, incomplete, cmd_param, override))
+ choices.extend(get_user_autocompletions(all_args, incomplete, ctx.command, cmd_param, override))
found_param = True
break