Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I implement incremental (find-as-you-type) search on command line?

Tags:

scripting

I'd like to write small scripts which feature incremental search (find-as-you-type) on the command line.

Use case: I have my mobile phone connected via USB, Using gammu --sendsms TEXT I can write text messages. I have the phonebook as CSV, and want to search-as-i-type on that.

What's the easiest/best way to do it? It might be in bash/zsh/Perl/Python or any other scripting language.

Edit: Solution: Modifying Term::Complete did what I want. See below for the answer.

like image 707
Florian Bw Avatar asked Feb 27 '23 03:02

Florian Bw


2 Answers

I get the impression GNU Readline supports this kind of thing. Though, I have not used it myself. Here is a C++ example of custom auto complete, which could easily be done in C too. There is also a Python API for readline.

This StackOverflow question gives examples in Python, one of which is ...

import readline
def completer(text, state):
    options = [x in addrs where x.startswith(text)]
    if state < options.length:
        return options[state]
    else
        return None
readline.set_completer(completer)

this article on Bash autocompletion may help. This article also gives examples of programming bash's auto complete feature.

like image 173
Aiden Bell Avatar answered Mar 05 '23 17:03

Aiden Bell


Following Aiden Bell's hint, I tried Readline in Perl. Solution 1 using Term::Complete (also used by CPAN, I think):

use Term::Complete;   
my $F; 
open($F,"<","bin/phonebook.csv"); 
my @terms = <$F>; chomp(@terms); 
close($F);


my $input;
while (!defined $input) {
   $input = Complete("Enter a name or number: ",@terms);
   my ($name,$number) = split(/\t/,$input);
   print("Sending SMS to $name ($number).\n");
   system("sudo gammu --sendsms TEXT $number");
}

Press \ to complete, press Ctrl-D to see all possibilities.

Solution 2: Ctrl-D is one keystroke to much, so using standard Term::Readline allows completion and the display off possible completions using only \.

use Term::ReadLine;

my $F;
open($F,"<","bin/phonebook.csv");
my @terms = <$F>; chomp(@terms);
close($F);

my $term = new Term::ReadLine;
$term->Attribs->{completion_function} = sub { return @terms; };

my $prompt = "Enter name or number >> ";

my $OUT = $term->OUT || \*STDOUT;
while ( defined (my $input = $term->readline($prompt)) ) {
   my ($name,$number) = split(/\t/,$input);
   print("Sending SMS to $name ($number).\n");
   system("sudo gammu --sendsms TEXT $number");
}

This solution still needs a for completion.

Edit: Final Solution Modifying Term::Complete (http://search.cpan.org/~jesse/perl-5.12.0/lib/Term/Complete.pm) does give me on the fly completion.

Source code: http://search.cpan.org/CPAN/authors/id/J/JE/JESSE/perl-5.12.0.tar.gz Solution number 1 works with this modification. I will put the whole sample online somewhere else if this can be used by somebody

Modifications of Completion.pm (just reusing it's code for Control-D and \ for each character):

170c172,189

        my $redo=0;

                @match = grep(/^\Q$return/, @cmp_lst);
                unless ($#match < 0) {
                    $l = length($test = shift(@match));
                    foreach $cmp (@match) {
                        until (substr($cmp, 0, $l) eq substr($test, 0, $l)) {
                            $l--;
                        }
                    }
                    print("\a");
                    print($test = substr($test, $r, $l - $r));
                          $redo = $l - $r == 0;
                          if ($redo) { print(join("\r\n", '', grep(/^\Q$return/, @cmp_lst)), "\r\n"); }
                    $r = length($return .= $test);
                }
                    if ($redo) { redo LOOP; } else { last CASE; }

like image 21
Florian Bw Avatar answered Mar 05 '23 17:03

Florian Bw