diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-04-13 19:31:52 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-04-13 19:31:52 -0400 |
commit | 202cc074d10f61742e22dcdcea217ee03cc3cc17 (patch) | |
tree | a5b8b6bc4f565d9700a3e9ffcf2f77f951be0128 /cmd2.py | |
parent | 9d9e843845b57419e4be1abc65119b1d04dfa0f0 (diff) | |
download | cmd2-git-202cc074d10f61742e22dcdcea217ee03cc3cc17.tar.gz |
Simplifying adding opening quotes
Diffstat (limited to 'cmd2.py')
-rwxr-xr-x | cmd2.py | 238 |
1 files changed, 72 insertions, 166 deletions
@@ -1321,7 +1321,7 @@ class Cmd(cmd.Cmd): On Success tokens: list of unquoted tokens this is generally the list needed for tab completion functions - raw_tokens: list of tokens as they appear on the command line, meaning their quotes are preserved + raw_tokens: list of tokens with any quotes preserved this can be used to know if a token was quoted or is missing a closing quote Both lists are guaranteed to have at least 1 item @@ -1349,7 +1349,7 @@ class Cmd(cmd.Cmd): break except ValueError: # ValueError can be caused by missing closing quote - if len(quotes_to_try) == 0: + if not quotes_to_try: # Since we have no more quotes to try, something else # is causing the parsing error. Return None since # this means the line is malformed. @@ -1481,7 +1481,7 @@ class Cmd(cmd.Cmd): matches = self.basic_complete(text, line, begidx, endidx, match_against) # Display only the portion of the match that's being completed based on delimiter - if len(matches) > 0: + if matches: # Get the common beginning for the matches common_prefix = os.path.commonprefix(matches) @@ -1489,7 +1489,7 @@ class Cmd(cmd.Cmd): # Calculate what portion of the match we are completing display_token_index = 0 - if len(prefix_tokens) > 0: + if prefix_tokens: display_token_index = len(prefix_tokens) - 1 # Get this portion for each match and store them in self.display_matches @@ -1497,7 +1497,7 @@ class Cmd(cmd.Cmd): match_tokens = cur_match.split(delimiter) display_token = match_tokens[display_token_index] - if len(display_token) == 0: + if not display_token: display_token = delimiter self.display_matches.append(display_token) @@ -1759,7 +1759,7 @@ class Cmd(cmd.Cmd): :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: + if not complete_blank and not text: return [] # If there are no path characters in the search text, then do shell command completion in the user's path @@ -1862,7 +1862,7 @@ class Cmd(cmd.Cmd): if rl_type == RlType.GNU: # Check if we should show display_matches - if len(self.display_matches) > 0: + if self.display_matches: matches_to_display = self.display_matches # Recalculate longest_match_length for display_matches @@ -1920,7 +1920,7 @@ class Cmd(cmd.Cmd): if rl_type == RlType.PYREADLINE: # Check if we should show display_matches - if len(self.display_matches) > 0: + if self.display_matches: matches_to_display = self.display_matches else: matches_to_display = matches @@ -1931,117 +1931,6 @@ class Cmd(cmd.Cmd): # Display the matches orig_pyreadline_display(matches_to_display) - def _handle_completion_token_quote(self, raw_completion_token): - """ - This is called by complete() to add an opening quote to the token being completed if it is needed - The readline input buffer is then updated with the new string - :param raw_completion_token: str - the token being completed as it appears on the command line - :return: True if a quote was added, False otherwise - """ - if len(self.completion_matches) == 0: - return False - - quote_added = False - - # Check if token on screen is already quoted - if len(raw_completion_token) == 0 or raw_completion_token[0] not in QUOTES: - - # Get the common prefix of all matches. This is what be written to the screen. - common_prefix = os.path.commonprefix(self.completion_matches) - - # If common_prefix contains a space, then we must add an opening quote to it - if ' ' in common_prefix: - - # Figure out what kind of quote to add - if '"' in common_prefix: - quote = "'" - else: - quote = '"' - - new_completion_token = quote + common_prefix - - # Handle a single result - if len(self.completion_matches) == 1: - str_to_append = '' - - # Add a closing quote if allowed - if self.allow_closing_quote: - str_to_append += quote - - orig_line = readline.get_line_buffer() - endidx = readline.get_endidx() - - # If we are at the end of the line, then add a space if allowed - if self.allow_appended_space and endidx == len(orig_line): - str_to_append += ' ' - - new_completion_token += str_to_append - - # Update the line - quote_added = True - self._replace_completion_token(raw_completion_token, new_completion_token) - - return quote_added - - def _replace_completion_token(self, raw_completion_token, new_completion_token): - """ - Replaces the token being completed in the readline line buffer which updates the screen - This is used for things like adding an opening quote for completions with spaces - :param raw_completion_token: str - the original token being completed as it appears on the command line - :param new_completion_token: str- the replacement token - :return: None - """ - orig_line = readline.get_line_buffer() - endidx = readline.get_endidx() - - starting_index = orig_line[:endidx].rfind(raw_completion_token) - - if starting_index != -1: - # Build the new line - new_line = orig_line[:starting_index] - new_line += new_completion_token - new_line += orig_line[endidx:] - - # Calculate the new cursor offset - len_diff = len(new_completion_token) - len(raw_completion_token) - new_point = endidx + len_diff - - # Replace the line and update the cursor offset - self._set_readline_line(new_line) - self._set_readline_point(new_point) - - @staticmethod - def _set_readline_line(new_line): - """ - Sets the readline line buffer - :param new_line: str - the new line contents - """ - if rl_type == RlType.GNU: - # Byte encode the new line - if six.PY3: - encoded_line = bytes(new_line, encoding='utf-8') - else: - encoded_line = bytes(new_line) - - # Replace the line - readline_lib.rl_replace_line(encoded_line, 0) - - elif rl_type == RlType.PYREADLINE: - readline.rl.mode.l_buffer.set_line(new_line) - - @staticmethod - def _set_readline_point(new_point): - """ - Sets the cursor offset in the readline line buffer - :param new_point: int - the new cursor offset - """ - if rl_type == RlType.GNU: - rl_point = ctypes.c_int.in_dll(readline_lib, "rl_point") - rl_point.value = new_point - - elif rl_type == RlType.PYREADLINE: - readline.rl.mode.l_buffer.point = new_point - # ----- Methods which override stuff in cmd ----- def complete(self, text, state): @@ -2119,21 +2008,30 @@ class Cmd(cmd.Cmd): self.completion_matches = [] return None - # readline still performs word breaks after a quote. Therefore something like quoted search - # text with a space would have resulted in begidx pointing to the middle of the token we - # we want to complete. Figure out where that token actually begins and save the beginning - # portion of it that was not part of the text readline gave us. We will remove it from the - # completions later since readline expects them to start with the original text. - actual_begidx = line[:endidx].rfind(tokens[-1]) + # Text we need to remove from completions later text_to_remove = '' - if actual_begidx != begidx: - text_to_remove = line[actual_begidx:begidx] + # Get the token being completed with any opening quote preserved + raw_completion_token = raw_tokens[-1] - # Adjust text and where it begins so the completer routines - # get unbroken search text to complete on. - text = text_to_remove + text - begidx = actual_begidx + # Check if the token being completed has an opening quote + if raw_completion_token and raw_completion_token[0] in QUOTES: + unclosed_quote = raw_completion_token[0] + + # readline still performs word breaks after a quote. Therefore something like quoted search + # text with a space would have resulted in begidx pointing to the middle of the token we + # we want to complete. Figure out where that token actually begins and save the beginning + # portion of it that was not part of the text readline gave us. We will remove it from the + # completions later since readline expects them to start with the original text. + actual_begidx = line[:endidx].rfind(tokens[-1]) + + if actual_begidx != begidx: + text_to_remove = line[actual_begidx:begidx] + + # Adjust text and where it begins so the completer routines + # get unbroken search text to complete on. + text = text_to_remove + text + begidx = actual_begidx # Check if a valid command was entered if command in self.get_all_commands(): @@ -2164,7 +2062,7 @@ class Cmd(cmd.Cmd): # call the completer function for the current command self.completion_matches = self._redirect_complete(text, line, begidx, endidx, compfunc) - if len(self.completion_matches) > 0: + if self.completion_matches: # Eliminate duplicates matches_set = set(self.completion_matches) @@ -2173,36 +2071,44 @@ class Cmd(cmd.Cmd): display_matches_set = set(self.display_matches) self.display_matches = list(display_matches_set) - # Get the token being completed as it appears on the command line - raw_completion_token = raw_tokens[-1] - - # Add an opening quote if needed - if self._handle_completion_token_quote(raw_completion_token): - # An opening quote was added and the screen was updated. Return no results. - self.completion_matches = [] - return None - - if text_to_remove or shortcut_to_restore: - # If self.display_matches is empty, then set it to self.completion_matches - # before we alter them. That way the suggestions will reflect how we parsed - # the token being completed and not how readline did. - if len(self.display_matches) == 0: - self.display_matches = copy.copy(self.completion_matches) - - # Check if we need to remove text from the beginning of tab completions - if text_to_remove: - self.completion_matches = \ - [m.replace(text_to_remove, '', 1) for m in self.completion_matches] - - # Check if we need to restore a shortcut in the tab completions - # so it doesn't get erased from the command line - if shortcut_to_restore: - self.completion_matches = \ - [shortcut_to_restore + match for match in self.completion_matches] - - # If the token being completed starts with a quote then we know it has an unclosed quote - if len(raw_completion_token) > 0 and raw_completion_token[0] in QUOTES: - unclosed_quote = raw_completion_token[0] + # If self.display_matches is empty, then set it to self.completion_matches + # before we alter them. That way the suggestions will reflect how we parsed + # the token being completed and not how readline did. + if not self.display_matches: + self.display_matches = copy.copy(self.completion_matches) + + # Check if we need to add an opening quote + if not unclosed_quote: + + # Get the common prefix of all matches. This is the actual tab completion. + common_prefix = os.path.commonprefix(self.completion_matches) + + # Join all matches into 1 string for ease of searching + all_matches_str = ''.join(self.completion_matches) + + # If there is a common_prefix and any of the matches have a space, + # then we must add an opening quote to the matches. + if common_prefix and ' ' in all_matches_str: + + # Figure out what kind of quote to add + if '"' in all_matches_str: + quote = "'" + else: + quote = '"' + + unclosed_quote = quote + self.completion_matches = [quote + match for match in self.completion_matches] + + # Check if we need to remove text from the beginning of tab completions + elif text_to_remove: + self.completion_matches = \ + [m.replace(text_to_remove, '', 1) for m in self.completion_matches] + + # Check if we need to restore a shortcut in the tab completions + # so it doesn't get erased from the command line + if shortcut_to_restore: + self.completion_matches = \ + [shortcut_to_restore + match for match in self.completion_matches] else: # Complete token against aliases and command names @@ -2226,7 +2132,7 @@ class Cmd(cmd.Cmd): self.completion_matches[0] += str_to_append # Otherwise sort matches - elif len(self.completion_matches) > 0: + elif self.completion_matches: self.completion_matches.sort() self.display_matches.sort() @@ -2871,7 +2777,7 @@ Usage: Usage: alias [name] | [<name> <value>] alias save_results "print_results > out.txt" """ # If no args were given, then print a list of current aliases - if len(arglist) == 0: + if not arglist: for cur_alias in self.aliases: self.poutput("alias {} {}".format(cur_alias, self.aliases[cur_alias])) @@ -2919,7 +2825,7 @@ Usage: Usage: unalias [-a] name [name ...] Options: -a remove all alias definitions """ - if len(arglist) == 0: + if not arglist: self.do_help('unalias') if '-a' in arglist: @@ -3233,7 +3139,7 @@ Usage: Usage: unalias [-a] name [name ...] # Support expanding ~ in quoted paths for index, _ in enumerate(tokens): - if len(tokens[index]) > 0: + if tokens[index]: # Check if the token is quoted. Since shlex.split() passed, there isn't # an unclosed quote, so we only need to check the first character. first_char = tokens[index][0] |