Class: RCommand

Inherits:
Object
  • Object
show all
Defined in:
lib/rcommand.rb

Overview

RCommand is a generic way for ruby scripts to present a full-featured command interface to users with command history and tab completion.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io_read, io_write) ⇒ RCommand

Creates a new RCommand interface object.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/rcommand.rb', line 68

def initialize(io_read, io_write)
  unless io_read.kind_of? IO
    raise "Expecting object of type IO, got #{io_read.class.name}."
  end
  unless io_write.kind_of? IO
    raise "Expecting object of type IO, got #{io_write.class.name}."
  end
  @io_read = io_read
  @io_write = io_write
  @history = []
  @history_index = nil
  @tab_count = 0
  @prompt_proc = nil
  @single_tab_proc = lambda do |partial|
    matches = RCommand.matches(partial, self.history)
    common_prefix = RCommand.common_prefix(matches)
    common_prefix.nil? ? partial : common_prefix
  end
  @double_tab_proc = lambda do |partial|
    matches = RCommand.matches(partial, self.history)
    if matches.size > 1
      self.io_write.puts()
      for match in matches.uniq
        self.io_write.puts(match)
      end
      self.prompt()
      self.io_write.print(partial)
    end
    partial
  end
  @key_up_proc = lambda do |partial|
    self.prev()
  end
  @key_down_proc = lambda do |partial|
    self.next()
  end
  @key_left_proc = nil
  @key_right_proc = nil
  @interupt_proc = lambda do
    self.io_write.print("\n")
    system("stty sane")
    exit
  end
end

Instance Attribute Details

#historyObject (readonly)

Returns the value of attribute history.



115
116
117
# File 'lib/rcommand.rb', line 115

def history
  @history
end

#history_indexObject

Returns the value of attribute history_index.



116
117
118
# File 'lib/rcommand.rb', line 116

def history_index
  @history_index
end

#io_readObject (readonly)

Returns the value of attribute io_read.



113
114
115
# File 'lib/rcommand.rb', line 113

def io_read
  @io_read
end

#io_writeObject (readonly)

Returns the value of attribute io_write.



114
115
116
# File 'lib/rcommand.rb', line 114

def io_write
  @io_write
end

Class Method Details

.common_prefix(command_list) ⇒ Object

Returns any prefix shared by all commands in a list, or the empty string if there was no prefix.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/rcommand.rb', line 46

def self.common_prefix(command_list)
  return "" unless command_list.kind_of? Enumerable
  return "" if command_list.size == 0
  command_list.each do |command|
    unless command.kind_of? String
      raise "Expecting String, got #{command.class.name} instead."
    end
  end
  prefix = ""
  command_list = command_list.sort do |a, b|
    a.size <=> b.size
  end
  for i in 0..command_list[0].size
    for command in command_list
      return prefix if command_list[0][i] != command[i]
    end
    prefix = command_list[0][0..i]
  end
  return prefix
end

.matches(partial, command_list) ⇒ Object

Returns all commands in a command list that match a partial command.



33
34
35
36
37
38
39
40
41
42
# File 'lib/rcommand.rb', line 33

def self.matches(partial, command_list)
  matches = []
  for command in command_list
    unless command.kind_of? String
      raise "Expecting String, got #{command.class.name} instead."
    end
    matches << command if command[0...(partial.size)] == partial
  end
  return matches
end

Instance Method Details

#<<(command) ⇒ Object

Adds a command to the command line.



159
160
161
162
163
164
165
166
167
# File 'lib/rcommand.rb', line 159

def <<(command)
  command = command[0..-1] if command[-1] == "\n"
  command = command[0..-1] if command[-1] == "\r"
  if @history[-1] == ""
    @history.pop()
  end
  @history << command if command != ""
  @history_index = nil
end

#for_prompt(&block) ⇒ Object



118
# File 'lib/rcommand.rb', line 118

def for_prompt(&block); @prompt_proc = block; end

#getsObject

Returns the command string entered by the user.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/rcommand.rb', line 170

