Filter list while typing and select entry

818 Views Asked by At

I try to build a command line / terminal application using the prompt_toolkit, which should do the following:

  • I have a list of (title, id) pairs, which is to big to fit on one screen but small enough to fit into memory. Let's say around 1000 entries.
  • To select an item, I start typing. While typing - for example "dog" - the list is filtered to only those entries, having "dog" in the title.
  • If the list is small enough (but not empty) I would like to use the arrow keys to select an entry.
  • I want to have the id associated with that title.

I'm completely new to prompt_toolkit and approach the problem from both ends:

  1. Using plain prompt() with autocompletion: I tried to misuse a custom completion method to filter my list, but did not managed to display it.
  2. I checked the widget / full screen examples, but the documentation is rather limited. I found for example the SearchToolbar but could not really figure out how it interacts with other widgets.
  3. I did not found any example on how to display a list to select an element. There are more complex examples, so I would expect that it's possible, but got lost.

Could somebody point me to an example that solves something like my use case or give me a starting point how to approach this in general?

1

There are 1 best solutions below

0
On

Here's a quick autocompleter that does what you want I think. This assumes the first field is unique. If it isn't, and the ID is, switch the keys and values in the dictionary.

This checks the first item in the tuple for a match with the input text, and displays both the first item and the ID, then returns the ID when a selection is made.

The display keyword arg lets you show different text in the autocomplete window than what you will return on the completion. The display_meta isn't strictly necessary here, but it does show what will be returned when an item is selected.

from prompt_toolkit import prompt
from prompt_toolkit.completion import Completer, Completion

data_list = [('Katherine Davis', '105221'),
             ('Brandy Norman', '331005'),
             ('Leah Williams', '326092'),
             ('Veronica Sanchez', '104658'),
             ('Joyce Jackson', '236807'),
             ('Scott Luna', '276109'),
             ('Stephanie Fields', '712971'),
             ('Katie Griffin', '324463'),
             ('Gregory Davis', '626086'),
             ('Michael Mullins', '588486')]


class MyCustomCompleter(Completer):
    def __init__(self, data_list):
        self.data_list = data_list
        self.data_dict = dict(data_list)

    def get_completions(self, document, complete_event):
        matches = [name for name in self.data_dict.keys() if document.text in name]
        for m in matches:
            yield Completion(self.data_dict[m], start_position=-len(document.text_before_cursor), display = m, display_meta = self.data_dict[m])
        
mycompleter = MyCustomCompleter(data_list)

if __name__ == '__main__':
    answer = prompt('>', completer = mycompleter)
    print('ID: %s' % answer)