Skip to content

Commit 66b6e19

Browse files
committed
Patch #416224: add readline completion to cmd.Cmd.
1 parent 9544fc5 commit 66b6e19

4 files changed

Lines changed: 110 additions & 16 deletions

File tree

Doc/lib/libcmd.tex

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ \section{\module{cmd} ---
1111
test harnesses, administrative tools, and prototypes that will
1212
later be wrapped in a more sophisticated interface.
1313

14-
\begin{classdesc}{Cmd}{}
14+
\begin{classdesc}{Cmd}{\optional{completekey}}
1515
A \class{Cmd} instance or subclass instance is a line-oriented
1616
interpreter framework. There is no good reason to instantiate
1717
\class{Cmd} itself; rather, it's useful as a superclass of an
1818
interpreter class you define yourself in order to inherit
1919
\class{Cmd}'s methods and encapsulate action methods.
20+
21+
The optional argument is the \refmodule{readline} name of a completion
22+
key; it defaults to \code{``tab''}. If \var{completekey} is not
23+
\code{None} and \module{readline} is available, command completion is
24+
done automatically.
25+
2026
\end{classdesc}
2127

2228
\subsection{Cmd Objects}
@@ -47,6 +53,16 @@ \subsection{Cmd Objects}
4753
beginning with the character \character{!} is dispatched to the
4854
method \method{do_shell} (if such a method is defined).
4955

56+
If completion is enabled, completing commands will be done
57+
automatically, and completing of commands args is done by calling
58+
\method{complete_foo()} with arguments \samp{text}, \samp{line},
59+
\samp{begidx}, \samp{endidx}. \samp{text} is string we are matching
60+
against, all returned matches must begin with it. \samp{line} is the
61+
current input line (lstripped), \samp{begidx} and \samp{endidx} are
62+
the beginning and end indexes of the text being matched, which could
63+
be used to provide different completion depending upon which position
64+
the argument is in.
65+
5066
All subclasses of \class{Cmd} inherit a predefined \method{do_help}.
5167
This method, called with an argument \code{bar}, invokes the
5268
corresponding method \method{help_bar()}. With no argument,
@@ -72,6 +88,12 @@ \subsection{Cmd Objects}
7288
error message and returns.
7389
\end{methoddesc}
7490

91+
\begin{methoddesc}{completedefault}{text, line, begidx, endidx}
92+
Method called to complete an input line when no command-specific
93+
\code{complete_} method is available. By default, it returns an
94+
empty list.
95+
\end{methoddesc}
96+
7597
\begin{methoddesc}{precmd}{}
7698
Hook method executed just before the command line is interpreted, but
7799
after the input prompt is generated and issued. This

Lib/cmd.py

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,20 @@
1515
commands, miscellaneous help topics, and undocumented commands.
1616
6. The command '?' is a synonym for `help'. The command '!' is a synonym
1717
for `shell', if a do_shell method exists.
18+
7. If completion is enabled, completing commands will be done automatically,
19+
and completing of commands args is done by calling complete_foo() with
20+
arguments text, line, begidx, endidx. text is string we are matching
21+
against, all returned matches must begin with it. line is the current
22+
input line (lstripped), begidx and endidx are the beginning and end
23+
indexes of the text being matched, which could be used to provide
24+
different completion depending upon which position the argument is in.
1825
1926
The `default' method may be overridden to intercept commands for which there
2027
is no do_ method.
2128
29+
The `completedefault' method may be overridden to intercept completions for
30+
commands that have no complete_ method.
31+
2232
The data member `self.ruler' sets the character used to draw separator lines
2333
in the help messages. If empty, no ruler line is drawn. It defaults to "=".
2434
@@ -56,7 +66,14 @@ class Cmd:
5666
nohelp = "*** No help on %s"
5767
use_rawinput = 1
5868

59-
def __init__(self): pass
69+
def __init__(self, completekey='tab'):
70+
if completekey:
71+
try:
72+
import readline
73+
readline.set_completer(self.complete)
74+
readline.parse_and_bind(completekey+": complete")
75+
except ImportError:
76+
pass
6077

6178
def cmdloop(self, intro=None):
6279
self.preloop()
@@ -99,21 +116,29 @@ def preloop(self):
99116
def postloop(self):
100117
pass
101118

102-
def onecmd(self, line):
119+
def parseline(self, line):
103120
line = line.strip()
104121
if not line:
105-
return self.emptyline()
122+
return None, None, line
106123
elif line[0] == '?':
107124
line = 'help ' + line[1:]
108125
elif line[0] == '!':
109126
if hasattr(self, 'do_shell'):
110127
line = 'shell ' + line[1:]
111128
else:
112-
return self.default(line)
113-
self.lastcmd = line
129+
return None, None, line
114130
i, n = 0, len(line)
115131
while i < n and line[i] in self.identchars: i = i+1
116132
cmd, arg = line[:i], line[i:].strip()
133+
return cmd, arg, line
134+
135+
def onecmd(self, line):
136+
cmd, arg, line = self.parseline(line)
137+
if not line:
138+
return self.emptyline()
139+
if cmd is None:
140+
return self.default(line)
141+
self.lastcmd = line
117142
if cmd == '':
118143
return self.default(line)
119144
else:
@@ -130,6 +155,59 @@ def emptyline(self):
130155
def default(self, line):
131156
print '*** Unknown syntax:', line
132157

158+
def completedefault(self, *ignored):
159+
return []
160+
161+
def completenames(self, text, *ignored):
162+
dotext = 'do_'+text
163+
return [a[3:] for a in self.get_names() if a.startswith(dotext)]
164+
165+
def complete(self, text, state):
166+
"""Return the next possible completion for 'text'.
167+
168+
If a command has not been entered, then complete against command list.
169+
Otherwise try to call complete_<command> to get list of completions.
170+
"""
171+
if state == 0:
172+
import readline
173+
origline = readline.get_line_buffer()
174+
line = origline.lstrip()
175+
stripped = len(origline) - len(line)
176+
begidx = readline.get_begidx() - stripped
177+
endidx = readline.get_endidx() - stripped
178+
if begidx>0:
179+
cmd, args, foo = self.parseline(line)
180+
if cmd == '':
181+
compfunc = self.completedefault
182+
else:
183+
try:
184+
compfunc = getattr(self, 'complete_' + cmd)
185+
except AttributeError:
186+
compfunc = self.completedefault
187+
else:
188+
compfunc = self.completenames
189+
self.completion_matches = compfunc(text, line, begidx, endidx)
190+
try:
191+
return self.completion_matches[state]
192+
except IndexError:
193+
return None
194+
195+
def get_names(self):
196+
# Inheritance says we have to look in class and
197+
# base classes; order is not important.
198+
names = []
199+
classes = [self.__class__]
200+
while classes:
201+
aclass = classes[0]
202+
if aclass.__bases__:
203+
classes = classes + list(aclass.__bases__)
204+
names = names + dir(aclass)
205+
del classes[0]
206+
return names
207+
208+
def complete_help(self, *args):
209+
return self.completenames(*args)
210+
133211
def do_help(self, arg):
134212
if arg:
135213
# XXX check arg syntax
@@ -147,16 +225,7 @@ def do_help(self, arg):
147225
return
148226
func()
149227
else:
150-
# Inheritance says we have to look in class and
151-
# base classes; order is not important.
152-
names = []
153-
classes = [self.__class__]
154-
while classes:
155-
aclass = classes[0]
156-
if aclass.__bases__:
157-
classes = classes + list(aclass.__bases__)
158-
names = names + dir(aclass)
159-
del classes[0]
228+
names = self.get_names()
160229
cmds_doc = []
161230
cmds_undoc = []
162231
help = {}

Lib/pstats.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ def f8(x):
538538

539539
class ProfileBrowser(cmd.Cmd):
540540
def __init__(self, profile=None):
541+
cmd.Cmd.__init__(self)
541542
self.prompt = "% "
542543
if profile:
543544
self.stats = Stats(profile)

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Library
1414
value using the minimal quoting required for the value; more
1515
reliable than using xml.sax.saxutils.escape() for attribute values.
1616

17+
- Readline completion support for cmd.Cmd was added.
18+
1719
New platforms
1820

1921
C API

0 commit comments

Comments
 (0)