How to continue with <space> - ruby

I need a method with which I can proof if the last entered letter is a space while I'm typing something so the program jumps to the next line code.
Normally it works with Enter but I want to continue it with Space.
I tried to use gets.chomp[-1] but you always have to press Enter.

You might have to use getch. See "Get single char from console immediately" and compose your own line input. gets doesn't act on pressing a space but groups the whole line input until Enter is pressed.
The gets documentation says it:
returns (and assigns to $_) the next line ... from standard input.
so if you press the space bar, and you could press backspace and do other things before you press Enter, and Ruby won't act on the space being pressed.

When you call gets, Ruby delegates the line editing to your terminal. With default settings, your terminal buffers the input and provides some basic line-editing: it prints the entered character to the screen and allows you to delete the last caracter via del or an entire word via ^W (depending on your terminal). Once you press enter, the terminal sends the finished buffer back to Ruby.
Ruby isn't aware of any of this. It doesn't see your editing, nor does it see a del control character in the resulting string. The only thing it gets is the composed line as a whole. Likewise, Ruby isn't aware of the separate key presses and can't intercept space on its own.
But fortunately, terminals are very flexible. Almost any setting can be configured, including the end-of-line character. So in order to make space work like enter, we have to adjust the terminal settings!
This can be done using the stty command line tool. In addition, you have to change Ruby's input record separator $/ (which defaults to newline) to space, so gets and chomp behave accordingly.
Here's a quick and dirty example:
begin
settings = `stty -g` # get current terminal settings
system('stty', 'eol', ' ') # make terminal recognize space as end-of-line
$/ = ' ' # set Ruby's input record separator to space
puts 'hit ^D to exit'
while input = gets(chomp: true)
p input: input
end
ensure
system('stty', settings) # revert terminal settings
end
Running the above code in a terminal and entering foo bar baz (with trailing space) gives:
hit ^D to exit
foo {:input=>"foo"}
bar {:input=>"bar"}
baz {:input=>"baz"}
Another option is to put your terminal into raw mode, either by calling stty or via require 'io/console' (see the docs). This disables all line editing features (including printing the entered characters) and passes the input directly to Ruby. It gives you even more control over the input but obviously needs much more work to get the line editing features we're accustomed to.
The documentation for termios provides more information.

Related

Ruby: "Parasite" characters with gets after pressing arrow key on getch

