Steven D'Aprano added the comment:
On 31/07/13 17:14, Larry Hastings wrote:
> IMO the optimal solution is that tab preceded by only whitespace indents, and
> tab preceded by any non-whitespace character attempts to complete. Can we
> goad readline into behaving this way?
Yes we can. Attached are a pair of lightweight modules I've used for this for
the last 3-4 years, and it works fine in Python 2.4 through 3.3. I put
something like this in my startup.py:
import completer
import history
history = history.History()
completer = completer.Completer(
bindings=(r'"\C-xo": overwrite-mode',
r'"\C-xd": dump-functions',
))
Originally they were in a single class, but I was persuaded that it was cleaner
to separate them.
----------
Added file: http://bugs.python.org/file31095/completer.py
Added file: http://bugs.python.org/file31096/history.py
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue5845>
_______________________________________
"""Command line completion.
This module relies on both the ``readline`` and ``rlcompleter`` modules.
Under Windows, you may be able to use the third-party ``pyreadline`` module
(untested).
Creating a ``Completer`` instance enables readline completion:
>>> completer = Completer() #doctest: +SKIP
By default, the TAB key is used for both indenting and completion. See
the ``Completer`` class for further instructions, including how to change
this behaviour.
"""
# Keep this module compatible with Python 2.4 and better.
# TODO: add "operate and go next" functionality like in bash.
# TODO: add filename completion.
try:
# Everything depends on readline.
import readline
except ImportError:
# May be Windows, so try using a substitute.
import pyreadline as readline
import rlcompleter
def using_libedit():
"""Return True if the underlying readline library is libedit."""
# This tests for the libedit library instead of libreadline, which
# may indicate OS-X or *BSD - See http://bugs.python.org/issue10666
#
# FIXME This is the canonical test as suggested by the docs, but
# surely there is a better test than this? Perhaps something like
# sys.platform == DARWIN?
return 'libedit' in readline.__doc__
# Set up command line completion:
class Completer(rlcompleter.Completer):
"""Readline tab completer with optional support for indenting.
All arguments to the class constructor are optional.
namespace::
None, or a namespace dict, to use for completions. See the
``rlcompleter`` module for more details.
key::
Key to use for completion. ``key`` should be a key combination
written in the appropriate syntax for your readline library.
If ``key`` is not given or is None, the default TAB key will be
used:
* if you are using libreadline, 'tab' will be used;
* if you are using libedit, '^I' will be used.
Any other choice for ``key`` will be used exactly as given, and
it is your responsibility to ensure it is in the correct format
for the underlying readline library.
indent::
String to insert for indents when the completer key is pressed
at the start of the line. The default is to insert '\\t' (a
literal tab). Another common choice is ' ' (four spaces). If
you pass None or the empty string, pressing the completer key
will *not* indent.
query_items::
The maximum number of items that the completer will show without
asking first. The default is 30.
bindings::
A tuple of additional readline bindings to be parsed. As a
convenience, if you have only one binding to use, you can pass
it as a string rather than inside a tuple. See your operating
system's readline documentation for syntax.
"""
def __init__(self, namespace=None,
# Tab completion:
key=None, indent='\t', query_items=30,
# Extra bindings to be used:
bindings=(),
):
# This is a classic class in Python 2.x, so no super().
rlcompleter.Completer.__init__(self, namespace)
self.key = key
self.indent = indent
self.query_items = query_items
if isinstance(bindings, str):
bindings = (bindings,)
self.bindings = bindings
self._enable()
def completer(self, text, state):
"""Completer function with optional support for indenting.
If self.indent is not empty or None, it will be used to indent at the
start of lines.
"""
# At the start of a line, indent.
if self.indent and (text == '' or text.isspace()):
return [self.indent, None][state]
return rlcompleter.Completer.complete(self, text, state)
def set_completer(self):
"""Set the completer."""
# Remove the previous completer (possibly installed by rlcompleter).
readline.set_completer(None)
if using_libedit():
cmd = 'bind %s rl_complete' % (self.key or '^I')
else:
cmd = '%s: complete' % (self.key or 'tab')
readline.parse_and_bind(cmd)
readline.set_completer(self.completer)
def _enable(self):
"""Enable tab completion."""
self.set_completer()
readline.parse_and_bind(
"set completion-query-items %d" % self.query_items)
s = ('\x4e\x4f\x42\x4f\x44\x59\x20\x65\x78\x70\x65\x63\x74'
'\x73\x20\x74\x68\x65\x20\x53\x70\x61\x6e\x69\x73\x68'
'\x20\x49\x6e\x71\x75\x69\x73\x69\x74\x69\x6f\x6e\x21')
readline.parse_and_bind(r'"\C-xi": "%s"' % s)
for binding in self.bindings:
readline.parse_and_bind(binding)
"""Enable and control command-line history.
This module relies on the ``readline`` module. When ``readline`` is not
available, e.g. under Windows, it may work using the third-party
``pyreadline`` module (untested).
Any text file can be used as a history file, each line in the file is
considered one history command.
Creating a ``History`` instance enables command-line history, reads in any
contents of the history file, and prepares to write history back to that
file when Python exits. Calling ``History()`` with no arguments uses the
default history file:
>>> history = History() #doctest: +SKIP
See the ``History`` class for details on the arguments accepted.
You can display the last few commands by calling the instance:
>>> history(4) #doctest: +SKIP
119: x = spam(23) + eggs(42)
120: do_this()
121: do_that()
122: do_something_else()
You can read lines from a history file at any time. The ``read_history``
method keeps any existing lines in the current history buffer; the
``load_history`` method replaces the current buffer.
You can write the current history buffer to a file at any time by calling
the ``write_history`` method. The number of lines written is controlled
by the readline ``set_history_length`` function.
"""
# Keep this module compatible with Python 2.4 and better.
try:
# Everything depends on readline.
import readline
except ImportError:
# May be Windows, so try using a substitute.
import pyreadline as readline
import atexit
import os
class History(object):
"""Enable and control command-line history.
Arguments:
history_file::
Name of the history file to use. If not given, the attribute
DEFAULT_HISTORY_FILE (defaults to '.python_history' in the
user's home directory) is used.
history_length::
The maximum number of lines which will be saved to the history
file. If not given, the attribute DEFAULT_HISTORY_LENGTH
(defaults to 500) is used.
"""
DEFAULT_HISTORY_FILE = '~/.python_history'
DEFAULT_HISTORY_LENGTH = 500
MAX_LINES_TO_SHOW = 10 # Use < 0 for unlimited.
def __init__(self, history_file=None, history_length=None):
if history_file is None:
history_file = self.DEFAULT_HISTORY_FILE
history_file = os.path.expanduser(history_file)
self.history_file = history_file
if history_length is None:
history_length = self.DEFAULT_HISTORY_LENGTH
self.history_length = history_length
self._enable()
def _enable(self):
filename = self.history_file
self.read_history(filename)
readline.set_history_length(self.history_length)
# Save the history file when exiting.
atexit.register(readline.write_history_file, filename)
def read_history(self, filename=None):
"""Read history from the named file (if possible).
If filename is None or not given, use the ``history_file``
instance attribute.
History lines read are appended to the current history buffer.
To replace the current buffer, use the ``load_history`` method.
"""
if filename is None:
filename = self.history_file
try:
readline.read_history_file(os.path.expanduser(filename))
except (IOError, OSError):
pass
def load_history(self, filename=None):
"""Clear the current history buffer, then load history from
the named file (if possible).
If filename is None or not given, use the ``history_file``
instance attribute.
To read history lines without overwriting the current buffer,
use the ``read_history`` method.
"""
readline.clear_history()
self.read_history(filename)
def write_history(self, filename=None):
"""Write command line history to the named file without waiting
for program exit.
If ``filename`` is None or not given, use the ``history_file``
instance attribute.
"""
if filename is None:
filename = self.history_file
readline.write_history_file(os.path.expanduser(filename))
def get_history_lines(self, start=1, end=None):
"""Yield history lines between ``start`` and ``end`` inclusive.
Unlike Python indexes, the readline history lines are numbered
from 1. If not given, ``start`` defaults to 1, and ``end``
defaults to the current history length.
"""
get = readline.get_history_item
if end is None:
end = readline.get_current_history_length()
return (get(i) for i in range(start, end+1))
def __call__(self, count=None, show_line_numbers=True):
"""Print the latest ``count`` lines from the history.
If ``count`` is None or not given, a default number of lines
is shown, given by the attribute MAX_LINES_TO_SHOW. Use a
negative count to show unlimited lines.
If ``show_line_numbers`` is true (the default), each history
line is preceeded by the line number.
"""
if count is None:
count = self.MAX_LINES_TO_SHOW
end = readline.get_current_history_length()
if count < 0:
start = 1
else:
start = max(end - count + 1, 1)
nums = range(start, end+1)
lines = self.get_history_lines(start, end)
if show_line_numbers:
# Can't use {} formatting as we have to support 2.4 and 2.5.
template = "%(lineno)3d: %(line)s"
else:
template = "%(line)s"
for i, line in zip(nums, lines):
print(template % {'lineno':i, 'line':line})
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com