Bug 1518586 - [mach] Implement bash completion for subcommands and arguments r=nalexander
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Fri, 11 Jan 2019 15:28:49 +0000
changeset 513486 729ea50e3045
parent 513485 5b651dea28d8
child 513487 ebff3389b3e4
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1518586
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518586 - [mach] Implement bash completion for subcommands and arguments r=nalexander Since we're calling into a mach command for the current completion implementation anyway (and incurring python startup penalties), we might as well move all the bash logic into the mach command. The new 'mach-completion' command was created in case there are scripts relying on the current behaviour of 'mach-commands'. Depends on D16254 Differential Revision: https://phabricator.services.mozilla.com/D16255
python/mach/bash-completion.sh
python/mach/mach/commands/commandinfo.py
--- a/python/mach/bash-completion.sh
+++ b/python/mach/bash-completion.sh
@@ -1,29 +1,18 @@
 function _mach()
 {
-  local cur cmds c subcommand
+  local cur targets
   COMPREPLY=()
 
-  # Load the list of commands
-  cmds=`"${COMP_WORDS[0]}" mach-commands`
-
-  # Look for the subcommand.
-  cur="${COMP_WORDS[COMP_CWORD]}"
-  subcommand=""
-  c=1
-  while [ $c -lt $COMP_CWORD ]; do
-    word="${COMP_WORDS[c]}"
-    for cmd in $cmds; do
-      if [ "$cmd" = "$word" ]; then
-        subcommand="$word"
-      fi
-    done
-    c=$((++c))
-  done
-
-  if [[ "$subcommand" == "help" || -z "$subcommand" ]]; then
-      COMPREPLY=( $(compgen -W "$cmds" -- ${cur}) )
+  # Calling `mach-completion` with -h/--help would result in the
+  # help text being used as the completion targets.
+  if [[ $COMP_LINE == *"-h"* || $COMP_LINE == *"--help"* ]]; then
+    return 0
   fi
 
+  # Load the list of targets
+  targets=`"${COMP_WORDS[0]}" mach-completion ${COMP_LINE}`
+  cur="${COMP_WORDS[COMP_CWORD]}"
+  COMPREPLY=( $(compgen -W "$targets" -- ${cur}) )
   return 0
 }
 complete -o default -F _mach mach
--- a/python/mach/mach/commands/commandinfo.py
+++ b/python/mach/mach/commands/commandinfo.py
@@ -1,14 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+import argparse
+from itertools import chain
+
 from mach.decorators import (
     CommandProvider,
     Command,
     CommandArgument,
 )
 
 
 @CommandProvider
@@ -45,8 +48,43 @@ class BuiltinCommands(object):
 
             print(command)
             print('=' * len(command))
             print('')
             print('File: %s' % inspect.getsourcefile(method))
             print('Class: %s' % cls.__name__)
             print('Method: %s' % handler.method)
             print('')
+
+    @Command('mach-completion', category='misc',
+             description='Prints a list of completion strings for the specified command.')
+    @CommandArgument('args', default=None, nargs=argparse.REMAINDER,
+                     help="Command to complete.")
+    def completion(self, args):
+        all_commands = sorted(self.command_keys)
+        if not args:
+            print("\n".join(all_commands))
+            return
+
+        is_help = 'help' in args
+        command = None
+        for i, arg in enumerate(args):
+            if arg in all_commands:
+                command = arg
+                args = args[i+1:]
+                break
+
+        if not command:
+            print("\n".join(all_commands))
+            return
+
+        handler = self.context.commands.command_handlers[command]
+        for arg in args:
+            if arg in handler.subcommand_handlers:
+                handler = handler.subcommand_handlers[arg]
+                break
+
+        parser = handler.parser
+        targets = sorted(handler.subcommand_handlers.keys())
+        if not is_help:
+            targets.append('help')
+            targets.extend(chain(*[action.option_strings for action in parser._actions]))
+        print("\n".join(targets))