diff --git a/spec/completion_spec.lua b/spec/completion_spec.lua index 0f5bdd9..ca39e7c 100644 --- a/spec/completion_spec.lua +++ b/spec/completion_spec.lua @@ -217,37 +217,84 @@ _comptest it("generates correct fish completion script", function() assert.equal([=[ +function __fish_comptest_print_command + set -l cmdline (commandline -poc) + set -l cmd + set -e cmdline[1] + for arg in $cmdline + switch $arg + case help + set cmd $cmd help + break + case completion + set cmd $cmd completion + break + case install i + set cmd $cmd install + break + case admin + set cmd $cmd admin + set -e cmdline[1] + for arg in $cmdline + switch $arg + case help + set cmd $cmd help + break + case add + set cmd $cmd add + break + case remove + set cmd $cmd remove + break + end + end + break + end + end + echo "$cmd" +end -complete -c comptest -n '__fish_use_subcommand' -xa 'help' -d 'Show help for commands' -complete -c comptest -n '__fish_use_subcommand' -xa 'completion' -d 'Output a shell completion script' -complete -c comptest -n '__fish_use_subcommand' -xa 'install' -d 'Install a rock' -complete -c comptest -n '__fish_use_subcommand' -xa 'i' -d 'Install a rock' -complete -c comptest -n '__fish_use_subcommand' -xa 'admin' -d 'Rock server administration interface' +function __fish_comptest_using_command + test (__fish_comptest_print_command) = "$argv" + and return 0 + or return 1 +end + +function __fish_comptest_seen_command + string match -q "$argv*" (__fish_comptest_print_command) + and return 0 + or return 1 +end + +complete -c comptest -n '__fish_comptest_using_command' -xa 'help' -d 'Show help for commands' +complete -c comptest -n '__fish_comptest_using_command' -xa 'completion' -d 'Output a shell completion script' +complete -c comptest -n '__fish_comptest_using_command' -xa 'install i' -d 'Install a rock' +complete -c comptest -n '__fish_comptest_using_command' -xa 'admin' -d 'Rock server administration interface' complete -c comptest -s h -l help -d 'Show this help message and exit' complete -c comptest -l completion -xa 'bash zsh fish' -d 'Output a shell completion script for the specified shell' complete -c comptest -s v -l verbose -d 'Set the verbosity level' complete -c comptest -s f -l files -r -d 'A description with illegal "\' characters' -complete -c comptest -n '__fish_seen_subcommand_from help' -xa 'help completion install i admin' -complete -c comptest -n '__fish_seen_subcommand_from help' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_using_command help' -xa 'help completion install i admin' +complete -c comptest -n '__fish_comptest_seen_command help' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from completion' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_seen_command completion' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from install i' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from install i' -l deps-mode -xa 'all one order none' -complete -c comptest -n '__fish_seen_subcommand_from install i' -l no-doc -d 'Install without documentation' +complete -c comptest -n '__fish_comptest_seen_command install' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_seen_command install' -l deps-mode -xa 'all one order none' +complete -c comptest -n '__fish_comptest_seen_command install' -l no-doc -d 'Install without documentation' -complete -c comptest -n '__fish_use_subcommand' -xa 'help' -d 'Show help for commands' -complete -c comptest -n '__fish_use_subcommand' -xa 'add' -d 'Add a rock to a server' -complete -c comptest -n '__fish_use_subcommand' -xa 'remove' -d 'Remove a rock from a server' -complete -c comptest -n '__fish_seen_subcommand_from admin' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_using_command admin' -xa 'help' -d 'Show help for commands' +complete -c comptest -n '__fish_comptest_using_command admin' -xa 'add' -d 'Add a rock to a server' +complete -c comptest -n '__fish_comptest_using_command admin' -xa 'remove' -d 'Remove a rock from a server' +complete -c comptest -n '__fish_comptest_seen_command admin' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from help' -xa 'help add remove' -complete -c comptest -n '__fish_seen_subcommand_from help' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_using_command admin help' -xa 'help add remove' +complete -c comptest -n '__fish_comptest_seen_command admin help' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from add' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_seen_command admin add' -s h -l help -d 'Show this help message and exit' -complete -c comptest -n '__fish_seen_subcommand_from remove' -s h -l help -d 'Show this help message and exit' +complete -c comptest -n '__fish_comptest_seen_command admin remove' -s h -l help -d 'Show this help message and exit' ]=], get_output("completion fish")) end) end) diff --git a/src/argparse.lua b/src/argparse.lua index a91da95..b17c746 100644 --- a/src/argparse.lua +++ b/src/argparse.lua @@ -584,12 +584,14 @@ function Option:_is_vararg() return self._maxargs ~= self._minargs end -function Parser:_get_fullname() +function Parser:_get_fullname(exclude_root) local parent = self._parent local buf = {self._name} while parent do - table.insert(buf, 1, parent._name) + if not exclude_root or parent._parent then + table.insert(buf, 1, parent._name) + end parent = parent._parent end @@ -1441,23 +1443,48 @@ local function fish_escape(string) return string:gsub("[\\']", "\\%0") end -function Parser:_fish_complete_help(buf, prefix) +function Parser:_fish_get_cmd(buf, indent) + if #self._commands == 0 then + return + end + + table.insert(buf, (" "):rep(indent) .. "set -e cmdline[1]") + table.insert(buf, (" "):rep(indent) .. "for arg in $cmdline") + table.insert(buf, (" "):rep(indent + 4) .. "switch $arg") + + for _, command in ipairs(self._commands) do + table.insert(buf, (" "):rep(indent + 8) .. "case " .. table.concat(command._aliases, " ")) + table.insert(buf, (" "):rep(indent + 12) .. "set cmd $cmd " .. command._name) + command:_fish_get_cmd(buf, indent + 12) + table.insert(buf, (" "):rep(indent + 12) .. "break") + end + + table.insert(buf, (" "):rep(indent + 4) .. "end") + table.insert(buf, (" "):rep(indent) .. "end") +end + +function Parser:_fish_complete_help(buf, basename) + local prefix = "complete -c " .. basename table.insert(buf, "") for _, command in ipairs(self._commands) do - for _, alias in ipairs(command._aliases) do - local line = ("%s -n '__fish_use_subcommand' -xa '%s'"):format(prefix, alias) - if command._description then - line = ("%s -d '%s'"):format(line, fish_escape(get_short_description(command))) - end - table.insert(buf, line) + local aliases = table.concat(command._aliases, " ") + local line + if self._parent then + line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") + :format(prefix, basename, self:_get_fullname(true), aliases) + else + line = ("%s -n '__fish_%s_using_command' -xa '%s'"):format(prefix, basename, aliases) end - + if command._description then + line = ("%s -d '%s'"):format(line, fish_escape(get_short_description(command))) + end + table.insert(buf, line) end if self._is_help_command then - local line = ("%s -n '__fish_seen_subcommand_from %s' -xa '%s'") - :format(prefix, table.concat(self._aliases, " "), self._parent:_get_commands()) + local line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") + :format(prefix, basename, self:_get_fullname(true), self._parent:_get_commands()) table.insert(buf, line) end @@ -1465,7 +1492,7 @@ function Parser:_fish_complete_help(buf, prefix) local parts = {prefix} if self._parent then - table.insert(parts, "-n '__fish_seen_subcommand_from " .. table.concat(self._aliases, " ") .. "'") + table.insert(parts, "-n '__fish_" .. basename .. "_seen_command " .. self:_get_fullname(true) .. "'") end for _, alias in ipairs(option._aliases) do @@ -1490,7 +1517,7 @@ function Parser:_fish_complete_help(buf, prefix) end for _, command in ipairs(self._commands) do - command:_fish_complete_help(buf, prefix) + command:_fish_complete_help(buf, basename) end end @@ -1498,8 +1525,31 @@ function Parser:get_fish_complete() self._basename = base_name(self._name) assert(self:_is_shell_safe()) local buf = {} - local prefix = "complete -c " .. self._basename - self:_fish_complete_help(buf, prefix) + + if #self._commands > 0 then + table.insert(buf, ([[ +function __fish_%s_print_command + set -l cmdline (commandline -poc) + set -l cmd]]):format(self._basename)) + self:_fish_get_cmd(buf, 4) + table.insert(buf, ([[ + echo "$cmd" +end + +function __fish_%s_using_command + test (__fish_%s_print_command) = "$argv" + and return 0 + or return 1 +end + +function __fish_%s_seen_command + string match -q "$argv*" (__fish_%s_print_command) + and return 0 + or return 1 +end]]):format(self._basename, self._basename, self._basename, self._basename)) + end + + self:_fish_complete_help(buf, self._basename) return table.concat(buf, "\n") .. "\n" end