summaryrefslogtreecommitdiff
path: root/lib/optparse.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/optparse.rb')
-rw-r--r--lib/optparse.rb3156
1 files changed, 1958 insertions, 1198 deletions
diff --git a/lib/optparse.rb b/lib/optparse.rb
index 50da8b329d..97178e284b 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -1,171 +1,529 @@
-# optparse library, not octopus.
-
-=begin
-= Summary
-Library for command line option analysis.
-
-features:
-(1) It is possible <option switch of a short form and a long form> to
- exist together. It is also possible in one to bring the switch of
- a short form together.
-(2) It is possible to write bringing specification and the handler of
- the switch together respectively in one place.
-(3) The argument of the switch is converted into the class which
- automatically specifies it.
-(4) The option summary can be made.
-(5) The option can be added on the way later.
-
-=end #'#"#`#
-# Not yet (;_;)
-=begin
-
-== Class tree
- * ((<OptionParser>)) front end
- * ((<OptionParser::Switch>)) each switches
- * ((<OptionParser::List>)) options list
- * ((<OptionParser::ParseError>)) errors on parsing
- * ((<OptionParser::AmbiguousOption>))
- * ((<OptionParser::NeedlessArgument>))
- * ((<OptionParser::MissingArgument>))
- * ((<OptionParser::InvalidOption>))
- * ((<OptionParser::InvalidArgument>))
- * ((<OptionParser::AmbiguousArgument>))
-
-== Object relations
- +--------------+
- | OptionParser |<>-----+
- +--------------+ | +--------+
- | ,-| Switch |
- on_head -------->+---------------+ / +--------+
- accept/reject -->| List |<|>-
- | |<|>- +----------+
- on ------------->+---------------+ `-| argument |
- : : | class |
- +---------------+ |==========|
- on_tail -------->| | |pattern |
- +---------------+ |----------|
- OptionParser.accept ->| DefaultList | |converter |
- reject |(shared between| +----------+
- | all instances)|
- +---------------+
-
-=end #'#"#`#
-
-=begin
-= Classes & Modules
-=end #'#"#`#
-
+# frozen_string_literal: true
+#
+# optparse.rb - command-line option analysis with the OptionParser class.
+#
+# Author:: Nobu Nakada
+# Documentation:: Nobu Nakada and Gavin Sinclair.
+#
+# See OptionParser for documentation.
+#
+require 'set' unless defined?(Set)
+
+#--
+# == Developer Documentation (not for RDoc output)
+#
+# === Class tree
+#
+# - OptionParser:: front end
+# - OptionParser::Switch:: each switches
+# - OptionParser::List:: options list
+# - OptionParser::ParseError:: errors on parsing
+# - OptionParser::AmbiguousOption
+# - OptionParser::NeedlessArgument
+# - OptionParser::MissingArgument
+# - OptionParser::InvalidOption
+# - OptionParser::InvalidArgument
+# - OptionParser::AmbiguousArgument
+#
+# === Object relationship diagram
+#
+# +--------------+
+# | OptionParser |<>-----+
+# +--------------+ | +--------+
+# | ,-| Switch |
+# on_head -------->+---------------+ / +--------+
+# accept/reject -->| List |<|>-
+# | |<|>- +----------+
+# on ------------->+---------------+ `-| argument |
+# : : | class |
+# +---------------+ |==========|
+# on_tail -------->| | |pattern |
+# +---------------+ |----------|
+# OptionParser.accept ->| DefaultList | |converter |
+# reject |(shared between| +----------+
+# | all instances)|
+# +---------------+
+#
+#++
+#
+# == OptionParser
+#
+# === New to +OptionParser+?
+#
+# See the {Tutorial}[optparse/tutorial.rdoc].
+#
+# === Introduction
+#
+# OptionParser is a class for command-line option analysis. It is much more
+# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
+# solution.
+#
+# === Features
+#
+# 1. The argument specification and the code to handle it are written in the
+# same place.
+# 2. It can output an option summary; you don't need to maintain this string
+# separately.
+# 3. Optional and mandatory arguments are specified very gracefully.
+# 4. Arguments can be automatically converted to a specified class.
+# 5. Arguments can be restricted to a certain set.
+#
+# All of these features are demonstrated in the examples below. See
+# #make_switch for full documentation.
+#
+# === Minimal example
+#
+# require 'optparse'
+#
+# options = {}
+# OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# options[:verbose] = v
+# end
+# end.parse!
+#
+# p options
+# p ARGV
+#
+# === Generating Help
+#
+# OptionParser can be used to automatically generate help for the commands you
+# write:
+#
+# require 'optparse'
+#
+# Options = Struct.new(:name)
+#
+# class Parser
+# def self.parse(options)
+# args = Options.new("world")
+#
+# opt_parser = OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
+# args.name = n
+# end
+#
+# parser.on("-h", "--help", "Prints this help") do
+# puts parser
+# exit
+# end
+# end
+#
+# opt_parser.parse!(options)
+# return args
+# end
+# end
+# options = Parser.parse %w[--help]
+#
+# #=>
+# # Usage: example.rb [options]
+# # -n, --name=NAME Name to say hello to
+# # -h, --help Prints this help
+#
+# === Required Arguments
+#
+# For options that require an argument, option specification strings may include an
+# option name in all caps. If an option is used without the required argument,
+# an exception will be raised.
+#
+# require 'optparse'
+#
+# options = {}
+# OptionParser.new do |parser|
+# parser.on("-r", "--require LIBRARY",
+# "Require the LIBRARY before executing your script") do |lib|
+# puts "You required #{lib}!"
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -r
+# optparse-test.rb:9:in '<main>': missing argument: -r (OptionParser::MissingArgument)
+# $ ruby optparse-test.rb -r my-library
+# You required my-library!
+#
+# === Type Coercion
+#
+# OptionParser supports the ability to coerce command line arguments
+# into objects for us.
+#
+# OptionParser comes with a few ready-to-use kinds of type
+# coercion. They are:
+#
+# - Date -- Anything accepted by +Date.parse+ (need to require +optparse/date+)
+# - DateTime -- Anything accepted by +DateTime.parse+ (need to require +optparse/date+)
+# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ (need to require +optparse/time+)
+# - URI -- Anything accepted by +URI.parse+ (need to require +optparse/uri+)
+# - Shellwords -- Anything accepted by +Shellwords.shellwords+ (need to require +optparse/shellwords+)
+# - String -- Any non-empty string
+# - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
+# - Float -- Any float. (e.g. 10, 3.14, -100E+13)
+# - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
+# - DecimalInteger -- Like +Integer+, but no octal format.
+# - OctalInteger -- Like +Integer+, but no decimal format.
+# - DecimalNumeric -- Decimal integer or float.
+# - TrueClass -- Accepts '+, yes, true, -, no, false' and
+# defaults as +true+
+# - FalseClass -- Same as +TrueClass+, but defaults to +false+
+# - Array -- Strings separated by ',' (e.g. 1,2,3)
+# - Regexp -- Regular expressions. Also includes options.
+#
+# We can also add our own coercions, which we will cover below.
+#
+# ==== Using Built-in Conversions
+#
+# As an example, the built-in +Time+ conversion is used. The other built-in
+# conversions behave in the same way.
+# OptionParser will attempt to parse the argument
+# as a +Time+. If it succeeds, that time will be passed to the
+# handler block. Otherwise, an exception will be raised.
+#
+# require 'optparse'
+# require 'optparse/time'
+# OptionParser.new do |parser|
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# p time
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -t nonsense
+# ... invalid argument: -t nonsense (OptionParser::InvalidArgument)
+# $ ruby optparse-test.rb -t 10-11-12
+# 2010-11-12 00:00:00 -0500
+# $ ruby optparse-test.rb -t 9:30
+# 2014-08-13 09:30:00 -0400
+#
+# ==== Creating Custom Conversions
+#
+# The +accept+ method on OptionParser may be used to create converters.
+# It specifies which conversion block to call whenever a class is specified.
+# The example below uses it to fetch a +User+ object before the +on+ handler receives it.
+#
+# require 'optparse'
+#
+# User = Struct.new(:id, :name)
+#
+# def find_user id
+# not_found = ->{ raise "No User Found for id #{id}" }
+# [ User.new(1, "Sam"),
+# User.new(2, "Gandalf") ].find(not_found) do |u|
+# u.id == id
+# end
+# end
+#
+# op = OptionParser.new
+# op.accept(User) do |user_id|
+# find_user user_id.to_i
+# end
+#
+# op.on("--user ID", User) do |user|
+# puts user
+# end
+#
+# op.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb --user 1
+# #<struct User id=1, name="Sam">
+# $ ruby optparse-test.rb --user 2
+# #<struct User id=2, name="Gandalf">
+# $ ruby optparse-test.rb --user 3
+# optparse-test.rb:15:in 'block in find_user': No User Found for id 3 (RuntimeError)
+#
+# === Store options to a Hash
+#
+# The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
+#
+# require 'optparse'
+#
+# options = {}
+# OptionParser.new do |parser|
+# parser.on('-a')
+# parser.on('-b NUM', Integer)
+# parser.on('-v', '--verbose')
+# end.parse!(into: options)
+#
+# p options
+#
+# Used:
+#
+# $ ruby optparse-test.rb -a
+# {:a=>true}
+# $ ruby optparse-test.rb -a -v
+# {:a=>true, :verbose=>true}
+# $ ruby optparse-test.rb -a -b 100
+# {:a=>true, :b=>100}
+#
+# === Complete example
+#
+# The following example is a complete Ruby program. You can run it and see the
+# effect of specifying various options. This is probably the best way to learn
+# the features of +optparse+.
+#
+# require 'optparse'
+# require 'optparse/time'
+# require 'ostruct'
+# require 'pp'
+#
+# class OptparseExample
+# Version = '1.0.0'
+#
+# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
+#
+# class ScriptOptions
+# attr_accessor :library, :inplace, :encoding, :transfer_type,
+# :verbose, :extension, :delay, :time, :record_separator,
+# :list
+#
+# def initialize
+# self.library = []
+# self.inplace = false
+# self.encoding = "utf8"
+# self.transfer_type = :auto
+# self.verbose = false
+# end
+#
+# def define_options(parser)
+# parser.banner = "Usage: example.rb [options]"
+# parser.separator ""
+# parser.separator "Specific options:"
+#
+# # add additional options
+# perform_inplace_option(parser)
+# delay_execution_option(parser)
+# execute_at_time_option(parser)
+# specify_record_separator_option(parser)
+# list_example_option(parser)
+# specify_encoding_option(parser)
+# optional_option_argument_with_keyword_completion_option(parser)
+# boolean_verbose_option(parser)
+#
+# parser.separator ""
+# parser.separator "Common options:"
+# # No argument, shows at tail. This will print an options summary.
+# # Try it and see!
+# parser.on_tail("-h", "--help", "Show this message") do
+# puts parser
+# exit
+# end
+# # Another typical switch to print the version.
+# parser.on_tail("--version", "Show version") do
+# puts Version
+# exit
+# end
+# end
+#
+# def perform_inplace_option(parser)
+# # Specifies an optional option argument
+# parser.on("-i", "--inplace [EXTENSION]",
+# "Edit ARGV files in place",
+# "(make backup if EXTENSION supplied)") do |ext|
+# self.inplace = true
+# self.extension = ext || ''
+# self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
+# end
+# end
+#
+# def delay_execution_option(parser)
+# # Cast 'delay' argument to a Float.
+# parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
+# self.delay = n
+# end
+# end
+#
+# def execute_at_time_option(parser)
+# # Cast 'time' argument to a Time object.
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# self.time = time
+# end
+# end
+#
+# def specify_record_separator_option(parser)
+# # Cast to octal integer.
+# parser.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
+# "Specify record separator (default \\0)") do |rs|
+# self.record_separator = rs
+# end
+# end
+#
+# def list_example_option(parser)
+# # List of arguments.
+# parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
+# self.list = list
+# end
+# end
+#
+# def specify_encoding_option(parser)
+# # Keyword completion. We are specifying a specific set of arguments (CODES
+# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
+# # the shortest unambiguous text.
+# code_list = (CODE_ALIASES.keys + CODES).join(', ')
+# parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
+# "(#{code_list})") do |encoding|
+# self.encoding = encoding
+# end
+# end
+#
+# def optional_option_argument_with_keyword_completion_option(parser)
+# # Optional '--type' option argument with keyword completion.
+# parser.on("--type [TYPE]", [:text, :binary, :auto],
+# "Select transfer type (text, binary, auto)") do |t|
+# self.transfer_type = t
+# end
+# end
+#
+# def boolean_verbose_option(parser)
+# # Boolean switch.
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# self.verbose = v
+# end
+# end
+# end
+#
+# #
+# # Return a structure describing the options.
+# #
+# def parse(args)
+# # The options specified on the command line will be collected in
+# # *options*.
+#
+# @options = ScriptOptions.new
+# @args = OptionParser.new do |parser|
+# @options.define_options(parser)
+# parser.parse!(args)
+# end
+# @options
+# end
+#
+# attr_reader :parser, :options
+# end # class OptparseExample
+#
+# example = OptparseExample.new
+# options = example.parse(ARGV)
+# pp options # example.options
+# pp ARGV
+#
+# === Shell Completion
+#
+# For modern shells (e.g. bash, zsh, etc.), you can use shell
+# completion for command line options.
+#
+# === Further documentation
+#
+# The above examples, along with the accompanying
+# {Tutorial}[optparse/tutorial.rdoc],
+# should be enough to learn how to use this class.
+# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
+#
class OptionParser
- RCSID = %w$Id$[1..-1].each {|s| s.freeze}.freeze
- Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1])
- LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2])
- Release = RCSID[2]
+ # The version string
+ VERSION = "0.8.1"
+ # An alias for compatibility
+ Version = VERSION
+ # :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
+ # :startdoc:
-=begin private
-== ((:OptionParser::Completion:))
-Keyword completion module.
-=end #'#"#`#
+ #
+ # Keyword completion module. This allows partial arguments to be specified
+ # and resolved against a list of acceptable values.
+ #
module Completion
-=begin private
---- OptionParser::Completion#complete(key[, pat])
- Searches ((|key|)), or ((|pat|)) with completion if not found.
- :Parameters:
- : ((|key|))
- keyword to search.
- : ((|pat|))
- completion pattern.
-=end #'#"#`#
- def complete(key, pat = nil)
- pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+(?=.)/, '\&\w*'),
- ignore_case?)
- canon, sw, k, v, cn = nil
+ # :nodoc:
+
+ def self.regexp(key, icase)
+ Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
+ end
+
+ def self.candidate(key, icase = false, pat = nil, &block)
+ pat ||= Completion.regexp(key, icase)
candidates = []
- each do |k, *v|
- (if Regexp === k
- kn = nil
- k === key
- else
- kn = defined?(k.id2name) ? k.id2name : k
- pat === kn
- end) or next
- v << k if v.empty?
+ block.call do |k, *v|
+ (if Regexp === k
+ kn = ""
+ k === key
+ else
+ kn = defined?(k.id2name) ? k.id2name : k
+ pat === kn
+ end) or next
+ v << k if v.empty?
candidates << [k, v, kn]
end
- candidates = candidates.sort_by {|k, v, kn| kn.size}
+ candidates
+ end
+
+ def self.completable?(key)
+ String.try_convert(key) or defined?(key.id2name)
+ end
+
+ def candidate(key, icase = false, pat = nil, &_)
+ Completion.candidate(key, icase, pat, &method(:each))
+ end
+
+ def complete(key, icase = false, pat = nil)
+ candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size}
if candidates.size == 1
canon, sw, * = candidates[0]
elsif candidates.size > 1
canon, sw, cn = candidates.shift
candidates.each do |k, v, kn|
next if sw == v
- if String === cn and String === kn
- if cn.rindex(kn, 0)
- canon, sw, cn = k, v, kn
- next
- elsif kn.rindex(cn, 0)
- next
- end
- end
- throw :ambiguous, key
- end
+ if String === cn and String === kn
+ if cn.rindex(kn, 0)
+ canon, sw, cn = k, v, kn
+ next
+ elsif kn.rindex(cn, 0)
+ next
+ end
+ end
+ throw :ambiguous, key
+ end
end
if canon
- block_given? or return key, *sw
- yield(key, *sw)
+ block_given? or return key, *sw
+ yield(key, *sw)
end
end
-=begin private
---- OptionParser::Completion#convert(opt, *val)
- Extracts the first element from result of
- ((<OptionParser::Completion#complete>)).
-=end #'#"#`#
def convert(opt = nil, val = nil, *)
val
end
-
- def ignore_case?
- false
- end
end
-=begin private
-== ((:OptionParser::OptionMap:))
-Map from option/keyword string to object with completion.
-=== Superclass
-(({Hash}))
-=== Including modules
-((<OptionParser::Completion>))
-=end #'#"#`#
+ #
+ # Map from option/keyword string to object with completion.
+ #
class OptionMap < Hash
include Completion
end
- class OptionCaseMap < OptionMap
- def ignore_case?
- true
- end
- end
-
-=begin
-== ((:OptionParser::Switch:))
-Individual switch class.
-=end #'#"#`#
+ #
+ # Individual switch class. Not important to the user.
+ #
+ # Defined within Switch are several Switch-derived classes: NoArgument,
+ # RequiredArgument, etc.
+ #
class Switch
+ # :nodoc:
+
attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
-=begin private
-=== Class methods
-=end
-=begin private
---- OptionParser::Switch.guess(arg)
- Guesses argument style from ((|arg|)).
- Returns corresponding ((<OptionParser::Switch>)) subclass.
-=end #'#"#`#
+ #
+ # Guesses argument style from +arg+. Returns corresponding
+ # OptionParser::Switch class (OptionalArgument, etc.).
+ #
def self.guess(arg)
case arg
when ""
@@ -182,106 +540,78 @@ Individual switch class.
end
def self.incompatible_argument_styles(arg, t)
- raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
+ ParseError.filter_backtrace(caller(2)))
end
def self.pattern
NilClass
end
-=begin private
---- OptionParser::Switch.new
-=end #'#"#`#
def initialize(pattern = nil, conv = nil,
- short = nil, long = nil, arg = nil,
- desc = ([] if short or long), block = Proc.new)
+ short = nil, long = nil, arg = nil,
+ desc = ([] if short or long), block = nil, values = nil, &_block)
raise if Array === pattern
- @pattern, @conv, @short, @long, @arg, @desc, @block =
- pattern, conv, short, long, arg, desc, block
- end
-
-=begin
-=== Instance methods
-=end
-=begin private
---- OptionParser::Switch#parse_arg(arg) {non-serious error handler}
- Parses argument and returns rest of ((|arg|)), and matched portion
- to the argument pattern.
- :Parameters:
- : ((|arg|))
- option argument to be parsed.
- : (({block}))
- yields when the pattern doesn't match sub-string.
-=end #'#"#`#
- def parse_arg(arg)
- pattern or return nil, arg
+ block ||= _block
+ @pattern, @conv, @short, @long, @arg, @desc, @block, @values =
+ pattern, conv, short, long, arg, desc, block, values
+ end
+
+ #
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
+ # argument pattern. Yields when the pattern doesn't match substring.
+ #
+ private def parse_arg(arg) # :nodoc:
+ pattern or return nil, [arg]
unless m = pattern.match(arg)
- yield(InvalidArgument, arg)
- return arg, nil
+ yield(InvalidArgument, arg)
+ return arg, []
end
if String === m
- m = [s = m]
+ m = [s = m]
else
- m = m.to_a
- s = m[0]
- return nil, m unless String === s
+ m = m.to_a
+ s = m[0]
+ return nil, m unless String === s
end
raise InvalidArgument, arg unless arg.rindex(s, 0)
return nil, m if s.length == arg.length
yield(InvalidArgument, arg) # didn't match whole arg
return arg[s.length..-1], m
end
- private :parse_arg
-
-=begin private
---- OptionParser::Switch#conv_arg(arg, val) {semi-error handler}
- Parses argument, convert and returns ((|arg|)), ((|block|)) and
- result of conversion.
- : Arguments to ((|@conv|))
- substrings matched to ((|@pattern|)), ((|$&|)), ((|$1|)),
- ((|$2|)) and so on.
- :Parameters:
- : ((|arg|))
- argument string follows the switch.
- : ((|val|))
- following argument.
- : (({block}))
- (({yields})) at semi-error condition, instead of raises exception.
-=end #'#"#`#
- def conv_arg(arg, val = nil)
- if block
- if conv
- val = conv.call(*val)
- else
- val = *val
- end
- return arg, block, val
+
+ #
+ # Parses argument, converts and returns +arg+, +block+ and result of
+ # conversion. Yields at semi-error condition instead of raising an
+ # exception.
+ #
+ private def conv_arg(arg, val = []) # :nodoc:
+ v, = *val
+ if conv
+ val = conv.call(*val)
else
- return arg, nil
+ val = proc {|v| v}.call(*val)
+ end
+ if @values
+ @values.include?(val) or raise InvalidArgument, v
end
+ return arg, block, val
end
- private :conv_arg
-
-=begin private
---- OptionParser::Switch#summarize(sdone, ldone, width, max, indent)
- Makes summary strings.
- :Parameters:
- : ((|sdone|))
- already summarized short style options keyed hash.
- : ((|ldone|))
- already summarized long style options keyed hash.
- : ((|width|))
- width of left side, option part. in other word, right side,
- description part strings start at ((|width|)) column.
- : ((|max|))
- maximum width of left side, options are filled within ((|max|)) columns.
- : ((|indent|))
- prefix string indents each summarized lines.
- : (({block}))
- to be passed each lines(without newline).
-=end #'#"#`#
- def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "")
- sopts, lopts, s = [], [], nil
+
+ #
+ # Produces the summary text. Each line of the summary is yielded to the
+ # block (without newline).
+ #
+ # +sdone+:: Already summarized short style options keyed hash.
+ # +ldone+:: Already summarized long style options keyed hash.
+ # +width+:: Width of left side (option part). In other words, the right
+ # side (description part) starts after +width+ columns.
+ # +max+:: Maximum width of left side -> the options are filled within
+ # +max+ columns.
+ # +indent+:: Prefix string indents all summarized lines.
+ #
+ def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "")
+ sopts, lopts = [], [], nil
@short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
@long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
return if sopts.empty? and lopts.empty? # completely hidden
@@ -290,401 +620,542 @@ Individual switch class.
right = desc.dup
while s = lopts.shift
- l = left[-1].length + s.length
- l += arg.length if left.size == 1 && arg
- l < max or left << ''
- left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s
+ l = left[-1].length + s.length
+ l += arg.length if left.size == 1 && arg
+ l < max or sopts.empty? or left << +''
+ left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
end
- left[0] << arg if arg
- mlen = left.collect {|s| s.length}.max.to_i
+ if arg
+ left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
+ end
+ mlen = left.collect {|ss| ss.length}.max.to_i
while mlen > width and l = left.shift
- mlen = left.collect {|s| s.length}.max.to_i if l.length == mlen
- yield(indent + l)
+ mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen
+ if l.length < width and (r = right[0]) and !r.empty?
+ l = l.to_s.ljust(width) + ' ' + r
+ right.shift
+ end
+ yield(indent + l)
end
- while (l = left.shift; r = right.shift; l or r)
- l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
- yield(indent + l)
+ while begin l = left.shift; r = right.shift; l or r end
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
+ yield(indent + l)
end
self
end
-=begin private
-== Switch classes
-=end #'#"#`#
+ def add_banner(to) # :nodoc:
+ unless @short or @long
+ s = desc.join
+ to << " [" + s + "]..." unless s.empty?
+ end
+ to
+ end
+
+ def match_nonswitch?(str) # :nodoc:
+ @pattern =~ str unless @short or @long
+ end
+
+ #
+ # Main name of the switch.
+ #
+ def switch_name
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
+ end
+
+ def compsys(sdone, ldone) # :nodoc:
+ sopts, lopts = [], []
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ (sopts+lopts).each do |opt|
+ # "(-x -c -r)-l[left justify]"
+ if /\A--\[no-\](.+)$/ =~ opt
+ o = $1
+ yield("--#{o}", desc.join(""))
+ yield("--no-#{o}", desc.join(""))
+ else
+ yield("#{opt}", desc.join(""))
+ end
+ end
+ end
-=begin private
-=== ((:OptionParser::Switch::NoArgument:))
-Switch that takes no arguments.
-==== Superclass
-((<OptionParser::Switch>))
-==== Instance methods
---- OptionParser::Switch::NoArgument#parse
- Raises an exception if any arguments given.
-=end #'#"#`#
+ def pretty_print_contents(q) # :nodoc:
+ if @block
+ q.text ":" + @block.source_location.join(":") + ":"
+ first = false
+ else
+ first = true
+ end
+ [@short, @long].each do |list|
+ list.each do |opt|
+ if first
+ q.text ":"
+ first = false
+ end
+ q.breakable
+ q.text opt
+ end
+ end
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.object_group(self) {pretty_print_contents(q)}
+ end
+
+ def omitted_argument(val) # :nodoc:
+ val.pop if val.size == 3 and val.last.nil?
+ val
+ end
+
+ #
+ # Switch that takes no arguments.
+ #
class NoArgument < self
- def parse(arg, argv, &error)
- yield(NeedlessArgument, arg) if arg
- conv_arg(arg)
+
+ #
+ # Raises an exception if any arguments given.
+ #
+ def parse(arg, argv)
+ yield(NeedlessArgument, arg) if arg
+ conv_arg(arg)
end
- def self.incompatible_argument_styles(*)
+
+ def self.incompatible_argument_styles(*) # :nodoc:
+ end
+
+ def self.pattern # :nodoc:
+ Object
end
- def self.pattern
- Object
+
+ def pretty_head # :nodoc:
+ "NoArgument"
end
end
-=begin private
-=== ((:OptionParser::Switch::RequiredArgument:))
-Switch that takes an argument.
-==== Superclass
-((<OptionParser::Switch>))
-==== Instance methods
---- OptionParser::Switch::RequiredArgument#parse
- Raises an exception if argument is not present.
-=end #'#"#`#
+ #
+ # Switch that takes an argument.
+ #
class RequiredArgument < self
- def parse(arg, argv, &error)
- unless arg
- raise MissingArgument if argv.empty?
- arg = argv.shift
- end
- conv_arg(*parse_arg(arg, &error))
+
+ #
+ # Raises an exception if argument is not present.
+ #
+ def parse(arg, argv, &_)
+ unless arg
+ raise MissingArgument if argv.empty?
+ arg = argv.shift
+ end
+ conv_arg(*parse_arg(arg, &method(:raise)))
+ end
+
+ def pretty_head # :nodoc:
+ "Required"
end
end
-=begin private
-=== ((:OptionParser::Switch::OptionalArgument:))
-Switch that can omit argument.
-==== Superclass
-((<OptionParser::Switch>))
-==== Instance methods
---- OptionParser::Switch::OptionalArgument#parse
- Parses argument if given, or uses default value.
-=end #'#"#`#
+ #
+ # Switch that can omit argument.
+ #
class OptionalArgument < self
+
+ #
+ # Parses argument if given, or uses default value.
+ #
def parse(arg, argv, &error)
- if arg
- conv_arg(*parse_arg(arg, &error))
- else
- conv_arg(arg)
- end
+ if arg
+ conv_arg(*parse_arg(arg, &error))
+ else
+ omitted_argument conv_arg(arg)
+ end
+ end
+
+ def pretty_head # :nodoc:
+ "Optional"
end
end
+ #
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
+ #
class PlacedArgument < self
+
+ #
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
+ #
def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
- return nil, block, nil
- end
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
+ return nil, block
+ end
opt = (val = parse_arg(val, &error))[1]
val = conv_arg(*val)
if opt and !arg
argv.shift
else
+ omitted_argument val
val[0] = nil
end
val
end
+
+ def pretty_head # :nodoc:
+ "Placed"
+ end
end
end
-
-=begin
-== ((:OptionParser::List:))
-Simple option list providing mapping from short and/or long option
-string to ((<OptionParser::Switch>)), and mapping from acceptable
-argument to matching pattern and converter pair. Also provides
-summary feature.
-=end #'#"#`#
+ #
+ # Simple option list providing mapping from short and/or long option
+ # string to OptionParser::Switch and mapping from acceptable argument to
+ # matching pattern and converter pair. Also provides summary feature.
+ #
class List
-=begin
-=== Class methods
-=end #'#"#`#
-=begin private
---- OptionParser::List.new
- Just initializes all instance variables.
-=end #'#"#`#
+ # :nodoc:
+
+ # Map from acceptable argument types to pattern and converter pairs.
+ attr_reader :atype
+
+ # Map from short style option switches to actual switch objects.
+ attr_reader :short
+
+ # Map from long style option switches to actual switch objects.
+ attr_reader :long
+
+ # List of all switches and summary string.
+ attr_reader :list
+
+ #
+ # Just initializes all instance variables.
+ #
def initialize
@atype = {}
@short = OptionMap.new
- @long = OptionCaseMap.new
+ @long = OptionMap.new
@list = []
end
-=begin
-=== Instance methods
-=end #'#"#`#
-=begin
---- OptionParser::List#atype
- Map from acceptable argument types to pattern and converter pairs.
---- OptionParser::List#short
- Map from short style option switches to actual switch objects.
---- OptionParser::List#long
- Map from long style option switches to actual switch objects.
---- OptionParser::List#list
- List of all switches and summary string.
-=end #'#"#`#
- attr_reader :atype, :short, :long, :list
-
-=begin private
---- OptionParser::List#accept(type[, pattern]) {...}
- see ((<OptionParser.accept>)).
---- OptionParser::List#reject(type)
- see ((<OptionParser.reject>)).
-=end #'#"#`#
- def accept(t, pat = /.*/, &block)
+ def pretty_print(q) # :nodoc:
+ q.group(1, "(", ")") do
+ @list.each do |sw|
+ next unless Switch === sw
+ q.group(1, "(" + sw.pretty_head, ")") do
+ sw.pretty_print_contents(q)
+ end
+ end
+ end
+ end
+
+ #
+ # See OptionParser.accept.
+ #
+ def accept(t, pat = /.*/m, &block)
if pat
- pat.respond_to?(:match) or raise TypeError, "has no `match'"
+ pat.respond_to?(:match) or
+ raise TypeError, "has no 'match'", ParseError.filter_backtrace(caller(2))
else
- pat = t if t.respond_to?(:match)
+ pat = t if t.respond_to?(:match)
end
unless block
- block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
end
@atype[t] = [pat, block]
end
+ #
+ # See OptionParser.reject.
+ #
def reject(t)
@atype.delete(t)
end
-=begin private
---- OptionParser::List#update(sw, sopts, lopts, nlopts = nil)
- Adds ((|sw|)) according to ((|sopts|)), ((|lopts|)) and
- ((|nlopts|)).
- :Parameters:
- : ((|sw|))
- ((<OptionParser::Switch>)) instance to be added.
- : ((|sopts|))
- short style options list.
- : ((|lopts|))
- long style options list.
- : ((|nlopts|))
- negated long style options list.
-=end #'#"#`#
- def update(sw, sopts, lopts, nsw = nil, nlopts = nil)
- o = nil
+ #
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
+ #
+ # +sw+:: OptionParser::Switch instance to be added.
+ # +sopts+:: Short style option list.
+ # +lopts+:: Long style option list.
+ # +nlopts+:: Negated long style options list.
+ #
+ private def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
sopts.each {|o| @short[o] = sw} if sopts
lopts.each {|o| @long[o] = sw} if lopts
nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
used = @short.invert.update(@long.invert)
@list.delete_if {|o| Switch === o and !used[o]}
end
- private :update
-
-=begin
---- OptionParser::List#prepend(switch, short_opts, long_opts, nolong_opts)
- Inserts ((|switch|)) at head of the list, and associates short,
- long and negated long options.
---- OptionParser::List#append(switch, short_opts, long_opts, nolong_opts)
- Appends ((|switch|)) at tail of the list, and associates short,
- long and negated long options.
- :Parameters:
- : ((|switch|))
- ((<OptionParser::Switch>)) instance to be inserted.
- : ((|short_opts|))
- list of short style options.
- : ((|long_opts|))
- list of long style options.
- : ((|nolong_opts|))
- list of long style options with (({"no-"})) prefix.
-=end #'#"#`#
+
+ #
+ # Inserts +switch+ at the head of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # prepend(switch, short_opts, long_opts, nolong_opts)
+ #
def prepend(*args)
update(*args)
@list.unshift(args[0])
end
+ #
+ # Appends +switch+ at the tail of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # append(switch, short_opts, long_opts, nolong_opts)
+ #
def append(*args)
update(*args)
@list.push(args[0])
end
-=begin
---- OptionParser::List#search(id, key) [{block}]
- Searches ((|key|)) in ((|id|)) list.
- :Parameters:
- : ((|id|))
- searching list.
- : ((|k|))
- searching key.
- : (({block}))
- yielded with the found value when succeeded.
-=end #'#"#`#
+ #
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
+ # block is given. If it isn't found, nil is returned.
+ #
def search(id, key)
if list = __send__(id)
- val = list.fetch(key) {return nil}
- return val unless block_given?
- yield(val)
+ val = list.fetch(key) {return nil}
+ block_given? ? yield(val) : val
end
end
-=begin
---- OptionParser::List#complete(id, opt, *pat, &block)
- Searches list ((|id|)) for ((|opt|)) and ((|*pat|)).
- :Parameters:
- : ((|id|))
- searching list.
- : ((|opt|))
- searching key.
- : ((|*pat|))
- optional pattern for completion.
- : (({block}))
- yielded with the found value when succeeded.
-=end #'#"#`#
- def complete(id, opt, *pat, &block)
- __send__(id).complete(opt, *pat, &block)
- end
-
-=begin
---- OptionParser::List#summarize(*args) {...}
- Making summary table, yields the (({block})) with each lines.
- Each elements of (({@list})) should be able to (({summarize})).
- :Parameters:
- : ((|args|))
- passed to elements#summarize through.
- : (({block}))
- to be passed each lines(without newline).
-=end #'#"#`#
+ #
+ # Searches list +id+ for +opt+ and the optional patterns for completion
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
+ # is returned or yielded if a block is given. If it isn't found, nil is
+ # returned.
+ #
+ def complete(id, opt, icase = false, *pat, &block)
+ __send__(id).complete(opt, icase, *pat, &block)
+ end
+
+ def get_candidates(id)
+ yield __send__(id).keys
+ end
+
+ #
+ # Iterates over each option, passing the option to the +block+.
+ #
+ def each_option(&block)
+ list.each(&block)
+ end
+
+ #
+ # Creates the summary table, passing each line to the +block+ (without
+ # newline). The arguments +args+ are passed along to the summarize
+ # method which is called on every option.
+ #
def summarize(*args, &block)
+ sum = []
+ list.reverse_each do |opt|
+ if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
+ s = []
+ opt.summarize(*args) {|l| s << l}
+ sum.concat(s.reverse)
+ elsif !opt or opt.empty?
+ sum << ""
+ elsif opt.respond_to?(:each_line)
+ sum.concat([*opt.each_line].reverse)
+ else
+ sum.concat([*opt.each].reverse)
+ end
+ end
+ sum.reverse_each(&block)
+ end
+
+ def add_banner(to) # :nodoc:
+ list.each do |opt|
+ if opt.respond_to?(:add_banner)
+ opt.add_banner(to)
+ end
+ end
+ to
+ end
+
+ def compsys(*args, &block) # :nodoc:
list.each do |opt|
- if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
- opt.summarize(*args, &block)
- elsif opt.empty?
- yield("")
- else
- opt.each(&block)
- end
+ if opt.respond_to?(:compsys)
+ opt.compsys(*args, &block)
+ end
end
end
end
-
-=begin private
-== ((:OptionParser::CompletingHash:))
-(({Hash})) with completion search feature.
-=== Superclass
-(({Hash}))
-=== Including modules
-((<OptionParser::Completion>))
-=end #'#"#`#
+ #
+ # Hash with completion search feature. See OptionParser::Completion.
+ #
class CompletingHash < Hash
include Completion
-=begin private
-=== Instance methods
---- OptionParser::CompletingHash#match(key)
- Completion for hash key.
-=end #'#"#`#
+ #
+ # Completion for hash key.
+ #
def match(key)
- return key, *fetch(key) {
- raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
+ *values = fetch(key) {
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
}
+ return key, *values
end
end
-
-=begin
-== ((:OptionParser:))
-The front-end of (({OptionParser})).
-=end #'#"#`#
-
-=begin
-=== Constants
-=end #'#"#`#
-
-=begin
---- OptionParser::ArgumentStyle
- Enumeration of acceptable argument styles; possible values are:
- : OptionParser::NO_ARGUMENT
- the switch takes no arguments. ((({:NONE})))
- : OptionParser::REQUIRED_ARGUMENT
- the switch requires an argument. ((({:REQUIRED})))
- : OptionParser::OPTIONAL_ARGUMENT
- the switch requires an optional argument, that is, may take or
- not. ((({:OPTIONAL})))
-
- Use like (({--switch=argument}))(long style) or
- (({-Xargument}))(short style). For short style, only portion
- matched to ((<argument pattern>)) is dealed as argument.
-=end #'#"#`#
+ # :stopdoc:
+
+ #
+ # Enumeration of acceptable argument styles. Possible values are:
+ #
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
+ #
+ # Use like --switch=argument (long style) or -Xargument (short style). For
+ # short style, only portion matched to argument pattern is treated as
+ # argument.
+ #
ArgumentStyle = {}
NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
ArgumentStyle.freeze
-=begin private
---- OptionParser::DefaultList
- Switches common used such as '--', and also provides default
- argument classes
-=end #'#"#`#
+ #
+ # Switches common used such as '--', and also provides default
+ # argument classes
+ #
DefaultList = List.new
DefaultList.short['-'] = Switch::NoArgument.new {}
DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
-=begin undocumented
-=== Default options
-Default options, which never appear in option summary.
---- --help
- Shows option summary.
---- --version
- Shows version string if (({::Version})) is defined.
-=end #'#"#`#
- DefaultList.long['help'] = Switch::NoArgument.new do
- puts ARGV.options
- exit
+ COMPSYS_HEADER = <<'XXX' # :nodoc:
+
+typeset -A opt_args
+local context state line
+
+_arguments -s -S \
+XXX
+
+ def compsys(to, name = File.basename($0)) # :nodoc:
+ to << "#compdef #{name}\n"
+ to << COMPSYS_HEADER
+ visit(:compsys, {}, {}) {|o, d|
+ to << %Q[ "#{o}[#{d.gsub(/[\\\"\[\]]/, '\\\\\&')}]" \\\n]
+ }
+ to << " '*:file:_files' && return 0\n"
end
- DefaultList.long['version'] = Switch::OptionalArgument.new do |pkg|
- if pkg
- begin
- require 'optparse/version'
- rescue LoadError
- pkg = nil
- else
- show_version(*pkg.split(/,/))
+
+ def help_exit
+ if $stdout.tty? && (pager = ENV.values_at(*%w[RUBY_PAGER PAGER]).find {|e| e && !e.empty?})
+ less = ENV["LESS"]
+ args = [{"LESS" => "#{less} -Fe"}, pager, "w"]
+ print = proc do |f|
+ f.puts help
+ rescue Errno::EPIPE
+ # pager terminated
end
- end
- unless pkg
- v = ARGV.options.ver and puts v
+ if Process.respond_to?(:fork) and false
+ IO.popen("-") {|f| f ? Process.exec(*args, in: f) : print.call($stdout)}
+ # unreachable
+ end
+ IO.popen(*args, &print)
+ else
+ puts help
end
exit
end
-=begin
-=== Class methods
-=end #'#"#`#
-
-=begin
---- OptionParser.with([banner[, width[, indent]]]) [{...}]
- Initializes new instance, and evaluates the block in context of
- the instance if called as iterator. This behavior is equivalent
- to older (({new})). This is ((*deprecated*)) method.
-
- cf. ((<OptionParser.new>))
- :Parameters:
- : ((|banner|))
- banner message.
- : ((|width|))
- summary width.
- : ((|indent|))
- summary indent.
- : (({block}))
- to be evaluated in the new instance context.
-=end #'#"#`#
+ #
+ # Default options for ARGV, which never appear in option summary.
+ #
+ Officious = {}
+
+ #
+ # --help
+ # Shows option summary.
+ #
+ Officious['help'] = proc do |parser|
+ Switch::NoArgument.new do |arg|
+ parser.help_exit
+ end
+ end
+
+ #
+ # --*-completion-bash=WORD
+ # Shows candidates for command line completion.
+ #
+ Officious['*-completion-bash'] = proc do |parser|
+ Switch::RequiredArgument.new do |arg|
+ puts parser.candidate(arg)
+ exit
+ end
+ end
+
+ #
+ # --*-completion-zsh[=NAME:FILE]
+ # Creates zsh completion file.
+ #
+ Officious['*-completion-zsh'] = proc do |parser|
+ Switch::OptionalArgument.new do |arg|
+ parser.compsys($stdout, arg)
+ exit
+ end
+ end
+
+ #
+ # --version
+ # Shows version string if Version is defined.
+ #
+ Officious['version'] = proc do |parser|
+ Switch::OptionalArgument.new do |pkg|
+ if pkg
+ begin
+ require_relative 'optparse/version'
+ rescue LoadError
+ else
+ show_version(*pkg.split(/,/)) or
+ abort("#{parser.program_name}: no version found in package #{pkg}")
+ exit
+ end
+ end
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
+ puts v
+ exit
+ end
+ end
+
+ # :startdoc:
+
+ #
+ # Class methods
+ #
+
+ #
+ # Initializes a new instance and evaluates the optional block in context
+ # of the instance. Arguments +args+ are passed to #new, see there for
+ # description of parameters.
+ #
+ # This method is *deprecated*, its behavior corresponds to the older #new
+ # method.
+ #
def self.with(*args, &block)
opts = new(*args)
opts.instance_eval(&block)
opts
end
-=begin
---- OptionParser.inc(arg[, default])
---- OptionParser#inc(arg[, default])
- Returns incremented value of ((|default|)) according to ((|arg|)).
-=end
+ #
+ # Returns an incremented value of +default+ according to +arg+.
+ #
def self.inc(arg, default = nil)
case arg
when Integer
@@ -693,170 +1164,223 @@ Default options, which never appear in option summary.
default.to_i + 1
end
end
+
+ #
+ # See self.inc
+ #
def inc(*args)
self.class.inc(*args)
end
-=begin
---- OptionParser.new([banner[, width[, indent]]]) [{...}]
- Initializes the instance, and yields itself if called as iterator.
- :Parameters:
- : ((|banner|))
- banner message.
- : ((|width|))
- summary width.
- : ((|indent|))
- summary indent.
- : (({block}))
- to be evaluated in the new instance context.
-=end #'#"#`#
+ #
+ # Initializes the instance and yields itself if called with a block.
+ #
+ # +banner+:: Banner message.
+ # +width+:: Summary width.
+ # +indent+:: Summary indent.
+ #
def initialize(banner = nil, width = 32, indent = ' ' * 4)
@stack = [DefaultList, List.new, List.new]
@program_name = nil
@banner = banner
@summary_width = width
@summary_indent = indent
+ @default_argv = ARGV
+ @require_exact = false
+ @raise_unknown = true
+ add_officious
yield self if block_given?
end
-=begin
---- OptionParser.terminate([arg])
- Terminates option parsing. Optional parameter ((|arg|)) would be
- pushed back if given.
- :Parameters:
- : ((|arg|))
- string pushed back to be first non-option argument
-=end #'#"#`#
+ def add_officious # :nodoc:
+ list = base()
+ Officious.each do |opt, block|
+ list.long[opt] ||= block.call(self)
+ end
+ end
+
+ #
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
+ # back to be the first non-option argument.
+ #
def terminate(arg = nil)
self.class.terminate(arg)
end
+ #
+ # See #terminate.
+ #
def self.terminate(arg = nil)
throw :terminate, arg
end
@stack = [DefaultList]
+ #
+ # Returns the global top option list.
+ #
+ # Do not use directly.
+ #
def self.top() DefaultList end
-=begin
---- OptionParser.accept(t, [pat]) {...}
---- OptionParser#accept(t, [pat]) {...}
- Directs to accept specified class argument.
- :Parameters:
- : ((|t|))
- argument class specifier, any object including Class.
- : ((|pat|))
- pattern for argument, defaulted to ((|t|)) if it respond to (({match})).
- : (({block}))
- receives argument string and should be convert to desired class.
-=end #'#"#`#
+ #
+ # Directs to accept specified class +t+. The argument string is passed to
+ # the block in which it should be converted to the desired class.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
+ #
+ # accept(t, pat, &block)
+ #
def accept(*args, &blk) top.accept(*args, &blk) end
+ #
+ # See #accept.
+ #
def self.accept(*args, &blk) top.accept(*args, &blk) end
-=begin
---- OptionParser.reject(t)
---- OptionParser#reject(t)
- Directs to reject specified class argument.
- :Parameters:
- : ((|t|))
- argument class specifier, any object including Class.
-=end #'#"#`#
+ #
+ # Directs to reject specified class argument.
+ #
+ # +type+:: Argument class specifier, any object including Class.
+ #
+ # reject(type)
+ #
def reject(*args, &blk) top.reject(*args, &blk) end
+ #
+ # See #reject.
+ #
def self.reject(*args, &blk) top.reject(*args, &blk) end
-
-=begin
-=== Instance methods
-=end #'#"#`#
-
-=begin
---- OptionParser#banner
---- OptionParser#banner=(heading)
- Heading banner preceding summary.
---- OptionParser#summary_width
---- OptionParser#summary_width=(width)
- Width for option list portion of summary. Must be (({Numeric})).
---- OptionParser#summary_indent
---- OptionParser#summary_indent=(indent)
- Indentation for summary. Must be (({String})) (or have (({+ String}))).
---- OptionParser#program_name
---- OptionParser#program_name=(name)
- Program name to be emitted in error message and default banner,
- defaulted to (({$0})).
-=end #'#"#`#
- attr_writer :banner, :program_name
- attr_accessor :summary_width, :summary_indent
+ #
+ # Instance methods
+ #
+ # Heading banner preceding summary.
+ attr_writer :banner
+
+ # Program name to be emitted in error message and default banner,
+ # defaults to $0.
+ attr_writer :program_name
+
+ # Width for option list portion of summary. Must be Numeric.
+ attr_accessor :summary_width
+
+ # Indentation for summary. Must be String (or have + String method).
+ attr_accessor :summary_indent
+
+ # Strings to be parsed in default.
+ attr_accessor :default_argv
+
+ # Whether to require that options match exactly (disallows providing
+ # abbreviated long option as short option).
+ attr_accessor :require_exact
+
+ # Whether to raise at unknown option.
+ attr_accessor :raise_unknown
+
+ #
+ # Heading banner preceding summary.
+ #
def banner
- @banner ||= "Usage: #{program_name} [options]"
+ unless @banner
+ @banner = +"Usage: #{program_name} [options]"
+ visit(:add_banner, @banner)
+ end
+ @banner
end
+ #
+ # Program name to be emitted in error message and default banner, defaults
+ # to $0.
+ #
def program_name
- @program_name || File.basename($0, '.*')
+ @program_name || strip_ext(File.basename($0))
end
-# for experimental cascading :-)
+ private def strip_ext(name) # :nodoc:
+ exts = /#{
+ require "rbconfig"
+ Regexp.union(*RbConfig::CONFIG["EXECUTABLE_EXTS"]&.split(" "))
+ }\z/o
+ name.sub(exts, "")
+ end
+
+ # for experimental cascading :-)
alias set_banner banner=
alias set_program_name program_name=
alias set_summary_width summary_width=
alias set_summary_indent summary_indent=
-=begin
---- OptionParser#version
---- OptionParser#version=(ver)
- Version.
---- OptionParser#release
---- OptionParser#release=(rel)
- Release code.
---- OptionParser#ver
- Returns version string from ((<program_name>)), (({version})) and
- (({release})).
-=end #'#"#`#
- attr_writer :version, :release
+ # Version
+ attr_writer :version
+ # Release code
+ attr_writer :release
+ #
+ # Version
+ #
def version
- @version || (defined?(::Version) && ::Version) || (defined?(::VERSION) && ::VERSION)
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
end
+ #
+ # Release code
+ #
def release
- @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
end
+ #
+ # Returns version string from program_name, version and release.
+ #
def ver
if v = version
- str = "#{program_name} #{[v].join('.')}"
+ str = +"#{program_name} #{[v].join('.')}"
str << " (#{v})" if v = release
str
end
end
+ #
+ # Shows warning message with the program name
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#warn.
+ #
def warn(mesg = $!)
- super(program_name + ': ' + mesg)
+ super("#{program_name}: #{mesg}")
end
+ #
+ # Shows message with the program name then aborts.
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#abort.
+ #
def abort(mesg = $!)
- super(program_name + ': ' + mesg)
+ super("#{program_name}: #{mesg}")
end
-=begin
---- OptionParser#top
- Subject of ((<on>))/((<on_head>)), ((<accept>))/((<reject>)).
-=end #'#"#`#
+ #
+ # Subject of #on / #on_head, #accept / #reject
+ #
def top
@stack[-1]
end
-=begin
---- OptionParser#base
- Subject of ((<on_tail>)).
-=end #'#"#`#
+ #
+ # Subject of #on_tail.
+ #
def base
@stack[1]
end
-=begin
---- OptionParser#new
- Pushes a new (({List})).
-=end #'#"#`#
+ #
+ # Pushes a new List.
+ #
+ # If a block is given, yields +self+ and returns the result of the
+ # block, otherwise returns +self+.
+ #
def new
@stack.push(List.new)
if block_given?
@@ -866,838 +1390,1074 @@ Default options, which never appear in option summary.
end
end
-=begin
---- OptionParser#remove
- Removes the last (({List})).
-=end #'#"#`#
+ #
+ # Removes the last List.
+ #
def remove
@stack.pop
end
-
-=begin
---- OptionParser#summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent)
- Puts option summary into ((|to|)), and returns ((|to|)).
- :Parameters:
- : ((|to|))
- output destination, which must have method ((|<<|)). Defaulted to (({[]})).
- : ((|width|))
- width of left side. Defaulted to ((|@summary_width|))
- : ((|max|))
- maximum length allowed for left side. Defaulted to (({((|width|)) - 1}))
- : ((|indent|))
- indentation. Defaulted to ((|@summary_indent|))
- : (({block}))
- yields with each line if called as iterator.
-=end #'#"#`#
+ #
+ # Puts option summary into +to+ and returns +to+. Yields each line if
+ # a block is given.
+ #
+ # +to+:: Output destination, which must have method <<. Defaults to [].
+ # +width+:: Width of left side, defaults to @summary_width.
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
+ # +indent+:: Indentation, defaults to @summary_indent.
+ #
def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
- visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/}))
+ nl = "\n"
+ blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
+ visit(:summarize, {}, {}, width, max, indent, &blk)
to
end
-=begin
---- OptionParser#help
---- OptionParser#to_s
- Returns option summary string.
-=end #'#"#`#
- def help; summarize(banner.to_s.sub(/\n?\z/, "\n")) end
+ #
+ # Returns option summary string.
+ #
+ def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
alias to_s help
-=begin
---- OptionParser#to_a
- Returns option summary list.
-=end #'#"#`#
- def to_a; summarize(banner.to_a.dup) end
-
-
-=begin
---- OptionParser#switch
- Creates ((<OptionParser::Switch>)).
- :Parameters:
- : ((|*opts|))
- option definition:
- : argument style
- see ((<OptionParser::ArgumentStyle>))
- : argument pattern
- acceptable option argument format, must pre-defined with
- ((<OptionParser.accept>)) or ((<OptionParser#accept>)), or
- (({Regexp})). This can appear once or assigned as (({String}))
- if not present, otherwise causes exception (({ArgumentError})).
-
- cf. ((<Acceptable argument classes>)).
- : Hash
- : Array
- possible argument values.
- : Proc
- : Method
- alternative way to give the ((*handler*)).
- : "--switch=MANDATORY", "--switch[=OPTIONAL]", "--switch"
- specifies long style switch that takes ((*mandatory*)),
- ((*optional*)) and ((*no*)) argument, respectively.
- : "-xMANDATORY", "-x[OPTIONAL]", "-x"
- specifies short style switch that takes ((*mandatory*)),
- ((*optional*)) and ((*no*)) argument, respectively.
- : "-[a-z]MANDATORY", "-[a-z][OPTIONAL]", "-[a-z]"
- special form short style switch that matches character
- range(not fullset of regular expression).
- : "=MANDATORY", "=[OPTIONAL]"
- argument style and description.
- : "description", ...
- ((*description*)) for this option.
- : (({block}))
- ((*handler*)) to convert option argument to arbitrary (({Class})).
-=end #'#"#`#
-=begin private
---- OptionParser#notwice(obj, prv, msg)
- Checks never given twice an argument.
- ((*Called from OptionParser#switch only*))
- :Parameters:
- : ((|obj|))
- new argument.
- : ((|prv|))
- previously specified argument.
- : ((|msg|))
- exception message
-=end #'#"#`#
- def notwice(obj, prv, msg)
- unless !prv or prv == obj
- begin
- raise ArgumentError, "argument #{msg} given twice: #{obj}"
- rescue
- $@[0, 2] = nil
- raise
+ def pretty_print(q) # :nodoc:
+ q.object_group(self) do
+ first = true
+ if @stack.size > 2
+ @stack.each_with_index do |s, i|
+ next if i < 2
+ next if s.list.empty?
+ if first
+ first = false
+ q.text ":"
+ end
+ q.breakable
+ s.pretty_print(q)
+ end
end
end
+ end
+
+ def inspect # :nodoc:
+ require 'pp'
+ pretty_print_inspect
+ end
+
+ #
+ # Returns option summary list.
+ #
+ def to_a; summarize("#{banner}".split(/^/)) end
+
+ #
+ # Checks if an argument is given twice, in which case an ArgumentError is
+ # raised. Called from OptionParser#switch only.
+ #
+ # +obj+:: New argument.
+ # +prv+:: Previously specified argument.
+ # +msg+:: Exception message.
+ #
+ private def notwice(obj, prv, msg) # :nodoc:
+ unless !prv or prv == obj
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
+ ParseError.filter_backtrace(caller(2)))
+ end
obj
end
- private :notwice
- def make_switch(*opts, &block)
+ SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
+
+ # :call-seq:
+ # make_switch(params, block = nil)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def make_switch(opts, block = nil)
short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
ldesc, sdesc, desc, arg = [], [], []
default_style = Switch::NoArgument
default_pattern = nil
klass = nil
- o = nil
- n, q, a = nil
+ q, a = nil
+ has_arg = false
+ values = nil
opts.each do |o|
# argument class
next if search(:atype, o) do |pat, c|
- klass = notwice(o, klass, 'type')
- if not_style and not_style != Switch::NoArgument
- not_pattern, not_conv = pat, c
- else
- default_pattern, conv = pat, c
- end
+ klass = notwice(o, klass, 'type')
+ if not_style and not_style != Switch::NoArgument
+ not_pattern, not_conv = pat, c
+ else
+ default_pattern, conv = pat, c
+ end
end
# directly specified pattern(any object possible to match)
- if !(String === o) and o.respond_to?(:match)
- pattern = notwice(o, pattern, 'pattern')
- conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
- next
+ if !Completion.completable?(o) and o.respond_to?(:match)
+ pattern = notwice(o, pattern, 'pattern')
+ if pattern.respond_to?(:convert)
+ conv = pattern.method(:convert).to_proc
+ else
+ conv = SPLAT_PROC
+ end
+ next
end
# anything others
case o
when Proc, Method
- block = notwice(o, block, 'block')
- when Array, Hash
- case pattern
- when CompletingHash
- when nil
- pattern = CompletingHash.new
- conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
- else
- raise ArgumentError, "argument pattern given twice"
- end
- o.each {|(o, *v)| pattern[o] = v.fetch(0) {o}}
+ block = notwice(o, block, 'block')
+ when Array, Hash, Set
+ if Array === o
+ o, v = o.partition {|v,| Completion.completable?(v)}
+ values = notwice(v, values, 'values') unless v.empty?
+ next if o.empty?
+ end
+ case pattern
+ when CompletingHash
+ when nil
+ pattern = CompletingHash.new
+ conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
+ else
+ raise ArgumentError, "argument pattern given twice"
+ end
+ o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
+ when Range
+ values = notwice(o, values, 'values')
when Module
- raise ArgumentError, "unsupported argument type: #{o}"
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
when *ArgumentStyle.keys
- style = notwice(ArgumentStyle[o], style, 'style')
- when /^--no-([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- o = notwice(a ? Object : TrueClass, klass, 'type')
- not_pattern, not_conv = search(:atype, o) unless not_style
- not_style = (not_style || default_style).guess(arg = a) if a
- default_style = Switch::NoArgument
- default_pattern, conv = search(:atype, FalseClass) unless default_pattern
- ldesc << "--no-#{q}"
- long << 'no-' + (q = q.downcase)
- nolong << q
- when /^--\[no-\]([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- o = notwice(a ? Object : TrueClass, klass, 'type')
- if a
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- ldesc << "--#{q}"
- long << (o = q.downcase)
- not_pattern, not_conv = search(:atype, FalseClass) unless not_style
- not_style = Switch::NoArgument
- nolong << 'no-' + o
- when /^--([^\[\]=\s]*)(.+)?/
- q, a = $1, $2
- if a
- o = notwice(NilClass, klass, 'type')
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- ldesc << "--#{q}"
- long << (o = q.downcase)
- when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
- q, a = $1, $2
- o = notwice(Object, klass, 'type')
- if a
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- sdesc << "-#{q}"
- short << Regexp.new(q)
- when /^-(.)(.+)?/
- q, a = $1, $2
- if a
- o = notwice(NilClass, klass, 'type')
- default_style = default_style.guess(arg = a)
- default_pattern, conv = search(:atype, o) unless default_pattern
- end
- sdesc << "-#{q}"
- short << q
- when /^=/
- style = notwice(default_style.guess(arg = o), style, 'style')
- default_pattern, conv = search(:atype, Object) unless default_pattern
+ style = notwice(ArgumentStyle[o], style, 'style')
+ when /\A--no-([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ not_pattern, not_conv = search(:atype, o) unless not_style
+ not_style = (not_style || default_style).guess(arg = a) if a
+ default_style = Switch::NoArgument
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
+ ldesc << "--no-#{q}"
+ (q = q.downcase).tr!('_', '-')
+ long << "no-#{q}"
+ nolong << q
+ when /\A--\[no-\]([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--[no-]#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
+ not_style = Switch::NoArgument
+ nolong << "no-#{o}"
+ when /\A--([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ when /\A-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
+ q, a = $1, $2
+ o = notwice(Object, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ else
+ has_arg = true
+ end
+ sdesc << "-#{q}"
+ short << Regexp.new(q)
+ when /\A-(.)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ sdesc << "-#{q}"
+ short << q
+ when /\A=/
+ style = notwice(default_style.guess(arg = o), style, 'style')
+ default_pattern, conv = search(:atype, Object) unless default_pattern
else
- desc.push(o)
+ desc.push(o) if o && !o.empty?
end
end
default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
- s = if short.empty? and long.empty?
- raise ArgumentError, "no switch given" if style or pattern or block
- desc
- else
- (style || default_style).new(pattern || default_pattern,
- conv, sdesc, ldesc, arg, desc, block)
- end
+ if Range === values and klass
+ unless (!values.begin or klass === values.begin) and
+ (!values.end or klass === values.end)
+ raise ArgumentError, "range does not match class"
+ end
+ end
+ if !(short.empty? and long.empty?)
+ if has_arg and default_style == Switch::NoArgument
+ default_style = Switch::RequiredArgument
+ end
+ s = (style || default_style).new(pattern || default_pattern,
+ conv, sdesc, ldesc, arg, desc, block, values)
+ elsif !block
+ if style or pattern
+ raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
+ end
+ s = desc
+ else
+ short << pattern
+ s = (style || default_style).new(pattern,
+ conv, nil, nil, arg, desc, block, values)
+ end
return s, short, long,
(not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
nolong
end
-=begin
---- OptionParser#on(*opts) [{...}]
---- OptionParser#def_option(*opts) [{...}]
---- OptionParser#on_head(*opts) [{...}]
---- OptionParser#def_head_option(*opts) [{...}]
---- OptionParser#on_tail(*opts) [{...}]
---- OptionParser#def_tail_option(*opts) [{...}]
- Defines option switch and handler. (({on_head})), (({def_head_option}))
- and (({on_tail})), (({def_tail_option})) put the switch at head
- and tail of summary, respectively.
-
- cf. ((<OptionParser#switch>)).
-=end #'#"#`#
+ # ----
+ # Option definition phase methods
+ #
+ # These methods are used to define options, or to construct an
+ # OptionParser instance in other words.
+
+ # :call-seq:
+ # define(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
def define(*opts, &block)
- top.append(*(sw = make_switch(*opts, &block)))
+ top.append(*(sw = make_switch(opts, block)))
sw[0]
end
+
+ # :call-seq:
+ # on(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
def on(*opts, &block)
define(*opts, &block)
self
end
alias def_option define
+ # :call-seq:
+ # define_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
def define_head(*opts, &block)
- top.prepend(*(sw = make_switch(*opts, &block)))
+ top.prepend(*(sw = make_switch(opts, block)))
sw[0]
end
+
+ # :call-seq:
+ # on_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the head of the summary.
+ #
def on_head(*opts, &block)
define_head(*opts, &block)
self
end
alias def_head_option define_head
+ # :call-seq:
+ # define_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
def define_tail(*opts, &block)
- base.append(*(sw = make_switch(*opts, &block)))
+ base.append(*(sw = make_switch(opts, block)))
sw[0]
end
+
+ #
+ # :call-seq:
+ # on_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the tail of the summary.
+ #
def on_tail(*opts, &block)
define_tail(*opts, &block)
self
end
alias def_tail_option define_tail
+ #
+ # Add separator in summary.
+ #
def separator(string)
top.append(string, nil, nil)
end
-
-=begin
---- OptionParser#order(*argv) [{...}]
---- OptionParser#order!([argv = ARGV]) [{...}]
- Parses ((|argv|)) in order. When non-option argument encountered,
- yields it if called as iterator, otherwise terminates the parse
- process.
- Returns rest of ((|argv|)) left unparsed.
-
- (({order!})) takes argument array itself, and removes switches
- destructively.
- Defaults to parse ((|ARGV|)).
- :Parameters:
- : ((|argv|))
- command line arguments to be parsed.
- : (({block}))
- called with each non-option argument.
-=end #'#"#`#
- def order(*argv, &block)
+ # ----
+ # Arguments parse phase methods
+ #
+ # These methods parse +argv+, convert, and store the results by
+ # calling handlers. As these methods do not modify +self+, +self+
+ # can be frozen.
+
+ #
+ # Parses command line arguments +argv+ in order. When a block is given,
+ # each non-option argument is yielded. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ # Returns the rest of +argv+ left unparsed.
+ #
+ def order(*argv, **keywords, &nonopt)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- order!(argv, &block)
+ order!(argv, **keywords, &nonopt)
+ end
+
+ #
+ # Same as #order, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def order!(argv = default_argv, into: nil, **keywords, &nonopt)
+ setter = ->(name, val) {into[name.to_sym] = val} if into
+ parse_in_order(argv, setter, **keywords, &nonopt)
end
- def order!(argv = ARGV, &nonopt)
- opt, arg, sw, val, rest = nil
- nonopt ||= proc {|arg| throw :terminate, arg}
+ private def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc:
+ opt, arg, val, rest = nil
+ nonopt ||= proc {|a| throw :terminate, a}
argv.unshift(arg) if arg = catch(:terminate) {
while arg = argv.shift
- case arg
- # long option
- when /\A--([^=]*)(?:=(.*))?/
- opt, rest = $1, $2
- begin
- sw, = complete(:long, opt)
- rescue ParseError
- raise $!.set_option(arg, true)
- end
- begin
- opt, sw, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
- sw.call(val) if sw
- rescue ParseError
- raise $!.set_option(arg, rest)
- end
-
- # short option
- when /\A-(.)((=).*|.+)?/
- opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2
- begin
- unless sw = search(:short, opt)
- begin
- sw, = complete(:short, opt)
- # short option matched.
- val = arg.sub(/\A-/, '')
- has_arg = true
- rescue InvalidOption
- # if no short options match, try completion with long
- # options.
- sw, = complete(:long, opt)
- eq ||= !rest
- end
- end
- rescue ParseError
- raise $!.set_option(arg, true)
- end
- begin
- opt, sw, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
- raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
- argv.unshift(opt) if opt and (opt = opt.sub(/\A-*/, '-')) != '-'
- sw.call(val) if sw
- rescue ParseError
- raise $!.set_option(arg, arg.length > 2)
- end
-
- # non-option argument
- else
- nonopt.call(arg)
- end
+ case arg
+ # long option
+ when /\A--([^=]*)(?:=(.*))?/m
+ opt, rest = $1, $2
+ opt.tr!('_', '-')
+ begin
+ if exact
+ sw, = search(:long, opt)
+ else
+ sw, = complete(:long, opt, true)
+ end
+ rescue ParseError
+ throw :terminate, arg unless raise_unknown
+ raise $!.set_option(arg, true)
+ else
+ unless sw
+ throw :terminate, arg unless raise_unknown
+ raise InvalidOption, arg
+ end
+ end
+ begin
+ opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ val = callback!(cb, 1, val) if cb
+ callback!(setter, 2, sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, rest)
+ end
+
+ # short option
+ when /\A-(.)((=).*|.+)?/m
+ eq, rest, opt = $3, $2, $1
+ has_arg, val = eq, rest
+ begin
+ sw, = search(:short, opt)
+ unless sw
+ begin
+ sw, = complete(:short, opt)
+ # short option matched.
+ val = arg.delete_prefix('-')
+ has_arg = true
+ rescue InvalidOption
+ raise if exact
+ # if no short options match, try completion with long
+ # options.
+ sw, = complete(:long, opt)
+ eq ||= !rest
+ end
+ end
+ rescue ParseError
+ throw :terminate, arg unless raise_unknown
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ else
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
+ end
+ begin
+ argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
+ val = callback!(cb, 1, val) if cb
+ callback!(setter, 2, sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ end
+
+ # non-option argument
+ else
+ catch(:prune) do
+ visit(:each_option) do |sw0|
+ sw = sw0
+ sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
+ end
+ nonopt.call(arg)
+ end
+ end
end
nil
}
+ visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
+
argv
end
-=begin
---- OptionParser#permute(*argv)
---- OptionParser#permute!([argv = ARGV])
- Parses ((|argv|)) in permutation mode, and returns list of
- non-option arguments.
-
- (({permute!})) takes argument array itself, and removes switches
- destructively.
- Defaults to parse ((|ARGV|)).
- :Parameters:
- : ((|argv|))
- command line arguments to be parsed.
-=end #'#"#`#
- def permute(*argv)
+ # Calls callback with _val_.
+ private def callback!(cb, max_arity, *args) # :nodoc:
+ args.compact!
+
+ if (size = args.size) < max_arity and cb.to_proc.lambda?
+ (arity = cb.arity) < 0 and arity = (1-arity)
+ arity = max_arity if arity > max_arity
+ args[arity - 1] = nil if arity > size
+ end
+ cb.call(*args)
+ end
+
+ #
+ # Parses command line arguments +argv+ in permutation mode and returns
+ # list of non-option arguments. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ def permute(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- permute!(argv)
+ permute!(argv, **keywords)
end
- def permute!(argv = ARGV)
+ #
+ # Same as #permute, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def permute!(argv = default_argv, **keywords)
nonopts = []
- arg = nil
- order!(argv) {|arg| nonopts << arg}
+ order!(argv, **keywords) {|nonopt| nonopts << nonopt}
argv[0, 0] = nonopts
argv
end
-=begin
---- OptionParser#parse(*argv)
---- OptionParser#parse!([argv = ARGV])
- Parses ((|argv|)) in order when environment variable (({POSIXLY_CORRECT}))
- is set, otherwise permutation mode
-
- (({parse!})) takes argument array itself, and removes switches
- destructively.
- Defaults to parse ((|ARGV|)).
- :Parameters:
- : ((|argv|))
- command line arguments to be parsed.
-=end #'#"#`#
- def parse(*argv)
+ #
+ # Parses command line arguments +argv+ in order when environment variable
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
+ # When optional +into+ keyword argument is provided, the parsed option
+ # values are stored there via <code>[]=</code> method (so it can be Hash,
+ # or OpenStruct, or other similar object).
+ #
+ def parse(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- parse!(argv)
+ parse!(argv, **keywords)
end
- def parse!(argv = ARGV)
+ #
+ # Same as #parse, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def parse!(argv = default_argv, **keywords)
if ENV.include?('POSIXLY_CORRECT')
- order!(argv)
+ order!(argv, **keywords)
else
- permute!(argv)
- end
- end
-
-
-=begin private
---- OptionParser#visit(id, *args) {block}
- Traverses (({stack}))s calling method ((|id|)) with ((|*args|)).
- :Parameters:
- : ((|id|))
- called method in each elements of (({stack}))s.
- : ((|*args|))
- passed to ((|id|)).
- : (({block}))
- passed to ((|id|)).
-=end #'#"#`#
- def visit(id, *args, &block)
- el = nil
+ permute!(argv, **keywords)
+ end
+ end
+
+ #
+ # Wrapper method for getopts.rb.
+ #
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
+ # # params["a"] = true # -a
+ # # params["b"] = "1" # -b1
+ # # params["foo"] = "1" # --foo
+ # # params["bar"] = "x" # --bar x
+ # # params["zot"] = "z" # --zot Z
+ #
+ # Option +symbolize_names+ (boolean) specifies whether returned Hash keys should be Symbols; defaults to +false+ (use Strings).
+ #
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option", symbolize_names: true)
+ # # params[:a] = true # -a
+ # # params[:b] = "1" # -b1
+ # # params[:foo] = "1" # --foo
+ # # params[:bar] = "x" # --bar x
+ # # params[:zot] = "z" # --zot Z
+ #
+ def getopts(*args, symbolize_names: false, **keywords)
+ argv = Array === args.first ? args.shift : default_argv
+ single_options, *long_options = *args
+
+ result = {}
+ setter = (symbolize_names ?
+ ->(name, val) {result[name.to_sym] = val}
+ : ->(name, val) {result[name] = val})
+
+ single_options.scan(/(.)(:)?/) do |opt, val|
+ if val
+ setter[opt, nil]
+ define("-#{opt} VAL")
+ else
+ setter[opt, false]
+ define("-#{opt}")
+ end
+ end if single_options
+
+ long_options.each do |arg|
+ arg, desc = arg.split(';', 2)
+ opt, val = arg.split(':', 2)
+ if val
+ setter[opt, (val unless val.empty?)]
+ define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact)
+ else
+ setter[opt, false]
+ define("--#{opt}", *[desc].compact)
+ end
+ end
+
+ parse_in_order(argv, setter, **keywords)
+ result
+ end
+
+ #
+ # See #getopts.
+ #
+ def self.getopts(*args, symbolize_names: false)
+ new.getopts(*args, symbolize_names: symbolize_names)
+ end
+
+ #
+ # Traverses @stack, sending each element method +id+ with +args+ and
+ # +block+.
+ #
+ private def visit(id, *args, &block) # :nodoc:
@stack.reverse_each do |el|
- el.send(id, *args, &block)
+ el.__send__(id, *args, &block)
end
nil
end
- private :visit
-
-=begin private
---- OptionParser#search(id, k)
- Searches ((|k|)) in stack for ((|id|)) hash, and returns it or yielded
- value if called as iterator.
- :Parameters:
- : ((|id|))
- searching table.
- : ((|k|))
- searching key.
- : (({block}))
- yielded with the found value when succeeded.
-=end #'#"#`#
- def search(id, k)
- visit(:search, id, k) do |k|
- return k unless block_given?
- return yield(k)
- end
- end
- private :search
-
-=begin private
---- OptionParser#complete(typ, opt, *etc)
- Completes shortened long style option switch, and returns pair of
- canonical switch and switch descriptor((<OptionParser::Switch>)).
- :Parameters:
- : ((|id|))
- searching table.
- : ((|opt|))
- searching key.
- : ((|*pat|))
- optional pattern for completion.
- : (({block}))
- yielded with the found value when succeeded.
-=end #'#"#`#
- def complete(typ, opt, *pat)
+
+ #
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
+ #
+ private def search(id, key) # :nodoc:
+ block_given = block_given?
+ visit(:search, id, key) do |k|
+ return block_given ? yield(k) : k
+ end
+ end
+
+ #
+ # Completes shortened long style option switch and returns pair of
+ # canonical switch and switch descriptor OptionParser::Switch.
+ #
+ # +typ+:: Searching table.
+ # +opt+:: Searching key.
+ # +icase+:: Search case insensitive if true.
+ # +pat+:: Optional pattern for completion.
+ #
+ private def complete(typ, opt, icase = false, *pat) # :nodoc:
if pat.empty?
search(typ, opt) {|sw| return [sw, opt]} # exact match or...
end
- raise AmbiguousOption, catch(:ambiguous) {
- visit(:complete, typ, opt, *pat) {|opt, *sw| return sw}
- raise InvalidOption, opt
+ ambiguous = catch(:ambiguous) {
+ visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw}
}
+ exc = ambiguous ? AmbiguousOption : InvalidOption
+ raise exc.new(opt, additional: proc {|o| additional_message(typ, o)})
end
- private :complete
-
-=begin undocumented
---- OptionParser#load([filename])
- Loads options from file named as ((|filename|)). Does nothing when
- the file is not present. Returns whether successfuly loaded.
- :Parameters:
- : ((|filename|))
- option file name. defaulted to basename of the program without
- suffix in a directory ((%~/.options%)).
-=end #'#"#`#
- def load(filename = nil)
- begin
- filename ||= File.expand_path(File.basename($0, '.*'), '~/.options')
- rescue
- return false
+
+ #
+ # Returns additional info.
+ #
+ def additional_message(typ, opt)
+ return unless typ and opt and defined?(DidYouMean::SpellChecker)
+ all_candidates = []
+ visit(:get_candidates, typ) do |candidates|
+ all_candidates.concat(candidates)
+ end
+ all_candidates.select! {|cand| cand.is_a?(String) }
+ checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
+ end
+
+ #
+ # Return candidates for +word+.
+ #
+ def candidate(word)
+ list = []
+ case word
+ when '-'
+ long = short = true
+ when /\A--/
+ word, arg = word.split(/=/, 2)
+ argpat = Completion.regexp(arg, false) if arg and !arg.empty?
+ long = true
+ when /\A-/
+ short = true
+ end
+ pat = Completion.regexp(word, long)
+ visit(:each_option) do |opt|
+ next unless Switch === opt
+ opts = (long ? opt.long : []) + (short ? opt.short : [])
+ opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
+ if /\A=/ =~ opt.arg
+ opts.map! {|sw| sw + "="}
+ if arg and CompletingHash === opt.pattern
+ if opts = opt.pattern.candidate(arg, false, argpat)
+ opts.map!(&:last)
+ end
+ end
+ end
+ list.concat(opts)
+ end
+ list
+ end
+
+ #
+ # Loads options from file names as +filename+. Does nothing when the file
+ # is not present. Returns whether successfully loaded.
+ #
+ # +filename+ defaults to basename of the program without suffix in a
+ # directory ~/.options, then the basename with '.options' suffix
+ # under XDG and Haiku standard places.
+ #
+ # The optional +into+ keyword argument works exactly like that accepted in
+ # method #parse.
+ #
+ def load(filename = nil, **keywords)
+ unless filename
+ basename = File.basename($0, '.*')
+ return true if load(File.expand_path("~/.options/#{basename}"), **keywords) rescue nil
+ basename << ".options"
+ if !(xdg = ENV['XDG_CONFIG_HOME']) or xdg.empty?
+ # https://specifications.freedesktop.org/basedir-spec/latest/#variables
+ #
+ # If $XDG_CONFIG_HOME is either not set or empty, a default
+ # equal to $HOME/.config should be used.
+ xdg = ['~/.config', true]
+ end
+ return [
+ xdg,
+
+ *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
+
+ # Haiku
+ ['~/config/settings', true],
+ ].any? {|dir, expand|
+ next if !dir or dir.empty?
+ filename = File.join(dir, basename)
+ filename = File.expand_path(filename) if expand
+ load(filename, **keywords) rescue nil
+ }
end
begin
- parse(*IO.readlines(filename).each {|s| s.chomp!})
+ parse(*File.readlines(filename, chomp: true), **keywords)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
end
end
-=begin undocumented
---- OptionParser#environment([env])
- Parses environment variable ((|env|)) or its uppercase with spliting
- like as shell.
- :Parameters:
- : ((|env|))
- defaulted to basename of the program.
-=end #'#"#`#
- def environment(env = File.basename($0, '.*'))
+ #
+ # Parses environment variable +env+ or its uppercase with splitting like a
+ # shell.
+ #
+ # +env+ defaults to the basename of the program.
+ #
+ def environment(env = File.basename($0, '.*'), **keywords)
env = ENV[env] || ENV[env.upcase] or return
- parse(*Shellwords.shellwords(env))
+ require 'shellwords'
+ parse(*Shellwords.shellwords(env), **keywords)
end
-
-=begin
-= Acceptable argument classes
-=end #'#"#`#
+ #
+ # Acceptable argument classes
+ #
-=begin
-: Object
- any string, and no conversion. this is fall-back.
-=end #'#"#`#
+ #
+ # Any string and no conversion. This is fall-back.
+ #
accept(Object) {|s,|s or s.nil?}
accept(NilClass) {|s,|s}
-=begin
-: String
- any none-empty string, and no conversion.
-=end #'#"#`#
- accept(String, /.+/) {|s,*|s}
-
-=begin
-: Integer
- Ruby/C-like integer, octal for (({0-7})) sequence, binary for
- (({0b})), hexadecimal for (({0x})), and decimal for others; with
- optional sign prefix. Converts to (({Integer})).
-=end #'#"#`#
+ #
+ # Any non-empty string, and no conversion.
+ #
+ accept(String, /.+/m) {|s,*|s}
+
+ #
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
+ # Integer.
+ #
decimal = '\d+(?:_\d+)*'
binary = 'b[01]+(?:_[01]+)*'
hex = 'x[\da-f]+(?:_[\da-f]+)*'
- octal = "0(?:[0-7]*(?:_[0-7]+)*|#{binary}|#{hex})"
+ octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
integer = "#{octal}|#{decimal}"
- accept(Integer, %r"\A[-+]?(?:#{integer})"io) {|s,| Integer(s) if s}
-
-=begin
-: Float
- Float number format, and converts to (({Float})).
-=end #'#"#`#
- float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
- floatpat = %r"\A[-+]?#{float}"io
+
+ accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,|
+ begin
+ Integer(s)
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Float number format, and converts to Float.
+ #
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
+ floatpat = %r"\A[-+]?#{float}\z"io
accept(Float, floatpat) {|s,| s.to_f if s}
-=begin
-: Numeric
- Generic numeric format, and converts to (({Integer})) for integer
- format, (({Float})) for float format.
-=end #'#"#`#
- accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s,| eval(s) if s}
-
-=begin
-: OptionParser::DecimalInteger
- Decimal integer format, to be converted to (({Integer})).
-=end #'#"#`#
- DecimalInteger = /\A[-+]?#{decimal}/io
- accept(DecimalInteger) {|s,| s.to_i if s}
-
-=begin
-: OptionParser::OctalInteger
- Ruby/C like octal/hexadecimal/binary integer format, to be converted
- to (({Integer})).
-=end #'#"#`#
- OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io
- accept(OctalInteger) {|s,| s.oct if s}
-
-=begin
-: OptionParser::DecimalNumeric
- Decimal integer/float number format, to be converted to
- (({Integer})) for integer format, (({Float})) for float format.
-=end #'#"#`#
- DecimalNumeric = floatpat # decimal integer is allowed as float also.
- accept(DecimalNumeric) {|s,| eval(s) if s}
-
-=begin
-: TrueClass
- Boolean switch, which means whether it is present or not, whether it
- is absent or not with prefix (({no-})), or it takes an argument
- (({yes/no/true/false/+/-})).
-: FalseClass
- Similar to ((<TrueClass>)), but defaulted to (({false})).
-=end #'#"#`#
+ #
+ # Generic numeric format, converts to Integer for integer format, Float
+ # for float format, and Rational for rational format.
+ #
+ real = "[-+]?(?:#{octal}|#{float})"
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
+ if n
+ Rational(d, n)
+ elsif f
+ Float(s)
+ else
+ Integer(s)
+ end
+ }
+
+ #
+ # Decimal integer format, to be converted to Integer.
+ #
+ DecimalInteger = /\A[-+]?#{decimal}\z/io
+ accept(DecimalInteger, DecimalInteger) {|s,|
+ begin
+ Integer(s, 10)
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
+ # Integer.
+ #
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
+ accept(OctalInteger, OctalInteger) {|s,|
+ begin
+ Integer(s, 8)
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Decimal integer/float number format, to be converted to Integer for
+ # integer format, Float for float format.
+ #
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
+ accept(DecimalNumeric, floatpat) {|s, f|
+ begin
+ if f
+ Float(s)
+ else
+ Integer(s)
+ end
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Boolean switch, which means whether it is present or not, whether it is
+ # absent or not with prefix no-, or it takes an argument
+ # yes/no/true/false/+/-.
+ #
yesno = CompletingHash.new
%w[- no false].each {|el| yesno[el] = false}
%w[+ yes true].each {|el| yesno[el] = true}
- yesno['nil'] = false # shoud be nil?
+ yesno['nil'] = false # should be nil?
accept(TrueClass, yesno) {|arg, val| val == nil or val}
+ #
+ # Similar to TrueClass, but defaults to false.
+ #
accept(FalseClass, yesno) {|arg, val| val != nil and val}
-=begin
-: Array
- List of strings separated by ","
-=end #'#"#`#
- accept(Array) do |s,|
+ #
+ # List of strings separated by ",".
+ #
+ accept(Array) do |s, |
if s
- s = s.split(',').collect {|s| s unless s.empty?}
+ s = s.split(',').collect {|ss| ss unless ss.empty?}
end
s
end
-
-=begin
-= Exceptions
-=end #'#"#`#
-
-=begin
-== ((:OptionParser::ParseError:))
-Base class of exceptions from ((<OptionParser>))
-=== Superclass
-(({RuntimeError}))
-=== Constants
-: OptionParser::ParseError::Reason
- Reason caused error.
-=== Instance methods
---- OptionParser::ParseError#recover(argv)
- Push backs erred argument(s) to ((|argv|)).
---- OptionParser::ParseError#reason
- Returns error reason. Override this to I18N.
---- OptionParser::ParseError#inspect
- Returns inspection string.
---- OptionParser::ParseError#message
---- OptionParser::ParseError#to_s
---- OptionParser::ParseError#to_str
- Default stringizing method to emit standard error message.
-=end #'#"#`#
+ #
+ # Regular expression with options.
+ #
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
+ f = 0
+ if o
+ f |= Regexp::IGNORECASE if /i/ =~ o
+ f |= Regexp::MULTILINE if /m/ =~ o
+ f |= Regexp::EXTENDED if /x/ =~ o
+ case o = o.delete("imx")
+ when ""
+ when "u"
+ s = s.encode(Encoding::UTF_8)
+ when "e"
+ s = s.encode(Encoding::EUC_JP)
+ when "s"
+ s = s.encode(Encoding::SJIS)
+ when "n"
+ f |= Regexp::NOENCODING
+ else
+ raise OptionParser::InvalidArgument, "unknown regexp option - #{o}"
+ end
+ else
+ s ||= all
+ end
+ Regexp.new(s, f)
+ end
+
+ #
+ # Exceptions
+ #
+
+ #
+ # Base class of exceptions from OptionParser.
+ #
class ParseError < RuntimeError
- Reason = 'parse error'.freeze
+ # Reason which caused the error.
+ Reason = 'parse error'
- def initialize(*args)
+ # :nodoc:
+ def initialize(*args, additional: nil)
+ @additional = additional
+ @arg0, = args
@args = args
@reason = nil
end
attr_reader :args
attr_writer :reason
+ attr_accessor :additional
+ #
+ # Pushes back erred argument(s) to +argv+.
+ #
def recover(argv)
argv[0, 0] = @args
argv
end
+ DIR = File.join(__dir__, '')
+ def self.filter_backtrace(array)
+ unless $DEBUG
+ array.delete_if {|bt| bt.start_with?(DIR)}
+ end
+ array
+ end
+
+ def set_backtrace(array)
+ super(self.class.filter_backtrace(array))
+ end
+
def set_option(opt, eq)
if eq
- @args[0] = opt
+ @args[0] = opt
else
- @args.unshift(opt)
+ @args.unshift(opt)
end
self
end
+ #
+ # Returns error reason. Override this for I18N.
+ #
def reason
@reason || self.class::Reason
end
def inspect
- "#<#{self.class.to_s}: #{args.join(' ')}>"
+ "#<#{self.class}: #{args.join(' ')}>"
end
+ #
+ # Default stringizing method to emit standard error message.
+ #
def message
- reason + ': ' + args.join(' ')
+ "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
end
alias to_s message
- alias to_str message
end
-=begin
-== ((:OptionParser::AmbiguousOption:))
-Raises when encountered ambiguously completable string.
-=== Superclass
-((<OptionParser::ParseError>))
-=end #'#"#`#
+ #
+ # Raises when ambiguously completable string is encountered.
+ #
class AmbiguousOption < ParseError
- const_set(:Reason, 'ambiguous option'.freeze)
+ Reason = 'ambiguous option' # :nodoc:
end
-=begin
-== ((:OptionParser::NeedlessArgument:))
-Raises when encountered argument for switch defined as which takes no
-argument.
-=== Superclass
-((<OptionParser::ParseError>))
-=end #'#"#`#
+ #
+ # Raises when there is an argument for a switch which takes no argument.
+ #
class NeedlessArgument < ParseError
- const_set(:Reason, 'needles argument'.freeze)
+ Reason = 'needless argument' # :nodoc:
end
-=begin
-== ((:OptionParser::MissingArgument:))
-Raises when no argument found for switch defined as which needs
-argument.
-=== Superclass
-((<OptionParser::ParseError>))
-=end #'#"#`#
+ #
+ # Raises when a switch with mandatory argument has no argument.
+ #
class MissingArgument < ParseError
- const_set(:Reason, 'missing argument'.freeze)
+ Reason = 'missing argument' # :nodoc:
end
-=begin
-== ((:OptionParser::InvalidOption:))
-Raises when undefined switch.
-=== Superclass
-((<OptionParser::ParseError>))
-=end #'#"#`#
+ #
+ # Raises when switch is undefined.
+ #
class InvalidOption < ParseError
- const_set(:Reason, 'invalid option'.freeze)
+ Reason = 'invalid option' # :nodoc:
end
-=begin
-== ((:OptionParser::InvalidArgument:))
-Raises when the given argument does not match required format.
-=== Superclass
-((<OptionParser::ParseError>))
-=end #'#"#`#
+ #
+ # Raises when the given argument does not match required format.
+ #
class InvalidArgument < ParseError
- const_set(:Reason, 'invalid argument'.freeze)
+ Reason = 'invalid argument' # :nodoc:
end
-=begin
-== ((:OptionParser::AmbiguousArgument:))
-Raises when the given argument word can't completed uniquely.
-=== Superclass
-((<OptionParser::InvalidArgument>))
-=end #'#"#`#
+ #
+ # Raises when the given argument word can't be completed uniquely.
+ #
class AmbiguousArgument < InvalidArgument
- const_set(:Reason, 'ambiguous argument'.freeze)
+ Reason = 'ambiguous argument' # :nodoc:
end
-
-=begin
-= Miscellaneous
-=end #'#"#`#
-=begin
-== ((:OptionParser::Arguable:))
-Extends command line arguments array to parse itself.
-=end #'#"#`#
+ #
+ # Miscellaneous
+ #
+
+ #
+ # Extends command line arguments array (ARGV) to parse itself.
+ #
module Arguable
-=begin
---- OptionParser::Arguable#options=(opt)
- Sets ((<OptionParser>)) object, when ((|opt|)) is (({false})) or
- (({nil})), methods ((<OptionParser::Arguable#options>)) and
- ((<OptionParser::Arguable#options=>)) are undefined. Thus, there
- is no ways to access the ((<OptionParser>)) object via the
- receiver object.
-=end #'#"#`#
+
+ #
+ # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods
+ # OptionParser::Arguable#options and OptionParser::Arguable#options= are
+ # undefined. Thus, there is no ways to access the OptionParser object
+ # via the receiver object.
+ #
def options=(opt)
unless @optparse = opt
- class << self
- undef_method(:options)
- undef_method(:options=)
- end
+ class << self
+ undef_method(:options)
+ undef_method(:options=)
+ end
end
end
-=begin
---- OptionParser::Arguable#options
- Actual ((<OptionParser>)) object, automatically created if not
- yet.
-
- If called as iterator, yields with the ((<OptionParser>)) object
- and returns the result of the block. In this case, rescues any
- ((<OptionParser::ParseError>)) exceptions in the block, just emits
- error message to ((<STDERR>)) and returns (({nil})).
-
- :Parameters:
- : (({block}))
- Yielded with the ((<OptionParser>)) instance.
-
-=end #'#"#`#
+ #
+ # Actual OptionParser object, automatically created if nonexistent.
+ #
+ # If called with a block, yields the OptionParser object and returns the
+ # result of the block. If an OptionParser::ParseError exception occurs
+ # in the block, it is rescued, a error message printed to STDERR and
+ # +nil+ returned.
+ #
def options
@optparse ||= OptionParser.new
+ @optparse.default_argv = self
block_given? or return @optparse
begin
- yield @optparse
+ yield @optparse
rescue ParseError
- @optparse.warn $!
- nil
+ @optparse.warn $!
+ nil
end
end
-=begin
---- OptionParser::Arguable#order!
---- OptionParser::Arguable#permute!
---- OptionParser::Arguable#parse!
- Parses ((|self|)) destructively, and returns ((|self|)) just contains
- rest arguments left without parsed.
-=end #'#"#`#
- def order!(&blk) options.order!(self, &blk) end
- def permute!() options.permute!(self) end
- def parse!() options.parse!(self) end
-
-=begin private
-Initializes instance variable.
-=end #'#"#`#
+ #
+ # Parses +self+ destructively in order and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def order!(**keywords, &blk) options.order!(self, **keywords, &blk) end
+
+ #
+ # Parses +self+ destructively in permutation mode and returns +self+
+ # containing the rest arguments left unparsed.
+ #
+ def permute!(**keywords) options.permute!(self, **keywords) end
+
+ #
+ # Parses +self+ destructively and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def parse!(**keywords) options.parse!(self, **keywords) end
+
+ #
+ # Substitution of getopts is possible as follows. Also see
+ # OptionParser#getopts.
+ #
+ # def getopts(*args)
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
+ # end
+ # rescue OptionParser::ParseError
+ # end
+ #
+ def getopts(*args, symbolize_names: false, **keywords)
+ options.getopts(self, *args, symbolize_names: symbolize_names, **keywords)
+ end
+
+ #
+ # Initializes instance variable.
+ #
def self.extend_object(obj)
super
obj.instance_eval {@optparse = nil}
end
- def initialize(*args)
+
+ def initialize(*args) # :nodoc:
super
@optparse = nil
end
end
-=begin
-== OptionParser::Acceptables
-Acceptable argument classes. Now contains (({DecimalInteger})),
-(({OctalInteger})) and (({DecimalNumeric})).
-see ((<Acceptable argument classes>)).
-=end #'#"#`#
+ #
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
+ #
module Acceptables
- const_set(:DecimalInteger, OptionParser::DecimalInteger)
- const_set(:OctalInteger, OptionParser::OctalInteger)
- const_set(:DecimalNumeric, OptionParser::DecimalNumeric)
+ # :stopdoc:
+ DecimalInteger = OptionParser::DecimalInteger
+ OctalInteger = OptionParser::OctalInteger
+ DecimalNumeric = OptionParser::DecimalNumeric
+ # :startdoc:
end
end
# ARGV is arguable by OptionParser
ARGV.extend(OptionParser::Arguable)
-
-if $0 == __FILE__
- Version = OptionParser::Version
- ARGV.options {|q|
- q.parse!.empty? or puts "what's #{ARGV.join(' ')}?"
- } or exit 1
-end
-__END__
-=begin example
-= Example
-<<< opttest.rb
-=end #'#"#`#
+# An alias for OptionParser.
+OptParse = OptionParser # :nodoc: