File Coverage

lib/Term/ReadLine/Perl5/OO/Keymap.pm
Criterion Covered Total %
statement 54 99 54.5
branch 17 54 31.4
condition 5 20 25.0
subroutine 10 16 62.5
pod 3 12 25.0
total 89 201 44.2


line stmt bran cond sub pod time code
1             package Term::ReadLine::Perl5::OO::Keymap;
2 10     10   77 use strict; use warnings;
  10     10   52  
  10         309  
  10         59  
  10         24  
  10         355  
3 10     10   665 eval "use rlib '.' "; # rlib is now optional
  10         924  
  10         85  
4 10     10   619 use Term::ReadLine::Perl5::Common;
  10         25  
  10         14857  
5              
6             # For extra debug messages
7             my $DEBUG = $ENV{'KEYMAP_DEBUG'};
8              
9             sub new {
10 2     2 0 10 my ($class, $name, $default) = @_;
11 2         11 my $self = {
12             name => $name,
13             default => canonic_command_function($default),
14             function => [],
15             };
16 2         6 bless $self, $class;
17 2         3 return $self;
18             }
19              
20             # A GNU ReadLine function
21             sub rl_make_bare_keymap() {
22 0     0 0 0 __PACKAGE__->new();
23             }
24              
25             sub lookup_key($$) {
26 0     0 0 0 my ($self, $key) = @_;
27 0         0 return $self->{function};
28             }
29              
30             =head2 bind_parsed_keyseq
31              
32             #B(I<$keyseq_list> I<$function>, I<$keyseq_str>)
33              
34             Actually inserts the binding for given I<$keyseq_list>
35             to I<$function> into the keymap object. I<$keyseq_list> is
36             reference an list of character ordinals.
37              
38             If C is more than one element long, all but the last will
39             cause meta maps to be created. The name will be derived from
40             $.
41              
42             I<$Function> will have an implicit I prepended to it.
43              
44             0 is returned if there is no error.
45              
46             =cut
47              
48             sub bind_parsed_keyseq($$;$)
49             {
50 2     2 1 5 my ($self, $keyseq_list, $function, $loc_str) = @_;
51 2 50       4 $loc_str = '' unless $loc_str;
52 2         3 my $bad = 0;
53 2         4 my @keys = @{$keyseq_list};
  2         4  
54             # make sure $key is set to an int if undefined.
55 2   50     6 my $key = shift(@keys) || 0;
56 2         7 my $func_tuple = $self->{function}[$key];
57             # use Data::Printer;
58             # p $func_tuple;
59             # p $key;
60             # p $function;
61 2 50       5 if (@keys) {
62 0         0 my $next_keymap;
63 0 0 0     0 if (defined($func_tuple) && $func_tuple->[0] eq 'PrefixMeta') {
64             # Good - extending an existing meta map.
65 0         0 $next_keymap = $func_tuple->[1];
66             } else {
67 0 0 0     0 if (defined($func_tuple) && $^W) {
68             my $mess =
69             sprintf("Warning%s: Rebinding char #%s from [%s] " .
70             "to meta in keymap %s\n",
71 0         0 $loc_str, $key, $func_tuple->[1], $self->{name});
72 0         0 warn $mess;
73             }
74             }
75 0         0 $self->{function}[$key] = ['F_PrefixMeta', $next_keymap];
76 0         0 return $next_keymap->bind_parsed_keyseq(\@keys, $function, $loc_str);
77             }
78              
79 2 0 33     6 if (defined($func_tuple) && $func_tuple->[0] ne 'F_PrefixMeta' &&
      33        
80             $function ne 'PrefixMeta') {
81 0 0       0 if ($^W) {
82             my $mess =
83             sprintf("Warning%s: Rebinding char #%s to " .
84             "non-meta (%s) in keymap %s\n",
85 0         0 $loc_str, $key, $function, $self->{name});
86 0         0 warn $mess;
87             }
88             }
89              
90             # FIXME 2nd arg really should be code ref when it is not
91             # a keymap. And do better than =~.
92 2 100       6 my $function_name =
93             ($function =~ /Term::ReadLine::Perl5::OO::Keymap/) ?
94             'PrefixMeta' : $function;
95              
96 2         5 $self->{function}[$key] = [$function_name, $function];
97             # p $self->{function}[$key];
98 2         8 return $bad;
99             }
100              
101             =head3 rl_bind_keyseq
102              
103             B(I<$keyspec>, I<$function>)
104              
105             Bind the key sequence represented by the string I to the
106             function function, beginning in the current keymap. This makes new
107             keymaps as necessary. The return value is non-zero if keyseq is
108             invalid. I<$keyspec> should be the name of key sequence in one of two
109             forms:
110              
111             Old (GNU readline documented) form:
112              
113             M-x to indicate Meta-x
114             C-x to indicate Ctrl-x
115             M-C-x to indicate Meta-Ctrl-x
116             x simple char x
117              
118             where I above can be a single character, or the special:
119              
120             special means
121             -------- -----
122             space space ( )
123             spc space ( )
124             tab tab (\t)
125             del delete (0x7f)
126             rubout delete (0x7f)
127             newline newline (\n)
128             lfd newline (\n)
129             ret return (\r)
130             return return (\r)
131             escape escape (\e)
132             esc escape (\e)
133              
134             New form:
135             "chars" (note the required double-quotes)
136              
137             where each char in the list represents a character in the sequence, except
138             for the special sequences:
139              
140             \\C-x Ctrl-x
141             \\M-x Meta-x
142             \\M-C-x Meta-Ctrl-x
143             \\e escape.
144             \\x x (if not one of the above)
145              
146              
147             C<$function> should be in the form C or C.
148              
149             It is an error for the function to not be known....
150              
151             As an example, the following lines in .inputrc will bind one's xterm
152             arrow keys:
153              
154             "\e[[A": previous-history
155             "\e[[B": next-history
156             "\e[[C": forward-char
157             "\e[[D": backward-char
158              
159             =cut
160              
161             sub rl_bind_keyseq($$$;$)
162             {
163 2     2 1 5 my ($self, $keyseq, $func, $location_msg) = @_;
164 2 50       5 $location_msg = '' unless $location_msg;
165 2         6 $func = canonic_command_function($func);
166              
167             # print "sequence [$keyseq] func [$func]\n"; ##DEBUG
168              
169 2         5 my @keys = ();
170 2 50       7 if ($keyseq =~ m/"((?:\\.|[^\\])*)"/s) {
171             # A new-style binding.
172 0         0 @keys = unescape("$1");
173             } else {
174             # An old-style binding... only one key (or Meta+key)
175 2         4 my $new_keyseq = $keyseq;
176 2         5 my $is_ctrl = ($new_keyseq =~ s{\b(C|Control|CTRL)-}{}i);
177 2 50       5 if ($keyseq =~ s{\b(M|Meta)-}{}i) {
178 0         0 push(@keys, ord("\e"));
179             }
180              
181             # Isolate key part. This matches GNU's implementation.
182             # If the key is '-', be careful not to delete it!
183 2         3 $new_keyseq =~ s/.*-(.)/$1/;
184 2 50       28 if ($new_keyseq =~ /^(space|spc)$/i) { $new_keyseq = ' '; }
  0 50       0  
    50          
    50          
    50          
    100          
    50          
185 0         0 elsif ($new_keyseq =~ /^(rubout|del)$/i) { $new_keyseq = "\x7f"; }
186 0         0 elsif ($new_keyseq =~ /^tab$/i) { $new_keyseq = "\t"; }
187 0         0 elsif ($new_keyseq =~ /^(return|ret)$/i) { $new_keyseq = "\r"; }
188 0         0 elsif ($new_keyseq =~ /^(newline|lfd)$/i) { $new_keyseq = "\n"; }
189 1         3 elsif ($new_keyseq =~ /^(escape|esc)$/i) { $new_keyseq = "\e"; }
190             elsif (length($new_keyseq) > 1) {
191 0 0       0 warn "Warning$location_msg: strange binding [$keyseq->$new_keyseq]\n"
192             if $^W;
193             }
194 2         4 my $key = ord($new_keyseq);
195 2 50       5 $key = Term::ReadLine::Perl5::Common::ctrl($key) if $is_ctrl;
196 2         3 push(@keys, $key);
197             }
198              
199             # Now do the mapping of the sequence represented in @keys
200 2 50       6 printf("rl_bind_keyseq(%s->%s, %s)\n",
201             $keyseq, $func, join(', ', @keys)) if $DEBUG;
202 2         7 $self->bind_parsed_keyseq(\@keys, $func);
203             }
204              
205             =head3 bind_keys
206              
207             Accepts an array as pairs ($keyspec, $function, [$keyspec, $function]...).
208             and maps the associated bindings to the current KeyMap.
209              
210             =cut
211              
212             sub bind_keys
213             {
214 2     2 1 3 my $self = shift;
215 2         3 my ($keyseq, $func);
216 2   66     14 while (defined($keyseq = shift(@_)) &&
217             defined($func = shift(@_))) {
218 2         6 $self->rl_bind_keyseq($keyseq, $func);
219             }
220             }
221              
222             sub classify($) {
223 0     0 0 0 my $ord = shift;
224 0 0       0 return 'C-' . chr($ord+96) if $ord <= 26;
225 0 0 0     0 return chr($ord) if $ord >= 33 && $ord < 127;
226 0 0       0 return 'DEL' if $ord == 127;
227 0 0       0 return "' '" if $ord == 32;
228 0 0       0 return "ESC" if $ord == 27;
229 0         0 return $ord;
230             }
231              
232             # Turn command function names into their GNU Readline equivalet according to
233             # these rules:
234             #
235             # * names start with a lowercase letter
236             # * a lowercase followed by an Uppercase letter gets turned into lower-case - lower-case
237             #
238             # Examples:
239             # Yank => yank
240             # BeginningOfLine => beginning-of-line
241             sub gnu_command_function($) {
242 0     0 0 0 my $function_name = shift;
243 0         0 $function_name = "\l$function_name";
244 0         0 $function_name =~ s/([a-z])([A-Z])/$1-\l$2/g;
245 0         0 $function_name;
246             }
247              
248             sub inspect($) {
249 0     0 0 0 my ($self, $prefix) = @_;
250 0         0 my @results = ();
251 0         0 my @continue = ();
252 0         0 for (my $i=0; $i<=127; $i++) {
253 0         0 my $command_name = $self->{function}[$i][0];
254 0 0       0 next unless defined($command_name);
255 0         0 push @results, sprintf("%s%s\t%s\n", $prefix,
256             classify($i),
257             gnu_command_function($command_name));
258 0 0       0 push @continue, $i if $command_name eq 'PrefixMeta';
259             }
260 0         0 return (\@results, \@continue);
261             }
262              
263             # GNU Emacs Meta Key bindings
264             sub EmacsMetaKeymap() {
265 1     1 0 3 my $keymap = __PACKAGE__->new('EmacsMeta', undef);
266 1         19 $keymap->bind_keys(
267             'k', 'unix-line-rubout',
268             );
269 1         3 return $keymap
270             }
271              
272             # GNU Emacs Key binding
273             sub EmacsKeymap() {
274 1     1 0 12 my $keymap = __PACKAGE__->new('Emacs', 'self-insert');
275 1         5 $keymap->bind_keys(
276             # 'C-a', 'beginning-of-line',
277             # 'C-b', 'backward-char',
278             # 'C-c', 'interrupt',
279             # 'C-d', 'delete-char',
280             # 'C-e', 'end-of-line',
281             # 'C-f', 'forward-char',
282             # 'C-h', 'backward-delete-char',
283             # 'C-j', 'accept-line',
284             # 'C-k', 'kill-line',
285             # 'C-l', 'clear-screen',
286             # 'C-m', 'accept-line',
287             # 'C-n', 'next-history',
288             # 'C-p', 'previous-history',
289             # 'C-r', 'reverse-search-history',
290             # 'C-t', 'transpose-chars',
291             # 'C-u', 'unix-line-discard',
292             # 'C-w', 'unix-word-rubout',
293             # 'C-z', 'suspend',
294             'ESC', EmacsMetaKeymap,
295             # 'DEL', 'backward-delete-char',
296             # 'ESC-b', 'backward-char',
297              
298             );
299 1         9 return $keymap
300             }
301              
302             # Vi input mode key bindings.
303             sub ViKeymap() {
304 0     0 0   my $keymap = __PACKAGE__->new('vi', 'self-insert');
305 0           $keymap->bind_keys(
306             # "\e", 'ViEndInsert',
307             'C-c', 'interrupt',
308             'C-h', 'backward-delete-char',
309             'C-u', 'unix-line-discard',
310             # 'C-v', 'quoted-insert',
311             'C-w', 'unix-word-rubout',
312             'DEL', 'backward-delete-char',
313             # "\n", 'ViAcceptInsert',
314             # "\r", 'ViAcceptInsert',
315             );
316 0           return $keymap;
317             };
318              
319             unless (caller) {
320             # foreach my $keymap (EmacsKeymap(), EmacsMetaKeymap(), ViKeymap()) {
321             foreach my $keymap (EmacsKeymap()) {
322             my ($results, $continue) = $keymap->inspect('');
323             foreach my $line (@{$results}) {
324             print $line;
325             }
326             print '=' x 30, "\n";
327             }
328             }
329              
330             1;