diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-03-31 00:49:42 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-03-31 00:49:42 -0400 |
commit | cba3dd190b7d8cdcc5a2a0dbf25a0aa2d754df90 (patch) | |
tree | 9522af005fe0f73b7b6881e9a2103c61ab020044 | |
parent | ad463db10b27c62bf86d2cfc9bb6327cc5950eec (diff) | |
download | cmd2-git-cba3dd190b7d8cdcc5a2a0dbf25a0aa2d754df90.tar.gz |
Removed unnecessary sorting and duplicate removal from the completers since all
results from these functions are returned to complete() which already does these things.
These changes also provide better examples of what is required to write a completer and what isn't.
-rwxr-xr-x | cmd2.py | 92 | ||||
-rw-r--r-- | tests/test_completion.py | 28 |
2 files changed, 53 insertions, 67 deletions
@@ -1401,17 +1401,9 @@ class Cmd(cmd.Cmd): :param begidx: int - the beginning index of the prefix text :param endidx: int - the ending index of the prefix text :param match_against: Collection - the list being matched against - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ - # Make sure we were given a Collection with items to match against - if not isinstance(match_against, Collection) or len(match_against) == 0: - return [] - - # Perform matching and eliminate duplicates - matches = [cur_match for cur_match in set(match_against) if cur_match.startswith(text)] - - matches.sort() - return matches + return [cur_match for cur_match in match_against if cur_match.startswith(text)] def delimiter_complete(self, text, line, begidx, endidx, match_against, delimiter): """ @@ -1444,7 +1436,7 @@ class Cmd(cmd.Cmd): :param endidx: int - the ending index of the prefix text :param match_against: Collection - the list being matched against :param delimiter: str - what delimits each portion of the matches (ex: paths are delimited by a slash) - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ matches = self.basic_complete(text, line, begidx, endidx, match_against) @@ -1486,7 +1478,7 @@ class Cmd(cmd.Cmd): 2. function that performs tab completion (ex: path_complete) :param all_else: Collection or function - an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ # Get all tokens through the one being completed tokens, _ = self.tokens_for_completion(line, begidx, endidx) @@ -1502,14 +1494,13 @@ class Cmd(cmd.Cmd): if flag in flag_dict: match_against = flag_dict[flag] - # Perform tab completion using a Collection. These matches are already sorted. + # Perform tab completion using a Collection if isinstance(match_against, Collection): completions_matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): completions_matches = match_against(text, line, begidx, endidx) - completions_matches.sort() return completions_matches @@ -1528,7 +1519,7 @@ class Cmd(cmd.Cmd): 2. function that performs tab completion (ex: path_complete) :param all_else: Collection or function - an optional parameter for tab completing any token that isn't at an index in index_dict - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ # Get all tokens through the one being completed tokens, _ = self.tokens_for_completion(line, begidx, endidx) @@ -1546,14 +1537,13 @@ class Cmd(cmd.Cmd): else: match_against = all_else - # Perform tab completion using a Collection. These matches are already sorted. + # Perform tab completion using a Collection if isinstance(match_against, Collection): matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): matches = match_against(text, line, begidx, endidx) - matches.sort() return matches @@ -1567,7 +1557,7 @@ class Cmd(cmd.Cmd): :param endidx: int - the ending index of the prefix text :param dir_exe_only: bool - only return directories and executables, not non-executable files :param dir_only: bool - only return directories - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ # Determine if a trailing separator should be appended to directory completions add_trailing_sep_if_dir = False @@ -1655,7 +1645,6 @@ class Cmd(cmd.Cmd): if tilde_expanded: matches = [cur_path.replace(user_path, '~', 1) for cur_path in matches] - matches.sort() return matches @staticmethod @@ -1663,7 +1652,7 @@ class Cmd(cmd.Cmd): """ Returns names of executables in a user's path :param starts_with: str - what the exes should start with. leave blank for all exes in path. - :return: List[str] - a sorted list of matching exe names + :return: List[str] - a list of matching exe names """ # Purposely don't match any executable containing wildcards wildcards = ['*', '?'] @@ -1685,9 +1674,7 @@ class Cmd(cmd.Cmd): for match in matches: exes_set.add(os.path.basename(match)) - exes_list = list(exes_set) - exes_list.sort() - return exes_list + return list(exes_set) def shell_cmd_complete(self, text, line, begidx, endidx, complete_blank=False): """Performs completion of executables either in a user's path or a given path @@ -1698,7 +1685,7 @@ class Cmd(cmd.Cmd): :param complete_blank: bool - If True, then a blank will complete all shell commands in a user's path If False, then no completion is performed Defaults to False to match Bash shell behavior - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ # Don't tab complete anything if no shell command has been started if not complete_blank and len(text) == 0: @@ -1724,7 +1711,7 @@ class Cmd(cmd.Cmd): :param endidx: int - the ending index of the prefix text :param compfunc: Callable - the completer function for the current command this will be called if we aren't completing for redirection - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ if self.allow_redirection: @@ -1794,11 +1781,6 @@ class Cmd(cmd.Cmd): else: matches_to_display = matches - # Eliminate duplicates and sort - matches_to_display_set = set(matches_to_display) - matches_to_display = list(matches_to_display_set) - matches_to_display.sort() - # We will use readline's display function (rl_display_match_list()), so we # need to encode our string as bytes to place in a C array. if six.PY3: @@ -1845,11 +1827,6 @@ class Cmd(cmd.Cmd): else: matches_to_display = matches - # Eliminate duplicates and sort - matches_to_display_set = set(matches_to_display) - matches_to_display = list(matches_to_display_set) - matches_to_display.sort() - # Display the matches orig_pyreadline_display(matches_to_display) @@ -2121,9 +2098,9 @@ class Cmd(cmd.Cmd): else: # Complete token against aliases and command names - alias_names = set(self.aliases.keys()) - visible_commands = set(self.get_visible_commands()) - strs_to_match = list(alias_names | visible_commands) + alias_names = list(self.aliases.keys()) + visible_commands = self.get_visible_commands() + strs_to_match = alias_names + visible_commands self.completion_matches = self.basic_complete(text, line, begidx, endidx, strs_to_match) # Eliminate duplicates and sort @@ -2131,6 +2108,10 @@ class Cmd(cmd.Cmd): self.completion_matches = list(matches_set) self.completion_matches.sort() + display_matches_set = set(self.display_matches) + self.display_matches = list(display_matches_set) + self.display_matches.sort() + # Handle single result if len(self.completion_matches) == 1: str_to_append = '' @@ -2152,19 +2133,14 @@ class Cmd(cmd.Cmd): def get_all_commands(self): """ - Returns a sorted list of all commands - Any duplicates have been removed as well + Returns a list of all commands """ - commands = [cur_name[3:] for cur_name in set(self.get_names()) if cur_name.startswith('do_')] - commands.sort() - return commands + return [cur_name[3:] for cur_name in self.get_names() if cur_name.startswith('do_')] def get_visible_commands(self): """ - Returns a sorted list of commands that have not been hidden - Any duplicates have been removed as well + Returns a list of commands that have not been hidden """ - # This list is already sorted and has no duplicates commands = self.get_all_commands() # Remove the hidden commands @@ -2175,13 +2151,13 @@ class Cmd(cmd.Cmd): return commands def get_help_topics(self): - """ Returns a sorted list of help topics with all duplicates removed """ - return [name[5:] for name in set(self.get_names()) if name.startswith('help_')] + """ Returns a list of help topics """ + return [name[5:] for name in self.get_names() if name.startswith('help_')] def complete_help(self, text, line, begidx, endidx): """ Override of parent class method to handle tab completing subcommands and not showing hidden commands - Returns a sorted list of possible tab completions + Returns a list of possible tab completions """ # The command is the token at index 1 in the command line @@ -2204,9 +2180,9 @@ class Cmd(cmd.Cmd): if index == cmd_index: # Complete token against topics and visible commands - topics = set(self.get_help_topics()) - visible_commands = set(self.get_visible_commands()) - strs_to_match = list(topics | visible_commands) + topics = self.get_help_topics() + visible_commands = self.get_visible_commands() + strs_to_match = topics + visible_commands matches = self.basic_complete(text, line, begidx, endidx, strs_to_match) # Check if we are completing a subcommand @@ -2871,12 +2847,14 @@ Usage: Usage: unalias [-a] name [name ...] """ # Get a sorted list of help topics help_topics = self.get_help_topics() - - cmds_doc = [] - cmds_undoc = [] + help_topics.sort() # Get a sorted list of visible command names visible_commands = self.get_visible_commands() + visible_commands.sort() + + cmds_doc = [] + cmds_undoc = [] for command in visible_commands: if command in help_topics: @@ -3067,7 +3045,7 @@ Usage: Usage: unalias [-a] name [name ...] :param line: str - the current input line with leading whitespace removed :param begidx: int - the beginning index of the prefix text :param endidx: int - the ending index of the prefix text - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ index_dict = {1: self.shell_cmd_complete} return self.index_based_complete(text, line, begidx, endidx, index_dict, self.path_complete) @@ -3102,7 +3080,7 @@ Usage: Usage: unalias [-a] name [name ...] :param line: str - the current input line with leading whitespace removed :param begidx: int - the beginning index of the prefix text :param endidx: int - the ending index of the prefix text - :return: List[str] - a sorted list of possible tab completions + :return: List[str] - a list of possible tab completions """ # The command is the token at index 0 in the command line cmd_index = 0 diff --git a/tests/test_completion.py b/tests/test_completion.py index e779e44b..69404aa7 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -73,7 +73,7 @@ def complete_tester(text, line, begidx, endidx, app): :param app: the cmd2 app that will run completions :return: The first matched string or None if there are no matches Matches are stored in app.completion_matches - These matches have been sorted by complete() + These matches also have been sorted by complete() """ def get_line(): return line @@ -116,7 +116,7 @@ def test_complete_empty_arg(cmd2_app): endidx = len(line) begidx = endidx - len(text) - expected = cmd2_app.complete_help(text, line, begidx, endidx) + expected = sorted(cmd2_app.complete_help(text, line, begidx, endidx)) first_match = complete_tester(text, line, begidx, endidx, cmd2_app) assert first_match is not None and \ @@ -167,7 +167,8 @@ def test_cmd2_help_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.complete_help(text, line, begidx, endidx) == ['help', 'history'] + matches = sorted(cmd2_app.complete_help(text, line, begidx, endidx)) + assert matches == ['help', 'history'] def test_cmd2_help_completion_nomatch(cmd2_app): text = 'fakecommand' @@ -269,8 +270,9 @@ def test_path_completion_multiple(cmd2_app, request): endidx = len(line) begidx = endidx - len(text) + matches = sorted(cmd2_app.path_complete(text, line, begidx, endidx)) expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep] - assert expected == cmd2_app.path_complete(text, line, begidx, endidx) + assert matches == expected def test_path_completion_nomatch(cmd2_app, request): test_dir = os.path.dirname(request.module.__file__) @@ -410,7 +412,8 @@ def test_basic_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) == sorted(food_item_strs) + matches = sorted(cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs)) + assert matches == sorted(food_item_strs) def test_basic_completion_nomatch(cmd2_app): text = 'q' @@ -449,7 +452,8 @@ def test_flag_based_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict) == sorted(food_item_strs) + matches = sorted(cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict)) + assert matches == sorted(food_item_strs) def test_flag_based_completion_nomatch(cmd2_app): text = 'q' @@ -499,7 +503,8 @@ def test_index_based_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict) == sorted(sport_item_strs) + matches = sorted(cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict)) + assert matches == sorted(sport_item_strs) def test_index_based_completion_nomatch(cmd2_app): text = 'q' @@ -744,7 +749,8 @@ def test_cmd2_help_subcommand_completion_multiple(sc_app): endidx = len(line) begidx = endidx - len(text) - assert sc_app.complete_help(text, line, begidx, endidx) == ['bar', 'foo', 'sport'] + matches = sorted(sc_app.complete_help(text, line, begidx, endidx)) + assert matches == ['bar', 'foo', 'sport'] def test_cmd2_help_subcommand_completion_nomatch(sc_app): @@ -899,7 +905,8 @@ def test_cmd2_help_submenu_completion_multiple(sb_app): endidx = len(line) begidx = endidx - len(text) - assert sb_app.complete_help(text, line, begidx, endidx) == ['py', 'pyscript'] + matches = sorted(sb_app.complete_help(text, line, begidx, endidx)) + assert matches == ['py', 'pyscript'] def test_cmd2_help_submenu_completion_nomatch(sb_app): @@ -916,4 +923,5 @@ def test_cmd2_help_submenu_completion_subcommands(sb_app): endidx = len(line) begidx = endidx - len(text) - assert sb_app.complete_help(text, line, begidx, endidx) == ['py', 'pyscript'] + matches = sorted(sb_app.complete_help(text, line, begidx, endidx)) + assert matches == ['py', 'pyscript'] |