4

I am in a Ruby script something like:

something = gets      # or STDIN.gets
puts something

def arrow_pressed
    puts "arrow totally pressed"
end

Once the user prompt exists I want all keyboard input to happen normally except for the arrow keys. If those are pressed I don't want anything to appear in the terminal (e.g. ^[[A). I just want to execute my arrow_pressed method. And I want to execute it without the user needing to hit Return

I already tried this gist - which works well except that even after changing it to let through most keystrokes normally, I still can't get Backspace (or other specials) to work.

I'm happy to use something other than gets (a library or whatever). Any help would be appreciated!

PlacateTheCattin
  • 1,356
  • 2
  • 11
  • 10
  • 1
    did you try [`remedy`](https://github.com/acook/remedy)? the library the gist you showed was rolled into? Seems fairly high functioning – engineersmnky Mar 28 '18 at 18:22
  • 1
    i'm not sure there's a way to do this without manually handling _all_ user input, but i would be interested to be proved wrong. – max pleaner Mar 28 '18 at 18:24
  • 1
    This is actually non-trivial. What you want is essentially "[cooked](http://ruby-doc.org/stdlib/libdoc/io/console/rdoc/IO.html#method-i-cooked)" input except for a few exceptional cases. There isn't going to be an easy solution. You can use something like getch or remedy or curses to get raw input access, but you'll need to re-implement line editing yourself i.e. handle cases like the user pressing left/right/backspace/delete to modify the input. – Max Mar 28 '18 at 18:42

2 Answers2

5

I'm the author of tty-reader that provides a higher-level API for handling keyboard input. Using it you can read a single keypress, entire line or multiline content. It also allows you to listen for different key events like key up or backspace.

For example, to prompt the user for input and perform actions when arrows or backspace are pressed:

require "tty-reader"

reader = TTY::Reader.new

reader.on(:keydown) { puts "down we go" }
      .on(:keyup) { puts "up we go" }
      .on(:keybackspace) { puts "backspace pressed" }

answer = reader.read_line

The gem has been tested to work on all major platforms including Windows.

Piotr Murach
  • 547
  • 6
  • 10
3

You could use getch but since arrow keys are given as ANSI escape sequences, some parsing is required:

require 'io/console'

loop do
  case $stdin.getch
  when 'q'    then exit
  when "\c?"  then puts 'backspace'
  when "\e"   # ANSI escape sequence
    case $stdin.getch
    when '['  # CSI
      case $stdin.getch
      when 'A' then puts 'up'
      when 'B' then puts 'down'
      when 'C' then puts 'right'
      when 'D' then puts 'left'
      end
    end
  end
end

This is just an example. There are many other escape sequences you'll have to consider.

Stefan
  • 109,145
  • 14
  • 143
  • 218