POE::Wheel::ReadLine - prompted terminal input
# Create the wheel. $heap->{wheel} = POE::Wheel::ReadLine->new( InputEvent => got_input, appname => 'mycli' ); # Trigger the wheel to read a line of input. $heap->{wheel}->get( 'Prompt: ' ); # Add a line to the wheel's input history. $heap->{wheel}->addhistory( $input ); # Input handler. If $input is defined, then it contains a line of # input. Otherwise $exception contains a word describing some kind # of user exception. Currently these are 'interrupt' and 'cancel'. sub got_input_handler { my ($heap, $input, $exception) = @_[HEAP, ARG0, ARG1]; if (defined $input) { $heap->{wheel}->addhistory($input); $heap->{wheel}->put("\tGot: $input"); $heap->{wheel}->get('Prompt: '); # get another line } else { $heap->{wheel}->put("\tException: $exception"); } } # Clear the terminal. $heap->{wheel}->clear();
ReadLine performs non-blocking, event-driven console input, using Term::Cap to interact with the terminal display and Term::ReadKey to interact with its keyboard.
ReadLine handles almost all common input editing keys; it provides an input history list; it has both vi and emacs modes; it provides incremental search facilities; it is fully customizable and it is compatible with standard readline(3) implementations such as Term::ReadLine::Gnu.
ReadLine is configured by placing commands in an initialization file (the inputrc file). The name of this file is taken from the value of the INPUTRC environment variable. If that variable is unset, the default is ~/.inputrc. When the wheel is instantiated, the init file is read and the key bindings and variables are set. There are only a few basic constructs allowed in the readline init file. Blank lines are ignored. Lines beginning with a '#' are comments. Lines beginning with a '$' indicate conditional constructs. Other lines denote key bindings and variable settings. Each program using this library may add its own commands and bindings. For more detail on the inputrc file, see readline(3).
The default editing mode will be emacs-style, although this can be configured by setting the 'editing-mode' variable within the inputrc, or by setting the EDITOR environment variable.
Adds a list of lines, presumably from previous input, into the ReadLine wheel's input history.
Returns the list of all currently known history lines.
writes the current history to FILENAME, overwriting FILENAME if necessary. If FILENAME is false, then write the history list to ~/.history. Returns true if successful, or false if not.
adds the contents of FILENAME to the history list, a line at a time. If FILENAME is false, then read from ~/.history. Start reading at line FROM and end at TO. If FROM is omitted or zero, start at the beginning. If TO is omitted or less than FROM, then read until the end of the file. Returns true if successful, or false if not.
Truncate the number of lines within FILE to be at most that specified by LINES. FILE defaults to ~/.history. If LINES is not specified, then the history file is cleared.
Clears the terminal.
Returns what ReadLine thinks are the current dimensions of the terminal. The return value is a list of two elements: the number of columns and number of rows respectively.
Provide a prompt and enable input. The wheel will display the prompt and begin paying attention to the console keyboard after this method is called. Once a line or an exception is returned, the wheel will resume its quiescent state wherein it ignores keystrokes.
The quiet period between input events gives a program the opportunity to change the prompt or process lines before the next one arrives.
Returns a reference to a hash of options that can be configured to modify the readline behaviour.
Bind a function to a named key sequence. The key sequence can be in any of the forms defined within readline(3). The function should either be a pre-registered name such as 'self-insert', or it should be a reference to a function. The binding is made in the current keymap. If you wish to change keymaps, then use the rl_set_keymap method.
Create a new (global) function definition which may be then bound to a key.
InputEvent contains the name of the event that will be fired upon successful (or unsuccessful) terminal input. Every InputEvent handler receives two additional parameters, only one of which is ever defined at a time. ARG0 contains the input line, if one was present. If ARG0 is not defined, then ARG1 contains a word describing a user-generated exception:
ARG0
ARG1
The 'interrupt' exception means a user pressed C-c (^C) to interrupt the program. It's up to the input event's handler to decide what to do next.
The 'cancel' exception means a user pressed C-g (^G) to cancel a line of input.
The 'eot' exception means the user pressed C-d (^D) while the input line was empty. EOT is the ASCII name for ^D.
Finally, ARG2 contains the ReadLine wheel's unique ID.
ARG2
PutMode specifies how the wheel will display text when its put() method is called.
put()
put() displays text immediately when the user isn't being prompted for input. It will also pre-empt the user to display text right away when PutMode is "immediate".
When PutMode is "after", all put() text is held until after the user enters or cancels (See C-g) her input.
PutMode can also be "idle". In this mode, text is displayed right away if the keyboard has been idle for a certain period (see the IdleTime parameter). Otherwise it's held as in "after" mode until input is completed or canceled, or until the keyboard becomes idle for at least IdleTime seconds. This is ReadLine's default mode.
IdleTime specifies how long the keyboard must be idle before put() becomes immediate or buffered text is flushed to the display. It is only meaningful when InputMode is "idle". IdleTime defaults to two seconds.
Registers an application name which is used to get appl-specific keybindings from the .inputrc. If not defined, then the default value is 'poe-readline'. You may use this in a standard inputrc file to define application specific settings. For example:
$if poe-readline # bind the following sequence in emacs mode set keymap emacs # display poe debug data Control-xP: poe-wheel-debug $endif
To bind keys to your own functions, the function name has to be made visible to the wheel before the binding is attempted. To register a function, use the method POE::Wheel::ReadLine::add_defun:
POE::Wheel::ReadLine->add_defun('reverse-line', \&reverse_line);
The function will be called with three parameters: a reference to the wheel object itself, the key sequence in a printable form, and the raw key sequence. When adding a new defun, an optional third parameter may be provided which is a key sequence to bind to. This should be in the same format as that understood by the inputrc parsing.
To configure completion, you need to modify the 'completion_function' value to be a reference to a function. The function should take three scalar parameters: the word being completed, the entire input text and the position within the input text of the word. The return result is expected to be a list of possible matches. An example usage is as follows:
my $attribs = $wheel->Attribs; $attribs->{completion_function} = sub { my ($text, $line, $start) = @_; return qw(a list of candidates to complete); }
This is the only form of completion currently supported.
Although modeled after the readline(3) library, there are some areas which have not been implemented. The only option settings which have effect in this implementation are: bell-style, editing-mode, isearch-terminators, comment-begin, print-completions-horizontally, show-all-if-ambiguous and completion_function.
The function 'tab-insert' is not implemented, nor are tabs displayed properly.
POE::Wheel, readline(3), Term::ReadKey, Term::Visual.
The SEE ALSO section in POE contains a table of contents covering the entire POE distribution.
POE::Wheel::ReadLine has some known issues:
Non-blocking input with Term::ReadKey does not work with Perl 5.8.0. The problem usually appears on Linux systems. See: http://rt.cpan.org/Ticket/Display.html?id=4524 and all the tickets related to it.
If you suspect your system is one where Term::ReadKey fails, you can run this test program to be sure. If you can, upgrade Perl to fix it. If you can't upgrade Perl, consider alternative input methods, such as Term::Visual.
#!/usr/bin/perl use Term::ReadKey; print "Press 'q' to quit this test.\n"; ReadMode 5; # Turns off controls keys while (1) { while (not defined ($key = ReadKey(-1))) { print "Didn't get a key. Sleeping 1 second.\015\012"; sleep (1); } print "Got key: $key\015\012"; ($key eq 'q') and last; } ReadMode 0; # Reset tty mode before exiting exit;
Dissociating the input and display cursors introduced a lot of code. Much of this code was thrown in hastily, and things can probably be done with less work. To do: Apply some thought to what's already been done.
The screen should update as quickly as possible, especially on slow systems. Do little or no calculation during displaying; either put it all before or after the display. Do it consistently for each handled keystroke, so that certain pairs of editing commands don't have extra perceived latency.
Input editing is not kept on one line. If it wraps, and a terminal cannot wrap back through a line division, the cursor will become lost. This bites, and it's the next against the wall in my bug hunting.
Unicode, or at least European code pages. I feel real bad about throwing away native representation of all the 8th-bit-set characters. I also have no idea how to do this, and I don't have a system to test this. Patches are recommended.
Q: Why do I lose my ReadLine prompt every time I send output to the screen?
A: You probably are using print or printf to write screen output. ReadLine doesn't track STDOUT itself, so it doesn't know when to refresh the prompt after you do this. Use ReadLine's put() method to write lines to the console.
Q: None of the editing keystrokes work. Ctrl-C displays "^c" rather than generating an interrupt. The arrow keys don't scroll through my input history. It's generally a bad experience.
A: You're probably a vi/vim user. In the absence of a ~/.inputrc file, POE::Wheel::ReadLine checks your EDITOR environment variable for clues about your editing preference. If it sees /vi/ in there, it starts in vi mode. You can override this by creating a ~/.inputrc file containing the line "set editing-mode emacs", or adding that line to your existing ~/.inputrc. While you're in there, you should totally get acquainted with all the other cool stuff you can do with .inputrc files.
Rocco Caputo - Original author. Nick Williams - Heavy edits, making it gnu readline-alike.
Please see POE for more information about other authors and contributors.
To install POE, copy and paste the appropriate command in to your terminal.
cpanm
cpanm POE
CPAN shell
perl -MCPAN -e shell install POE
For more information on module installation, please visit the detailed CPAN module installation guide.