summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md7
-rw-r--r--cmd2/cmd2.py44
-rw-r--r--cmd2/utils.py13
-rw-r--r--tests/test_completion.py6
4 files changed, 49 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bda144e..33edad74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,10 @@
* Bug Fixes
* Fixed bug where ``preparse`` wasn't getting called
* Enhancements
- * Improved implementation of lifecycle hooks to to support a plugin
+ * Improved implementation of lifecycle hooks to support a plugin
framework, see ``docs/hooks.rst`` for details.
* New dependency on ``attrs`` third party module
+ * Added ``matches_sorted`` member to support custom sorting of tab-completion matches
* Deprecations
* Deprecated the following hook methods, see ``hooks.rst`` for full details:
* ``cmd2.Cmd.preparse()`` - equivilent functionality available
@@ -14,6 +15,10 @@
* ``cmd2.Cmd.postparsing_postcmd()`` - equivilent functionality available
via ``cmd2.Cmd.register_postcmd_hook()``
+## 0.8.9 (TBD 2018)
+* Bug Fixes
+ * Fixed extra slash that could print when tab completing users on Windows
+
## 0.9.3 (July 12, 2018)
* Bug Fixes
* Fixed bug when StatementParser ``__init__()`` was called with ``terminators`` equal to ``None``
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index d34e7161..e485973b 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -369,7 +369,7 @@ class Cmd(cmd.Cmd):
except AttributeError:
pass
- # initialize plugin system
+ # initialize plugin system
# needs to be done before we call __init__(0)
self._initialize_plugin_system()
@@ -482,11 +482,11 @@ class Cmd(cmd.Cmd):
# in reset_completion_defaults() and it is up to completer functions to set them before returning results.
############################################################################################################
- # If true and a single match is returned to complete(), then a space will be appended
+ # If True and a single match is returned to complete(), then a space will be appended
# if the match appears at the end of the line
self.allow_appended_space = True
- # If true and a single match is returned to complete(), then a closing quote
+ # If True and a single match is returned to complete(), then a closing quote
# will be added if there is an unmatched opening quote
self.allow_closing_quote = True
@@ -504,6 +504,10 @@ class Cmd(cmd.Cmd):
# quote matches that are completed in a delimited fashion
self.matches_delimited = False
+ # Set to True before returning matches to complete() in cases where matches are sorted with custom ordering.
+ # If False, then complete() will sort the matches alphabetically before they are displayed.
+ self.matches_sorted = False
+
# Set the pager(s) for use with the ppaged() method for displaying output using a pager
if sys.platform.startswith('win'):
self.pager = self.pager_chop = 'more'
@@ -678,6 +682,7 @@ class Cmd(cmd.Cmd):
self.completion_header = ''
self.display_matches = []
self.matches_delimited = False
+ self.matches_sorted = False
if rl_type == RlType.GNU:
readline.set_completion_display_matches_hook(self._display_matches_gnu_readline)
@@ -994,12 +999,15 @@ class Cmd(cmd.Cmd):
users = []
# Windows lacks the pwd module so we can't get a list of users.
- # Instead we will add a slash once the user enters text that
+ # Instead we will return a result once the user enters text that
# resolves to an existing home directory.
if sys.platform.startswith('win'):
expanded_path = os.path.expanduser(text)
if os.path.isdir(expanded_path):
- users.append(text + os.path.sep)
+ user = text
+ if add_trailing_sep_if_dir:
+ user += os.path.sep
+ users.append(user)
else:
import pwd
@@ -1083,6 +1091,10 @@ class Cmd(cmd.Cmd):
self.allow_appended_space = False
self.allow_closing_quote = False
+ # Sort the matches before any trailing slashes are added
+ matches.sort(key=str.lower)
+ self.matches_sorted = True
+
# Build display_matches and add a slash to directories
for index, cur_match in enumerate(matches):
@@ -1446,11 +1458,8 @@ class Cmd(cmd.Cmd):
if self.completion_matches:
# Eliminate duplicates
- matches_set = set(self.completion_matches)
- self.completion_matches = list(matches_set)
-
- display_matches_set = set(self.display_matches)
- self.display_matches = list(display_matches_set)
+ self.completion_matches = utils.remove_duplicates(self.completion_matches)
+ self.display_matches = utils.remove_duplicates(self.display_matches)
if not self.display_matches:
# Since self.display_matches is empty, set it to self.completion_matches
@@ -1521,10 +1530,11 @@ class Cmd(cmd.Cmd):
self.completion_matches[0] += str_to_append
- # Otherwise sort matches
- elif self.completion_matches:
- self.completion_matches.sort()
- self.display_matches.sort()
+ # Sort matches alphabetically if they haven't already been sorted
+ if not self.matches_sorted:
+ self.completion_matches.sort(key=str.lower)
+ self.display_matches.sort(key=str.lower)
+ self.matches_sorted = True
try:
return self.completion_matches[state]
@@ -2270,7 +2280,7 @@ Usage: Usage: unalias [-a] name [name ...]
else:
# Get rid of duplicates
- arglist = list(set(arglist))
+ arglist = utils.remove_duplicates(arglist)
for cur_arg in arglist:
if cur_arg in self.aliases:
@@ -2316,11 +2326,11 @@ Usage: Usage: unalias [-a] name [name ...]
"""
# Get a sorted list of help topics
help_topics = self.get_help_topics()
- help_topics.sort()
+ help_topics.sort(key=str.lower)
# Get a sorted list of visible command names
visible_commands = self.get_visible_commands()
- visible_commands.sort()
+ visible_commands.sort(key=str.lower)
cmds_doc = []
cmds_undoc = []
diff --git a/cmd2/utils.py b/cmd2/utils.py
index d03e7f6f..d8afd922 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -144,3 +144,16 @@ def is_text_file(file_path: str) -> bool:
pass
return valid_text_file
+
+
+def remove_duplicates(list_to_prune: List) -> List:
+ """
+ Removes duplicates from a list while preserving order of the items
+ :param list_to_prune: the list being pruned of duplicates
+ :return: The pruned list
+ """
+ temp_dict = collections.OrderedDict()
+ for item in list_to_prune:
+ temp_dict[item] = None
+
+ return list(temp_dict.keys())
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 2faa4a08..138ab413 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -14,6 +14,7 @@ import sys
import pytest
import cmd2
+from cmd2 import utils
from .conftest import complete_tester, StdOut
from examples.subcommands import SubcommandsExample
@@ -408,9 +409,8 @@ def test_delimiter_completion(cmd2_app):
cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, '/')
# Remove duplicates from display_matches and sort it. This is typically done in complete().
- display_set = set(cmd2_app.display_matches)
- display_list = list(display_set)
- display_list.sort()
+ display_list = utils.remove_duplicates(cmd2_app.display_matches)
+ display_list.sort(key=str.lower)
assert display_list == ['other user', 'user']