Merge remote-tracking branch 'luarocks/refs/pull/1/head'

This commit is contained in:
daurnimator
2019-05-29 00:04:42 +10:00
6 changed files with 210 additions and 92 deletions

View File

@@ -28,6 +28,47 @@ If the property ``add_help`` of a parser is set to ``false``, no help option wil
Options: Options:
/? Show this help message and exit. /? Show this help message and exit.
Help command
------------
If the property ``add_help_command`` of a parser is set to ``true``, a help command will be added to it. A string or table value can be used to configure the command.
.. code-block:: lua
:linenos:
local parser = argparse()
:add_help_command(true)
parser:command "install"
:description "Install a rock."
.. code-block:: none
$ lua script.lua help
.. code-block:: none
Usage: script.lua [-h] <command> ...
Options:
-h, --help Show this help message and exit.
Commands:
help Show help for commands.
install Install a rock.
.. code-block:: none
$ lua script.lua help install
.. code-block:: none
Usage: script.lua install [-h]
Install a rock.
Options:
-h, --help Show this help message and exit.
Disabling option handling Disabling option handling
------------------------- -------------------------
@@ -132,6 +173,7 @@ Property Type
``require_command`` Boolean ``require_command`` Boolean
``handle_options`` Boolean ``handle_options`` Boolean
``add_help`` Boolean or string or table ``add_help`` Boolean or string or table
``add_help_command`` Boolean or string or table
``command_target`` String ``command_target`` String
``usage_max_width`` Number ``usage_max_width`` Number
``usage_margin`` Number ``usage_margin`` Number

View File

