Bash completions: add support for nested commands

This commit is contained in:
Paul Ouellette
2019-08-07 23:39:50 -04:00
parent 0b081a1838
commit ff9c838e45
2 changed files with 87 additions and 44 deletions

View File

@@ -19,10 +19,10 @@ describe("tests related to generation of shell completion scripts", function()
assert.equal([=[ assert.equal([=[
_comptest() { _comptest() {
local IFS=$' \t\n' local IFS=$' \t\n'
local cur prev cmd opts arg local args cur prev cmd opts arg
args=("${COMP_WORDS[@]}")
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
cmd="comptest"
opts="-h --help --completion -v --verbose -f --files" opts="-h --help --completion -v --verbose -f --files"
case "$prev" in case "$prev" in
@@ -36,31 +36,60 @@ _comptest() {
;; ;;
esac esac
for arg in ${COMP_WORDS[@]:1}; do args=("${args[@]:1}")
for arg in "${args[@]}"; do
case "$arg" in case "$arg" in
help)
cmd="help"
opts="$opts -h --help"
break
;;
completion) completion)
cmd="completion" cmd="completion"
opts="$opts -h --help"
break break
;; ;;
install|i) install|i)
cmd="install" cmd="install"
opts="$opts -h --help --deps-mode --no-doc"
break break
;; ;;
admin) admin)
cmd="admin" cmd="admin"
opts="$opts -h --help"
args=("${args[@]:1}")
for arg in "${args[@]}"; do
case "$arg" in
help)
cmd="$cmd help"
opts="$opts -h --help"
break
;;
add)
cmd="$cmd add"
opts="$opts -h --help"
break
;;
remove)
cmd="$cmd remove"
opts="$opts -h --help"
break
;;
esac
done
break break
;; ;;
esac esac
done done
case "$cmd" in case "$cmd" in
comptest) '')
COMPREPLY=($(compgen -W "help completion install i admin" -- "$cur")) COMPREPLY=($(compgen -W "help completion install i admin" -- "$cur"))
;; ;;
completion) 'help')
opts="$opts -h --help" COMPREPLY=($(compgen -W "help completion install i admin" -- "$cur"))
;; ;;
install) 'install')
case "$prev" in case "$prev" in
--deps-mode) --deps-mode)
COMPREPLY=($(compgen -W "all one order none" -- "$cur")) COMPREPLY=($(compgen -W "all one order none" -- "$cur"))
@@ -68,10 +97,12 @@ _comptest() {
;; ;;
esac esac
opts="$opts -h --help --deps-mode --no-doc"
;; ;;
admin) 'admin')
opts="$opts -h --help" COMPREPLY=($(compgen -W "help add remove" -- "$cur"))
;;
'admin help')
COMPREPLY=($(compgen -W "help add remove" -- "$cur"))
;; ;;
esac esac

View File

