parser: automatically generate usage banners
This commit is contained in:
parent
893ec3284f
commit
b7977244ea
@ -122,6 +122,8 @@ module Homebrew
|
||||
|
||||
@args = Homebrew::CLI::Args.new
|
||||
|
||||
@command_name = caller_locations(2, 1).first.label.chomp("_args")
|
||||
|
||||
@constraints = []
|
||||
@conflicts = []
|
||||
@switch_sources = {}
|
||||
@ -129,6 +131,8 @@ module Homebrew
|
||||
@named_args_type = nil
|
||||
@max_named_args = nil
|
||||
@min_named_args = nil
|
||||
@description = nil
|
||||
@usage_banner = nil
|
||||
@hide_from_man_page = false
|
||||
@formula_options = false
|
||||
|
||||
@ -137,6 +141,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
instance_eval(&block) if block
|
||||
|
||||
generate_banner
|
||||
end
|
||||
|
||||
def switch(*names, description: nil, replacement: nil, env: nil, required_for: nil, depends_on: nil,
|
||||
@ -176,8 +182,12 @@ module Homebrew
|
||||
Homebrew::EnvConfig.try(:"#{env}?")
|
||||
end
|
||||
|
||||
def description(text)
|
||||
@description = text.chomp
|
||||
end
|
||||
|
||||
def usage_banner(text)
|
||||
@parser.banner = "#{text}\n"
|
||||
@usage_banner, @description = text.chomp.split("\n\n", 2)
|
||||
end
|
||||
|
||||
def usage_banner_text
|
||||
@ -432,6 +442,70 @@ module Homebrew
|
||||
|
||||
private
|
||||
|
||||
SYMBOL_TO_USAGE_MAPPING = {
|
||||
text_or_regex: "<text>|`/`<regex>`/`",
|
||||
url: "<URL>",
|
||||
}.freeze
|
||||
|
||||
def generate_usage_banner
|
||||
command_names = ["`#{@command_name.tr("_", "-")}`"]
|
||||
aliases_to_skip = %w[instal uninstal]
|
||||
command_names += Commands::HOMEBREW_INTERNAL_COMMAND_ALIASES.map do |command_alias, command|
|
||||
next if aliases_to_skip.include? command_alias
|
||||
|
||||
"`#{command_alias.tr("_", "-")}`" if command == @command_name
|
||||
end.compact.sort
|
||||
|
||||
options = if @processed_options.any?
|
||||
" [<options>]"
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
named_args = ""
|
||||
if @named_args_type.present? && @named_args_type != :none
|
||||
arg_type = if @named_args_type.is_a? Array
|
||||
types = @named_args_type.map do |type|
|
||||
next unless type.is_a? Symbol
|
||||
next SYMBOL_TO_USAGE_MAPPING[type] if SYMBOL_TO_USAGE_MAPPING.key?(type)
|
||||
|
||||
"<#{type}>"
|
||||
end.compact
|
||||
types << "<subcommand>" if @named_args_type.any? { |type| type.is_a? String }
|
||||
types.join("|")
|
||||
elsif SYMBOL_TO_USAGE_MAPPING.key? @named_args_type
|
||||
SYMBOL_TO_USAGE_MAPPING[@named_args_type]
|
||||
else
|
||||
"<#{@named_args_type}>"
|
||||
end
|
||||
|
||||
named_args = if @min_named_args.blank? && @max_named_args == 1
|
||||
" [#{arg_type}]"
|
||||
elsif @min_named_args.blank?
|
||||
" [#{arg_type} ...]"
|
||||
elsif @min_named_args == 1 && @max_named_args == 1
|
||||
" #{arg_type}"
|
||||
elsif @min_named_args == 1
|
||||
" #{arg_type} [...]"
|
||||
else
|
||||
" #{arg_type} ..."
|
||||
end
|
||||
end
|
||||
|
||||
"#{command_names.join(", ")}#{options}#{named_args}"
|
||||
end
|
||||
|
||||
def generate_banner
|
||||
@usage_banner ||= generate_usage_banner
|
||||
|
||||
@parser.banner = <<~BANNER
|
||||
#{@usage_banner}
|
||||
|
||||
#{@description}
|
||||
|
||||
BANNER
|
||||
end
|
||||
|
||||
def set_switch(*names, value:, from:)
|
||||
names.each do |name|
|
||||
@switch_sources[option_to_name(name)] = from
|
||||
|
||||
@ -318,6 +318,90 @@ describe Homebrew::CLI::Parser do
|
||||
end
|
||||
end
|
||||
|
||||
describe "usage banner generation" do
|
||||
it "includes `[options]` if options are available" do
|
||||
parser = described_class.new do
|
||||
switch "--foo"
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\]/)
|
||||
end
|
||||
|
||||
it "doesn't include `[options]` if options are available" do
|
||||
allow(described_class).to receive(:global_options).and_return([])
|
||||
parser = described_class.new
|
||||
expect(parser.generate_help_text).not_to match(/\[options\]/)
|
||||
end
|
||||
|
||||
it "includes a description" do
|
||||
parser = described_class.new do
|
||||
description <<~EOS
|
||||
This command does something
|
||||
EOS
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/This command does something/)
|
||||
end
|
||||
|
||||
it "allows the usage banner to be overriden" do
|
||||
parser = described_class.new do
|
||||
usage_banner "`test` [foo] <bar>"
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/test \[foo\] bar/)
|
||||
end
|
||||
|
||||
it "allows a usage banner and a description to be overriden" do
|
||||
parser = described_class.new do
|
||||
usage_banner "`test` [foo] <bar>"
|
||||
description <<~EOS
|
||||
This command does something
|
||||
EOS
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/test \[foo\] bar/)
|
||||
expect(parser.generate_help_text).to match(/This command does something/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for no named argument" do
|
||||
parser = described_class.new do
|
||||
named_args :none
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\]\n/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for a single typed argument" do
|
||||
parser = described_class.new do
|
||||
named_args :formula, number: 1
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\] formula\n/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for a subcommand argument with a maximum" do
|
||||
parser = described_class.new do
|
||||
named_args %w[off on], max: 1
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\] \[subcommand\]\n/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for multiple typed argument with no maximum or minimum" do
|
||||
parser = described_class.new do
|
||||
named_args [:tap, :command]
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\] \[tap|command ...\]\n/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for a subcommand argument with a minimum of 1" do
|
||||
parser = described_class.new do
|
||||
named_args :installed_formula, min: 1
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\] installed_formula \[...\]\n/)
|
||||
end
|
||||
|
||||
it "shows the correct usage for a subcommand argument with a minimum greater than 1" do
|
||||
parser = described_class.new do
|
||||
named_args :installed_formula, min: 2
|
||||
end
|
||||
expect(parser.generate_help_text).to match(/\[options\] installed_formula ...\n/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "named_args" do
|
||||
let(:parser_none) {
|
||||
described_class.new do
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user