Class: A4Tools::Prompt

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Prompt

Returns a new instance of Prompt.



23
24
25
26
27
28
29
30
31
32
# File 'lib/net_shell/prompt.rb', line 23

def initialize(params={})
  @history = []
  @autosuggest = true
  reset_line

  @prompt = params[:prompt] || lambda { ">" }
  @tab = params[:tab] || lambda { |cmdline| [] }
  self.history_file = File.expand_path(params[:history_file]) || nil
  @history_length = params[:history_length] || 100
end

Instance Attribute Details

#autosuggestObject

Returns the value of attribute autosuggest.



21
22
23
# File 'lib/net_shell/prompt.rb', line 21

def autosuggest
  @autosuggest
end

#historyObject

Returns the value of attribute history.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def history
  @history
end

#history_fileObject

Returns the value of attribute history_file.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def history_file
  @history_file
end

#history_lengthObject

Returns the value of attribute history_length.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def history_length
  @history_length
end

#keystrokeObject

Returns the value of attribute keystroke.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def keystroke
  @keystroke
end

#promptObject

Returns the value of attribute prompt.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def prompt
  @prompt
end

#tabObject

Returns the value of attribute tab.



20
21
22
# File 'lib/net_shell/prompt.rb', line 20

def tab
  @tab
end

Instance Method Details

#add_argument_from_tab(new_arg, add_space = true) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/net_shell/prompt.rb', line 124

def add_argument_from_tab(new_arg, add_space = true)
  if @line.match(/\s$/) then
    # Last character is a space; we're adding a brand new argument
    @line += quoted_argument(new_arg)
  else
    # We're completing an existing argument
    last = split_args.last || ""
    true_last = split_args(false).last || ""
    last = "\"" + last if last.match(/\s/) or true_last.start_with? "\""
    @line = @line[0..(-1 - last.length)] + quoted_argument(new_arg)
  end

  @line += " " if add_space
end

#add_to_history(line) ⇒ Object



57
58
59
60
61
62
# File 'lib/net_shell/prompt.rb', line 57

def add_to_history(line)
  return if(history.last == line or line.match(/^\s*$/))
  @history.push(line)
  @history = @history[1 .. @history_length] unless @history_length.nil? or @history.length <= @history_length
  File.write(@history_file, @history.join("\n")) if @history_file
end

#at_suggestion_boundaryObject



245
246
247
248
249
# File 'lib/net_shell/prompt.rb', line 245

def at_suggestion_boundary
  return true if @line.length == 0
  return true if @line.match(/\s$/)
  false
end

#clear_lineObject



84
85
86
87
# File 'lib/net_shell/prompt.rb', line 84

def clear_line
  print "\r"
  print " "*Curses.cols + "\r"
end

#common_denominator(set) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/net_shell/prompt.rb', line 97

def common_denominator(set)
  return "" if set.empty?
  denom = set[0].to_s
  set.each do |item|
    while(not item.to_s.start_with?(denom)) do
      denom = denom[0..-2]
    end
  end

  denom
end

#handle_backspaceObject



149
150
151
152
153
154
155
156
# File 'lib/net_shell/prompt.rb', line 149

def handle_backspace
  print "\a" if @cursor >= @line.length
  if @cursor == 0 then
    @line = @line[0 .. -2] || ""
  else
    @line = (@line[0 .. @cursor-2] + @line[@cursor .. -1]) || ""
  end
end

#handle_character(c) ⇒ Object



214
215
216
217
218
219
220
# File 'lib/net_shell/prompt.rb', line 214

def handle_character(c)
  if @cursor == 0 then
    @line += c
  else
    @line = (@line[0 .. @cursor-1] + c + @line[@cursor .. -1]) || ""
  end
end

#handle_ctrl_cObject



139
140
141
142
# File 'lib/net_shell/prompt.rb', line 139

def handle_ctrl_c
  puts ""
  reset_line
end

#handle_ctrl_dObject



144
145
146
147
# File 'lib/net_shell/prompt.rb', line 144

def handle_ctrl_d
  exit 0 if @line.length == 0
  print "\a"
end

#handle_down_arrowObject



169
170
171
172
173
174
175
176
177
# File 'lib/net_shell/prompt.rb', line 169

def handle_down_arrow
  if @autosuggest and not at_suggestion_boundary then
    @suggestion_offset += 1
  else
    print "\a" if @offset == -1
    @offset = [@offset - 1, -1].max
    @line = pad_line(history_at_offset)
  end
end

#handle_endObject



226
227
228
# File 'lib/net_shell/prompt.rb', line 226

def handle_end
  @cursor = 0
end

#handle_homeObject



222
223
224
# File 'lib/net_shell/prompt.rb', line 222

def handle_home
  @cursor = -@line.length
end

#handle_left_arrowObject



194
195
196
# File 'lib/net_shell/prompt.rb', line 194

def handle_left_arrow
  @cursor = [-@line.length, @cursor - 1].max
end

#handle_newlineObject



198
199
200
201
202
203
# File 'lib/net_shell/prompt.rb', line 198

def handle_newline
  add_to_history(@line)
  @suggestion = "" # clear the ghost text off the screen
  redraw_line
  puts "\r"
end

#handle_pgdownObject



230
231
232
233
234
# File 'lib/net_shell/prompt.rb', line 230

def handle_pgdown
  @offset = -1
  clear_line
  @line = history_at_offset
end

#handle_right_arrowObject



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/net_shell/prompt.rb', line 179

def handle_right_arrow
  if @cursor >= 0 then
    return unless @autosuggest
    opts = tab_completion(@line)
    if opts.empty?
      print "\a"
    else
      suggestion = opts[@suggestion_offset % opts.length]
      add_argument_from_tab(suggestion) unless suggestion.nil? or suggestion.empty?
    end
  else
    @cursor += 1
  end
end

#handle_tabObject



205
206
207
208
209
210
211
212
# File 'lib/net_shell/prompt.rb', line 205

def handle_tab
  opts = tab_completion(@line)
  forced = common_denominator(opts)
  add_argument_from_tab(forced, opts.length == 1) unless forced.empty?

  print "\a" if opts.length != 1
  puts "\r\n"+opts.sort.join(", ") if opts.length > 1
end

#handle_up_arrowObject



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

def handle_up_arrow
  if @autosuggest and not at_suggestion_boundary then
    @suggestion_offset -= 1
  else
    print "\a" if @offset == @history.length - 1
    @saved_cmd = @line if @offset == -1
    @offset = [@offset + 1, @history.length - 1].min
    @line = pad_line(history_at_offset)
  end
end

#history_at_offset(offset = nil) ⇒ Object



64
65
66
67
68
# File 'lib/net_shell/prompt.rb', line 64

def history_at_offset(offset=nil)
  offset ||= @offset
  history = (offset == -1) ? @saved_cmd : @history.reverse[offset]
  history || ""
end

#pad_line(line) ⇒ Object



240
241
242
243
# File 'lib/net_shell/prompt.rb', line 240

def pad_line(line)
  line += " " unless line.empty? or line.match(/\s$/)
  line
end

#quoted_argument(argument) ⇒ Object



34
35
36
# File 'lib/net_shell/prompt.rb', line 34

def quoted_argument(argument)
  argument.match(/\s+/) ? "\"#{argument}\"" : argument
end

#read_charObject



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/net_shell/prompt.rb', line 109

def read_char
  STDIN.echo = false
 
  input = STDIN.getc.chr
  if input == "\e" then
    input << STDIN.read_nonblock(4) rescue nil
    input << STDIN.read_nonblock(3) rescue nil
    input << STDIN.read_nonblock(2) rescue nil
  end
ensure
  STDIN.echo = true
 
  return input
end

#read_lineObject



