Skip to content

Commit 0ef0e9b

Browse files
committed
Fixed a case where display_matches wasn't being set. Added unit tests.
1 parent ce76120 commit 0ef0e9b

2 files changed

Lines changed: 59 additions & 22 deletions

File tree

cmd2.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,8 +1330,8 @@ def tokens_for_completion(self, line, begidx, endidx):
13301330

13311331
for cur_initial_token in initial_tokens:
13321332

1333-
# Keep empty and quoted tokens
1334-
if len(cur_initial_token) == 0 or cur_initial_token[0] in QUOTES:
1333+
# Save tokens up to 1 character in length or quoted tokens. No need to parse these.
1334+
if len(cur_initial_token) <= 1 or cur_initial_token[0] in QUOTES:
13351335
raw_tokens.append(cur_initial_token)
13361336
continue
13371337

@@ -2068,20 +2068,23 @@ def complete(self, text, state):
20682068
self.completion_matches = []
20692069
return None
20702070

2071-
# Check if we need to remove text from the beginning of tab completions
2072-
if text_to_remove:
2073-
self.completion_matches = [m.replace(text_to_remove, '', 1) for m in self.completion_matches]
2074-
2075-
# Check if we need to restore a shortcut in the tab completions
2076-
if shortcut_to_restore:
2071+
if text_to_remove or shortcut_to_restore:
20772072
# If self.display_matches is empty, then set it to self.completion_matches
2078-
# before we restore the shortcut so the tab completion suggestions that display to
2079-
# the user don't have the shortcut character.
2073+
# before we alter them. That way the suggestions will reflect how we parsed
2074+
# the token being completed and not how readline did.
20802075
if len(self.display_matches) == 0:
20812076
self.display_matches = self.completion_matches
20822077

2083-
# Prepend all tab completions with the shortcut so it doesn't get erased from the command line
2084-
self.completion_matches = [shortcut_to_restore + match for match in self.completion_matches]
2078+
# Check if we need to remove text from the beginning of tab completions
2079+
if text_to_remove:
2080+
self.completion_matches = \
2081+
[m.replace(text_to_remove, '', 1) for m in self.completion_matches]
2082+
2083+
# Check if we need to restore a shortcut in the tab completions
2084+
# so it doesn't get erased from the command line
2085+
if shortcut_to_restore:
2086+
self.completion_matches = \
2087+
[shortcut_to_restore + match for match in self.completion_matches]
20852088

20862089
# Check if the token being completed has an unclosed quote
20872090
if len(raw_completion_token) == 1:

tests/test_completion.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,27 @@ def test_cmd2_help_completion_nomatch(cmd2_app):
177177
assert cmd2_app.complete_help(text, line, begidx, endidx) == []
178178

179179

180-
def test_shell_command_completion(cmd2_app):
180+
def test_shell_command_completion_shortcut(cmd2_app):
181+
# Made sure ! runs a shell command and all matches start with ! since there
182+
# isn't a space between ! and the shell command. Display matches won't
183+
# begin with the !.
181184
if sys.platform == "win32":
182-
text = 'calc'
183-
expected = ['calc.exe']
185+
text = '!calc'
186+
expected = ['!calc.exe ']
187+
expected_display = ['calc.exe']
184188
else:
185-
text = 'egr'
186-
expected = ['egrep']
189+
text = '!egr'
190+
expected = ['!egrep ']
191+
expected_display = ['egrep']
187192

188-
line = 'shell {}'.format(text)
193+
line = text
189194
endidx = len(line)
190-
begidx = endidx - len(text)
191-
assert cmd2_app.complete_shell(text, line, begidx, endidx) == expected
195+
begidx = 0
196+
197+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
198+
assert first_match is not None and \
199+
cmd2_app.completion_matches == expected and \
200+
cmd2_app.display_matches == expected_display
192201

193202
def test_shell_command_completion_doesnt_match_wildcards(cmd2_app):
194203
if sys.platform == "win32":
@@ -742,7 +751,7 @@ def test_cmd2_help_subcommand_completion_nomatch(sc_app):
742751
def test_subcommand_tab_completion(sc_app):
743752
# This makes sure the correct completer for the sport subcommand is called
744753
text = 'Foot'
745-
line = 'base sport Foot'
754+
line = 'base sport {}'.format(text)
746755
endidx = len(line)
747756
begidx = endidx - len(text)
748757

@@ -755,12 +764,37 @@ def test_subcommand_tab_completion_with_no_completer(sc_app):
755764
# This tests what happens when a subcommand has no completer
756765
# In this case, the foo subcommand has no completer defined
757766
text = 'Foot'
758-
line = 'base foo Foot'
767+
line = 'base foo {}'.format(text)
768+
endidx = len(line)
769+
begidx = endidx - len(text)
770+
771+
first_match = complete_tester(text, line, begidx, endidx, sc_app)
772+
assert first_match is None
773+
774+
def test_subcommand_tab_completion_add_quote(sc_app):
775+
# This makes sure an opening quote is added to the readline line buffer
776+
text = 'Space'
777+
line = 'base sport {}'.format(text)
759778
endidx = len(line)
760779
begidx = endidx - len(text)
761780

762781
first_match = complete_tester(text, line, begidx, endidx, sc_app)
782+
783+
# No matches are returned when an opening quote is added to the screen
763784
assert first_match is None
785+
assert readline.get_line_buffer() == 'base sport "Space Ball" '
786+
787+
def test_subcommand_tab_completion_space_in_text(sc_app):
788+
text = 'B'
789+
line = 'base sport "Space {}'.format(text)
790+
endidx = len(line)
791+
begidx = endidx - len(text)
792+
793+
first_match = complete_tester(text, line, begidx, endidx, sc_app)
794+
795+
assert first_match is not None and \
796+
sc_app.completion_matches == ['Ball" '] and \
797+
sc_app.display_matches == ['Space Ball']
764798

765799
class SecondLevel(cmd2.Cmd):
766800
"""To be used as a second level command class. """

0 commit comments

Comments
 (0)