Patch #416224: add readline completion to cmd.Cmd.

This commit is contained in:
Martin v. Löwis 2001-07-28 14:44:03 +00:00
parent 9544fc5027
commit 66b6e192b9
4 changed files with 110 additions and 16 deletions

View File

@ -11,12 +11,18 @@ line-oriented command interpreters. These are often useful for
test harnesses, administrative tools, and prototypes that will
later be wrapped in a more sophisticated interface.
\begin{classdesc}{Cmd}{}
\begin{classdesc}{Cmd}{\optional{completekey}}
A \class{Cmd} instance or subclass instance is a line-oriented
interpreter framework. There is no good reason to instantiate
\class{Cmd} itself; rather, it's useful as a superclass of an
interpreter class you define yourself in order to inherit
\class{Cmd}'s methods and encapsulate action methods.
The optional argument is the \refmodule{readline} name of a completion
key; it defaults to \code{``tab''}. If \var{completekey} is not
\code{None} and \module{readline} is available, command completion is
done automatically.
\end{classdesc}
\subsection{Cmd Objects}
@ -47,6 +53,16 @@ the method \method{do_help()}. As another special case, a line
beginning with the character \character{!} is dispatched to the
method \method{do_shell} (if such a method is defined).
If completion is enabled, completing commands will be done
automatically, and completing of commands args is done by calling
\method{complete_foo()} with arguments \samp{text}, \samp{line},
\samp{begidx}, \samp{endidx}. \samp{text} is string we are matching
against, all returned matches must begin with it. \samp{line} is the
current input line (lstripped), \samp{begidx} and \samp{endidx} are
the beginning and end indexes of the text being matched, which could
be used to provide different completion depending upon which position
the argument is in.
All subclasses of \class{Cmd} inherit a predefined \method{do_help}.
This method, called with an argument \code{bar}, invokes the
corresponding method \method{help_bar()}. With no argument,
@ -72,6 +88,12 @@ recognized. If this method is not overridden, it prints an
error message and returns.
\end{methoddesc}
\begin{methoddesc}{completedefault}{text, line, begidx, endidx}
Method called to complete an input line when no command-specific
\code{complete_} method is available. By default, it returns an
empty list.
\end{methoddesc}
\begin{methoddesc}{precmd}{}
Hook method executed just before the command line is interpreted, but
after the input prompt is generated and issued. This

View File

@ -15,10 +15,20 @@ Interpreters constructed with this class obey the following conventions:
commands, miscellaneous help topics, and undocumented commands.
6. The command '?' is a synonym for `help'. The command '!' is a synonym
for `shell', if a do_shell method exists.
7. If completion is enabled, completing commands will be done automatically,
and completing of commands args is done by calling complete_foo() with
arguments text, line, begidx, endidx. text is string we are matching
against, all returned matches must begin with it. line is the current
input line (lstripped), begidx and endidx are the beginning and end
indexes of the text being matched, which could be used to provide
different completion depending upon which position the argument is in.
The `default' method may be overridden to intercept commands for which there
is no do_ method.
The `completedefault' method may be overridden to intercept completions for
commands that have no complete_ method.
The data member `self.ruler' sets the character used to draw separator lines
in the help messages. If empty, no ruler line is drawn. It defaults to "=".
@ -56,7 +66,14 @@ class Cmd:
nohelp = "*** No help on %s"
use_rawinput = 1
def __init__(self): pass
def __init__(self, completekey='tab'):
if completekey:
try:
import readline
readline.set_completer(self.complete)
readline.parse_and_bind(completekey+": complete")
except ImportError:
pass
def cmdloop(self, intro=None):
self.preloop()
@ -99,21 +116,29 @@ class Cmd:
def postloop(self):
pass
def onecmd(self, line):
def parseline(self, line):
line = line.strip()
if not line:
return self.emptyline()
return None, None, line
elif line[0] == '?':
line = 'help ' + line[1:]
elif line[0] == '!':
if hasattr(self, 'do_shell'):
line = 'shell ' + line[1:]
else:
return self.default(line)
self.lastcmd = line
return None, None, line
i, n = 0, len(line)
while i < n and line[i] in self.identchars: i = i+1
cmd, arg = line[:i], line[i:].strip()
return cmd, arg, line
def onecmd(self, line):
cmd, arg, line = self.parseline(line)
if not line:
return self.emptyline()
if cmd is None:
return self.default(line)
self.lastcmd = line
if cmd == '':
return self.default(line)
else:
@ -130,6 +155,59 @@ class Cmd:
def default(self, line):
print '*** Unknown syntax:', line
def completedefault(self, *ignored):
return []
def completenames(self, text, *ignored):
dotext = 'do_'+text
return [a[3:] for a in self.get_names() if a.startswith(dotext)]
def complete(self, text, state):
"""Return the next possible completion for 'text'.
If a command has not been entered, then complete against command list.
Otherwise try to call complete_<command> to get list of completions.
"""
if state == 0:
import readline
origline = readline.get_line_buffer()
line = origline.lstrip()
stripped = len(origline) - len(line)
begidx = readline.get_begidx() - stripped
endidx = readline.get_endidx() - stripped
if begidx>0:
cmd, args, foo = self.parseline(line)
if cmd == '':
compfunc = self.completedefault
else:
try:
compfunc = getattr(self, 'complete_' + cmd)
except AttributeError:
compfunc = self.completedefault
else:
compfunc = self.completenames
self.completion_matches = compfunc(text, line, begidx, endidx)
try:
return self.completion_matches[state]
except IndexError:
return None
def get_names(self):
# Inheritance says we have to look in class and
# base classes; order is not important.
names = []
classes = [self.__class__]
while classes:
aclass = classes[0]
if aclass.__bases__:
classes = classes + list(aclass.__bases__)
names = names + dir(aclass)
del classes[0]
return names
def complete_help(self, *args):
return self.completenames(*args)
def do_help(self, arg):
if arg:
# XXX check arg syntax
@ -147,16 +225,7 @@ class Cmd:
return
func()
else:
# Inheritance says we have to look in class and
# base classes; order is not important.
names = []
classes = [self.__class__]
while classes:
aclass = classes[0]
if aclass.__bases__:
classes = classes + list(aclass.__bases__)
names = names + dir(aclass)
del classes[0]
names = self.get_names()
cmds_doc = []
cmds_undoc = []
help = {}

View File

@ -538,6 +538,7 @@ if __name__ == '__main__':
class ProfileBrowser(cmd.Cmd):
def __init__(self, profile=None):
cmd.Cmd.__init__(self)
self.prompt = "% "
if profile:
self.stats = Stats(profile)

View File

@ -14,6 +14,8 @@ Library
value using the minimal quoting required for the value; more
reliable than using xml.sax.saxutils.escape() for attribute values.
- Readline completion support for cmd.Cmd was added.
New platforms
C API