-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support markdown in help strings #134
base: master
Are you sure you want to change the base?
Conversation
Thanks, looks good. I think it would make sense to also allow markdown in the description and the epilog. Also needs tests+docs. |
Yes, I agree this is not ready to merge — I just whipped it together in response to a question on discourse. I'm not sure I have the time to polish it myself, but I wanted to post it as a starting point for anyone who wants to work on this. |
Discourse questioner here! I have checked out stevengj's branch and am working on adding support for markdown description/epilog (which is where I would probably use it most IRL): https://github.com/maxkapur/ArgParse.jl/tree/sgj/markdown But it's not ready for review yet. Actually, the Stack trace
|
I think the simplest approach is just to convert Markdown into an ordinary string (with ANSI formatting escape codes) before subsequent processing, as in my example PR above. Maybe make it simpler with:
and just call this on any string-like argument that might be markdown. You can do something similar for the new StyledStrings stdlib. |
Thanks for the tip! I took the approach you suggested. I had make an alternative implementation for the following function, however, which is used to print the description and epilog blocks. It does some manual splitting and formatting that plays very poorly with Markdown. For example, if you just remove the type restriction on 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, width = width)
println_unnbsp(io, message_wrapped)
end
end
println(io)
end
end My alternative is simply: function show_message(io::IO, message::Markdown.MD, preformatted::Bool, width::Int)
if !isempty(message)
println(io, string_format(message))
println(io)
end
end This looks right for typical inputs (see my additions I added a (somewhat unsightly) test: @test stringhelp(s) == "usage: argparse_test15.jl [--opt1 OPT1] [--opt2 OPT2]\n\nTest 15 for \e[36margparse.jl\e[39m: Markdown strings. This description text uses\n various Markdown features such as \e[1mstrong text\e[22m, text with \e[4memphasis\e[24m, \e[36mcode\n snippets\e[39m, and math: \e[35my = mx + b\e[39m.\n\n\e[1m Subheading\e[22m\n\e[1m ==========\e[22m\n\n The following list should be numbered automatically:\n\n 1. Here\n\n 2. Is\n\n 3. A\n\n 4. List\n\noptional arguments:\n --opt1 OPT1 a flag with String helptext (type: Int64)\n --opt2 OPT2 a flag with ~~String~~ \e[1mmarkdown\e[22m helptext\n\nSee our website (https://example.com) for more information.\n\n" And updated the docs and docstrings where relevant. I held off on Let me know if this works and if I can submit a PR from my branch: https://github.com/maxkapur/ArgParse.jl/tree/sgj/markdown |
You should add an argument to |
OK, here's what that looks like (pushed to my branch): # src/common.jl
# ...
# Wrapper function to lower markdown strings to REPL representation
function string_format(s::Markdown.MD; width::Int)
context = IOContext(stdout, :color => true, :displaysize => (displaysize()[1], width))
lstrip(repr("text/plain", s; context=context))
end
string_format(s::AbstractString; width::Int) = TextWrap.wrap(s; width=width) Unfortunately, we have to construct our own repr("text/plain", s; context=(:color => true, :displaysize => (displaysize()[1], width))) is a I assume that the I'm also not in love with how everything after the first line of the description is getting lpadded by 2: This doesn't happen for plain string input. I'm still working out why this happens; I can't find any instances of Edit: Figured out the lpad thing, will return with update. |
I think it will ignore the Equivalently, you can do: buf = IOBuffer()
show(IOContext(buf, :color => true, :displaysize => (100, width)), "text/plain", s)
s = lstrip(String(take!(buf))) (I wouldn't use |
You would not believe how tricky the lpad issue is! Basically,
So, maybe what we really need is
Replacing
So, the problem is particular to having Maybe one could fix this with a suitably tortured regex, but I don't really want to advocate for that solution. In my branch, I've decided to just let the indent stay put for the description and epilog, which greatly simplifies function string_format(s::Markdown.MD; width::Int)
buf = IOBuffer()
context = IOContext(buf, :color => true, :displaysize => (100, width))
repr("text/plain", s; context=context)
# NOTE: repr() above automatically indents the string by 2 spaces. We can
# remove the indentation using Base.unindent() as follows, but the results
# are inconsistent with :color => true enabled when s contains subheadings,
# because repr() inserts the ANSI escape code enabling the bold font
# *before* the leading two spaces.
# ansi_escaped = repr("text/plain", s; context=context)
# unindented = Base.unindent(ansi_escaped, 2)
end For the help text (short descriptions of each option), I've used the What do you think about this solution? |
Draft PR to support Markdown text in help strings.