=begin pod

=head1 NAME

Prompt - a smarter prompt for user interaction

=head1 SYNOPSIS

=begin code :lang<raku>
# Best with any of Terminal::LineEditor, Linenoise or
# Readline distributions installed
use Prompt;

my $prompt = Prompt.new(:history<here>);  # auto-select editor
loop {
    last without my $line = $prompt.readline("> ");
    say $line;
}
$prompt.save-history;
=end code

=head1 DESCRIPTION

The Prompt module offers a better C<prompt> experience, drawing
on the logic of the Raku REPL.

It also optionally provides a replacement for Raku's standard
L<C<prompt>|https://docs.raku.org/routine/prompt> functionality.

=head1 ROLES

The C<Prompt> role is what usually gets punned into a class.

The C<Prompt::Fallback> role serves as a base role for specific editor
roles, such as C<Prompt::Readline>, C<Prompt::LineEditor> and
C<Prompt::Linenoise>, each of which have their pros and cons.

The C<Prompt::Fallback> role provides all of the logic if no specific
editor has been found, but is not recommended for production use.

=head2 Prompt

The C<Prompt> role embodies the information needed to read a line of
input from a user, and optionally keep a history of the lines that
were entered.

=head3 method new

=begin code :lang<raku>
my $Prompt = Prompt.new(
  :editor(Any),    # or "Readline", "LineEditor", "Linenoise"
  :history<here>,                       # default: none
  :completions(@completions),           # default: none
  :additional-completions(&one, &two),  # default: none
);
loop {
    last without my $line = $Prompt.readline;
    say $line;
}
$Prompt.save-history;
=end code

The C<new> method is called to instantiate a C<Prompt> object.  It
takes a number of named arguments described below.

=head4 :editor

The editor logic to be used.  Can be specified as a string, or as an
instantiated object that implements the C<Prompt::Fallback> role.

If the value is not a string, it is expected to be a class that
implements to the C<Prompt::Fallback> interface.

Otherwise defaults to C<Any>, which means to search first for unknown
roles in the C<Prompt::> namespace, then to try if there is support
installed for L<C<Readline>|https://raku.land/zef:clarkema/Readline>,
L<C<LineEditor>|https://raku.land/zef:japhb/Terminal::LineEditor>, or
L<C<Linenoise>|https://raku.land/zef:raku-community-modules/Linenoise>.

If that failed, then the C<Fallback> editor logic will be used, which
may cause a cumbersome user experience, unless the process was wrapped
with a call to the L<C<rlwrap>|https://github.com/hanslub42/rlwrap>
readline wrapper.

Used value available with the C<.editor> method.

=head4 :history

String or C<IO::Path> object.  Specifies the path to keep any lines that
have been previously entered and/or will be entered in the future by
any user interactions.  Defaults to C<Any>, indicating B<no> history will
be kept.

Will load any persistent history already available if the specified path
already existed.

Used value available with the C<.history> method.

=head4 :completions

A C<List> of strings to be used for tab-completions by the editor.  Will
only be useful if the C<.supports-completions> method returns C<True>.

=head4 :additional-completions

A C<List> of C<Callables> to be called to support additional completion
logic.  Will only be useful if the C<.supports-completions> method
returns C<True>.

=head3 method readline

=begin code :lang<raku>
my $line = $Prompt.readline("> ");
=end code

The C<readline> method takes a single positional argument for the prompt
to be shown (defaults to "> ") and returns a line of input from the user.

The prompt will be scanned for escape sequence
L<interpolation|#INTERPOLATIONS>.

An undefined value is returned if the user has indicated there is no more
input to be obtained.

If the C<Prompt> object was made with a history file, then that history
will be available to the user depending on whether the C<editor> used
supports that (the C<Prompt::Fallback> editor does B<not>).  And in that
case, the C<readline> will make sure that any new lines will be added
to the history.

Note that to persistently store the history, one B<must> call the
C<save-history>, when appropriate (usually when the user is done).

=head3 method read

=begin code :lang<raku>
my $line = $Prompt.read("> ");
=end code

The C<read> method takes a single positional argument for the prompt
to be shown and returns a line of input from the user.  It does B<not>
handle anything history related, nor does it do any escape code analysis.

=head3 method completions

=begin code :lang<raku>
.say for $Prompt.completions;

$Prompt.completions(<a b c>);
=end code

The C<completions> method returns the current sorted C<List> of completions.

If called with a C<Positional>, will sort that and use that as the list
of completions to use.

=head3 method additional-completions

