File Coverage

blib/lib/Term/ReadLine/Zoid/ViCommand.pm
Criterion Covered Total %
statement 153 178 85.9
branch 53 80 66.2
condition 21 26 80.7
subroutine 25 29 86.2
pod 1 20 5.0
total 253 333 75.9


line stmt bran cond sub pod time code
1             package Term::ReadLine::Zoid::ViCommand;
2              
3 3     3   2793 use strict;
  3         8  
  3         168  
4 3     3   15 use vars '$AUTOLOAD';
  3         5  
  3         461  
5 3     3   18 no strict 'refs';
  3         5  
  3         93  
6 3     3   1065 use AutoLoader;
  3         1708  
  3         25  
7 3     3   297 use base 'Term::ReadLine::Zoid';
  3         53  
  3         940  
8 3     3   25 no warnings; # undef == '' down here
  3         6  
  3         4650  
9              
10             our $VERSION = 0.05;
11              
12             sub AUTOLOAD { # more intelligent inheritance
13 13     13   27 my $sub = $AUTOLOAD;
14 13         61 $sub =~ s/.*:://;
15 13 50       52 my $m = $_[0]->can($sub) ? 'AutoLoader' : 'Term::ReadLine::Zoid';
16 13         19 ${$m.'::AUTOLOAD'} = $AUTOLOAD;
  13         42  
17 13         20 goto &{$m.'::AUTOLOAD'};
  13         66  
18             }
19              
20             =head1 NAME
21              
22             Term::ReadLine::Zoid::ViCommand - a readline command mode
23              
24             =head1 SYNOPSIS
25              
26             This class is used as a mode under L,
27             see there for usage details.
28              
29             =head1 DESCRIPTION
30              
31             This mode provides a "vi command mode" as specified by the posix spec for the sh(1)
32             utility. It intends to include at least all key-bindings
33             mentioned by the posix spec for the vi mode in sh(1).
34             It also contains some extensions borrowed from vim(1) and some private extensions.
35              
36             This mode has a "kill buffer" that stores the last killed text so it can
37             be yanked again. This buffer has only one value, it isn't a "kill ring".
38              
39             =head1 KEY MAPPING
40              
41             Since ViCommand inherits from MultiLine, which in turn inherits
42             from Term::ReadLine::Zoid, key bindings are also inherited unless explicitly overloaded.
43              
44             Control-d is ignored in this mode.
45              
46             =over 4
47              
48             =cut
49              
50             our %_keymap = (
51             return => 'accept_line',
52             ctrl_D => 'bell',
53             ctrl_Z => 'sigtstp',
54             backspace => 'backward_char',
55             escape => 'vi_reset',
56             ctrl_A => 'vi_increment',
57             ctrl_X => 'vi_increment',
58             _on_switch => 'vi_switch',
59             _isa => 'multiline', # but wait .. self_insert is overloaded
60             );
61              
62 2     2 1 34 sub keymap { return \%_keymap }
63              
64             sub vi_switch {
65 31     31 0 49 my $self = shift;
66 31 50       85 return $$self{_loop} = undef if $$self{_vi_mini_b};
67 31         54 $$self{vi_command} = '';
68 31   100     84 $$self{vi_history} ||= [];
69 31 100 66     230 $self->backward_char unless $_[1] or $$self{pos}[0] == 0;
70             }
71              
72             our @vi_motions = (' ', ',', qw/0 b F l W ^ $ ; E f T w | B e h t/);
73             our %vi_subs = (
74             '#' => 'vi_comment', '=' => 'vi_complete',
75             '\\' => 'vi_complete', '*' => 'vi_complete',
76             '@' => 'vi_macro', '~' => 'vi_case',
77             '.' => 'vi_repeat', ' ' => 'forward_char',
78             '^' => 'vi_home', '$' => 'end_of_line',
79             '0' => 'beginning_of_line', '|' => 'vi_cursor',
80             ';' => 'vi_c_repeat', ',' => 'vi_c_repeat',
81             '_' => 'vi_topic', '-' => 'vi_K',
82             '+' => 'vi_J',
83            
84             'l' => 'forward_char', 'h' => 'backward_char',
85             't' => 'vi_F', 'T' => 'vi_F',
86             );
87             our %vi_commands = (
88             '/' => 'bsearch',
89             '?' => 'fsearch',
90             '!' => 'shell',
91             'q' => 'quit',
92             );
93              
94             sub self_insert {
95 95     95 0 824 my ($self, $key) = @_;
96              
97 95 50       172 if (length($key) > 1) { # no vague chars
98 0         0 $self->bell;
99 0         0 $$self{vi_command} = '';
100             }
101 95         164 else { $$self{vi_command} .= $key }
102              
103 95 50       510 if ($$self{vi_command} =~ /^[\/\?\:]/) {
    100          
104 0         0 $self->vi_mini_buffer($key)
105             }
106             elsif ($$self{vi_command} =~ /^0|^(\d*)(\D)/) { # this is where a command gets executed
107 81   100     446 my ($cnt, $cmd) = ($1||1, $2||'0');
      100        
108 81   66     293 my $sub = $vi_subs{$cmd} || 'vi_'.uc($cmd);
109             #print STDERR "string: $$self{vi_command} cnt: $cnt sub: $sub\n";
110 81         77 my $r;
111 81 50       284 if ($self->can($sub)) {
112 81         253 my $s = $self->save();
113 81         1348 $r = $self->$sub($cmd, $cnt); # key, count
114 38         80 push @{$$self{undostack}}, $s unless lc($cmd) eq 'u'
  81         232  
115 81 100 66     275 or join("\n", @{$$s{lines}}) eq join("\n", @{$$self{lines}});
  81         384  
116             }
117 0         0 else { $self->bell() }
118 1480         2222 $$self{vi_last_cmd} = $$self{vi_command}
119 81 100 100     278 if $$self{vi_command} && ! grep( {$_ eq $cmd} @vi_motions, '.'); # for repeat ('.')
120 81         117 $$self{vi_command} = '';
121             #print STDERR "return: $r vi_last_cmd: $$self{vi_last_cmd}\n";
122 81         257 return $r;
123             }
124             else {
125 14 50       81 return if $$self{vi_command} =~ /^\d+$/;
126             #print STDERR "string: $$self{vi_command} rejected\n";
127 0         0 $self->bell;
128 0         0 $$self{vi_command} = '';
129             }
130 0         0 return 0;
131             }
132              
133             # ############ #
134             # Key bindings #
135             # ############ #
136              
137             # Subs get args ($self, $key, $count)
138              
139 2     2 0 5 sub vi_reset { $_[0]{vi_command} = ''; return 0 }
  2         5  
