How to get/set the console cursor position in Ruby (Windows)
-
18-09-2019 - |
Question
I'm trying to write a shell in Ruby, and to implement tab completion I am using the WinAPI function getch
to read in a character at a time from the user, checking for tabs.
The problem with this is the backspace key:
- It moves the cursor further back than the prompt (eg, with prompt
hello>
, the user can backspace the cursor on to theh
. I would like it to stop at the final space. - When the user's text overflows on to the next row of the console, backspace will not move back up to the previous line.
(I know both of these behaviours are by design.)
My imagined solution to these problems involve controlling the cursor movement; I need to know where the cursor is, and be able to move it.
On Linux, I would use ANSI escape sequences, but these aren't supported by the Windows console.
I have looked into the WinAPI and tried to find functions that would let me do this, but all I could find was GetConsoleCursorInfo
function, which only returns the size and visibility of the cursor.
Examples would be appreciated, as I am hopeless at using the Win32API class for anything other than primitive functions.
Thanks.
Solution
You're probably better off using readline. It is included in the Ruby One-Click installer. A basic setup is:
require 'readline'
while line = Readline.readline('hello> ', true)
#do something with line
break if line == 'quit'
end
Already you'll have standard readline capabilities such as backspacing, Alt+backspace to delete a word, history, and tab completion. There's good documentation on how to customize it for your needs here.
Edit:
If you don't have readline installed, you can get it and other external libraries here. You'll want the readline-4.3-2-mswin32 package. Copy the readline.dll file (located in the bin
directory) to your ruby\bin
directory. That should do it.
Although it isn't documented on the Ruby homepage, it looks as if you can also use readline 5, available here. Specifically, you need the binaries distribution. Copy readline5.dll (in the bin
directory) to your ruby\bin
directory, and rename it to readline.dll
.
Also, as a side note, don't be alarmed if require 'readline'
returns false when using irb, since it appears to pre-load it.
OTHER TIPS
Hmm, it's certainly possible to back up, and with reasonably portable code, as bash(1) can back up to the previous line even in a dos box. I imagine it is using termcap
or ncurses
, and it has in the termcap database a set of control codes that work for the dos box.
In Ruby, I don't believe there are any termcap bindings, so you use ncurses, rather than hardwiring into your program a set of device-dependent control codes. (You would want ncurses over termcap anyway.)
Once you switch to ncurses I believe you will find API elements to do everything you need, including backing up lines and not overwriting the prompt. (And certainly you should not back up over anything that you didn't output to start with, no matter what library is in use.)
Actually, I kind of like Pesto's answer. Use ncurses if readline
doesn't work out or if you need cursor addressing for some other reason.
For a windows-friendly readline implementation try this ruby-based readline