[前][次][番号順一覧][スレッド一覧]

ruby-changes:17821

From: nobu <ko1@a...>
Date: Fri, 19 Nov 2010 20:27:05 +0900 (JST)
Subject: [ruby-changes:17821] Ruby:r29832 (trunk): * lib/optparse.rb: shell completion support for bash.

nobu	2010-11-19 20:26:54 +0900 (Fri, 19 Nov 2010)

  New Revision: 29832

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29832

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

  Added files:
    trunk/misc/rb_optparse.bash
    trunk/test/optparse/test_bash_completion.rb
  Modified files:
    trunk/lib/optparse.rb
    trunk/test/optparse/test_getopts.rb

Index: misc/rb_optparse.bash
===================================================================
--- misc/rb_optparse.bash	(revision 0)
+++ misc/rb_optparse.bash	(revision 29832)
@@ -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 "$@"
+}

Property changes on: misc/rb_optparse.bash
___________________________________________________________________
Added: svn:eol-style
   + LF

Index: lib/optparse.rb
===================================================================
--- lib/optparse.rb	(revision 29831)
+++ lib/optparse.rb	(revision 29832)
@@ -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 @@
   # 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 @@
         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 @@
   # --help
   # Shows option summary.
   #
+  # --help=complete=WORD
+  # Shows candidates for command line completion.
+  #
   Officious['help'] = proc do |parser|
-    Switch::NoArgument.new do
-      puts parser.help
+    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 @@
   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 @@
 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
Index: test/optparse/test_getopts.rb
===================================================================
--- test/optparse/test_getopts.rb	(revision 29831)
+++ test/optparse/test_getopts.rb	(revision 29832)
@@ -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
Index: test/optparse/test_bash_completion.rb
===================================================================
--- test/optparse/test_bash_completion.rb	(revision 0)
+++ test/optparse/test_bash_completion.rb	(revision 29832)
@@ -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

Property changes on: test/optparse/test_bash_completion.rb
___________________________________________________________________
Added: svn:eol-style
   + LF


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]