The C<additional-completions> method returns an C<Array> of C<Callable>s
that will be called whenever a completion is requested.  Each C<Callable>
is expected to accepted two positional arguments: the first is the line
that has been entered so far by the user, and the second is the position
of the cursor when a completion was requested.

=head3 method editor-name

=begin code :lang<raku>
say $Prompt.editor-name;
=end code

The C<editor-name> method returns the name of the editor support that was
activated.  It is intended to be purely informatonal.

=head3 method history

=begin code :lang<raku>
my $history = $Prompt.history;

$Prompt.save-history;
$Prompt.history("another-file");
=end code

Returns the C<IO::Path> object representing the persistent history file.

Can also be called with a positional argument, indicating the file to be
used for history keeping from now on.  Note that if history was already
being kept in another history file, any additions to that will be lost
unless a call to C<save-history> was done before that.

=head3 method add-history

=begin code :lang<raku>
$Prompt.add-history($line);
=end code

The C<add-history> method can be called to add a line to the current
history.  It takes a single positional argument: the line to be added.

Note that calling this method is not needed if the C<readline> method
is being used to obtain input from a user.

=head3 method load-history

Will (re-)load the history from the file indicated with C<history>.

=head3 method save-history

Will save the current history to the file indicated with C<history>.

=head1 EDITOR ROLES

An editor role must supply the methods as defined by the
C<Prompt::Fallback> role.  Its C<new> method should either return an
instantiated class, or C<Nil> if the class could not be instantiated
(usually because of lack of installed module dependencies).

The other methods are (in alphabetical order):

=head3 add-history

Expected to take a single string argument to be added to the
(possibly persistent) history.  Does not perform any action by default
in C<Prompt::Fallback>.

=head3 history

If called as an accessor, should return the C<IO::Path> object of the
history file.

If called as a mutator with a single positional argument, should set
that as the current history file, and load that history.

=head3 load-history

Expected to take no arguments and load any persistent history information,
as indicated by its C<history> method.  Does not perform any action by the
default implementation in the C<Prompt::Fallback> role.

=head2 read

Expected to take a string argument with the prompt to be shown, and
return the next line of input from the user.  Expected to return an
undefined value to indicate that no further input is to be expected.

Defaults to showing the prompt and taking a line from C<$*IN> in
C<Prompt::Fallback>.

=head2 save-history

Expected to take no arguments and save any persistent history information,
as indicated by its C<history> method.  Does not perform any action by the
default implementation in the C<Prompt::Fallback> role.

=head2 supports-completions

Expected to take no arguments and return a C<Bool> indicating whether
this editor supports completions.  Defaults to C<False> in the
C<Prompt::Fallback> role.

=head2 Prompt::Fallback

Apart from the definition of the interface for editors, it provides
the default logic for handling the interaction with the user.

=head2 Prompt::Readline

The role that implements the user interface using the
L<C<Readline>|https://raku.land/zef:clarkema/Readline> module.

=head2 Prompt::LineEditor

The role that implements the user interface using the
L<C<Terminal::LineEditor>|https://raku.land/zef:japhb/Terminal::LineEditor>
module.

=head2 Prompt::Linenoise

The role that implements the user interface using the
L<C<Linenoise>|https://raku.land/zef:raku-community-modules/Linenoise>
module.

=head1 SUBROUTINES

=head2 sub prompt

=begin code :lang<raku>
use Prompt :prompt;

say prompt("> ");
=end code

If the C<:prompt> named argument is specified with the C<use Prompt>
statement, a lexical replacement for the standard C<prompt> functionality
will be exported.

=head3 $*PROMPT

The C<$*PROMPT> dynamic variable will be used to obtain the C<Prompt>
object to be used in the interaction.  If none was found, a new
C<Prompt> object will be created and stored as C<$*PROMPT> in the
C<PROCESS::> stash (making it automatically available for any
subsequent call to the exported C<prompt> subroutine).

=begin code :lang<raku>
use Prompt :prompt;

my $*PROMPT = Prompt.new(...);
say prompt("> ");
=end code

If one needs specific initializations with the C<Prompt> object, one
can also define ones own C<$*PROMPT> dynamic variable before calling
C<prompt>.

=head1 AUTHOR

Elizabeth Mattijsen <liz@raku.rocks>

Source can be located at: https://codeberg.org/lizmat/Prompt . Comments and
Pull Requests are welcome.

If you like this module, or what I'm doing more generally, committing to a
L<small sponsorship|https://github.com/sponsors/lizmat/>  would mean a great
deal to me!

=head1 COPYRIGHT AND LICENSE

Copyright 2024, 2025, 2026 Elizabeth Mattijsen

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

# vim: expandtab shiftwidth=4
