Skip to content

Instantly share code, notes, and snippets.

@fredrikhl
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fredrikhl/eb7934613d1acfc94db9 to your computer and use it in GitHub Desktop.
Save fredrikhl/eb7934613d1acfc94db9 to your computer and use it in GitHub Desktop.
pyrc-autocomplete
#!/usr/bin/env python
#
"""Python startup script."""
class IndentOrCompleteMixin(object):
u""" Mixin to add tab completion for inserting indents.
The idea is that a (base10) natural number (e.g. 3, 45) has no sensible
auto-complete value. This mixin, intended for use with rlcompleter, adds
completion for numbers. When suggesting number completions, however, this
mixin only returns one suggestion - which is a string containing that
number of indents.
"""
bind_backend = {'libedit': u'bind ^I rl_complete',
'readline': u'tab: complete', }
u""" The command used to bind tab to completion with different
python readline implementations. """
def __init__(self, indent=' '):
u""" Initialize completer.
:param str indent: The intent string we want (default=' ')
"""
self.__tab = indent
super(IndentOrCompleteMixin, self).__init__()
@classmethod
def get_bind_command(cls, readline_module):
u""" Get keybind command suggestion for `readline_module'.
Parses the docstring of `readline_module', to decide which
implementation it is.
:param readline_module: The imported readline module.
:return str:
Returns a command string that can be passed to
`readline_module.parse_and_bind()'
"""
if 'GNU readline' in getattr(readline_module, '__doc__', ''):
return cls.bind_backend['readline']
elif 'libedit' in getattr(readline_module, '__doc__', ''):
return cls.bind_backend['libedit']
# No match, gamble that it will work with readline command
return cls.bind_backend['readline']
def complete(self, text, state):
u""" Fetch next suggestion.
:param str text: The actual text we're completing.
:param int state: The suggestion number
:ref: `readline documentation <python:readline>`
:rtype: NoneType, basestring
:return: Suggestion string, or None if no more suggestions exist.
"""
if text.isdigit():
if state == 0:
num = int(text)
return self.__tab * num
return None
else:
return super(IndentOrCompleteMixin, self).complete(text, state)
@classmethod
def get_completer(cls, completer=type):
u""" Prepare a completer class with this mixin.
:type completer: type
:param completer:
Use `completer' as base class. If `completer' already have `cls' in
its bases, we do nothing (just return `completer' as is).
If `completer' is `type' or `cls', we try to use
`rlcompleter.Completer' as base class (this is the default).
:return type: Returns a completer class with this mixin.
"""
if type(completer) is not type:
raise TypeError(
"`completer' must be of type 'type' (not %s)" %
type(completer))
if completer is type or completer == cls:
from rlcompleter import Completer as completer
if not issubclass(completer, cls):
completer = type('_IndentOrComplete',
(cls, completer, object),
{})
return completer
@classmethod
def register(cls, *args, **kwargs):
u""" Register with readline.
Performs the neccessay readline magic to enable this completer in
an interactive session.
"""
import readline
completer_cls = cls.get_completer()
readline.parse_and_bind(cls.get_bind_command(readline))
readline.set_completer(completer_cls(*args, **kwargs).complete)
if __name__ == '__main__':
try:
IndentOrCompleteMixin.register()
except ImportError:
print(u"NOTE: Unable to set up tab completion")
@fredrikhl
Copy link
Author

To use from scripts that invoke an interactive session (e.g. with code.interact()), just do execfile('/path/to/thisfile') before the interactive session is set up.

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