Skip to content

Commit e906a05

Browse files
committed
Fixed issue where completion display function was overwritten when a submenu quits.
Fixed issue where submenus did not pass up all completion parameters to complete().
1 parent 09e0fc0 commit e906a05

File tree

1 file changed

+35
-32
lines changed

1 file changed

+35
-32
lines changed

cmd2.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,6 @@ class RlType(Enum):
164164
import ctypes
165165
readline_lib = ctypes.CDLL(readline.__file__)
166166

167-
# Save address that rl_basic_quote_characters is pointing to since we need to override and restore it
168-
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
169-
orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
170-
171167

172168
# BrokenPipeError and FileNotFoundError exist only in Python 3. Use IOError for Python 2.
173169
if six.PY3:
@@ -889,11 +885,22 @@ def complete_submenu(_self, text, line, begidx, endidx):
889885
try:
890886
# copy over any shared attributes
891887
self._copy_in_shared_attrs(_self)
888+
889+
# Reset the submenu's tab completion parameters
890+
submenu.allow_appended_space = True
891+
submenu.allow_closing_quote = True
892+
submenu.display_matches = []
893+
892894
return _complete_from_cmd(submenu, text, line, begidx, endidx)
893895
finally:
894896
# copy back original attributes
895897
self._copy_out_shared_attrs(_self, original_attributes)
896898

899+
# Pass the submenu's tab completion parameters back up to the menu that called complete()
900+
_self.allow_appended_space = submenu.allow_appended_space
901+
_self.allow_closing_quote = submenu.allow_closing_quote
902+
_self.display_matches = copy.copy(submenu.display_matches)
903+
897904
original_do_help = cmd_obj.do_help
898905
original_complete_help = cmd_obj.complete_help
899906

@@ -1324,6 +1331,11 @@ def set_completion_defaults(self):
13241331
self.allow_closing_quote = True
13251332
self.display_matches = []
13261333

1334+
if rl_type == RlType.GNU:
1335+
readline.set_completion_display_matches_hook(self._display_matches_gnu_readline)
1336+
elif rl_type == RlType.PYREADLINE:
1337+
readline.rl.mode._display_completions = self._display_matches_pyreadline
1338+
13271339
def tokens_for_completion(self, line, begidx, endidx):
13281340
"""
13291341
Used by tab completion functions to get all tokens through the one being completed
@@ -2699,38 +2711,33 @@ def _cmdloop(self):
26992711
"""
27002712
# An almost perfect copy from Cmd; however, the pseudo_raw_input portion
27012713
# has been split out so that it can be called separately
2702-
if self.use_rawinput and self.completekey:
2714+
if self.use_rawinput and self.completekey and rl_type != RlType.NONE:
27032715

27042716
# Set up readline for our tab completion needs
27052717
if rl_type == RlType.GNU:
2706-
readline.set_completion_display_matches_hook(self._display_matches_gnu_readline)
2707-
27082718
# Set GNU readline's rl_basic_quote_characters to NULL so it won't automatically add a closing quote
27092719
# We don't need to worry about setting rl_completion_suppress_quote since we never declared
27102720
# rl_completer_quote_characters.
2711-
rl_basic_quote_characters.value = None
2721+
basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
2722+
old_basic_quote_characters = ctypes.cast(basic_quote_characters, ctypes.c_void_p).value
2723+
basic_quote_characters.value = None
27122724

2713-
elif rl_type == RlType.PYREADLINE:
2714-
readline.rl.mode._display_completions = self._display_matches_pyreadline
2725+
old_completer = readline.get_completer()
2726+
old_delims = readline.get_completer_delims()
2727+
readline.set_completer(self.complete)
27152728

2716-
try:
2717-
self.old_completer = readline.get_completer()
2718-
self.old_delims = readline.get_completer_delims()
2719-
readline.set_completer(self.complete)
2729+
# Break words on whitespace and quotes when tab completing
2730+
completer_delims = " \t\n" + ''.join(QUOTES)
27202731

2721-
# Break words on whitespace and quotes when tab completing
2722-
completer_delims = " \t\n" + ''.join(QUOTES)
2732+
if self.allow_redirection:
2733+
# If redirection is allowed, then break words on those characters too
2734+
completer_delims += ''.join(REDIRECTION_CHARS)
27232735

2724-
if self.allow_redirection:
2725-
# If redirection is allowed, then break words on those characters too
2726-
completer_delims += ''.join(REDIRECTION_CHARS)
2736+
readline.set_completer_delims(completer_delims)
27272737

2728-
readline.set_completer_delims(completer_delims)
2738+
# Enable tab completion
2739+
readline.parse_and_bind(self.completekey + ": complete")
27292740

2730-
# Enable tab completion
2731-
readline.parse_and_bind(self.completekey + ": complete")
2732-
except NameError:
2733-
pass
27342741
stop = None
27352742
try:
27362743
while not stop:
@@ -2754,19 +2761,15 @@ def _cmdloop(self):
27542761
# Run the command along with all associated pre and post hooks
27552762
stop = self.onecmd_plus_hooks(line)
27562763
finally:
2757-
if self.use_rawinput and self.completekey:
2764+
if self.use_rawinput and self.completekey and rl_type != RlType.NONE:
27582765

27592766
# Restore what we changed in readline
2760-
try:
2761-
readline.set_completer(self.old_completer)
2762-
readline.set_completer_delims(self.old_delims)
2763-
except NameError:
2764-
pass
2767+
readline.set_completer(old_completer)
2768+
readline.set_completer_delims(old_delims)
27652769

27662770
if rl_type == RlType.GNU:
27672771
readline.set_completion_display_matches_hook(None)
2768-
rl_basic_quote_characters.value = orig_rl_basic_quote_characters_addr
2769-
2772+
basic_quote_characters.value = old_basic_quote_characters
27702773
elif rl_type == RlType.PYREADLINE:
27712774
readline.rl.mode._display_completions = orig_pyreadline_display
27722775

0 commit comments

Comments
 (0)