Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Python library for custom autocompletion?

Is there a generic library that would allow me to have autocompletion according to a custom grammar and item list?

Here's an example of what I am looking for.

Grammar:

  • You can munch apples and mangoes
  • You can drink milk and water
  • You can move everything
  • Sentence structure: verb [+ adjective] + object

Items:

  • 1 green apple
  • 1 microscopic apple
  • 1 green mango
  • 1 yellow mango
  • 1 mango [no color given]
  • 1 milk
  • 1 water

Expected behaviour (user's input on the first line, suggestions on the second)

m
move, munch

mo
move

move g
move green apple, move green mango

move y
move yellow mango

move m
move milk, move mango, move microscopic apple
like image 500
mac Avatar asked Jun 27 '26 07:06

mac


2 Answers

One module for auto-completion I'm aware of is Qt's QCompleter, which you can use in Python via PyQt or PySide. I don't think it understands grammar in the way you mean, but it's generic enough to allow you writing code that does.

like image 92
Eli Bendersky Avatar answered Jun 28 '26 20:06

Eli Bendersky


I finally found an acceptable solution by using a combination of SPARK (for grammar parsing / syntactic analysis) and my own code for autocompletion.

About SPARK

SPARK stands for the Scanning, Parsing, and Rewriting Kit. It formerly had no name, and was referred to as the "little language framework." The first version (circa 1998) was described in the paper Compiling Little Languages in Python at the 7th International Python Conference.

SPARK is written in 100% pure Python, and is made available as open source.

The autocompletion code

In the following code:

  • category is the kind of word we are autocompleting. This is obtained by parsing the current command line. For example: if the user is typing "drink m", the parser will know to expect a word in the category "liquids" defined in the grammar.
  • The user input is stored in a list (self.chars)
  • _get_list_of_existing() returns a list of existing words in a given category
  • _get_common_beginning() return - if available - the longest initial supersequence for multiple matches. For example if the user input is writing "ma" and possible autocompletion words are [magnolia, magnifying glass] the _get_common_beginning() will return "magn".

Here's the relevant code snippets:

def autocomplete(self, category):
    '''
    If possible, autocomplete a word according to its category.
    '''
    root = ''.join(self.chars).split()[-1]  #The bit after the last space
    pool = self._get_list_of_existing(category)
    matches = [i for i in pool if i.find(root) == 0]
    if len(matches) == 1:
        match = matches[0]+' '
    elif len(matches) > 1:
        match = self._get_common_beginning(matches)
    else:
        return
    self.chars.extend(list(match[len(root):]))

def _get_common_beginning(self, strings):
    '''
    Return the strings that is common to the beginning of each string in
    the strings list.
    '''
    result = []
    limit = min([len(s) for s in strings])
    for i in range(limit):
        chs = set([s[i] for s in strings])
        if len(chs) == 1:
            result.append(chs.pop())
        else:
            break
    return ''.join(result)
like image 20
mac Avatar answered Jun 28 '26 20:06

mac