Search code examples
pythonreadlinelibreadline

Python + GNU/readline bindings: keep my sort order


GNU/readline seems to sort my data no matter what I do. My code code looks just like in the documentation:

tags = [tag.lower() for tag in tags]
def completer(text, state):
    text = text.lower()
    options = [tag for tag in tags if tag.startswith(text)]
    try:
        return options[state]
    except IndexError:
        return None

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

If my tags are ['jarre', 'abba', 'beatles'], I keep getting ['abba', 'beatles', 'jarre']. How can I force my order to be kept?


Solution

  • There is dedicated option for this: rl_sort_completion_matches. It sorts the options lexicographically, and removes duplicates - so if you override it, you'll need to take care of duplicates yourself.

    However, it's not accessible from the Python's bindings.

    Fortunately it doesn't mean you can't get it to work - you can change it using cdll or ctypes. Since it's a global variable rather than a function, I'll use in_dll method:

    import ctypes
    rl = ctypes.cdll.LoadLibrary('libreadline.so')
    sort = ctypes.c_ulong.in_dll(rl, 'rl_sort_completion_matches')
    sort.value = 0
    

    After this, the matches should be retrieved in correct order.

    This isn't a very portable method, unfortunately - for example, in Windows you should use .dll suffix rather than Linux's .so. However, focusing on ctypes portability is outside the scope of this answer.