323
324
325
326
327
328
329
330
331
332
333
# File 'lib/net_shell/prompt.rb', line 323

def read_line
  reset_line
  begin
    system("stty raw -echo")
    scan_line
  ensure
    system("stty -raw echo")
  end

  @line
end

#redraw_lineObject



78
79
80
81
82
# File 'lib/net_shell/prompt.rb', line 78

def redraw_line
  clear_line
  print "#{@last_prompt}#{@line}#{@suggestion.colorize(:light_black)}"
  print "\b"*(@suggestion.length-@cursor)
end

#refresh_promptObject



74
75
76
# File 'lib/net_shell/prompt.rb', line 74

def refresh_prompt
  @last_prompt = prompt
end

#reset_lineObject



313
314
315
316
317
318
319
320
321
# File 'lib/net_shell/prompt.rb', line 313

def reset_line
  @line = ""
  @suggestion = ""
  @suggestion_offset = 0
  @offset = -1
  @cursor = 0
  @last_prompt = prompt
  @last_tab = nil
end

#scan_lineObject



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/net_shell/prompt.rb', line 268

def scan_line
  c = nil
  while c != "\r"
    redraw_line
    c = read_char

    start_line = @line
    case c
    when "\u0003" # CTRL+C
      handle_ctrl_c
    when "\u0004" # CTRL+D
      handle_ctrl_d
    when "\177" # backspace
      handle_backspace
    when "\e[A" # up arrow
      handle_up_arrow
    when "\e[B" # down arrow
      handle_down_arrow
    when "\e[C" # right arrow
      handle_right_arrow
    when "\e[D" # left arrow
      handle_left_arrow
    when "\e[H" # home
      handle_home
    when "\e[F" # end
      handle_end
    when "\e[6~" # page down
      handle_pgdown
    when "\r" # newline
      handle_newline
    when "\t" # tab
      handle_tab
    else
      handle_character(c) if c.length == 1 # guard against other weird metacharacters
    end

    @last_tab = nil if start_line != @line # altering the line invalidated tab complete cache
    @suggestion_offset = 0 if at_suggestion_boundary
    @keystroke.call(@line) unless @keystroke.nil?
    update_autosuggest if @autosuggest
  end

  @line
end

#show_history(offset) ⇒ Object



70
71
72
# File 'lib/net_shell/prompt.rb', line 70

def show_history(offset)
  print "\r#{prompt}#{history_at_offset(offset)}"
end

#show_prompt(force_newline = false) ⇒ Object



46
47
48
49
50
# File 'lib/net_shell/prompt.rb', line 46

def show_prompt(force_newline=false)
  puts if force_newline
  print prompt
  STDOUT.flush
end

#split_args(partial = true) ⇒ Object



236
237
238
# File 'lib/net_shell/prompt.rb', line 236

def split_args(partial=true)
  partial ? @line.shellsplit_partial : @line.split(/\s+/)
end

#suggest(str) ⇒ Object



251
252
253
254
# File 'lib/net_shell/prompt.rb', line 251

def suggest(str)
  str ||= ""
  @suggestion = str.style(:light_black)
end

#tab_completion(cmdline) ⇒ Object



89
90
91
92
93
94
95
# File 'lib/net_shell/prompt.rb', line 89

def tab_completion(cmdline)
  if @last_tab.nil?
    @last_tab = @tab.call(cmdline)
  end

  @last_tab
end

#update_autosuggestObject



256
257
258
259
260
261
262
263
264
265
266
# File 'lib/net_shell/prompt.rb', line 256

def update_autosuggest
  opts = tab_completion(@line)
  suggested = opts[@suggestion_offset % opts.length] rescue nil
  if suggested.nil? or at_suggestion_boundary
    suggest("")
    return
  end

  suggested = suggested[ (split_args.last.length rescue 0) .. -1] unless at_suggestion_boundary
  suggest(suggested) unless suggested.nil?
end