Term::ReadLine::Perl on Windows

2010-11-06 perl command line user interface terminal Term::ReadLine Term::ReadLine::Perl

I plan to write simple console application to interface with databases I have to maintain. Something like mysql command do, but with few advanced features like autocompletion (which does not work in mysql on Windows) or sending output to pager.

Term::ReadLine came as natural choice, so I took the example from SYNOPSIS and tried to add simple auto-completion. Traversal along the line and history worked fine, but I could not make it react on Tab at all.

Finally runned my script with Devel::Trace turned on and in its lenghty output I found this part:

>> C:/Perl/lib/Term/ReadLine/readline.pm:1536:     if ($dumb_term) {
>> C:/Perl/lib/Term/ReadLine/readline.pm:1537: 	return readline_dumb;

It looks like it is using some dumb terminal for some reason. Quick inspection of readline_dumb revealed, that apart of printing the prompt it simplifies its job to just calling get_line that look like this.

sub get_line {
    my $self = shift;
    my $fh = $self->[0];
    scalar <$fh>;
}

I was quite surprised, but it looks all history and editing magic is just feature of reading line from STDIN. And it is really so, running

perl -le "print <STDIN>"

allows to use up/down arrows to get history and quite good line editing features.

With little more inspection I found that Windows set TERM environment variable to dumb, which switch on this behavior. Setting it to anything else turn on both autocompletion and various keyboard shortcuts normal on Unix command-lines.

Following snippet works and does simple case-insensitive auto-completion on few SQL commands.

use Term::ReadLine;

$ENV{TERM} = 'not dumb' if $^O eq 'MSWin32';

my $term   = Term::ReadLine->new('SQL Console');
my $prompt = "> ";
my $OUT    = $term->OUT || \*STDOUT;

$term->Attribs->{completion_function} = sub {
    my ($text, $line, $start) = @_;
    return grep { /^$text/i } (qw(
            SELECT INSERT UPDATE DELETE FROM WHERE AS IN ASC DESC
        ),'ORDER BY');
};

while (defined($_ = $term->readline($prompt))) {
    print $OUT $_, "\n";
    $term->addhistory($_) if /\S/;
}