Skip to content

Commit

Permalink
Add help_width and help_alignment_width settings (#132)
Browse files Browse the repository at this point in the history
Allow to control the auto-generated help text formatting

Fix #129
  • Loading branch information
carlobaldassi authored May 6, 2024
1 parent 7deac9b commit ab9d3cf
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 14 deletions.
24 changes: 13 additions & 11 deletions src/parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function usage_string(settings::ArgParseSettings)

usage_pre = "usage: " * (isempty(settings.prog) ? "<PROGRAM>" : settings.prog)

lc_len_limit = 24
lc_len_limit = settings.help_alignment_width

cmd_lst = String[]
pos_lst = String[]
Expand Down Expand Up @@ -238,7 +238,8 @@ function usage_string(settings::ArgParseSettings)

str_nonwrapped = usage_pre * excl_str * optl_str * posl_str * cmdl_str
str_wrapped = TextWrap.wrap(str_nonwrapped, break_long_words = false, break_on_hyphens = false,
subsequent_indent = min(usage_len, lc_len_limit))
subsequent_indent = min(usage_len, lc_len_limit),
width = settings.help_width)


out_str = replace(str_wrapped, nbspc => ' ')
Expand Down Expand Up @@ -282,7 +283,8 @@ function gen_help_text(arg::ArgParseField, settings::ArgParseSettings)
end

