class A4Tools::Prompt

Attributes

autosuggest[RW]
history[RW]
history_file[RW]
history_length[RW]
keystroke[RW]
prompt[RW]
tab[RW]

Public Class Methods

new(params={}) click to toggle source
# 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

Public Instance Methods

add_argument_from_tab(new_arg, add_space = true) click to toggle source
# 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) click to toggle source
# 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_boundary() click to toggle source
# 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_line() click to toggle source
# File lib/net_shell/prompt.rb, line 84
def clear_line
  print "\r"
  print " "*Curses.cols + "\r"
end
common_denominator(set) click to toggle source
# 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_backspace() click to toggle source
# 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) click to toggle source
# 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_c() click to toggle source
# File lib/net_shell/prompt.rb, line 139
def handle_ctrl_c
  puts ""
  reset_line
end
handle_ctrl_d() click to toggle source
# File lib/net_shell/prompt.rb, line 144
def handle_ctrl_d
  exit 0 if @line.length == 0
  print "\a"
end
handle_down_arrow() click to toggle source
# 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_end() click to toggle source
# File lib/net_shell/prompt.rb, line 226
def handle_end
  @cursor = 0
end
handle_home() click to toggle source
# File lib/net_shell/prompt.rb, line 222
def handle_home
  @cursor = -@line.length
end
handle_left_arrow() click to toggle source
# File lib/net_shell/prompt.rb, line 194
def handle_left_arrow
  @cursor = [-@line.length, @cursor - 1].max
end
handle_newline() click to toggle source
# 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_pgdown() click to toggle source
# File lib/net_shell/prompt.rb, line 230
def handle_pgdown
  @offset = -1
  clear_line
  @line = history_at_offset
end
handle_right_arrow() click to toggle source
# 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_tab() click to toggle source
# 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_arrow() click to toggle source
# 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) click to toggle source
# 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
history_file=(file) click to toggle source
# File lib/net_shell/prompt.rb, line 52
def history_file=(file)
  @history_file = file
  @history = IO.read(file).split("\n") if File.exist?(file)
end
pad_line(line) click to toggle source
# File lib/net_shell/prompt.rb, line 240
def pad_line(line)
  line += " " unless line.empty? or line.match(/\s$/)
  line
end
prompt=(new_prompt) click to toggle source
# File lib/net_shell/prompt.rb, line 38
def prompt=(new_prompt)
  @prompt = new_prompt
end
quoted_argument(argument) click to toggle source
# File lib/net_shell/prompt.rb, line 34
def quoted_argument(argument)
  argument.match(/\s+/) ? "\"#{argument}\"" : argument
end
read_char() click to toggle source
# 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_line() click to toggle source
# 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_line() click to toggle source
# 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_prompt() click to toggle source
# File lib/net_shell/prompt.rb, line 74
def refresh_prompt
  @last_prompt = prompt
end
reset_line() click to toggle source
# 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_line() click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# File lib/net_shell/prompt.rb, line 236
def split_args(partial=true)
  partial ? @line.shellsplit_partial : @line.split(/\s+/)
end
suggest(str) click to toggle source
# File lib/net_shell/prompt.rb, line 251
def suggest(str)
  str ||= ""
  @suggestion = str.style(:light_black)
end
tab_completion(cmdline) click to toggle source
# 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_autosuggest() click to toggle source
# 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