Skip to content

Commit 0ec3c1d

Browse files
committed
Fixed slicing bug
1 parent 1ef418f commit 0ec3c1d

File tree

2 files changed

+32
-18
lines changed

2 files changed

+32
-18
lines changed

cmd2.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,16 @@ def flag_based_complete(text, line, begidx, endidx, flag_dict, default_completer
185185

186186
# Get all tokens prior to text being completed
187187
try:
188-
prev_space_index = line.rfind(' ', 0, begidx)
188+
prev_space_index = max(line.rfind(' ', 0, begidx), 0)
189189
tokens = shlex.split(line[:prev_space_index], posix=POSIX_SHLEX)
190190
except ValueError:
191191
# Invalid syntax for shlex (Probably due to missing closing quote)
192192
return []
193193

194+
# Nothing to do
195+
if len(tokens) == 0:
196+
return []
197+
194198
completions = []
195199
flag_processed = False
196200

@@ -247,7 +251,7 @@ def index_based_complete(text, line, begidx, endidx, index_dict, default_complet
247251

248252
# Get all tokens prior to text being completed
249253
try:
250-
prev_space_index = line.rfind(' ', 0, begidx)
254+
prev_space_index = max(line.rfind(' ', 0, begidx), 0)
251255
tokens = shlex.split(line[:prev_space_index], posix=POSIX_SHLEX)
252256
except ValueError:
253257
# Invalid syntax for shlex (Probably due to missing closing quote)
@@ -1348,7 +1352,7 @@ def complete_help(self, text, line, begidx, endidx):
13481352

13491353
# Get all tokens prior to text being completed
13501354
try:
1351-
prev_space_index = line.rfind(' ', 0, begidx)
1355+
prev_space_index = max(line.rfind(' ', 0, begidx), 0)
13521356
tokens = shlex.split(line[:prev_space_index], posix=POSIX_SHLEX)
13531357
except ValueError:
13541358
# Invalid syntax for shlex (Probably due to missing closing quote)
@@ -1464,7 +1468,7 @@ def parseline(self, line):
14641468
if line.startswith(shortcut):
14651469
# If the next character after the shortcut isn't a space, then insert one
14661470
shortcut_len = len(shortcut)
1467-
if len(line) > shortcut_len and line[shortcut_len] != ' ':
1471+
if len(line) == shortcut_len or line[shortcut_len] != ' ':
14681472
expansion += ' '
14691473

14701474
# Expand the shortcut
@@ -2066,21 +2070,26 @@ def _shell_command_complete(text, line, begidx, endidx):
20662070
# Get a list of every directory in the PATH environment variable and ignore symbolic links
20672071
paths = [p for p in os.getenv('PATH').split(os.path.pathsep) if not os.path.islink(p)]
20682072

2073+
# Use a set to store exe names since there can be duplicates
2074+
exes = set()
2075+
20692076
# Find every executable file in the PATH that matches the pattern
2070-
exes = []
20712077
for path in paths:
20722078
full_path = os.path.join(path, text)
20732079
matches = [f for f in glob.glob(full_path + '*') if os.path.isfile(f) and os.access(f, os.X_OK)]
20742080

20752081
for match in matches:
2076-
exes.append(os.path.basename(match))
2082+
exes.add(os.path.basename(match))
2083+
2084+
# Sort the exes alphabetically
2085+
results = list(exes)
2086+
results.sort()
20772087

20782088
# If there is a single completion and we are at end of the line, then add a space at the end for convenience
2079-
if len(exes) == 1 and endidx == len(line):
2080-
exes[0] += ' '
2089+
if len(results) == 1 and endidx == len(line):
2090+
results[0] += ' '
20812091

2082-
exes.sort()
2083-
return exes
2092+
return results
20842093

20852094
def complete_shell(self, text, line, begidx, endidx):
20862095
"""Handles tab completion of executable commands and local file system paths.
@@ -2092,26 +2101,31 @@ def complete_shell(self, text, line, begidx, endidx):
20922101
:return: List[str] - a list of possible tab completions
20932102
"""
20942103

2095-
# Get all tokens through the text being completed
2104+
# Get all tokens prior to text being completed
20962105
try:
2097-
tokens = shlex.split(line[:endidx], posix=POSIX_SHLEX)
2106+
prev_space_index = max(line.rfind(' ', 0, begidx), 0)
2107+
tokens = shlex.split(line[:prev_space_index], posix=POSIX_SHLEX)
20982108
except ValueError:
20992109
# Invalid syntax for shlex (Probably due to missing closing quote)
21002110
return []
21012111

2102-
if len(tokens) == 1:
2103-
# Don't tab complete anything if user only typed shell
2112+
# Nothing to do
2113+
if len(tokens) == 0:
21042114
return []
21052115

21062116
# Check if we are still completing the shell command
2107-
elif len(tokens) == 2 and not line.endswith(' '):
2117+
elif len(tokens) == 1:
21082118

21092119
# Readline places begidx after ~ and path separators (/) so we need to get the whole token
21102120
# and see if it begins with a possible path in case we need to do path completion
21112121
# to find the shell command executables
2112-
cur_token = tokens[-1]
2122+
cmd_token = line[prev_space_index + 1:begidx + 1]
2123+
2124+
# Don't tab complete anything if no shell command has been started
2125+
if len(cmd_token) == 0:
2126+
return []
21132127

2114-
if not (cur_token.startswith('~') or os.path.sep in cur_token):
2128+
if not (cmd_token.startswith('~') or os.path.sep in cmd_token):
21152129
# No path characters are in this token, it is OK to try shell command completion.
21162130
command_completions = self._shell_command_complete(text, line, begidx, endidx)
21172131

tests/test_completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ def get_endidx():
643643

644644
def test_cmd2_subcommand_completion_single_mid(sc_app):
645645
text = 'f'
646-
line = 'base f'
646+
line = 'base fo'
647647
endidx = len(line) - 1
648648
begidx = endidx - len(text)
649649
state = 0

0 commit comments

Comments
 (0)