Perl Modules: AnyEvent::ReadLine::Gnu
2013-09-06
REPLs (Read-Eval–Print Loop) can be handy little things. In UAV::Pilot, the uav
shell takes arbitrary Perl expressions and eval()
‘s them.
Before integrating with AnyEvent, handling the prompt was done by Term::ReadLine::Gnu. When AnyEvent was integrated, I wanted the shell to use AnyEvent’s non-blocking I/O, so it was migrated to AnyEvent::ReadLine::Gnu.
This also handles command history. Hit the ‘Up’ arrow to get your previous command. No code is necessary; AnyEvent::ReadLine::Gnu does it for you.
ReadLine also has options for tab-completion. I would like to add this to the uav
shell eventually.
Using the AnyEvent version is quite simple. You pass a callback that takes input. In the uav
callback, we only run the code when it ends with a semicolon (ignoring trailing whitespace). If it doesn’t, we save it in a buffer and wait for more input.
Here’s how this is implemented in UAV::Pilot:
my $readline; $readline = AnyEvent::ReadLine::Gnu->new(
prompt => 'uav> ',
on_line => sub {
my ($line) = @_;
add_cmd( $line );
if( $line =~ /; \s* \z/x ) {
my $cmd = full_cmd;
$readline->hide;
my $do_continue = run_cmd( $cmd, $repl );
$readline->show;
$cv->send( $do_continue ) unless $do_continue;
}
},
);
The add_cmd()
method adds the input to the buffer. If that did end with a semicolon, then we call full_cmd()
to get back the text of the code. It also clears the buffer. run_cmd()
then eval()
‘s the code. If we’re meant to exit the program, it returns false, which we handle with the $cv->send
.
The $readline->hide
and $readline->show
calls stop ReadLine from outputting the prompt when we might have other output going.