140              
141 0     0 0 0 sub sigtstp { kill 20, $$ } # SIGTSTP
142              
143             =item escape
144              
145             Reset the command mode.
146              
147             =item return
148              
149             =item ^J
150              
151             Return the current edit line to the application for execution.
152              
153             =item ^Z
154              
155             Send a SIGSTOP to the process of the application. Might not work when the application
156             ignores those, which is something shells tend to do.
157              
158             =item i
159              
160             Switch back to insert mode.
161              
162             =item I
163              
164             Switch back to insert mode at the begin of the edit line.
165              
166             =item a
167              
168             Enter insert mode after the current cursor position.
169              
170             =item A
171              
172             Enter insert mode at the end of the edit line.
173              
174             =cut
175              
176             sub vi_I {
177 0 0   0 0 0 $_[0]{pos}[0] = 0 if $_[1] eq 'I';
178 0         0 $_[0]->switch_mode();
179             }
180              
181             sub vi_A {
182 0 0   0 0 0 ($_[1] eq 'A') ? $_[0]->end_of_line : $_[0]->forward_char ;
183 0         0 $_[0]->switch_mode();
184             }
185              
186             =item m
187              
188             Switch to multiline insert mode, see L.
189             (private extension)
190              
191             =item M
192              
193             Switch to multiline insert mode at the end of the edit buffer.
194             (private extension)
195              
196             =cut
197              
198             sub vi_M {
199 0 0   0 0 0 if ($_[1] eq 'M') {
200 0         0 $_[0]{pos}[1] = $#{$_[0]{lines}};
  0         0  
201 0         0 $_[0]->end_of_line;
202             }
203 0         0 else { $_[0]->forward_char }
204 0         0 $_[0]->switch_mode('multiline')
205             }
206              
207             =item R
208              
209             Enter insert mode with replace toggled on.
210             (vim extension)
211              
212             =cut
213              
214             sub vi_R {
215 2     2 0 3 my $self = shift;
216 2 50       42 return $self->vi_r(@_) if $_[0] eq 'r';
217 0         0 $self->switch_mode();
218 0         0 $$self{replace} = 1;
219             }
220              
221             ## more bindings are defined in __END__ section for autosplit ##
222              
223             __END__