@@ -586,6 +586,9 @@ end
function Parser:_get_fullname(exclude_root) function Parser:_get_fullname(exclude_root)
local parent = self._parent local parent = self._parent
if exclude_root and not parent then
return ""
end
local buf = {self._name} local buf = {self._name}
while parent do while parent do
@@ -1236,45 +1239,51 @@ function Parser:_bash_option_args(buf, indent)
end end
end end
function Parser:_bash_get_cmd(buf) function Parser:_bash_get_cmd(buf, indent)
local cmds = {} if #self._commands == 0 then
for _, command in ipairs(self._commands) do return
if not command._is_help_command then
table.insert(cmds, (" "):rep(12) .. table.concat(command._aliases, "|") .. ")")
table.insert(cmds, (" "):rep(16) .. 'cmd="' .. command._name .. '"')
table.insert(cmds, (" "):rep(16) .. "break")
table.insert(cmds, (" "):rep(16) .. ";;")
end
end end
if #cmds > 0 then table.insert(buf, (" "):rep(indent) .. 'args=("${args[@]:1}")')
table.insert(buf, (" "):rep(4) .. "for arg in ${COMP_WORDS[@]:1}; do") table.insert(buf, (" "):rep(indent) .. 'for arg in "${args[@]}"; do')
table.insert(buf, (" "):rep(8) .. 'case "$arg" in') table.insert(buf, (" "):rep(indent + 4) .. 'case "$arg" in')
table.insert(buf, table.concat(cmds, "\n"))
table.insert(buf, (" "):rep(8) .. "esac") for _, command in ipairs(self._commands) do
table.insert(buf, (" "):rep(4) .. "done\n") table.insert(buf, (" "):rep(indent + 8) .. table.concat(command._aliases, "|") .. ")")
if self._parent then
table.insert(buf, (" "):rep(indent + 12) .. 'cmd="$cmd ' .. command._name .. '"')
else
table.insert(buf, (" "):rep(indent + 12) .. 'cmd="' .. command._name .. '"')
end end
table.insert(buf, (" "):rep(indent + 12) .. 'opts="$opts ' .. command:_get_options() .. '"')
command:_bash_get_cmd(buf, indent + 12)
table.insert(buf, (" "):rep(indent + 12) .. "break")
table.insert(buf, (" "):rep(indent + 12) .. ";;")
end
table.insert(buf, (" "):rep(indent + 4) .. "esac")
table.insert(buf, (" "):rep(indent) .. "done")
end end
function Parser:_bash_cmd_completions(buf) function Parser:_bash_cmd_completions(buf)
local subcmds = {} local cmd_buf = {}
for _, command in ipairs(self._commands) do if self._parent then
if #command._options > 0 and not command._is_help_command then self:_bash_option_args(cmd_buf, 12)
table.insert(subcmds, (" "):rep(8) .. command._name .. ")")
command:_bash_option_args(subcmds, 12)
table.insert(subcmds, (" "):rep(12) .. 'opts="$opts ' .. command:_get_options() .. '"')
table.insert(subcmds, (" "):rep(12) .. ";;")
end end
if #self._commands > 0 then
table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self:_get_commands() .. '" -- "$cur"))')
elseif self._is_help_command then
table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self._parent:_get_commands() .. '" -- "$cur"))')
end
if #cmd_buf > 0 then
table.insert(buf, (" "):rep(8) .. "'" .. self:_get_fullname(true) .. "')")
table.insert(buf, table.concat(cmd_buf, "\n"))
table.insert(buf, (" "):rep(12) .. ";;")
end end
table.insert(buf, (" "):rep(4) .. 'case "$cmd" in') for _, command in ipairs(self._commands) do
table.insert(buf, (" "):rep(8) .. self._basename .. ")") command:_bash_cmd_completions(buf)
table.insert(buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self:_get_commands() .. '" -- "$cur"))')
table.insert(buf, (" "):rep(12) .. ";;")
if #subcmds > 0 then
table.insert(buf, table.concat(subcmds, "\n"))
end end
table.insert(buf, (" "):rep(4) .. "esac\n")
end end
function Parser:get_bash_complete() function Parser:get_bash_complete()
@@ -1283,17 +1292,20 @@ function Parser:get_bash_complete()
local buf = {([[ local buf = {([[
_%s() { _%s() {
local IFS=$' \t\n' local IFS=$' \t\n'
local cur prev cmd opts arg local args cur prev cmd opts arg
args=("${COMP_WORDS[@]}")
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
cmd="%s"
opts="%s" opts="%s"
]]):format(self._basename, self._basename, self:_get_options())} ]]):format(self._basename, self:_get_options())}
self:_bash_option_args(buf, 4) self:_bash_option_args(buf, 4)
self:_bash_get_cmd(buf) self:_bash_get_cmd(buf, 4)
if #self._commands > 0 then if #self._commands > 0 then
table.insert(buf, "")
table.insert(buf, (" "):rep(4) .. 'case "$cmd" in')
self:_bash_cmd_completions(buf) self:_bash_cmd_completions(buf)
table.insert(buf, (" "):rep(4) .. "esac\n")
end end
table.insert(buf, ([=[ table.insert(buf, ([=[