source: perl/lib/BarnOwl/Complete/Client.pm @ bc5e856

release-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since bc5e856 was bc5e856, checked in by David Benjamin <davidben@mit.edu>, 15 years ago
Add completer for filter expressions It parses the incomplete input to figure out what keywords, etc., you can add next. Signed-off-by: David Benjamin <davidben@mit.edu>
  • Property mode set to 100644
File size: 6.3 KB
Line 
1use strict;
2use warnings;
3
4# Completers for client state
5
6package BarnOwl::Complete::Client;
7
8use BarnOwl::Completion::Util qw(complete_flags);
9
10my @all_colors = qw(default
11                    black
12                    blue
13                    cyan
14                    green
15                    magenta
16                    red
17                    white
18                    yellow);
19
20my %show = (
21    information => undef,
22    colors      => undef,
23    commands    => undef,
24    command     => \&complete_command,
25    filters     => undef,
26    filter      => \&complete_filter_name,
27    license     => undef,
28    quickstart  => undef,
29    startup     => undef,
30    status      => undef,
31    styles      => undef,
32    subscriptions       => undef,
33    subs        => undef,
34    terminal    => undef,
35    variables   => undef,
36    variable    => \&complete_variable,
37    version     => undef,
38    view        => undef,
39    zpunts      => undef,
40   );
41
42sub complete_command { return sort @BarnOwl::all_commands; }
43sub complete_color { return @all_colors; }
44sub complete_filter_name { return @{BarnOwl::all_filters()}; }
45sub complete_variable {}        # stub
46
47my %filter_cmds = (
48    sender    => \&BarnOwl::Complete::Zephyr::complete_user,
49    recipient => \&BarnOwl::Complete::Zephyr::complete_user,
50    class     => \&BarnOwl::Complete::Zephyr::complete_class,
51    instance  => \&BarnOwl::Complete::Zephyr::complete_instance,
52    opcode    => \&BarnOwl::Complete::Zephyr::complete_opcode,
53    realm     => \&BarnOwl::Complete::Zephyr::complete_realm,
54    body      => undef,
55    hostname  => undef,
56    type      => sub { qw(zephyr aim admin); },
57    direction => sub { qw(in out none); },
58    login     => sub { qw(login logout none); },
59    filter    => \&complete_filter_name,
60    perl      => undef,
61);
62
63# Returns:
64# - where to look next after pulling out an expression
65# - $INCOMPLETE if this cannot form a complete expression (or w/e)
66# - pushes to completion list as it finds valid completions
67
68my $INCOMPLETE = -1;
69sub _complete_filter_expr {
70    # Takes as arguments context and the index into $ctx->words where the
71    # filter expression starts
72    my $ctx = shift;
73    my $start = shift;
74    my $o_comp = shift;
75    my $end = $ctx->word;
76
77    # Grab an expression; we don't allow empty
78    my $i = $start;
79    $i = _complete_filter_primitive_expr($ctx, $start, $o_comp);
80    return $INCOMPLETE if $start == $INCOMPLETE;
81
82    while ($i <= $end) {
83        if ($i == $end) {
84            # We could and/or another clause
85            push @$o_comp, qw(and or);
86            return $end; # Or we could let the parent do his thing
87        }
88
89        if ($ctx->words->[$i] ne 'and' && $ctx->words->[$i] ne 'or') {
90            return $i; # We're done. Let the parent do his thing
91        }
92
93        # Eat the and/or
94        $i++;
95
96        # Grab an expression
97        $i = _complete_filter_primitive_expr($ctx, $i, $o_comp);
98        return $INCOMPLETE if $i == $INCOMPLETE;
99    }
100   
101    return $i; # Well, it looks like we're happy
102    # (Actually, I'm pretty sure this never happens...)
103}
104
105sub _complete_filter_primitive_expr {
106    my $ctx = shift;
107    my $start = shift;
108    my $o_comp = shift;
109    my $end = $ctx->word;
110
111    if ($start >= $end) {
112        push @$o_comp, "(";
113        push @$o_comp, qw(true false not);
114        push @$o_comp, keys %filter_cmds;
115        return $INCOMPLETE;
116    }
117
118    my $word = $ctx->words->[$start];
119    if ($word eq "(") {
120        $start = _complete_filter_expr($ctx, $start+1, $o_comp);
121        return $INCOMPLETE if $start == $INCOMPLETE;
122
123        # Now, we expect a ")"
124        if ($start >= $end) {
125            push @$o_comp, ")";
126            return $INCOMPLETE;
127        }
128        if ($ctx->words->[$start] ne ')') {
129            # User is being confusing. Give up.
130            return $INCOMPLETE;
131        }
132        return $start+1; # Eat the )
133    } elsif ($word eq "not") {
134        # We just want another primitive expression
135        return _complete_filter_primitive_expr($ctx, $start+1, $o_comp);
136    } elsif ($word eq "true" || $word eq "false") {
137        # No arguments
138        return $start+1; # Eat the boolean. Mmmm, tasty.
139    } else {
140        # It's of the form 'CMD ARG'
141        return $start+2 if ($start+1 < $end); # The user supplied the argument
142
143        # complete the argument
144        my $arg_func = $filter_cmds{$word};
145        push @$o_comp, ($arg_func ? ($arg_func->()) : ());
146        return $INCOMPLETE;
147    }
148}
149
150sub complete_filter_expr {
151    my $ctx = shift;
152    my $start = shift;
153    my @completions = ();
154    _complete_filter_expr($ctx, $start, \@completions);
155    # Get rid of duplicates and sort
156    my %hash = ();
157    @hash{@completions} = ();
158    @completions = sort keys %hash;
159    return @completions;
160}
161
162sub complete_filter_args {
163    my $ctx = shift;
164    my $arg = shift;
165    return complete_filter_name() unless $arg;
166    my $idx = 2; # skip the filter name
167    while ($idx < $ctx->word) {
168        last unless ($ctx->words->[$idx] =~ m{^-});
169        $idx += 2; # skip the flag and the argument
170    }
171    return complete_filter_expr($ctx, $idx);
172}
173
174sub complete_help {
175    my $ctx = shift;
176    if($ctx->word == 1) {
177        return complete_command();
178    }
179}
180
181sub complete_show {
182    my $ctx = shift;
183    if($ctx->word == 1) {
184        return keys %show;
185    } elsif ($ctx->word == 2) {
186        my $cmd = $show{$ctx->words->[1]};
187        if($cmd) {
188            return $cmd->($ctx);
189        }
190    }
191}
192
193sub complete_filter {
194    my $ctx = shift;
195    return complete_flags($ctx,
196        [qw()],
197        {
198           "-c" => \&complete_color,
199           "-b" => \&complete_color,
200        },
201         \&complete_filter_args
202        );
203}
204
205sub complete_getvar {
206    my $ctx = shift;
207    return unless ($ctx->word == 1);
208    return complete_variable();
209}
210
211sub complete_set {
212    my $ctx = shift;
213    return complete_flags($ctx,
214        [qw(-q)],
215        {
216        },
217         \&complete_set_args
218        );
219}
220sub complete_set_args {
221    my $ctx = shift;
222    my $arg = shift;
223    return if $arg;
224    return complete_variable();
225}
226
227BarnOwl::Completion::register_completer(help    => \&complete_help);
228BarnOwl::Completion::register_completer(filter  => \&complete_filter);
229BarnOwl::Completion::register_completer(show    => \&complete_show);
230BarnOwl::Completion::register_completer(getvar  => \&complete_getvar);
231BarnOwl::Completion::register_completer(set     => \&complete_set);
232
2331;
Note: See TracBrowser for help on using the repository browser.