@@ -77,13 +77,13 @@ Options:
parser:option "--config" parser:option "--config"
assert.equal([[ assert.equal([[
Usage: foo [-q] --from <from> [--config <config>] [-h] Usage: foo [-h] [-q] --from <from> [--config <config>]
Options: Options:
-h, --help Show this help message and exit.
-q, --quiet -q, --quiet
--from <from> --from <from>
--config <config> --config <config>]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("adds margin for multiline descriptions", function() it("adds margin for multiline descriptions", function()
@@ -97,13 +97,13 @@ Sets verbosity level.
-vv: Report all debugging information.]] -vv: Report all debugging information.]]
assert.equal([[ assert.equal([[
Usage: foo [-v] [-h] Usage: foo [-h] [-v]
Options: Options:
-h, --help Show this help message and exit.
-v Sets verbosity level. -v Sets verbosity level.
-v: Report all warnings. -v: Report all warnings.
-vv: Report all debugging information. -vv: Report all debugging information.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("puts different aliases on different lines if there are arguments", function() it("puts different aliases on different lines if there are arguments", function()
@@ -112,12 +112,12 @@ Options:
parser:option "-o --output" parser:option "-o --output"
assert.equal([[ assert.equal([[
Usage: foo [-o <output>] [-h] Usage: foo [-h] [-o <output>]
Options: Options:
-h, --help Show this help message and exit.
-o <output>, -o <output>,
--output <output> --output <output>]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("handles description with more lines than usage", function() it("handles description with more lines than usage", function()
@@ -131,13 +131,13 @@ If '-' is passed, output to stdount.
]] ]]
assert.equal([[ assert.equal([[
Usage: foo [-o <output>] [-h] Usage: foo [-h] [-o <output>]
Options: Options:
-h, --help Show this help message and exit.
-o <output>, Sets output file. -o <output>, Sets output file.
--output <output> If missing, 'a.out' is used by default. --output <output> If missing, 'a.out' is used by default.
If '-' is passed, output to stdount. If '-' is passed, output to stdount.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("handles description with less lines than usage", function() it("handles description with less lines than usage", function()
@@ -147,12 +147,12 @@ Options:
:description "Sets output file." :description "Sets output file."
assert.equal([[ assert.equal([[
Usage: foo [-o <output>] [-h] Usage: foo [-h] [-o <output>]
Options: Options:
-h, --help Show this help message and exit.
-o <output>, Sets output file. -o <output>, Sets output file.
--output <output> --output <output>]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("handles very long argument lists", function() it("handles very long argument lists", function()
@@ -167,10 +167,10 @@ Options:
Usage: foo [-h] [-t <foo> <bar> <baz> ...] Usage: foo [-h] [-t <foo> <bar> <baz> ...]
Options: Options:
-h, --help Show this help message and exit.
-t <foo> <bar> <baz> ..., -t <foo> <bar> <baz> ...,
--at-least-three <foo> <bar> <baz> ... --at-least-three <foo> <bar> <baz> ...
Sometimes argument lists are really long. Sometimes argument lists are really long.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("shows default values", function() it("shows default values", function()
@@ -182,12 +182,12 @@ Options:
:description "Port." :description "Port."
assert.equal([[ assert.equal([[
Usage: foo [-o <o>] [-p <p>] [-h] Usage: foo [-h] [-o <o>] [-p <p>]
Options: Options:
-h, --help Show this help message and exit.
-o <o> default: a.out -o <o> default: a.out
-p <p> Port. (default: 8080) -p <p> Port. (default: 8080)]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("does not show default value when show_default == false", function() it("does not show default value when show_default == false", function()
@@ -201,12 +201,12 @@ Options:
:description "Port." :description "Port."
assert.equal([[ assert.equal([[
Usage: foo [-o <o>] [-p <p>] [-h] Usage: foo [-h] [-o <o>] [-p <p>]
Options: Options:
-h, --help Show this help message and exit.
-o <o> -o <o>
-p <p> Port. -p <p> Port.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("creates correct help message for commands", function() it("creates correct help message for commands", function()
@@ -217,11 +217,11 @@ Options:
run:option "--where" run:option "--where"
assert.equal([[ assert.equal([[
Usage: foo [-q] [-h] <command> ... Usage: foo [-h] [-q] <command> ...
Options: Options:
-q, --quiet
-h, --help Show this help message and exit. -h, --help Show this help message and exit.
-q, --quiet
Commands: Commands:
run Run! ]], parser:get_help()) run Run! ]], parser:get_help())
@@ -234,11 +234,11 @@ Commands:
run:option "--where" run:option "--where"
assert.equal([[ assert.equal([[
Usage: foo run [--where <where>] [-h] Usage: foo run [-h] [--where <where>]
Options: Options:
--where <where> -h, --help Show this help message and exit.
-h, --help Show this help message and exit.]], run:get_help()) --where <where>]], run:get_help())
end) end)
it("uses message provided by user", function() it("uses message provided by user", function()
@@ -265,14 +265,14 @@ I don't like your format of help messages]], parser:get_help())
:hidden(true) :hidden(true)
assert.equal([[ assert.equal([[
Usage: foo [--feature] [-h] <normal> <command> ... Usage: foo [-h] [--feature] <normal> <command> ...
Arguments: Arguments:
normal normal
Options: Options:
--feature
-h, --help Show this help message and exit. -h, --help Show this help message and exit.
--feature
Commands: Commands:
good good
@@ -359,8 +359,8 @@ Options for setting style:
parser:command "another-command" parser:command "another-command"
assert.equal([[ assert.equal([[
Usage: foo [--use-default-args] [--something] [--test <test>] Usage: foo [-h] [--use-default-args] [--something] [--test <test>]
[--version] [-h] <foo> <bar> [<optional>] <command> ... [--version] <foo> <bar> [<optional>] <command> ...
Main arguments: Main arguments:
foo foo
@@ -375,8 +375,8 @@ Main options:
--test <test> --test <test>
Other options: Other options:
--version
-h, --help Show this help message and exit. -h, --help Show this help message and exit.
--version
Some commands: Some commands:
foo foo
@@ -404,7 +404,7 @@ Because it has lots of complex behaviour.
That needs documenting.]] That needs documenting.]]
assert.equal([[ assert.equal([[
Usage: foo [-p] [-f <foo>] [-h] <arg1> <arg2> Usage: foo [-h] [-p] [-f <foo>] <arg1> <arg2>
Arguments: Arguments:
@@ -414,13 +414,13 @@ Arguments:
Options: Options:
-h, --help Show this help message and exit.
-p This is a thing. -p This is a thing.
-f <foo>, And this things uses many lines. -f <foo>, And this things uses many lines.
--foo <foo> Because it has lots of complex behaviour. --foo <foo> Because it has lots of complex behaviour.
That needs documenting. That needs documenting.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("inherits help_vertical_space in commands", function() it("inherits help_vertical_space in commands", function()
@@ -439,29 +439,29 @@ Options:
cmd2:flag("-d", "Do d thing.") cmd2:flag("-d", "Do d thing.")
assert.equal([[ assert.equal([[
Usage: foo cmd1 [-a] [-b] [-h] Usage: foo cmd1 [-h] [-a] [-b]
Options: Options:
-h, --help Show this help message and exit.
-a Do a thing. -a Do a thing.
-b Do b thing. -b Do b thing.]], cmd1:get_help())
-h, --help Show this help message and exit.]], cmd1:get_help())
assert.equal([[ assert.equal([[
Usage: foo cmd2 [-c] [-d] [-h] Usage: foo cmd2 [-h] [-c] [-d]
Options: Options:
-h, --help Show this help message and exit.
-c Do c thing. -c Do c thing.
-d Do d thing. -d Do d thing.]], cmd2:get_help())
-h, --help Show this help message and exit.]], cmd2:get_help())
end) end)
it("allows configuring margins using help_usage_margin and help_description_margin", function() it("allows configuring margins using help_usage_margin and help_description_margin", function()
@@ -483,18 +483,18 @@ Because it has lots of complex behaviour.
That needs documenting.]] That needs documenting.]]
assert.equal([[ assert.equal([[
Usage: foo [-p] [-f <foo>] [-h] <arg1> <arg2> Usage: foo [-h] [-p] [-f <foo>] <arg1> <arg2>
Arguments: Arguments:
arg1 Argument number one. arg1 Argument number one.
arg2 Argument number two. arg2 Argument number two.
Options: Options:
-h, --help Show this help message and exit.
-p This is a thing. -p This is a thing.
-f <foo>, And this things uses many lines. -f <foo>, And this things uses many lines.
--foo <foo> Because it has lots of complex behaviour. --foo <foo> Because it has lots of complex behaviour.
That needs documenting. That needs documenting.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
describe("autowrap", function() describe("autowrap", function()
@@ -512,9 +512,10 @@ Options:
:description "See above." :description "See above."
assert.equal([[ assert.equal([[
Usage: foo [-f <foo>] [-b <bar>] [-h] Usage: foo [-h] [-f <foo>] [-b <bar>]
Options: Options:
-h, --help Show this help message and exit.
-f <foo>, Lorem ipsum dolor sit amet, consectetur adipiscing -f <foo>, Lorem ipsum dolor sit amet, consectetur adipiscing
--foo <foo> elit, sed do eiusmod tempor incididunt ut labore et --foo <foo> elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis dolore magna aliqua. Ut enim ad minim veniam, quis
@@ -525,8 +526,7 @@ Options:
cupidatat non proident, sunt in culpa qui officia cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum. deserunt mollit anim id est laborum.
-b <bar>, See above. -b <bar>, See above.
--bar <bar> --bar <bar>]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("preserves existing line breaks", function() it("preserves existing line breaks", function()
@@ -542,16 +542,16 @@ Another one.
parser:option "-b --bar" parser:option "-b --bar"
assert.equal([[ assert.equal([[
Usage: foo [-f <foo>] [-b <bar>] [-h] Usage: foo [-h] [-f <foo>] [-b <bar>]
Options: Options:
-h, --help Show this help message and exit.
-f <foo>, This is a long line, it should be broken down into -f <foo>, This is a long line, it should be broken down into
--foo <foo> several lines. It just keeps going and going. --foo <foo> several lines. It just keeps going and going.
This should always be a new line. This should always be a new line.
Another one. Another one.
-b <bar>, -b <bar>,
--bar <bar> --bar <bar>]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("preserves indentation", function() it("preserves indentation", function()
@@ -565,17 +565,17 @@ Options:
" That was an empty line there, preserve it.") " That was an empty line there, preserve it.")
assert.equal([[ assert.equal([[
Usage: foo [-f <foo>] [-h] Usage: foo [-h] [-f <foo>]
Options: Options:
-h, --help Show this help message and exit.
-f <foo>, This is a long line, it should be broken down into -f <foo>, This is a long line, it should be broken down into
--foo <foo> several lines. --foo <foo> several lines.
This paragraph is indented with three spaces, so This paragraph is indented with three spaces, so
when it gets broken down into several lines, they when it gets broken down into several lines, they
will be, too. will be, too.
That was an empty line there, preserve it. That was an empty line there, preserve it.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("preserves indentation of list items", function() it("preserves indentation of list items", function()
@@ -591,9 +591,10 @@ Options:
assert.equal([[ assert.equal([[
Usage: foo [-f <foo>] [-h] Usage: foo [-h] [-f <foo>]
Options: Options:
-h, --help Show this help message and exit.
-f <foo>, Let's start a list: -f <foo>, Let's start a list:
--foo <foo> --foo <foo>
* Here is a list item. * Here is a list item.
@@ -602,8 +603,7 @@ Options:
+ Here is a nested list item. Word. Word. Word. Word. + Here is a nested list item. Word. Word. Word. Word.
Word. Bird. Word. Bird. Bird. Bird. Word. Bird. Word. Bird. Bird. Bird.
* Back to normal list, this one uses several spaces * Back to normal list, this one uses several spaces
after the list item mark. Bird. Bird. Bird. after the list item mark. Bird. Bird. Bird.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("preserves multiple spaces between words", function() it("preserves multiple spaces between words", function()
@@ -614,12 +614,12 @@ Options:
:description("This is a long line with two spaces between words, it should be broken down.") :description("This is a long line with two spaces between words, it should be broken down.")
assert.equal([[ assert.equal([[
Usage: foo [-f <foo>] [-h] Usage: foo [-h] [-f <foo>]
Options: Options:
-h, --help Show this help message and exit.
-f <foo>, This is a long line with two spaces between -f <foo>, This is a long line with two spaces between
--foo <foo> words, it should be broken down. --foo <foo> words, it should be broken down.]], parser:get_help())
-h, --help Show this help message and exit.]], parser:get_help())
end) end)
it("autowraps description and epilog", function() it("autowraps description and epilog", function()

View File

@@ -18,7 +18,7 @@ describe("tests related to CLI behaviour #unsafe", function()
describe("error messages", function() describe("error messages", function()
it("generates correct error message without arguments", function() it("generates correct error message without arguments", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
Error: missing argument 'input' Error: missing argument 'input'
]], get_output("")) ]], get_output(""))
@@ -26,7 +26,7 @@ Error: missing argument 'input'
it("generates correct error message with too many arguments", function() it("generates correct error message with too many arguments", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
Error: unknown command 'bar' Error: unknown command 'bar'
]], get_output("foo bar")) ]], get_output("foo bar"))
@@ -34,7 +34,7 @@ Error: unknown command 'bar'
it("generates correct error message with unexpected argument", function() it("generates correct error message with unexpected argument", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
Error: option '--verbose' does not take arguments Error: option '--verbose' does not take arguments
]], get_output("--verbose=true")) ]], get_output("--verbose=true"))
@@ -42,7 +42,7 @@ Error: option '--verbose' does not take arguments
it("generates correct error message with unexpected option", function() it("generates correct error message with unexpected option", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
Error: unknown option '-q' Error: unknown option '-q'
Did you mean one of these: '-h' '-v'? Did you mean one of these: '-h' '-v'?
@@ -51,7 +51,7 @@ Did you mean one of these: '-h' '-v'?
it("generates correct error message and tip with unexpected command", function() it("generates correct error message and tip with unexpected command", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
Error: unknown command 'nstall' Error: unknown command 'nstall'
Did you mean 'install'? Did you mean 'install'?
@@ -60,7 +60,7 @@ Did you mean 'install'?
it("generates correct error message without arguments in command", function() it("generates correct error message without arguments in command", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ install [-f <from>] [-h] <rock> [<version>] Usage: ]]..script..[[ install [-h] [-f <from>] <rock> [<version>]
Error: missing argument 'rock' Error: missing argument 'rock'
]], get_output("foo install")) ]], get_output("foo install"))
@@ -68,7 +68,7 @@ Error: missing argument 'rock'
it("generates correct error message and tip in command", function() it("generates correct error message and tip in command", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ install [-f <from>] [-h] <rock> [<version>] Usage: ]]..script..[[ install [-h] [-f <from>] <rock> [<version>]
Error: unknown option '--form' Error: unknown option '--form'
Did you mean '--from'? Did you mean '--from'?
@@ -77,9 +77,9 @@ Did you mean '--from'?
end) end)
describe("help messages", function() describe("help messages", function()
it("generates correct help message", function() it("generates correct help message using help flag", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ [-v] [-h] <input> [<command>] ... Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
A testing program. A testing program.
@@ -87,17 +87,37 @@ Arguments:
input input
Options: Options:
-v, --verbose Sets verbosity level.
-h, --help Show this help message and exit. -h, --help Show this help message and exit.
-v, --verbose Sets verbosity level.
Commands: Commands:
help Show help for commands.
install Install a rock. install Install a rock.
]], get_output("--help")) ]], get_output("--help"))
end) end)
it("generates correct help message for command", function() it("generates correct help message using help command", function()
assert.equal([[ assert.equal([[
Usage: ]]..script..[[ install [-f <from>] [-h] <rock> [<version>] Usage: ]]..script..[[ [-h] [-v] <input> [<command>] ...
A testing program.
Arguments:
input
Options:
-h, --help Show this help message and exit.
-v, --verbose Sets verbosity level.
Commands:
help Show help for commands.
install Install a rock.
]], get_output("foo help"))
end)
it("generates correct help message for command using help flag", function()
assert.equal([[
Usage: ]]..script..[[ install [-h] [-f <from>] <rock> [<version>]
Install a rock. Install a rock.
@@ -106,11 +126,28 @@ Arguments:
version Version of the rock. version Version of the rock.
Options: Options:
-h, --help Show this help message and exit.
-f <from>, Fetch the rock from this server. -f <from>, Fetch the rock from this server.
--from <from> --from <from>
-h, --help Show this help message and exit.
]], get_output("foo install --help")) ]], get_output("foo install --help"))
end) end)
it("generates correct help message for command using help command", function()
assert.equal([[
Usage: ]]..script..[[ install [-h] [-f <from>] <rock> [<version>]
Install a rock.
Arguments:
rock Name of the rock.
version Version of the rock.
Options:
-h, --help Show this help message and exit.
-f <from>, Fetch the rock from this server.
--from <from>
]], get_output("foo help install"))
end)
end) end)
describe("data flow", function() describe("data flow", function()

View File

@@ -3,6 +3,7 @@ local Parser = require "argparse"
local parser = Parser() local parser = Parser()
:description "A testing program." :description "A testing program."
:add_help_command(true)
:require_command(false) :require_command(false)
parser:argument "input" parser:argument "input"

View File

@@ -320,7 +320,7 @@ Usage: foo ([--opt1 <opt1>] | [--opt3 <opt3>] | [--opt5 <opt5>])
:args "*" :args "*"
assert.equals([=[ assert.equals([=[
Usage: foo [--set-important-property <set_important_property>] [-h] Usage: foo [-h] [--set-important-property <set_important_property>]
<long_argument_name> <very_long_words> [--include [<include>] ...]]=], parser:get_usage()) <long_argument_name> <very_long_words> [--include [<include>] ...]]=], parser:get_usage())
end) end)
@@ -336,9 +336,9 @@ Usage: foo [--set-important-property <set_important_property>] [-h]
:args "*" :args "*"
assert.equals([=[ assert.equals([=[
Usage: foo Usage: foo [-h]
[--set-important-property <set_important_property>] [--set-important-property <set_important_property>]
[-h] <long_argument_name> <very_long_words> <long_argument_name> <very_long_words>
[--include [<include>] ...]]=], parser:get_usage()) [--include [<include>] ...]]=], parser:get_usage())
end) end)
end) end)

View File

@@ -202,9 +202,9 @@ end}
local add_help = {"add_help", function(self, value) local add_help = {"add_help", function(self, value)
typecheck("add_help", {"boolean", "string", "table"}, value) typecheck("add_help", {"boolean", "string", "table"}, value)
if self._has_help then if self._help_option_idx then
table.remove(self._options) table.remove(self._options, self._help_option_idx)
self._has_help = false self._help_option_idx = nil
end end
if value then if value then
@@ -223,7 +223,50 @@ local add_help = {"add_help", function(self, value)
help "-h" "--help" help "-h" "--help"
end end
self._has_help = true self._help_option_idx = #self._options
end
end}
local add_help_command = {"add_help_command", function(self, value)
typecheck("add_help_command", {"boolean", "string", "table"}, value)
if self._help_command_idx then
table.remove(self._commands, self._help_command_idx)
self._help_command_idx = nil
end
if value then
local help = self:command()
:description "Show help for commands."
help:argument "command"
:description "The command to show help for."
:args "?"
:action(function(_, _, cmd)
if not cmd then
print(self:get_help())
os.exit(0)
else
for _, command in ipairs(self._commands) do
for _, alias in ipairs(command._aliases) do
if alias == cmd then
print(command:get_help())
os.exit(0)
end
end
end
end
help:error(("unknown command '%s'"):format(cmd))
end)
if value ~= true then
help = help(value)
end
if not help._name then
help "help"
end
self._help_command_idx = #self._commands
end end
end} end}
@@ -252,7 +295,8 @@ local Parser = class({
typechecked("help_usage_margin", "number"), typechecked("help_usage_margin", "number"),
typechecked("help_description_margin", "number"), typechecked("help_description_margin", "number"),
typechecked("help_max_width", "number"), typechecked("help_max_width", "number"),
add_help add_help,
add_help_command
}) })
local Command = class({ local Command = class({
@@ -600,13 +644,7 @@ end
function Parser:option(...) function Parser:option(...)
local option = Option(...) local option = Option(...)
table.insert(self._options, option)
if self._has_help then
table.insert(self._options, #self._options, option)
else
table.insert(self._options, option)
end
return option return option
end end