diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-08-01 03:08:22 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-08-01 03:08:22 -0400 |
commit | 519283d460e4797ca1376348b63f737749ccc016 (patch) | |
tree | 519cb8890789f3f5cec96ed99c281ece1d4f5d90 | |
parent | bc559df2afcc51d1804e5d068d7e2c57bc4f72af (diff) | |
download | cmd2-git-519283d460e4797ca1376348b63f737749ccc016.tar.gz |
Added matches_sorted member to support custom sorting order of tab-completion matches
Made all sorting alphabetical
Fixed case where extra slash was printing when tab completing users on Windows
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | cmd2/cmd2.py | 44 | ||||
-rw-r--r-- | cmd2/utils.py | 13 | ||||
-rw-r--r-- | tests/test_completion.py | 6 |
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'] |