function print_group(io::IO, lst::Vector, desc::AbstractString, lc_usable_len::Int, lc_len::Int,
lmargin::AbstractString, rmargin::AbstractString, sindent::AbstractString)
lmargin::AbstractString, rmargin::AbstractString, sindent::AbstractString,
width::Int)
isempty(lst) && return
println(io, desc, ":")
for l in lst
Expand All @@ -291,12 +293,12 @@ function print_group(io::IO, lst::Vector, desc::AbstractString, lc_usable_len::I
rfill = " "^(lc_len - l1len)
ll_nonwrapped = l[1] * rfill * rmargin * l[2]
ll_wrapped = TextWrap.wrap(ll_nonwrapped, break_long_words = false, break_on_hyphens = false,
initial_indent = lmargin, subsequent_indent = sindent)
initial_indent = lmargin, subsequent_indent = sindent, width = width)
println_unnbsp(io, ll_wrapped)
else
println_unnbsp(io, lmargin, l[1])
l2_wrapped = TextWrap.wrap(l[2], break_long_words = false, break_on_hyphens = false,
initial_indent = sindent, subsequent_indent = sindent)
initial_indent = sindent, subsequent_indent = sindent, width = width)
println_unnbsp(io, l2_wrapped)
end
end
Expand All @@ -307,7 +309,7 @@ show_help(settings::ArgParseSettings; kw...) = show_help(stdout, settings; kw...

function show_help(io::IO, settings::ArgParseSettings; exit_when_done = !isinteractive())

lc_len_limit = 24
lc_len_limit = settings.help_alignment_width
lc_left_indent = 2
lc_right_margin = 2

Expand Down Expand Up @@ -366,25 +368,25 @@ function show_help(io::IO, settings::ArgParseSettings; exit_when_done = !isinter

println(io, usage_str)
println(io)
show_message(io, settings.description, settings.preformatted_description)
show_message(io, settings.description, settings.preformatted_description, settings.help_width)

for ag in settings.args_groups
print_group(io, group_lists[ag.name], ag.desc, lc_usable_len, lc_len,
lmargin, rmargin, sindent)
lmargin, rmargin, sindent, settings.help_width)
end

show_message(io, settings.epilog, settings.preformatted_epilog)
show_message(io, settings.epilog, settings.preformatted_epilog, settings.help_width)
exit_when_done && exit(0)
return
end

function show_message(io::IO, message::AbstractString, preformatted::Bool)
function show_message(io::IO, message::AbstractString, preformatted::Bool, width::Int)
if !isempty(message)
if preformatted
print(io, message)
else
for l in split(message, "\n\n")
message_wrapped = TextWrap.wrap(l, break_long_words = false, break_on_hyphens = false)
message_wrapped = TextWrap.wrap(l, break_long_words = false, break_on_hyphens = false, width = width)
println_unnbsp(io, message_wrapped)
end
end
Expand Down
15 changes: 13 additions & 2 deletions src/settings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ This is the list of general settings currently available:
action) is added to the argument table.
* `add_version` (default = `false`): if `true`, a `--version` option (triggering the `:show_version`
action) is added to the argument table.
* `help_width` (default = `70`): set the width of the help text. This does not affect the
`usage` line if it is provided by the user rather than being auto-generated; it also does not
affect the `description/epilog` lines if the `preformatted_description/epilog` options are set to
`false`.
* `help_alignment_width` (default = `24`): set the maximum width of the left column of the help
text, where options and arguments names are listed. This also affects the indentation of the usage
line when it is auto-generated. It must be ≥ 4 and should be less than `help_width`.
* `fromfile_prefix_chars` (default = `Set{Char}()`): an argument beginning with one of these
characters will specify a file from which arguments will be read, one argument read per line.
Alphanumeric characters and the hyphen-minus (`'-'`) are prohibited.
Expand Down Expand Up @@ -246,6 +253,8 @@ mutable struct ArgParseSettings
version::AbstractString
add_help::Bool
add_version::Bool
help_width::Int
help_alignment_width::Int
fromfile_prefix_chars::Set{Char}
autofix_names::Bool
error_on_conflict::Bool
Expand All @@ -269,6 +278,8 @@ mutable struct ArgParseSettings
version::AbstractString = "Unspecified version",
add_help::Bool = true,
add_version::Bool = false,
help_width::Integer = 70,
help_alignment_width::Integer = 24,
fromfile_prefix_chars = Set{Char}(),
autofix_names::Bool = false,
error_on_conflict::Bool = true,
Expand All @@ -283,8 +294,8 @@ mutable struct ArgParseSettings
fromfile_prefix_chars = check_prefix_chars(fromfile_prefix_chars)
return new(
prog, description, epilog, usage, version, add_help, add_version,
fromfile_prefix_chars, autofix_names, error_on_conflict,
suppress_warnings, allow_ambiguous_opts, commands_are_required,
help_width, help_alignment_width, fromfile_prefix_chars, autofix_names,
error_on_conflict, suppress_warnings, allow_ambiguous_opts, commands_are_required,
copy(std_groups), "", ArgParseTable(), exc_handler,
preformatted_description, preformatted_epilog,
exit_after_help
Expand Down
182 changes: 182 additions & 0 deletions test/argparse_test13.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# test 13: help_width and help_alignment_width settings

@testset "test 13" begin

function ap_settings13()

s = ArgParseSettings(description = "Test 13 for ArgParse.jl. This description is made unnecessarily long for the sake of testing the help_text_width setting.",
epilog = "This epilog is also made unnecessarily long for the same reason as the description, i.e., testing the help_text_width setting.",
exc_handler = ArgParse.debug_handler)

@add_arg_table! s begin
"--option1"
arg_type = Int
default = 0
help = "an option, not used for much really, " *
"indeed it is not actually used for anything. " *
"That is why its name is so undescriptive."
"-O", "--long-option"
arg_type = Symbol
default = :xyz
help = "another option, this time it has a fancy name " *
"and yet it is still completely useless."
"arg1"
help = "first argument"
required = true
"arg2"
default = "no arg2 given"
help = "second argument"
end

return s
end

let s = ap_settings13()
@test stringhelp(s) == """
usage: $(basename(Base.source_path())) [--option1 OPTION1] [-O LONG-OPTION] arg1
[arg2]
Test 13 for ArgParse.jl. This description is made unnecessarily long
for the sake of testing the help_text_width setting.
positional arguments:
arg1 first argument
arg2 second argument (default: "no arg2 given")
optional arguments:
--option1 OPTION1 an option, not used for much really, indeed it
is not actually used for anything. That is why
its name is so undescriptive. (type: $Int,
default: 0)
-O, --long-option LONG-OPTION
another option, this time it has a fancy name
and yet it is still completely useless. (type:
Symbol, default: :xyz)
This epilog is also made unnecessarily long for the same reason as the
description, i.e., testing the help_text_width setting.
"""

s.help_width = 120

@test stringhelp(s) == """
usage: $(basename(Base.source_path())) [--option1 OPTION1] [-O LONG-OPTION] arg1 [arg2]
Test 13 for ArgParse.jl. This description is made unnecessarily long for the sake of testing the help_text_width
setting.
positional arguments:
arg1 first argument
arg2 second argument (default: "no arg2 given")
optional arguments:
--option1 OPTION1 an option, not used for much really, indeed it is not actually used for anything. That is why
its name is so undescriptive. (type: $Int, default: 0)
-O, --long-option LONG-OPTION
another option, this time it has a fancy name and yet it is still completely useless. (type:
Symbol, default: :xyz)
This epilog is also made unnecessarily long for the same reason as the description, i.e., testing the help_text_width
setting.
"""

s.help_width = 50

@test stringhelp(s) == """
usage: $(basename(Base.source_path())) [--option1 OPTION1]
[-O LONG-OPTION] arg1
[arg2]
Test 13 for ArgParse.jl. This description is made
unnecessarily long for the sake of testing the
help_text_width setting.
positional arguments:
arg1 first argument
arg2 second argument (default:
"no arg2 given")
optional arguments:
--option1 OPTION1 an option, not used for
much really, indeed it is
not actually used for
anything. That is why its
name is so undescriptive.
(type: $Int, default: 0)
-O, --long-option LONG-OPTION
another option, this time
it has a fancy name and
yet it is still completely
useless. (type: Symbol,
default: :xyz)
This epilog is also made unnecessarily long for
the same reason as the description, i.e., testing
the help_text_width setting.
"""

s.help_width = 100
s.help_alignment_width = 50

@test stringhelp(s) == """
usage: $(basename(Base.source_path())) [--option1 OPTION1] [-O LONG-OPTION] arg1 [arg2]
Test 13 for ArgParse.jl. This description is made unnecessarily long for the sake of testing the
help_text_width setting.
positional arguments:
arg1 first argument
arg2 second argument (default: "no arg2 given")
optional arguments:
--option1 OPTION1 an option, not used for much really, indeed it is not actually used
for anything. That is why its name is so undescriptive. (type:
$Int, default: 0)
-O, --long-option LONG-OPTION another option, this time it has a fancy name and yet it is still
completely useless. (type: Symbol, default: :xyz)
This epilog is also made unnecessarily long for the same reason as the description, i.e., testing
the help_text_width setting.
"""

s.help_width = 50
s.help_alignment_width = 4

@test stringhelp(s) == """
usage: $(basename(Base.source_path())) [--option1 OPTION1]
[-O LONG-OPTION] arg1 [arg2]
Test 13 for ArgParse.jl. This description is made
unnecessarily long for the sake of testing the
help_text_width setting.
positional arguments:
arg1
first argument
arg2
second argument (default: "no arg2 given")
optional arguments:
--option1 OPTION1
an option, not used for much really, indeed it
is not actually used for anything. That is why
its name is so undescriptive. (type: $Int,
default: 0)
-O, --long-option LONG-OPTION
another option, this time it has a fancy name
and yet it is still completely useless. (type:
Symbol, default: :xyz)
This epilog is also made unnecessarily long for
the same reason as the description, i.e., testing
the help_text_width setting.
"""

end

end
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module ArgParseTests

include("common.jl")

for i = 1:12
for i = 1:13
try
s_i = lpad(string(i), 2, "0")
include("argparse_test$s_i.jl")
Expand Down

0 comments on commit ab9d3cf

Please sign in to comment.