def gets()
  command = ""
  char = nil
  loop do
    char = getc()
    next if char.nil?
    next if char == 0
    if char != 9
      @tab_count = 0
    end
    if char == 3
      @interupt_proc.call() unless @interupt_proc.nil?
    elsif char == 10
      @io_write.print("\n")
      self << command
      break
    elsif char == 9
      @tab_count += 1
      replacement = nil
      if @tab_count == 1
        unless @single_tab_proc.nil?
          replacement =
            @single_tab_proc.call(command)
        end
      elsif @tab_count > 1
        unless @double_tab_proc.nil?
          replacement =
            @double_tab_proc.call(command)
        end
      end
      if replacement != nil && replacement.size > command.size
        @io_write.print(
          8.chr * command.size +
          " " * command.size +
          8.chr * command.size)
        @io_write.print(replacement)
        command = replacement
      end
    elsif char == 8 || char == 127
      if command.size > 0
        command = command[0..-2]
        @io_write.print(8.chr + " " + 8.chr)
      end
    else
      command << char.chr
      replacement = nil
      if command.index("\e[A") != nil
        command.gsub!("\e[A", "")
        unless @key_up_proc.nil?
          replacement =
            @key_up_proc.call(command)
        end
      elsif command.index("\e[B") != nil
        command.gsub!("\e[B", "")
        unless @key_down_proc.nil?
          replacement =
            @key_down_proc.call(command)
        end
      elsif command.index("\e[C") != nil
        command.gsub!("\e[C", "")
        unless @key_right_proc.nil?
          replacement =
            @key_right_proc.call(command)
        end
      elsif command.index("\e[D") != nil
        command.gsub!("\e[D", "")
        unless @key_left_proc.nil?
          replacement =
            @key_left_proc.call(command)
        end
      else
        if (char == "["[0] && command[-2] != "\e"[0]) ||
            (char != "\e"[0] && char != "["[0])
          @io_write.print(char.chr)
        end
      end
      if replacement != nil
        @io_write.print(
          8.chr * command.size +
          " " * command.size +
          8.chr * command.size)
        @io_write.print(replacement)
        command = replacement
      end
    end
  end
  return command
end

#nextObject

Sets the command line to the next command.



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rcommand.rb', line 144

def next()
  if self.history_index.nil?
    nil
  else
    self.history_index += 1
    if self.history_index > self.history.size - 1
      self.history_index = self.history.size - 1
      ""
    else
      self.history[self.history_index]
    end
  end
end

#on_double_tab(&block) ⇒ Object



120
# File 'lib/rcommand.rb', line 120

def on_double_tab(&block); @double_tab_proc = block; end

#on_interupt(&block) ⇒ Object



125
# File 'lib/rcommand.rb', line 125

def on_interupt(&block); @interupt_proc = block; end

#on_key_down(&block) ⇒ Object



122
# File 'lib/rcommand.rb', line 122

def on_key_down(&block); @key_down_proc = block; end

#on_key_left(&block) ⇒ Object



123
# File 'lib/rcommand.rb', line 123

def on_key_left(&block); @key_left_proc = block; end

#on_key_right(&block) ⇒ Object



124
# File 'lib/rcommand.rb', line 124

def on_key_right(&block); @key_right_proc = block; end

#on_key_up(&block) ⇒ Object



121
# File 'lib/rcommand.rb', line 121

def on_key_up(&block); @key_up_proc = block; end

#on_single_tab(&block) ⇒ Object



119
# File 'lib/rcommand.rb', line 119

def on_single_tab(&block); @single_tab_proc = block; end

#prevObject

Sets the command line to the previous command.



133
134
135
136
137
138
139
140
141
# File 'lib/rcommand.rb', line 133

def prev()
  if self.history_index.nil?
    self.history_index = self.history.size - 1
  else
    self.history_index -= 1
    self.history_index = 0 if self.history_index < 0
  end
  self.history[self.history_index]
end

#promptObject

Displays the prompt, if any.



128
129
130
# File 'lib/rcommand.rb', line 128

def prompt()
  @prompt_proc.call() unless @prompt_proc.nil?
end