# dummy items to play with my @animals = (qw( cow camel dog cat )); my @foods = (qw( fish pasture meat )); my @places = (qw( wood sea desert )); # commands my $commands = { select => { completion_list => \@animals, commands => { give => { completion_list => \@foods, }, take_to => { completion_list => \@places, }, }, }, kill => { completion_list => \@animals, } }; #### Press TAB for autocompletion or available options autocompletion nested>("kill", "select") # 'TAB' on empty input shows available commands autocompletion nested>select c("camel", "cat", "cow") # 'cTAB' shows animals but not 'dog' autocompletion nested>select ca("camel", "cat") # 'caTAB' shows two animals starting with 'ca' autocompletion nested>select camel ("give", "take_to") # I press 'mTAB' thae autocomplete in 'camel' # 'TAB' again for next level commands: give and take_to autocompletion nested>select camel take_to ("desert", "sea", "wood") # 'tTAB' and 'TAB' again autocompletion nested>select camel take_to desert # 'desTAB' leads to 'desert' # ENTER 1 autocompletion nested> # ENTER 2 exits the user input cycle CHOOSEN: select camel take_to desert # finally we print the resulting path #### #!perl use strict; use warnings; use Data::Diver qw( Dive ); use Data::Dump; # https://www.perlmonks.org/?node_id=1108329 BEGIN { # you can force Term::ReadLine to load the ::Perl # by setting the ENV PERL_RL variable # but you have to do it in a begin block # before the use statement # try to comment next line: probably ::Perl will be loaded # anyway if it is installed # try to set to a non available sub module # see details: https://metacpan.org/pod/Term::ReadLine#ENVIRONMENT $ENV{PERL_RL}="Perl"; # on win32 systems it ENV TERM is 'dumb' # autocompletion is not possible. # try to comment next line on win32 and watch how does not work # see also http://bvr.github.io/2010/11/term-readline/ $ENV{TERM} = 'not dumb' if $^O eq 'MSWin32'; } use Term::ReadLine; my $term=Term::ReadLine->new("test"); my $debug = 0; # 0 1 2 # Term::ReadLine is a wrapper module: it initilizes an Attribs hasref # internally used to fill in the right configuration. # You can use this hashref to set autompletion manually. As i did here. # # YOU MUST NOT USE readline::rl_basic_commands DIRECTLY! # # you cannot print anything from hashref returned by $term->Attribs #foreach my $k (keys $term->Attribs){print "$k ${$term->Attribs}{$k}\n"} # dummy items to play with my @animals = (qw( cow camel dog cat )); my @foods = (qw( fish pasture meat )); my @places = (qw( wood sea desert)); # commands my $commands = { select => { completion_list => \@animals, commands => { give => { completion_list => \@foods, }, take_to => { completion_list => \@places, }, }, }, kill => { completion_list => \@animals, } }; # starting from top level of $commands my $hr = $commands; # first time initialization of autocompletion define_completion_func($hr); print "The Term::ReadLine effective module loaded is: ",$term->ReadLine,"\n" if $debug; print "Press TAB for autocompletion or available options\n"; my @choosen; my $prompt = "autocompletion nested>"; while ( defined ( $_ = $term->readline( $prompt ) ) ) { s/\s+//g; if (/^$/){ print "CHOOSEN: ", (join ' ', @choosen),"\n"; last; } } sub define_completion_func { my $cur_hr = shift; # completion_list OR keys of the current HASH my @cur_list = $cur_hr->{completion_list} ? @{$cur_hr->{completion_list}} : keys %$cur_hr; $term->Attribs->{completion_function} = sub { my ($text, $line, $start) = @_; if ( $debug ){ print "\n\tDEBUG: text, line, start = [$text] [$line] [$start]\n";} # populate results (0 1 or more) my @res = grep { /^$text/i } sort @cur_list ; ##################################### # one result AND defined in the %HASH if ( 1 == scalar @res and defined Dive($cur_hr, $res[0]) ){ # change the current pointer to the HASH to what entered $hr = $cur_hr->{ $res[0] }; if ( $debug > 1 ){ print "CHANGING \$hr to: "; dd $hr; } # save the result push @choosen, $res[0]; # redefine autocmpletion define_completion_func($hr); } ######################################################## # one result ( but NOT defined this result in the HASH ) elsif( 1 == scalar @res ){ # change the current pointer to the HASH to one level above.. $hr = Dive( $cur_hr, @choosen[0..$#choosen-1] ); # ..to commands $hr = $hr->{commands}; if ( $debug > 1 ){ print "BACK to commands. \$hr: "; dd $hr; } # save the result push @choosen, $res[0]; # redefine autocmpletion define_completion_func($hr); } ##################################################### # more then one result: print options still available elsif ( scalar @res > 1){ # show available options dd @res; # mimicry of the prompt and the line at this moment print $prompt,(join ' ',@choosen), (@choosen ? " " : ""), $text; # nothing to return return; } ############# # TAB pressed elsif ( $text eq '' ){ # show available options dd @res; # mimicry of the prompt and the line at this moment print $prompt,(join ' ',@choosen), @choosen ? " " : ""; # nothing to return return; } # elsif ( 0 == @cur_list ){ # how to tell the while loop to terminate?? # } # RELAXED ?? # extra stuff can be provided ? filling @choosen_relaxed? else{ die "Unknown happened.." } # BACKSPACE ?? modify the line? no... # my $check_line = join ' ',@choosen; # unless ( $check_line =~ /$line/ ){ # print "BACKSPACE!\n"; # dd @choosen; # } if ( $debug > 1 ){ print "RETURNING "; dd @res; } # return results return @res; }; }