I've been looking into a way to implement a "Press any key to continue" feature into my program, and figured I'd use $stdin.getch.
Seems to work in most cases, but I noticed that if I pressed an arrow key when prompted to press any key, the next gets command would have "parasites" characters at the beginning of the string it returned, presumably because arrow keys give several characters as input and only the first one is read by getch, with the rest being saved for the next gets. More precisely, when I press the Right Arrow key, getch returns "\e" and the next gets return has a "[C" prefixed to it.
I thought maybe I had to flush/empty the stdin buffer, but flush, iflush and ioflush don't seem to do anything. Trying to write '' into stdin before the gets doesn't work either. I've also tried sync and fsync on $stdin, but it doesn't work either. Any idea what the problem could be?
getch puts the IO in so-called raw mode and then reads a single character.
However, there are keys that generate more than one character when being pressed. The right arrow key → for example is usually configured to generate the 3-byte sequence \e[C which is the ANSI escape sequence for "cursor forward".
Now, if you attempt to read that sequence via getch, you'll only get \e, i.e. the very first character. All subsequent characters (here: [ and C) will be returned by the next read operation.
Instead of reading a single character via getch, you could put stdin into raw mode manually and read all available characters (up to a reasonable limit) via readpartial, e.g.:
$stdin.raw { |io| io.readpartial(100) }
The above returns the whole "\e[C" sequence when pressing → and behaves like getch for single-character keys.
Note that terminal emulators and operating systems behave differently. The above works just fine for me, but you might want to test the solution on various systems yourself.

Can interpolation be done on print output and in the middle of the output, is it possible to use gets in Ruby? (expecting reply from Ruby folks)

print """Input 1: (here I want *gets* to have some input)
Input 2:
"""
is it possible to get input in the middle of the printed 2 or more lines string in Ruby?
I can't tell exactly from your question, but I think this is what you are after:
This solution uses something called ANSI escape codes, which are "magic" sequences of characters you can use to control the terminal. You can do a variety of things with them, most commonly change output colour, but here we're using them to move the cursor around.
This works on my Mac, and should work on Linux - I'm not sure if this will work on Windows, you may have to enable ANSI escape sequences manually.
The general approach is:
Print the prompts
Move the cursor up to the line of the first prompt
Move the cursor forward to the end of that prompt
Accept input with gets
Move the cursor forward to the end of the second prompt
Accept input again
Here's a test script I wrote to do this, with enough comments to hopefully explain what is going on. I've changed the prompts from your original question so that they each have different lengths, to test that this solution can handle that.
prompt_1 = "First Input:"
prompt_2 = "Another Input:"
print "#{prompt_1}\n#{prompt_2}"
# Move the cursor back up to the line with the first prompt on
print "\033[1A"
# Move the cursor back to the start of the line, so we know we're in column zero
# Then move it to after the prompt
print "\033[1000D"
print "\033[#{prompt_1.length + 1}C"
# Get input
first_number = gets
# Hitting Enter after `gets` will have moved us to the next line already
# Move the cursor to the end of the second prompt
print "\033[1000D"
print "\033[#{prompt_2.length + 1}C"
# Get input again
second_number = gets
# Print output
puts "Addition is: #{first_number.strip.to_i + second_number.strip.to_i}"

'gets' doesn't wait for user input

I'm attempting to develop a program on repl.it using its Ruby platform. Here's what I've got:
puts "Copy the entire request page and paste it into this console, then
hit ENTER."
request_info = gets("Edit Confirmation").chomp.gsub!(/\s+/m, '
').strip.split(" ")
puts "What is your name?"
your_name = gets.chomp
puts "Thanks, #{your_name}!"
The way I've got it, the user pastes a multi-line request, which ends with "Edit Confirmation", and then it splits the request, word-by-word, into its own array for me to parse and pull the relevant data.
But I can't seem to use the gets command a second time after initially inquiring the user for a multi-line input at the start. Any other gets command I attempt to use after that is skipped, and the program ends.
Your code is doing something quite unusual: By passing a string to the gets method, you are actually changing the input separator:
gets(sep, limit [, getline_args]) → string or nil
Reads the next “line'' from the I/O stream; lines are separated by sep.
The reason why your code is not working as you expect is because a trailing "\n" character is left in the input buffer - so calling gets a second time instantly returns this string.
Perhaps the easiest way to resolve this would just be to absorb this character in the first gets call:
request_info = gets("Edit Confirmation\n").chomp.gsub!(/\s+/m, ' ').strip.split(" ")
For a "complex" multi-line input like this, it would be more common to pass a file name parameter to the ruby script, and read this file rather than pasting it into the terminal.
Or, you could use gets(nil) to read until an EOF character and ask the user to press CTRL+D to signify the end of the multi-line input.

Forward delete character?

To dynamically delete a character from a string, you can use the /b character.
puts "Hello\b World!" #=> Hell World!
\b basically does the same thing as a backspace. Is there a character that emulates a forward delete?
In the execution of:
puts "Hello\b World!"
The \b doesn't delete the prior character. This is a common misconception since a backspace used on a keyboard will delete the previously typed character prior to the cursor on screen and from the keyboard input buffer. That behavior occurs because of how the keyboard input software of the operating system is designed.
In the case of the above puts, the o still exists in the string. What happens is that, when displayed, the backspace causes the o to be overwritten by the following space. This occurs because the o is display first, followed by the backspace (output cursor is backed up one character position), followed by the space, in sequence.
If you could have such a case where:
puts "Hello<del> World!"
would display HelloWorld!, then that would mean the output of the value of <del> would somehow cause the following output of space () to not occur. In other words, the <del> would have the function of, "whatever the next charter is that comes for the output, skip it". I don't believe such a control character exists in Windows or Linux output, although I suppose it would be possible to write an output driver that would have that behavior for some defined control character.
You might even be able to do something like this:
"Hello W<left-arrow><left-arrow><del><right-arrow>orld!"
Which would display HelloWorld if your terminal is set up to accept control characters that move the cursor left or right. But it still obviously isn't the same functionality as the "delete in the future" case.

Emacs showing ^M in a process buffer

At the moment, I have a process-buffer which is utf-8-auto (emacs modeline reports the buffer as utf-8-auto-dos) with CRLF style newlines. When I write multi-line text into the buffer via a process-send-region or process-send-string each line is suffixed with ^M.
What makes this problem odd is that text written to the process-buffer directly from the process, does not contain ^M's.
It doesn't seem to make any difference where the source text comes from, in fact, even a multi-line region marked and sent that already appears in the process buffer (that doesn't contain ^M) will have them when sent.
(Note the source text for the process-send-region will always come from a Emacs buffer, process-send-string, when multi-line will be from the Windows clipboard interface to the killring, or again from an Emacs buffer to killring.)
I should also add that the incoming text to the buffer is parsed by a after-change-functions hook (to do some colorisation based on input) so a last resort I'd do an additional regexp-replace-in-string on this incoming text as part of that hook function, I'd like to avoid that because it seems wrong, but I'll add it as a hacky solution if nothing else works.
Addendum
I updated the encoding settings for the buffer and the process to use utf-8-dos instead of utf-8-auto and the ^M's vanished.
So in the buffer setup part of my app, I did...
(switch-to-buffer "sock-buffer")
(set-process-coding-system (get-process sock-process) 'utf-8-dos 'utf-8-dos)
(set-buffer-file-coding-system 'utf-8-dos nil)
(set-buffer-process-coding-system 'utf-8-dos 'utf-8-dos)
Then reduced this to just...
(switch-to-buffer "sock-buffer")
(set-buffer-process-coding-system 'utf-8-dos 'utf-8-dos)
And everything worked fine.
This is because those files are in DOS/Windows line endings. You can use C-x [Enter] f unix [Enter] to convert them to the Unix encoding.
^L is a page break. I've seen them some times to separate different parts of source code (for old-fashioned listings in a text printer), or in text documentation to insert an actual "new page" command.
As of the update, here you can see that you have to select set-process-coding-system to the correct coding system.
Alternately to the dos2unix approach, you could use one of the MULE commands in Emacs, or (my favorite), since these characters are mistakenly treated as part of the text, you can replace them using the command to replace a string in the text: M-% C-q C-M RETURN
M-% is the query-replace command.
C-q means "let me type the next character without interpreting it as the RETURN key".
I believe you see those because of the inconsistencies in your newlines (e.g. windows newlines vs *nux ones), you should probably try dos2unix

Resources