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:
Items:
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
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.
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.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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With