* lib/optparse.rb: shell completion support for bash.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29832 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2010-11-19 11:26:54 +00:00
Родитель 3c1f696a50
Коммит 644f0445e8
4 изменённых файлов: 127 добавлений и 9 удалений

Просмотреть файл

@ -195,6 +195,11 @@
# options = OptparseExample.parse(ARGV)
# pp options
#
# === Shell Completion
#
# For modern shells (e.g. bash, zsh, etc.), you can use shell
# completion for command line options.
#
# === Further documentation
#
# The above examples should be enough to learn how to use this class. If you
@ -218,12 +223,15 @@ class OptionParser
# and resolved against a list of acceptable values.
#
module Completion
def complete(key, icase = false, pat = nil)
pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'),
icase)
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)
canon, sw, cn = nil
candidates = []
each do |k, *v|
block.call do |k, *v|
(if Regexp === k
kn = nil
k === key
@ -234,7 +242,16 @@ class OptionParser
v << k if v.empty?
candidates << [k, v, kn]
end
candidates = candidates.sort_by {|k, v, kn| kn.size}
candidates
end
def candidate(key, icase = false, pat = nil)
Completion.candidate(key, icase, pat, &method(:each))
end
public
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
@ -717,9 +734,17 @@ class OptionParser
# --help
# Shows option summary.
#
# --help=complete=WORD
# Shows candidates for command line completion.
#
Officious['help'] = proc do |parser|
Switch::NoArgument.new do
Switch::OptionalArgument.new do |arg|
case arg
when /\Acomplete=(.*)/
puts parser.candidate($1)
else
puts parser.help
end
exit
end
end
@ -1461,6 +1486,35 @@ class OptionParser
end
private :complete
def candidate(word)
list = []
case word
when /\A--/
word, arg = word.split(/=/, 2)
argpat = Completion.regexp(arg, false) if arg and !arg.empty?
long = true
when /\A-(!-)/
short = true
when /\A-/
long = short = true
end
pat = Completion.regexp(word, true)
visit(:each_option) do |opt|
opts = [*(opt.long if long), *(opt.short if short)]
opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
if /\A=/ =~ opt.arg
opts = 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.
@ -1818,6 +1872,6 @@ ARGV.extend(OptionParser::Arguable)
if $0 == __FILE__
Version = OptionParser::Version
ARGV.options {|q|
q.parse!.empty? or puts "what's #{ARGV.join(' ')}?"
q.parse!.empty? or print "what's #{ARGV.join(' ')}?\n"
} or abort(ARGV.options.to_s)
end

20
misc/rb_optparse.bash Normal file
Просмотреть файл

@ -0,0 +1,20 @@
#! /bin/bash
# Completion for bash:
#
# (1) install this file,
#
# (2) load the script, and
# . ~/.profile.d/rb_optparse.bash
#
# (3) define completions in your .bashrc,
# rb_optparse command_using_optparse_1
# rb_optparse command_using_optparse_2
_rb_optparse() {
COMPREPLY=($("${COMP_WORDS[0]}" --help=complete="${COMP_WORDS[COMP_CWORD]}"))
return 0
}
rb_optparse () {
[ $# = 0 ] || complete -o default -F _rb_optparse "$@"
}

Просмотреть файл

@ -0,0 +1,42 @@
require 'test/unit'
require 'optparse'
class TestOptionParser < Test::Unit::TestCase
end
class TestOptionParser::BashCompletion < Test::Unit::TestCase
def setup
@opt = OptionParser.new
@opt.define("-z", "zzz") {}
@opt.define("--foo") {}
@opt.define("--bar=BAR") {}
@opt.define("--for=TYPE", [:hello, :help, :zot]) {}
end
def test_empty
assert_equal([], @opt.candidate(""))
end
def test_one_hyphen
assert_equal(%w[-z --foo --bar= --for=], @opt.candidate("-"))
end
def test_two_hyphen
assert_equal(%w[--foo --bar= --for=], @opt.candidate("--"))
end
def test_long_f
assert_equal(%w[--foo --for=], @opt.candidate("--f"))
end
def test_long_for_option
assert_equal(%w[--for=], @opt.candidate("--for"))
end
def test_long_for_option_args
assert_equal(%w[hello help zot], @opt.candidate("--for="))
end
def test_long_for_option_complete
assert_equal(%w[hello help], @opt.candidate("--for=h"))
end
end

Просмотреть файл

@ -1,7 +1,9 @@
require 'test/unit'
require 'optparse'
class TestOptionParserGetopts < Test::Unit::TestCase
class TestOptionParser < Test::Unit::TestCase
end
class TestOptionParser::Getopts < Test::Unit::TestCase
def setup
@opt = OptionParser.new
end