mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
Patch #416224: add readline completion to cmd.Cmd.
This commit is contained in:
parent
9544fc5027
commit
66b6e192b9
4 changed files with 110 additions and 16 deletions
|
@ -11,12 +11,18 @@ line-oriented command interpreters. These are often useful for
|
||||||
test harnesses, administrative tools, and prototypes that will
|
test harnesses, administrative tools, and prototypes that will
|
||||||
later be wrapped in a more sophisticated interface.
|
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
|
A \class{Cmd} instance or subclass instance is a line-oriented
|
||||||
interpreter framework. There is no good reason to instantiate
|
interpreter framework. There is no good reason to instantiate
|
||||||
\class{Cmd} itself; rather, it's useful as a superclass of an
|
\class{Cmd} itself; rather, it's useful as a superclass of an
|
||||||
interpreter class you define yourself in order to inherit
|
interpreter class you define yourself in order to inherit
|
||||||
\class{Cmd}'s methods and encapsulate action methods.
|
\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}
|
\end{classdesc}
|
||||||
|
|
||||||
\subsection{Cmd Objects}
|
\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
|
beginning with the character \character{!} is dispatched to the
|
||||||
method \method{do_shell} (if such a method is defined).
|
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}.
|
All subclasses of \class{Cmd} inherit a predefined \method{do_help}.
|
||||||
This method, called with an argument \code{bar}, invokes the
|
This method, called with an argument \code{bar}, invokes the
|
||||||
corresponding method \method{help_bar()}. With no argument,
|
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.
|
error message and returns.
|
||||||
\end{methoddesc}
|
\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}{}
|
\begin{methoddesc}{precmd}{}
|
||||||
Hook method executed just before the command line is interpreted, but
|
Hook method executed just before the command line is interpreted, but
|
||||||
after the input prompt is generated and issued. This
|
after the input prompt is generated and issued. This
|
||||||
|
|
99
Lib/cmd.py
99
Lib/cmd.py
|
@ -15,10 +15,20 @@ Interpreters constructed with this class obey the following conventions:
|
||||||
commands, miscellaneous help topics, and undocumented commands.
|
commands, miscellaneous help topics, and undocumented commands.
|
||||||
6. The command '?' is a synonym for `help'. The command '!' is a synonym
|
6. The command '?' is a synonym for `help'. The command '!' is a synonym
|
||||||
for `shell', if a do_shell method exists.
|
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
|
The `default' method may be overridden to intercept commands for which there
|
||||||
is no do_ method.
|
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
|
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 "=".
|
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"
|
nohelp = "*** No help on %s"
|
||||||
use_rawinput = 1
|
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):
|
def cmdloop(self, intro=None):
|
||||||
self.preloop()
|
self.preloop()
|
||||||
|
@ -99,21 +116,29 @@ class Cmd:
|
||||||
def postloop(self):
|
def postloop(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def onecmd(self, line):
|
def parseline(self, line):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
return self.emptyline()
|
return None, None, line
|
||||||
elif line[0] == '?':
|
elif line[0] == '?':
|
||||||
line = 'help ' + line[1:]
|
line = 'help ' + line[1:]
|
||||||
elif line[0] == '!':
|
elif line[0] == '!':
|
||||||
if hasattr(self, 'do_shell'):
|
if hasattr(self, 'do_shell'):
|
||||||
line = 'shell ' + line[1:]
|
line = 'shell ' + line[1:]
|
||||||
else:
|
else:
|
||||||
return self.default(line)
|
return None, None, line
|
||||||
self.lastcmd = line
|
|
||||||
i, n = 0, len(line)
|
i, n = 0, len(line)
|
||||||
while i < n and line[i] in self.identchars: i = i+1
|
while i < n and line[i] in self.identchars: i = i+1
|
||||||
cmd, arg = line[:i], line[i:].strip()
|
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 == '':
|
if cmd == '':
|
||||||
return self.default(line)
|
return self.default(line)
|
||||||
else:
|
else:
|
||||||
|
@ -130,6 +155,59 @@ class Cmd:
|
||||||
def default(self, line):
|
def default(self, line):
|
||||||
print '*** Unknown syntax:', 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):
|
def do_help(self, arg):
|
||||||
if arg:
|
if arg:
|
||||||
# XXX check arg syntax
|
# XXX check arg syntax
|
||||||
|
@ -147,16 +225,7 @@ class Cmd:
|
||||||
return
|
return
|
||||||
func()
|
func()
|
||||||
else:
|
else:
|
||||||
# Inheritance says we have to look in class and
|
names = self.get_names()
|
||||||
# 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]
|
|
||||||
cmds_doc = []
|
cmds_doc = []
|
||||||
cmds_undoc = []
|
cmds_undoc = []
|
||||||
help = {}
|
help = {}
|
||||||
|
|
|
@ -538,6 +538,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
class ProfileBrowser(cmd.Cmd):
|
class ProfileBrowser(cmd.Cmd):
|
||||||
def __init__(self, profile=None):
|
def __init__(self, profile=None):
|
||||||
|
cmd.Cmd.__init__(self)
|
||||||
self.prompt = "% "
|
self.prompt = "% "
|
||||||
if profile:
|
if profile:
|
||||||
self.stats = Stats(profile)
|
self.stats = Stats(profile)
|
||||||
|
|
|
@ -14,6 +14,8 @@ Library
|
||||||
value using the minimal quoting required for the value; more
|
value using the minimal quoting required for the value; more
|
||||||
reliable than using xml.sax.saxutils.escape() for attribute values.
|
reliable than using xml.sax.saxutils.escape() for attribute values.
|
||||||
|
|
||||||
|
- Readline completion support for cmd.Cmd was added.
|
||||||
|
|
||||||
New platforms
|
New platforms
|
||||||
|
|
||||||
C API
|
C API
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue