0

我有一个 Ruby 应用程序,它使用 readline 和命令完成。

在输入第一个字符串(命令)后,我希望能够完成它的参数。参数列表应基于所选命令。

有人有一个简单的例子吗?

这些是命令:

COMMANDS = [
  'collect', 'watch'
].sort

COLLECT = [
  'stuff', 'otherstuff'
].sort

comp = proc do |s| 
  COMMANDS.grep( /^#{Regexp.escape(s)}/ )        
end

Readline.completion_proc = comp

每次按TAB时,都会执行 proc 块并COMMANDS匹配来自数组的命令。在其中一个命令完全匹配后,我想开始仅在COLLECT数组中搜索参数。

4

2 回答 2

3

因为每次我寻找这样的东西时你的问题都会首先出现,所以我想与其他人分享我的代码。

#!/usr/bin/env ruby
require 'readline'

module Shell
  PROMPT = "shell> "
  module InputCompletor
    CORE_WORDS = %w[ clear help show exit export]
    SHOW_ARGS = %w[ list user ]
    EXPORT_ARGS = %w[ file ]
    COMPLETION_PROC = proc { |input|
      case input
      when /^(show|export) (.*)/
        command = $1
        receiver = $2
        DISPATCH_TABLE[$1].call($2)
      when /^(h|s|c|e.*)/
        receiver = $1
        CORE_WORDS.grep(/^#{Regexp.quote(receiver)}/)
      when /^\s*$/
        puts
        CORE_WORDS.map{|d| print "#{d}\t"}
        puts
        print PROMPT
      end
    }
    def self.show(receiver)
      if SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
        SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/)
      elsif SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
        "show #{SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
      end
    end
    def self.export(receiver)
      if EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
        EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/)
      elsif EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
        "export #{EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
      end
    end
    DISPATCH_TABLE = {'show' => lambda {|x| show(x)} ,
                      'export' => lambda {|x| export(x)}}
  end
  class CLI
    Readline.completion_append_character = ' '
    Readline.completer_word_break_characters = "\x00"
    Readline.completion_proc = Shell::InputCompletor::COMPLETION_PROC
    def initialize
      while line = Readline.readline("#{PROMPT}",true)
        Readline::HISTORY.pop if /^\s*$/ =~ line
        begin
          if Readline::HISTORY[-2] == line
            Readline::HISTORY.pop
          end
        rescue IndexError
        end
        cmd = line.chomp
        case cmd
        when /^clear/
          system('clear')
        when /^help/
          puts 'no help here'
        when /show list/
          puts 'nothing to show'
        when /^show\s$/
          puts 'missing args'
        when /export file/
          puts 'nothing to export'
        when /^export\s$/
          puts 'missing args'
        when /^exit/
          exit
        end
      end
    end
  end
end
Shell::CLI.new
于 2014-03-20T21:18:42.813 回答
2

想了想,解决方法很简单:

comp = proc do |s| 
  if Readline.line_buffer =~ /^.* /
    COLLECT.grep( /^#{Regexp.escape(s)}/ ) 
  else
    COMMANDS.grep( /^#{Regexp.escape(s)}/ ) 
  end
end

现在我只需要将它变成更灵活/可用的东西。

于 2012-05-30T21:09:55.333 回答