summaryrefslogtreecommitdiffstats
path: root/lib/Reply/Plugin.pm
blob: ed3efbb6fec8652ee3f8362baf8d0ea3dbe72d5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package Reply::Plugin;
use strict;
use warnings;
# ABSTRACT: base class for Reply plugins

=head1 SYNOPSIS

  package Reply::Plugin::Foo;
  use strict;
  use warnings;

  use base 'Reply::Plugin';

  # ...

=head1 DESCRIPTION

A L<Reply> plugin is an object which adds some functionality to a Reply
instance by implementing some specific methods which the Reply object will call
at various points during execution. Plugins may implement as many callback
methods as necessary to implement their functionality (although the more
callbacks a given plugin implements, the more likely it is that the plugin may
be more useful as multiple independent plugins).

Callback methods have two potential calling conventions:

=over 4

=item wrapped

Wrapped plugins receive a coderef as their first argument (before any arguments
to the callback itself), and that coderef can be used to call the next callback
in the list (if more than one plugin implements a given callback). In
particular, this allows calling the next plugin multiple times, or not at all
if necessary. Wrapped plugins should always call their coderef in list context.
All plugins listed below are wrapped plugins unless indicated otherwise.

=item chained

Chained plugins receive a list of arguments, and return a new list of arguments
which will be passed to the next plugin in the chain. This allows each plugin a
chance to modify a value before it's actually used by the repl.

=back

=head2 CALLBACKS

=over 4

=item prompt

Called to determine the prompt to use when reading the next line. Takes no
arguments, and returns a single string to use as the prompt. The default
implementation returns C<< ">" >>

=item read_line

Called to actually read a line from the user. Takes no arguments, and returns a
single string. The default implementation uses the C<< <> >> operator to read a
single line from the user.

=item command_C<$name> (chained)

If the line read from the user is of the form C<"#foo args...">, then plugins
will be searched for a callback method named C<command_foo>. This callback
takes a single string containing the provided arguments, and returns a new line
to evaluate instead, if any.

=item mangle_line (chained)

Modifies the line read from the user before it's evaluated. Takes the line as a
string and returns the modified line.

=item compile

Compiles the string of Perl code into a coderef. Takes the line of code as a
string and a hash of extra parameters, and returns the coderef to be executed.
The default implementation uses L<Eval::Closure> to compile the given string.

The hash of extra parameters is passed directly to C<eval_closure>.

=item execute

Executes the coderef which has just been compiled. Takes the coderef and a list
of parameters to pass to it, and returns the list of results returned by
calling the coderef. The default implementation just calls the coderef
directly.

=item mangle_error (chained)

If the C<compile> or C<execute> callbacks throw an exception, this callback
will be called to modify the exception before it is passed to C<print_error>.
It receives the exception and returns the modified exception.

=item print_error

If the C<compile> or C<execute> callbacks throw an exception, this callback
will be called to display it to the user. It receives the exception and returns
nothing. The default implementation just uses C<print> to print it to the
screen.

=item mangle_result (chained)

This callback is used to modify the result of evaluating the line of code
before it is displayed. It receives the list of results and returns a modified
list of results.

=item print_result

This callback displays to the user the results of evaluating the given line of
code. It receives the list of results, and returns nothing. The default
implementation just uses C<print> to print them to the screen.

=item loop (chained)

This callback is called at the end of each evaluation. It receives whether the
repl has been requested to terminate so far, and returns whether the repl
should terminate.

=back

Reply plugins can also communicate among each other via a pub/sub mechanism. By
calling the C<publish> method, all plugins which respond to the given message
(implement a method of the given name) will have that method called with the
given arguments, and all of the responses will be collected and returned. Some
messages used by the default plugins are:

=over 4

=item tab_handler ($line)

Plugins can publish this message when they want to attempt tab completion.
Plugins that respond to this message should return a list of potential
completions of the line which is passed in.

=item lexical_environment ($name, $env)

Plugins which wish to modify the lexical environment should do so by publishing
this message. This will register it with the repl itself, as well as allowing
other plugins which introspect the lexical environment to see it.

There can be more than one lexical environment, which are all merged together
when the line is evaluated. C<default> is the primary environment (typically
corresponding to the user's code). All other environments should typically have
unique names, and are used to add additonal variables to the environment that
won't be overridden if a plugin decides to replace the default environment.

=item package ($name)

Plugins which wish to modify the currently active package should do so by
publishing this message. This will register it with the repl itself, as well as
allowing other plugins which introspect the current package to see it.

=back

Your plugins, however, are not limited to these messages - you can use whatever
messages you want to communicate.

=cut

sub new {
    my $class = shift;
    my (%opts) = @_;

    die "publisher is required" unless $opts{publisher};

    return bless {
        publisher => $opts{publisher},
    }, $class;
}

=method publish ($name, @args)

Publish a message to other plugins which respond to it. All loaded plugins
which implement a method named C<$name> will have it called with C<@args> as
the parameters. Returns a list of everything that each plugin responded with.

=cut

sub publish {
    my $self = shift;

    $self->{publisher}->(@_);
}

=for Pod::Coverage
  new

=cut

1;