Search code examples
perlreadline

How can I read user input using Term::ReadLine without having the newline character printed out?


How can I read user input using Term::ReadLine without having a newline character printed out when the user presses Enter?

The reason why I want to do this is because I want to read user input from a prompt at the very bottom of the screen (as in less or vim). Currently, pressing Enter causes the screen to scroll down, and that can be an issue. Also, I'd like to avoid having to appeal to ncurses at this point.

Setting $term->Attribs->{echo_control_characters} to 0, undef, or off doesn't seem to work.

#!perl
use Term::ReadLine;
use Term::ReadKey;
my $term = new Term::ReadLine ('me');
$term->ornaments(0);
$term->Attribs->{echo_control_characters} = 0;
print STDERR "\e[2J\e[s\e[" . ( ( GetTerminalSize() ) [1] )  . ";1H";  # clear screen, save cursor position, and go to the bottom;
my $input = $term->readline('> ');
print STDOUT "\e[uinput = $input\n";   # restore cursor position, and print

I could do that using Term::ReadKey with cbreak as read mode:

#!perl
use Term::ReadKey;
ReadMode 'cbreak';
my ( $k, $input );
print STDERR "> ";
while ( defined ( $k = ReadKey 0 ) and $k !~ /\n/ )
{
  $input .= $k;
  print STDERR $k;
}
print STDOUT "input = $input\n";
ReadMode 'restore';

But then I wouldn't be able to use Term::ReadLine features, such as history, completion, and line editing.


Solution

  • You can set the rl_getc_function to intercept the carriage return before it is printed as shown in this question. The following works for me:

    use strict;
    use warnings;
    BEGIN {
        $ENV{PERL_RL} = "Gnu";
    }
    use Term::ReadLine;
    use Term::ReadKey;
    
    my $term = Term::ReadLine->new('me');
    my $attr = $term->Attribs;
    $term->ornaments(0);
    $attr->{getc_function} = sub {
        my $ord = $term->getc($attr->{instream});
        if ( $ord == 13 ) {  # carriage return pressed
            $attr->{done} = 1;
            return 0;
        }
        return $ord;
    };
    print STDERR "\e[2J\e[s\e[" . ( ( GetTerminalSize() ) [1] )  . ";1H";
    my $input = $term->readline('> ');
    print STDOUT "\e[uinput = $input\n";   # restore cursor position, and print