NAME¶
POE::Wheel::ReadLine - non-blocking Term::ReadLine for POE
SYNOPSIS¶
#!perl
use warnings;
use strict;
use POE qw(Wheel::ReadLine);
POE::Session->create(
inline_states=> {
_start => \&setup_console,
got_user_input => \&handle_user_input,
}
);
POE::Kernel->run();
exit;
sub handle_user_input {
my ($input, $exception) = @_[ARG0, ARG1];
my $console = $_[HEAP]{console};
unless (defined $input) {
$console->put("$exception caught. B'bye!");
$_[KERNEL]->signal($_[KERNEL], "UIDESTROY");
$console->write_history("./test_history");
return;
}
$console->put(" You entered: $input");
$console->addhistory($input);
$console->get("Go: ");
}
sub setup_console {
$_[HEAP]{console} = POE::Wheel::ReadLine->new(
InputEvent => 'got_user_input'
);
$_[HEAP]{console}->read_history("./test_history");
$_[HEAP]{console}->clear();
$_[HEAP]{console}->put(
"Enter some text.",
"Ctrl+C or Ctrl+D exits."
);
$_[HEAP]{console}->get("Go: ");
}
DESCRIPTION¶
POE::Wheel::ReadLine is a non-blocking form of Term::ReadLine that's compatible
with POE. It uses Term::Cap to interact with the terminal display and
Term::ReadKey to interact with the keyboard.
POE::Wheel::ReadLine handles almost all common input editing keys. It provides
an input history list. It has both vi and emacs modes. It supports incremental
input search. It's fully customizable, and it's compatible with standard
readline(3) implementations such as Term::ReadLine::Gnu.
POE::Wheel::ReadLine is configured by placing commands in an "inputrc"
initialization file. The file's name is taken from the "INPUTRC"
environment variable, or ~/.inputrc by default. POE::Wheel::ReadLine will read
the inputrc file and configure itself according to the commands and variables
therein. See
readline(3) for details about inputrc files.
The default editing mode will be emacs-style, although this can be configured by
setting the 'editing-mode' variable within an inputrc file. If all else fails,
POE::Wheel::ReadLine will determine the user's favorite editor by examining
the EDITOR environment variable.
PUBLIC METHODS¶
Constructor¶
Most of POE::Wheel::ReadLine's interaction is through its constructor,
new().
new
new() creates and returns a new POE::Wheel::ReadLine object. Be sure to
instantiate only one, as multiple console readers would conflict.
InputEvent
"InputEvent" names the event that will indicate a new line of console
input. See "PUBLIC EVENTS" for more details.
PutMode
"PutMode" controls how output is displayed when
put() is called
during user input.
When set to "immediate",
put() pre-empts the user immediately.
The input prompt and user's input to date are redisplayed after
put()
is done.
The "after" "PutMode" tells
put() to wait until after
the user enters or cancels her input.
Finally, "idle" will allow
put() to pre-empt user input if the
user stops typing for "IdleTime" seconds. This mode behaves like
"after" if the user can't stop typing long enough. This is
POE::Wheel::ReadLine's default mode.
IdleTime
"IdleTime" tells POE::Wheel::ReadLine how long the keyboard must be
idle before "put()" becomes immediate or buffered text is flushed to
the display. It is only meaningful when "PutMode" is
"idle". "IdleTime" defaults to 2 seconds.
AppName
"AppName" registers an application name which is used to retrieve
application-specific key bindings from the inputrc file. The default
"AppName" is "poe-readline".
# If using POE::Wheel::ReadLine, set
# the key mapping to emacs mode and
# trigger debugging output on a certain
# key sequence.
$if poe-readline
set keymap emacs
Control-xP: poe-wheel-debug
$endif
History List Management¶
POE::Wheel::ReadLine supports an input history, with searching.
add_history
add_history() accepts a list of lines to add to the input history.
Generally it's called with a single line: the last line of input received from
the terminal. The "SYNOPSIS" shows
add_history() in action.
get_history
get_history() returns a list containing POE::Wheel::ReadLine's current
input history. It may not contain everything entered into the wheel
write_history
write_history() writes the current input history to a file. It accepts
one optional parameter: the name of the file where the input history will be
written.
write_history() will write to ~/.history if no file name is
specified.
Returns true on success, or false if not.
The "SYNOPSIS" shows an example of
write_history() and the
corresponding
read_history().
read_history
read_history(FILENAME, START, END) reads a previously saved input history from a
named file, or from ~/.history if no file name is specified. It may also read
a subset of the history file if it's given optional START and END parameters.
The file will be read from the beginning if START is omitted or zero. It will
be read to the end if END is omitted or earlier than START.
Returns true on success, or false if not.
The "SYNOPSIS" shows an example of
read_history() and the
corresponding
write_history().
Read the first ten history lines:
$_[HEAP]{console}->read_history("filename", 0, 9);
history_truncate_file
history_truncate_file() truncates a history file to a certain number of
lines. It accepts two parameters: the name of the file to truncate, and the
maximum number of history lines to leave in the file. The history file will be
cleared entirely if the line count is zero or omitted.
The file to be truncated defaults to ~/.history. So calling
history_truncate_file() with no parameters clears ~/.history.
Returns true on success, or false if not.
Note that
history_trucate_file() removes the earliest lines from the
file. The later lines remain intact since they were the ones most recently
entered.
Keep ~/.history down to a manageable 100 lines:
$_[HEAP]{console}->history_truncate_file(undef, 100);
Key Binding Methods¶
bind_key
bind_key(KEYSTROKE, FUNCTION) binds a FUNCTION to a named KEYSTROKE sequence.
The keystroke sequence can be in any of the forms defined within
readline(3). The function should either be a pre-defined name, such as
"self-insert" or a function reference. The binding is made in the
current keymap. Use the
rl_set_keymap() method to change keymaps, if
desired.
add_defun NAME FN
add_defun(NAME, FUNCTION) defines a new global FUNCTION, giving it a specific
NAME. The function may then be bound to keystrokes by that NAME.
Console I/O Methods¶
clear
Clears the terminal.
terminal_size
Returns what POE::Wheel::ReadLine thinks are the current dimensions of the
terminal. Returns a list of two values: the number of columns and number of
rows, respectively.
sub some_event_handler {
my ($columns, $rows) = $_[HEAP]{console}->terminal_size;
$_[HEAP]{console}->put(
"Terminal columns: $columns",
"Terminal rows: $rows",
);
}
get
get() causes POE::Wheel::ReadLine to display a prompt and then wait for
input. Input is not noticed unless
get() has enabled the wheel's
internal I/O watcher.
After
get() is called, the next line of input or exception on the console
will trigger an "InputEvent" with the appropriate parameters.
POE::Wheel::ReadLine will then enter an inactive state until
get() is
called again.
Calls to
get() without an argument will preserve the current prompt.
Calling
get() with an argument before a whole line of input is received
will change the prompt on the fly.
See the "SYNOPSIS" for sample usage.
put
put() accepts a list of lines to put on the terminal.
POE::Wheel::ReadLine is line-based. See POE::Wheel::Curses for more funky
display options.
Please do not use
print() with POE::Wheel::ReadLine.
print()
invariably gets the newline wrong, leaving an application's output to
stairstep down the terminal. Also,
put() understands when a user is
entering text, and "PutMode" may be used to avoid interrupting the
user.
ReadLine Option Methods¶
attribs
attribs() returns a reference to a hash of readline options. The returned
hash may be used to query or modify POE::Wheel::ReadLine's behavior.
option
option(NAME) returns a specific member of the hash returned by
attribs().
It's a more convenient way to query POE::Wheel::ReadLine options.
PUBLIC EVENTS¶
POE::Wheel::ReadLine emits only a single event.
"InputEvent" names the event that will be emitted upon any kind of
complete terminal input. Every "InputEvent" handler receives three
parameters:
$_[ARG0] contains a line of input. It may be an empty string if the user entered
an empty line. An undefined $_[ARG0] indicates some exception such as
end-of-input or the fact that the user canceled their input or pressed C-c
(^C).
$_[ARG1] describes an exception, if one occurred. It may contain one of the
following strings:
- cancel
- The "cancel" exception indicates when a user has canceled a line
of input. It's sent when the user triggers the "abort" function,
which is bound to C-g (^G) by default.
- eot
- "eot" is the ASCII code for "end of tape". It's
emitted when the user requests that the terminal be closed. By default,
it's triggered when the user presses C-d (^D) on an empty line.
- interrupt
- "interrupt" is sent as a result of the user pressing C-c (^C) or
otherwise triggering the "interrupt" function.
Finally, $_[ARG2] contains the ID for the POE::Wheel::ReadLine object that sent
the "InputEvent".
CUSTOM BINDINGS¶
POE::Wheel::ReadLine allows custom functions to be bound to keystrokes. The
function must be made visible to the wheel before it can be bound. To register
a function, use POE::Wheel::ReadLine's
add_defun() method:
POE::Wheel::ReadLine->add_defun('reverse-line', \&reverse_line);
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.
Bound functions receive three parameters: A reference to the wheel object
itself, the key sequence that triggered the function (in printable form), and
the raw key sequence. The bound function is expected to dig into the
POE::Wheel::ReadLine data members to do its work and display the new line
contents itself.
This is less than ideal, and it may change in the future.
CUSTOM COMPLETION¶
An application may modify POE::Wheel::ReadLine's "completion_function"
in order to customize how input should be completed. The new completion
function must accept three scalar parameters: the word being completed, the
entire input text, and the position within the input text of the word being
completed.
The completion function should return a list of possible matches. For example:
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.
IMPLEMENTATION DIFFERENCES¶
Although POE::Wheel::ReadLine is 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.
SEE ALSO¶
POE::Wheel describes the basic operations of all wheels in more depth. You need
to know this.
readline(3), Term::Cap, Term::ReadKey.
The SEE ALSO section in POE contains a table of contents covering the entire POE
distribution.
Term::Visual is an alternative to POE::Wheel::ReadLine. It provides scrollback
and a status bar in addition to editable user input. Term::Visual supports POE
despite the lack of "POE" in its name.
BUGS¶
POE::Wheel::ReadLine has some known issues:
Perl 5.8.0 is Broken¶
Non-blocking input with Term::ReadKey does not work with Perl 5.8.0, especially
on Linux systems for some reason. Upgrading Perl will fix things. If you can't
upgrade Perl, consider alternative input methods, such as Term::Visual.
<
http://rt.cpan.org/Ticket/Display.html?id=4524> and related tickets
explain the issue in detail. If you suspect your system is one where
Term::ReadKey fails, you can run this test program to be sure.
#!/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;
Non-Optimal Code¶
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.
Unimplemented Features¶
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.
Unicode support. 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 very much welcome.
GOTCHAS / FAQ¶
Lost Prompts¶
Q: Why do I lose my 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.
Edit Keystrokes Display as ^C¶
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.
Lack of Windows Support¶
Q: Why doesn't POE::Wheel::ReadLine work on Windows? Term::ReadLine does.
A: POE::Wheel::ReadLine requires
select(), because that's what POE uses
by default to detect keystrokes without blocking. About half the flavors of
Perl on Windows implement
select() in terms of the same function in the
WinSock library, which limits
select() to working only with sockets.
Your console isn't a socket, so
select() doesn't work with your version
of Perl on Windows.
Really good workarounds are possible but don't exist as of this writing. They
involve writing a special POE::Loop for Windows that either uses a
Win32-specific module for better multiplexing, that polls for input, or that
uses blocking I/O watchers in separate threads.
Cygwin Support¶
Q: Why does POE::Wheel::ReadLine complain about my "dumb" terminal?
A: Do you have Strawberry Perl installed? Due to the way it works, on
installation it sets a global environment variable in MSWin32 for TERM=dumb. (
it may be fixed in a future version, but it's here to stay for now, ha! ) In
this case, logging into the Cygwin shell via the cygwin.bat launcher results
in a nonfunctional readline.
Normally, Cygwin will set TERM=cygwin in the launcher. However, if the TERM was
already set it will not alter the value. Hence, the "bug" appears!
What you can do is to hack the cygwin.bat file to add this line:
SET TERM=cygwin
Other users reported that you can have better results by editing the
~/.bash_profile file to set TERM=cygwin because on a Cygwin upgrade it
overwrites the cygwin.bat file.
Alternatively, you could install different terminals like "xterm" or
"rxvt" as shown here:
<
http://c2.com/cgi/wiki?BetterCygwinTerminal>. Please let us know if you
encounter problems using any terminal other than "dumb".
If you feel brave, you can peruse the RT ticket at
<
http://rt.cpan.org/Ticket/Display.html?id=55365> for more information
on this problem.
AUTHORS & COPYRIGHTS¶
POE::Wheel::ReadLine was originally written by Rocco Caputo.
Nick Williams virtually rewrote it to support a larger subset of GNU readline.
Please see POE for more information about other authors and contributors.