use strict; use warnings; # Completers for client state package BarnOwl::Complete::Client; use BarnOwl::Completion::Util qw(complete_flags); my @all_colors = qw(default black blue cyan green magenta red white yellow); my %show = ( information => undef, colors => undef, commands => undef, command => \&complete_command, filters => undef, filter => \&complete_filter_name, license => undef, quickstart => undef, startup => undef, status => undef, styles => undef, subscriptions => undef, subs => undef, terminal => undef, variables => undef, variable => \&complete_variable, version => undef, view => undef, zpunts => undef, ); sub complete_command { return sort @BarnOwl::all_commands; } sub complete_color { return @all_colors; } sub complete_filter_name { return @{BarnOwl::all_filters()}; } sub complete_variable { return @{BarnOwl::all_variables()}; } sub complete_style { return @{BarnOwl::all_styles()}; } my %filter_cmds = ( sender => \&BarnOwl::Complete::Zephyr::complete_user, recipient => \&BarnOwl::Complete::Zephyr::complete_user, class => \&BarnOwl::Complete::Zephyr::complete_class, instance => \&BarnOwl::Complete::Zephyr::complete_instance, opcode => \&BarnOwl::Complete::Zephyr::complete_opcode, realm => \&BarnOwl::Complete::Zephyr::complete_realm, body => undef, hostname => undef, type => sub { qw(zephyr aim admin); }, direction => sub { qw(in out none); }, login => sub { qw(login logout none); }, filter => \&complete_filter_name, perl => undef, ); # Returns: # - where to look next after pulling out an expression # - $INCOMPLETE if this cannot form a complete expression (or w/e) # - pushes to completion list as it finds valid completions my $INCOMPLETE = -1; sub _complete_filter_expr { # Takes as arguments context and the index into $ctx->words where the # filter expression starts my $ctx = shift; my $start = shift; my $o_comp = shift; my $end = $ctx->word; # Grab an expression; we don't allow empty my $i = $start; $i = _complete_filter_primitive_expr($ctx, $start, $o_comp); return $INCOMPLETE if $start == $INCOMPLETE; while ($i <= $end) { if ($i == $end) { # We could and/or another clause push @$o_comp, qw(and or); return $end; # Or we could let the parent do his thing } if ($ctx->words->[$i] ne 'and' && $ctx->words->[$i] ne 'or') { return $i; # We're done. Let the parent do his thing } # Eat the and/or $i++; # Grab an expression $i = _complete_filter_primitive_expr($ctx, $i, $o_comp); return $INCOMPLETE if $i == $INCOMPLETE; } return $i; # Well, it looks like we're happy # (Actually, I'm pretty sure this never happens...) } sub _complete_filter_primitive_expr { my $ctx = shift; my $start = shift; my $o_comp = shift; my $end = $ctx->word; if ($start >= $end) { push @$o_comp, "("; push @$o_comp, qw(true false not); push @$o_comp, keys %filter_cmds; return $INCOMPLETE; } my $word = $ctx->words->[$start]; if ($word eq "(") { $start = _complete_filter_expr($ctx, $start+1, $o_comp); return $INCOMPLETE if $start == $INCOMPLETE; # Now, we expect a ")" if ($start >= $end) { push @$o_comp, ")"; return $INCOMPLETE; } if ($ctx->words->[$start] ne ')') { # User is being confusing. Give up. return $INCOMPLETE; } return $start+1; # Eat the ) } elsif ($word eq "not") { # We just want another primitive expression return _complete_filter_primitive_expr($ctx, $start+1, $o_comp); } elsif ($word eq "true" || $word eq "false") { # No arguments return $start+1; # Eat the boolean. Mmmm, tasty. } else { # It's of the form 'CMD ARG' return $start+2 if ($start+1 < $end); # The user supplied the argument # complete the argument my $arg_func = $filter_cmds{$word}; push @$o_comp, ($arg_func ? ($arg_func->()) : ()); return $INCOMPLETE; } } sub complete_filter_expr { my $ctx = shift; my $start = shift; my @completions = (); _complete_filter_expr($ctx, $start, \@completions); # Get rid of duplicates and sort my %hash = (); @hash{@completions} = (); @completions = sort keys %hash; return @completions; } sub complete_filter_args { my $ctx = shift; my $arg = shift; return complete_filter_name() unless $arg; my $idx = 2; # skip the filter name while ($idx < $ctx->word) { last unless ($ctx->words->[$idx] =~ m{^-}); $idx += 2; # skip the flag and the argument } return complete_filter_expr($ctx, $idx); } sub complete_help { my $ctx = shift; if($ctx->word == 1) { return complete_command(); } } sub complete_show { my $ctx = shift; if($ctx->word == 1) { return keys %show; } elsif ($ctx->word == 2) { my $cmd = $show{$ctx->words->[1]}; if($cmd) { return $cmd->($ctx); } } } sub complete_filter { my $ctx = shift; return complete_flags($ctx, [qw()], { "-c" => \&complete_color, "-b" => \&complete_color, }, \&complete_filter_args ); } sub complete_view { my $ctx = shift; if ($ctx->word == 1) { return ("--home", "-d", "-r", "-s", complete_filter_name()); } if ($ctx->words->[1] eq "--home") { return; } if ($ctx->words->[1] eq "-d") { return complete_filter_expr($ctx, 2); } if ($ctx->words->[1] eq "-s") { return complete_style(); } return; } sub complete_getvar { my $ctx = shift; return unless ($ctx->word == 1); return complete_variable(); } sub complete_set { my $ctx = shift; return complete_flags($ctx, [qw(-q)], { }, \&complete_set_args ); } sub complete_set_args { my $ctx = shift; my $arg = shift; return if $arg; return complete_variable(); } BarnOwl::Completion::register_completer(help => \&complete_help); BarnOwl::Completion::register_completer(filter => \&complete_filter); BarnOwl::Completion::register_completer(view => \&complete_view); BarnOwl::Completion::register_completer(show => \&complete_show); BarnOwl::Completion::register_completer(getvar => \&complete_getvar); BarnOwl::Completion::register_completer(set => \&complete_set); 1;