diff --git a/python/mach/bash-completion.sh b/python/mach/bash-completion.sh index 13935cf88cd2..82dc9fb0f515 100644 --- a/python/mach/bash-completion.sh +++ b/python/mach/bash-completion.sh @@ -1,3 +1,8 @@ +# Is bash completion too slow to your taste? +# Consider generating fast completion scripts with: +# ./mach mach-completion bash -f path/to/mach.bash-completion +# See https://firefox-source-docs.mozilla.org/mach/usage.html#tab-completion + function _mach() { local cur targets diff --git a/python/mach/mach/commands/commandinfo.py b/python/mach/mach/commands/commandinfo.py index 414de9fd10b2..f31c36a8472b 100644 --- a/python/mach/mach/commands/commandinfo.py +++ b/python/mach/mach/commands/commandinfo.py @@ -230,7 +230,12 @@ def _zsh_describe(value, description=None): help="File path to save completion script.", ) def completion_bash(command_context, outfile): - commands_subcommands = [] + # Note: Make sure to generate a Bash 3-compatible script, because macOS + # ships with Bash 3 by default. One of the consequences is that Bash 4 + # features such as associative arrays ("declare -A") cannot be used. + # For example, while completion_zsh uses the associative array syntax for + # commands_subcommands, here we use case/esac instead. + case_commands_subcommands = [] case_options = [] case_subcommands = [] for i, cmd in enumerate(commands_info(command_context)): @@ -274,9 +279,15 @@ def completion_bash(command_context, outfile): # Build case statement for subcommands. subcommands = [_zsh_describe(s.subcommand, None) for s in cmd.subcommands] if subcommands: - commands_subcommands.append( - '[{}]=" {} "'.format( - cmd.name, " ".join([h.subcommand for h in cmd.subcommands]) + comsubs = " ".join([h.subcommand for h in cmd.subcommands]) + case_commands_subcommands.append( + "\n".join( + [ + " ({})".format(cmd.name), + ' comsubs=" {} "'.format(comsubs), + " ;;", + "", + ] ) ) @@ -295,10 +306,10 @@ def completion_bash(command_context, outfile): opt for opt_strs in global_options(command_context) for opt in opt_strs ] context = { + "case_commands_subcommands": "\n".join(sorted(case_commands_subcommands)), "case_options": "\n".join(case_options), "case_subcommands": "\n".join(case_subcommands), "commands": " ".join(commands(command_context)), - "commands_subcommands": " ".join(sorted(commands_subcommands)), "globalopts": " ".join(sorted(globalopts)), } diff --git a/python/mach/mach/commands/completion_templates/bash.template b/python/mach/mach/commands/completion_templates/bash.template index 53723087024e..6c9c002d6caa 100644 --- a/python/mach/mach/commands/completion_templates/bash.template +++ b/python/mach/mach/commands/completion_templates/bash.template @@ -2,7 +2,6 @@ _mach_complete() { local com coms comsubs cur opts script sub subs COMPREPLY=() - declare -A comsubs=( %(commands_subcommands)s ) _get_comp_words_by_ref -n : cur words # for an alias, get the real script behind it @@ -19,7 +18,10 @@ _mach_complete() if [[ -z $com ]]; then com=$word - elif [[ "${comsubs[$com]}" == *" $word "* ]]; then + case $com in +%(case_commands_subcommands)s + esac + elif [[ $comsubs == *" $word "* ]]; then sub=$word break fi