views:

1998

answers:

1

Python, through it's readline bindings allows for great command-line autocompletion (as described in here).

But, the completion only seems to work at the beginning of strings. If you want to match the middle or end of a string readline doesn't work.

I would like to autocomplete strings, in a command-line python program by matching what I type with any of the strings in a list of available strings.

  • A good example of the type of autocompletion I would like to have is the type that happens in GMail when you type in the To field. If you type one of your contacts' last name, it will come up just as well as if you typed her first name.
  • Some use of the up and down arrows or some other method to select from the matched strings may be needed (and not needed in the case of readline) and that is fine in my case.
  • My particular use case is a command-line program that sends emails.
  • Specific code examples would be very helpful.

Using terminal emulators like curses would be fine. It only has to run on linux, not Mac or Windows.

Here is an example: Say I have the following three strings in a list

['Paul Eden <[email protected]>', 
'Eden Jones <[email protected]>', 
'Somebody Else <[email protected]>']

I would like some code that will autocomplete the first two items in the list after I type 'Eden' and then allow me to pick one of them (all through the command-line using the keyboard).

+4  A: 

I'm not sure I understand the problem. You could use readline.clear_history and readline.add_history to set up the completable strings you want, then control-r to search backword in the history (just as if you were at a shell prompt). For example:

#!/usr/bin/env python

import readline

readline.clear_history()
readline.add_history('foo')
readline.add_history('bar')

while 1:
    print raw_input('> ')

Alternatively, you could write your own completer version and bind the appropriate key to it. This version uses caching in case your match list is huge:

#!/usr/bin/env python

import readline

values = ['Paul Eden <[email protected]>', 
          'Eden Jones <[email protected]>', 
          'Somebody Else <[email protected]>']
completions = {}

def completer(text, state):
    try:
        matches = completions[text]
    except KeyError:
        matches = [value for value in values
                   if text.upper() in value.upper()]
        completions[text] = matches
    try:
        return matches[state]
    except IndexError:
        return None

readline.set_completer(completer)
readline.parse_and_bind('tab: menu-complete')

while 1:
    a = raw_input('> ')
    print 'said:', a
Just Some Guy
Yes, I would like to be able to search in the middle or end of strings and not just in the beginning.
Paul D. Eden
The above example works that way. If I press "control-r o", it selects "foo".
Just Some Guy
Novel idea. Is there a way to do automatically (i.e., without pressing control-r first)?
Paul D. Eden
The 2nd option is excellent for my needs, Thank You.
Paul D. Eden