hotshot has asked for the wisdom of the Perl Monks concerning the following question:

Morning all !!

I'v written a shell program (a perl script that opens a shell and the user can explore menus there). I'm currently using Term::ReadLine for tab completion of commands in the shell. The tab completion is using a predefined array of words to be completed as the module requires, I define it as follows in the begining of my script:
my $term = new Term::ReadLine 'Command Line Interface'; my $attribs = $term->Attribs; $attribs->{completion_entry_function} = $attribs->{'list_completion_fu +nction'}; $attribs->{completion_word} = \@commands; # array of supported comma +nds
My problem is that my prompt is built like a tree, a user can get down on the tree and his promp is changes accordingly, for example:
Main> Main> nfs # user enters the nfs menu Main/NFS> # new prompt
The thing is that the array of words to be completed is containg (at the moment) all the commands that can be typed in my shell, no matter what menu the user is in, so pressing tab twice on any menu will give the list of all word in the array and not only the relevant for each menu.
is there a way to handle this more efficiently or I'll have to maintain commands array for each menu I'm in?

Hotshot

Replies are listed 'Best First'.
Re: Tab completion in a shell
by Aristotle (Chancellor) on Sep 19, 2002 at 19:49 UTC
    Well, in one way or other you'll have to. You might like to use something like this: $attribs->{completion_word} = [ map /([^:]+):.*\b\Q$menu\E\b/, @commands ]; Now you can write elements of @command as 'command: menu1 menu2 menu3' where menu1, menu2, menu3 etc are menu names. Provided the menu name is stored in $menu, the regex will filter the appropriate entries and provide the first part in front of the colon as command name. In your example you could have something like an 'nfs: main network' element.

    Makeshifts last the longest.

Re: Tab completion in a shell
by zengargoyle (Deacon) on Sep 21, 2002 at 21:07 UTC

    Something like this?

    whacl> <tab><tab>
    dig          help         llocks       need_commit  rcs          unlock
    exit         list         lock         perform      touch_lm     untouch_lm
    
    whacl> help rcs
    Usage: rcs <rtr>
    
    whacl> rcs rtrA
    rcs(rtrA)> <tab><tab>
    exit        help        lock        publish     syncronize  unlock
    
    rcs(rtrA)> exit 
    whacl> help lock
    Usage: lock <rtr> <acl>
    
    whacl> lock rtrA external-blocks
    acl(rtrA,external-blocks)> <tab><tab>
    block            help             list             unlock_and_exit
    
    acl(rtrA,external-blocks)> block 192.168.254.1/32
    block(192.168.254.1/32)> <tab><tab>
    case     end      freshen  insert   mac      note     priv     status
    delete   exit     help     list     modify   options  show     update
    
    block(192.168.254.1/32)> delete
    block(192.168.254.1/32)> exit 
    acl(rtrA,external-blocks)> unlock_and_exit 
    whacl> exit 
    

    This goes something like:

    use Q::Shell; use Q::RCSShell; use Q::ACLShell; use Q::WhaclShell; Q::WhaclShell->new ( prompt => 'whacl> ' )->loop(); exit 0; ##### use Q::Shell; package Q::WhaclShell; our (@ISA) = qw( Q::Shell ); sub new { my ($class,%param) = @_; # blah blah SUPER::new $self->add_dispatch( rcs => [ \&_rcs, "short help", "long help" ], lock => [ \&_lock, "short help", "long help" ], # ... ); return $self; } sub _rcs { my ($self,@arg) = @_; Q::RCSShell->new( )->loop(@arg); } sub _lock { my ($self,@arg) = @_; Q::ACLShell->new( )->loop(@arg); } 1; # and so on for Q::RCSShell and Q::ACLShell package Q::Shell; my %default_commands = ( help => [ \&_help, "short help", "long help" ], exit => [ \&_exit, "short help", "long help" ], ); sub add_dispatch { } sub del_dispatch { } sub _cmd_list { keys %{$_[0]->{_dispatch}} } sub _help { # help - show all short help # help <cmd> - show long help for <cmd> } sub _exit { $_[0]->{done} = 1 } sub new { # setup ReadLine # copy default_commands to $self->{_dispatch} return $self; } sub dispatch { # find and do <cmd> or warn whatever } sub loop { # setup ReadLine with completions from $self->_cmd_list while ( not $self->{done} ) { # read and dispatch <cmd> <args> # reset ReadLine stuff (a command may have changed them) } } 1;