root/dotfiles/bash/kana/dot.bash.d/svk-completion.pl

Revision 132, 6.8 kB (checked in by kana, 16 months ago)

dotfiles/bash/kana-bash.d, dotfiles/bash/kana-bash_profile, dotfiles/bash/kana-bashrc:
* Import from my repository.
dotfiles/screen/kana-screenrc:
* Import from my repository.
dotfiles/vim/kana-vim, dotfiles/vim/kana-vimrc:
* Import from my repository.

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings FATAL => 'all';
5
6package Shell::Complete;
7
8sub new
9{
10        my $proto = shift;
11        my $class = ref($proto)? ref $proto: $proto;
12        my $self = bless {}, $proto;
13        $self->rebless;
14        return $self->init(@_);
15}
16
17sub rebless
18{
19        my $self = shift;
20        my $shell = $ENV{'SHELL'} || 'bash';
21        $shell =~ s/^.*[\\\/]//;
22        $shell = ucfirst $shell;
23        my $class = 'Shell::Complete::'. $shell;
24        # eval "require $class";
25        return warn("couldn't find $class") unless UNIVERSAL::can( $class, 'init' );
26        if( ref( $self ) eq 'Shell::Complete' ) {
27                bless $self, $class;
28        } else {
29                my $cur = ref $self;
30                my @tmp = eval "map s/^Shell::Complete\$/\Q$class\E/, \@$cur\:\:ISA";
31                warn $@ if( $@ );
32                unless( grep $_, @tmp ) {
33                        warn "$cur class doesn't inherit interface from Shell::Complete";
34                }
35        }
36        return;
37}
38
39sub init { return $_[0] }
40
41sub command { return $_[0]->{'command'} }
42sub cur { return $_[0]->{'cur'} }
43sub prev { return $_[0]->{'prev'} }
44sub line { return $_[0]->{'line'} }
45sub pos { return $_[0]->{'pos'} }
46
47sub _set_cur
48{
49        my $self = shift;
50        my $new_cur = shift;
51        my $old_cur = $self->cur;
52        substr( $self->{'line'}, $self->pos - length($old_cur), length($old_cur) ) =
53                $new_cur;
54        $self->{'cur'} = $new_cur;
55        $self->{'pos'} += length($new_cur) - length($old_cur);
56        return $old_cur;
57}
58
59sub args { return @{ $_[0]->{'args'} } }
60
61sub at_last_pos { return length($_[0]->line) == $_[0]->pos }
62
63sub is_option { return $_[0]->cur =~ /^-/ }
64sub is_long_option { return $_[0]->cur =~ /^--/ }
65sub options_strings { return (); }
66
67sub options
68{
69        my $self = shift;
70        my $long = $self->is_long_option;
71        my @res = map { s/[^\w|].*$//;
72                        split /\|/
73                      } $self->options_strings;
74                     
75        @res = grep length() == 1, @res unless $long;
76        return map { length() == 1? "-$_": "--$_" } @res;
77}
78
79sub filter_list
80{
81        my ($self, @list) = @_;
82        my $cur = $self->cur;
83        return grep /^\Q$cur/, @list;
84}
85
86sub output_list
87{
88        my $self = shift;
89        print "$_\n" foreach $self->filter_list( @_ );
90        return;
91}
92
93
941;
95
96package Shell::Complete::Bash;
97
98our @ISA = qw(Shell::Complete);
99
100sub init
101{
102        my $self = shift;
103        $self->{'command'} = $ARGV[0] || die "empty \@ARGV";
104        $self->{'cur'} = $ARGV[1] || '';
105        $self->{'prev'} = $ARGV[2] || '';
106        $self->{'line'} = $ENV{'COMP_LINE'} || die "empty \$ENV{COMP_LINE}";
107        $self->{'pos'} = $ENV{'COMP_POINT'} || die "empty \$ENV{COMP_POINT}";
108        my( $cmd, @args ) = split /\s+/, $self->{'line'};
109        push @args, '' if $self->{'line'} =~ /\s+$/;
110        $self->{'args'} = [ @args ];
111        return $self;
112}
113
1141;
115
116package Shell::Complete::Zsh;
117
118our @ISA = qw(Shell::Complete);
119
120sub init
121{
122        my $self = shift;
123        $self->{'command'} = $ARGV[0] || die "empty \@ARGV";
124        $self->{'cur'} = $ARGV[1] || '';
125        $self->{'prev'} = $ARGV[2] || '';
126        $self->{'line'} = $ENV{'BUFFER'} || die "empty \$ENV{BUFFER}";
127        $self->{'pos'} = $ENV{'CURSOR'} || die "empty \$ENV{CURSOR}";
128        my( $cmd, @args ) = split /\s+/, $self->{'line'};
129        push @args, '' if $self->{'line'} =~ /\s+$/;
130        $self->{'args'} = [ @args ];
131        return $self;
132}
133
1341;
135
136package SVK::Complete;
137
138our @ISA = qw(Shell::Complete);
139
140
141sub svk_commands
142{
143        my $self = shift;
144        unless( $self->{'svk_commands'} ) {
145                require SVK::Command;
146                my @cmds = $self->svk_command_aliases;
147
148                require File::Find;
149                my $dir = $INC{"SVK/Command.pm"};
150                $dir =~ s/\.pm$//;
151                File::Find::find(sub { push @cmds, lc() if s/\.pm$// }, $dir );
152
153                my %seen = ();
154                @cmds = grep !$seen{$_}++, @cmds;
155                $self->{'svk_commands'} = [ sort @cmds ];
156        }
157        return @{ $self->{'svk_commands'} };
158}
159
160# only works for svk >1.04
161sub svk_command_aliases
162{
163        my $self = shift;
164        unless( exists $self->{'svk_command_aliases'} ) {
165                require SVK::Command;
166                local $@;
167                my %cmds = (eval { SVK::Command->alias });
168                $self->{'svk_command_aliases'} = \%cmds;
169        }
170        return %{ $self->{'svk_command_aliases'} };
171}
172
173sub svk_command
174{
175        my( $self ) = @_;
176        unless( exists $self->{'svk_command'} ) {
177                my @args = $self->args;
178                my $cur = $self->cur;
179                if( scalar(@args) <= 1 ) {
180                        $self->{'svk_command'} = undef;
181                } else {
182                        my $cmd = $args[0] || '';
183                        $self->{'svk_command'} = (grep $_ eq $cmd,
184                                                       $self->svk_commands
185                                                 )? $cmd: '';
186                        my %alias = $self->svk_command_aliases;
187                        $self->{'svk_command'} = $alias{ $cmd } if $alias{ $cmd };
188                        # old svk versions <= 1.04 doesn't allow to get aliases map
189                        # but have util SVK::Command::_cmd_map
190                        $self->{'svk_command'} ||= eval { SVK::Command::_cmd_map($cmd) } || '';
191                }
192        }
193        return $self->{'svk_command'};
194}
195
196sub svk_command_obj
197{
198        my $self = shift;
199        unless( exists $self->{'svk_command_obj'} ) {
200                my $cmd = $self->svk_command;
201                $cmd = "SVK::Command::". ucfirst($cmd);
202                eval "require $cmd";
203                die $@ if $@;
204                $self->{'svk_command_obj'} = $cmd->new();
205        }
206        return $self->{'svk_command_obj'};
207}
208
209sub options_strings
210{
211        my $obj = $_[0]->svk_command_obj;
212        my %opt = eval{ $obj->command_options };
213        %opt = $obj->options if $@;
214        return sort keys %opt;
215}
216
217sub __svk_sys_path
218{
219        require File::Spec;
220        return $ENV{SVKROOT} || File::Spec->catfile($ENV{HOME}, ".svk");
221}
222
223sub __svk_xd
224{
225        my $self = shift;
226        my $svkpath = $self->__svk_sys_path;
227        require SVK::XD; require Data::Hierarchy;
228        my $xd = SVK::XD->new ( giantlock => File::Spec->catfile($svkpath, 'lock'),
229                                statefile => File::Spec->catfile($svkpath, 'config'),
230                                svkpath => $svkpath,
231                              );
232        $xd->load;
233        return $xd;
234}
235
236sub is_svk_depotname
237{
238        return $_[0]->cur =~ /^\/[^\/]*$/;
239}
240
241sub svk_depotnames
242{
243        return map "/$_/", sort keys %{$_[0]->__svk_xd->{depotmap}};
244}
245
246sub is_svk_depotpath
247{
248        my $self = shift;
249        return unless $self->cur =~ /^(\/[^\/]*\/)/;
250        my $dn = $1;
251        return unless grep $dn eq $_, $self->svk_depotnames;
252        return 1;
253}
254
255sub svk_depotpaths
256{
257        my $self = shift;
258        my $cur = $self->cur;
259        $cur =~ s/[^\/]+$//;
260        my $xd = $self->__svk_xd;
261
262        my $output = '';
263        open my $sfh, ">:scalar", \$output or die;
264        require SVK::Command;
265        eval { require SVK::Path::Checkout };
266        eval { require SVK::Target };
267        SVK::Command->invoke($xd, 'list', $sfh, "-f", $cur );
268        close( $sfh );
269
270        return split /\r*\n/, $output;
271}
272
2731;
274
275package main;
276
277my $complete = new SVK::Complete;
278
279# if we aren't at last pos, get out of here
280exit unless $complete->at_last_pos;
281
282my @args = $complete->args;
283
284my $svk_command = $complete->svk_command;
285
286unless( defined $svk_command ) {
287        $complete->output_list( $complete->svk_commands );
288        exit 0;
289}
290
291unless( $svk_command ) {
292        print STDERR "\nwarning: unknown svk command\n";
293        exit 0;
294}
295
296if( $complete->is_option ) {
297        $complete->output_list( $complete->options );
298        exit 0;
299}
300
301if( $complete->is_svk_depotname ) {
302        my @list = $complete->filter_list( $complete->svk_depotnames );
303        unless( @list == 1 ) {
304                $complete->output_list( @list );
305                exit 0;
306        }
307        $complete->_set_cur( $list[0] );
308}
309
310if( $complete->is_svk_depotpath ) { while(1) {
311        my @list = $complete->filter_list( $complete->svk_depotpaths );
312        unless( @list == 1 ) {
313                $complete->output_list( @list );
314                exit 0;
315        }
316        $complete->_set_cur( $list[0] );
317} }
318
319
320exit 0;
321
Note: See TracBrowser for help on using the browser.