crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Customizable Indentation in OptionParser Help Messages

Open kojix2 opened this issue 1 year ago • 3 comments

Hi!

I am using Crystal to create a command line tool for myself and I've been working with OptionParser. It would be great to have more flexibility in customizing the help messages.

Here's an example:

require "option_parser"

struct Point
  property x : Float64 = 0
  property y : Float64 = 0
end

point = Point.new

OptionParser.parse do |parser|
  parser.on("-x FLOAT", "x-coordinate") { |x| point.x = x.to_f64 }
  parser.on("-y FLOAT", "y-coordinate") { |y| point.y = y.to_f64 }
  parser.on("-h", "--help", "Prints this help") { puts parser; exit }
end

p point

When I use --help, it displays as follows:

    -x FLOAT                         x-coordinate
    -y FLOAT                         y-coordinate
    -h, --help                       Prints this help
              |||||| too wide |||||||

The space between the flag and its description is excessively long.

https://github.com/crystal-lang/crystal/blob/866d51d42bdd25839b0c0010fb96948fd795c2d9/src/option_parser.cr#L319-L327

As per my understanding, the indentation is hardcoded to 33 spaces. An alternative could be reducing it to 14, for instance.

class OptionParser
  private def append_flag(flag, description)
    indent = " " * 37
    description = description.gsub("\n", "\n#{indent}")
    if flag.size >= 14
      @flags << "    #{flag}\n#{indent}#{description}"
    else
      @flags << "    #{flag}#{" " * (14 - flag.size)}#{description}"
    end
  end
end

With this change, --help would display as:

    -x FLOAT      x-coordinate
    -y FLOAT      y-coordinate
    -h, --help    Prints this help

Crystal supports open classes, but having a more configurable option would be even better.

kojix2 avatar Jan 01 '24 09:01 kojix2

The magic indent: 37

We could increase this to more magic 42!

rubyFeedback avatar Jan 23 '24 21:01 rubyFeedback

I'm not sure what exactly lead to this number. But it might be some sort of convention. Or just grown out of experience. And it typically works pretty well. I don't see much point in changing it. But I suppose it would be acceptable to offer the possibility for changing it via a class property.

straight-shoota avatar Jan 23 '24 22:01 straight-shoota

Hi, Thanks for taking a look at this issue.

Ruby provides two options: width and indent.

These options can be specified as keyword arguments when initializing.

OptionParser#new(banner = nil, width = 32, indent = ' ' * 4)

You can also change it later by calling methods such as summary_width

require "optparse"

opts = OptionParser.new do |opts|
  opts.on_head("-i", "--init")
  opts.on("-u", "--update")
  opts.on_tail("-h", "--help")
end

opts.summary_width     # => 32
opts.summarize
# => ["    -i, --init\n", "    -u, --update\n", "    -h, --help\n"]
opts.summary_width = 8
opts.summary_width     # =>8
opts.summarize
# => ["    -i\n", "        --init\n", "    -u\n", "        --update\n", "    -h\n", "        --help\n"]

or summary_indent.


require "optparse"

opts = OptionParser.new do |opts|
  opts.on_head("-i", "--init")
  opts.on("-u", "--update")
  opts.on_tail("-h", "--help")
end

opts.summary_indent         # => "    "
opts.summarize
# => ["    -i, --init\n", "    -u, --update\n", "    -h, --help\n"]
opts.summary_indent = "  "
opts.summary_indent         # => "  "
opts.summarize
# => ["  -i, --init\n", "  -u, --update\n", "  -h, --help\n"]

I created a ruby command line tool called YouPlot, where I used summary_width. Crystal does not need to behave the same as the Ruby language, but I am sure that some option is needed.

Other language option parsers should have similar settings, but I am not familiar with their APIs. I feel it is important to look at other languages before rushing to make them the same as Ruby. I would appreciate it if someone could look into this.

kojix2 avatar Jan 24 '24 05:01 kojix2