Skip to content

Instantly share code, notes, and snippets.

@iamatypeofwalrus
Created May 23, 2013 17:38
Show Gist options
  • Save iamatypeofwalrus/5637895 to your computer and use it in GitHub Desktop.
Save iamatypeofwalrus/5637895 to your computer and use it in GitHub Desktop.
A simple Python Tab Completer for either system paths OR lists.
import os
import sys
import readline
import glob
class tabCompleter(object):
"""
A tab completer that can either complete from
the filesystem or from a list.
Partially taken from:
http://stackoverflow.com/questions/5637124/tab-completion-in-pythons-raw-input
"""
def pathCompleter(self,text,state):
"""
This is the tab completer for systems paths.
Only tested on *nix systems
"""
line = readline.get_line_buffer().split()
return [x for x in glob.glob(text+'*')][state]
def createListCompleter(self,ll):
"""
This is a closure that creates a method that autocompletes from
the given list.
Since the autocomplete function can't be given a list to complete from
a closure is used to create the listCompleter function with a list to complete
from.
"""
def listCompleter(text,state):
line = readline.get_line_buffer()
if not line:
return [c + " " for c in ll][state]
else:
return [c + " " for c in ll if c.startswith(line)][state]
self.listCompleter = listCompleter
if __name__=="__main__":
t = tabCompleter()
t.createListCompleter(["ab","aa","bcd","bdf"])
readline.set_completer_delims('\t')
readline.parse_and_bind("tab: complete")
readline.set_completer(t.listCompleter)
ans = raw_input("Complete from list ")
print ans
readline.set_completer(t.pathCompleter)
ans = raw_input("What file do you want? ")
print ans
@atomicpages
Copy link

atomicpages commented May 7, 2018

Add tilde ~ support by doing the following:

def pathCompleter(self, text, state):
    """ 
    This is the tab completer for systems paths.
    Only tested on *nix systems
    """
    line = readline.get_line_buffer().split()
    
    # replace ~ with the user's home dir. See https://docs.python.org/2/library/os.path.html
    if '~' in text:
        text = os.path.expanduser('~')

    # autocomplete directories with having a trailing slash
    if os.path.isdir(text):
        text += '/'

    return [x for x in glob.glob(text + '*')][state]

@chapmanjacobd
Copy link

you probably meant

    if '~' in text:
        text = os.path.expanduser(text)

@chapmanjacobd
Copy link

chapmanjacobd commented Jan 20, 2023

If you have many items you can easily truncate the results like so:

def set_readline_completion(list_):
    def create_completer(list_):
        def list_completer(_text, state):
            line   = readline.get_line_buffer()

            if not line:
                return [c + " " for c in list_][:25][state]

            else:
                return [c + " " for c in list_ if c.startswith(line)][:15][state]

        return list_completer

    listCompleter = create_completer(list_)
    readline.set_completer_delims('\t')
    readline.parse_and_bind("tab: complete")
    readline.set_completer(listCompleter)

although this will lead to some premature path diving... maybe something like this is better if your list is a bunch of pathlike strings (of course if your paths are fs paths you are better off with os.walk or os.scandir):

def set_readline_completion(list_):
    def create_completer(list_):
        def list_completer(_text, state):
            line = readline.get_line_buffer()

            if not line:
                min_depth = min([s.count(os.sep) for s in list_]) + 1
                result_list = [c + " " for c in list_ if c.count(os.sep) <= min_depth]
                shuffle(result_list)
                return result_list[:25][state]
            else:
                match_list = [s for s in list_ if s.startswith(line)]
                min_depth = min([s.count(os.sep) for s in match_list]) + 1
                result_list = [c + " " for c in match_list if c.count(os.sep) <= min_depth]
                shuffle(result_list)
                return result_list[:15][state]

        return list_completer

    readline.set_completer(create_completer(list_))
    readline.set_completer_delims('\t')
    readline.parse_and_bind("tab: complete")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment