summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2018-03-19 22:33:17 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2018-03-19 22:33:17 -0400
commitbe197be2637b04584f9658da9cf1c7bdfa676c7f (patch)
tree3e3215e702e48c32ab11a07c12628dd892eacffa
parent0c367e19d71e78a1e60c87c69df8bfb91192d045 (diff)
downloadcmd2-git-be197be2637b04584f9658da9cf1c7bdfa676c7f.tar.gz
Simplified function that tokenizes for tab completion
-rw-r--r--cmd2.py65
1 files changed, 45 insertions, 20 deletions
diff --git a/cmd2.py b/cmd2.py
index 00ff1392..98eb2a88 100644
--- a/cmd2.py
+++ b/cmd2.py
@@ -224,14 +224,19 @@ def set_use_arg_list(val):
USE_ARG_LIST = val
-def tokenize_line(line, begidx, endidx):
+def tokens_for_completion(line, begidx, endidx, preserve_quotes=False):
"""
Used by tab completion functions to get all tokens through the one being completed
This also handles tab-completion within quotes
: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: A list of tokens and whether or not the token being completed has an unclosed quote
+ :param preserve_quotes - if True, then the tokens will be returned still wrapped in whatever quotes
+ appeared on the command line. This defaults to False since tab completion routines
+ generally need the tokens unquoted. The only reason to set this to True would
+ be to check if the token being completed has an unclosed quote. complete() is the
+ only functions that does this.
+ :return: A list of tokens
"""
tokens = []
unclosed_quote = ''
@@ -259,11 +264,17 @@ def tokenize_line(line, begidx, endidx):
# No tokens were parsed
if len(tokens) == 0:
- unclosed_quote = ''
+ return tokens
+
+ if preserve_quotes:
+ # If the token being completed had an unclosed quote, we need remove the closing quote that was added
+ if unclosed_quote:
+ tokens[-1] = tokens[-1][:-1]
# Unquote all tokens
- for index, cur_token in enumerate(tokens):
- tokens[index] = strip_quotes(cur_token)
+ else:
+ for index, cur_token in enumerate(tokens):
+ tokens[index] = strip_quotes(cur_token)
# If begidx is equal to endidx, then the readline text variable is blank.
# Check if begidx is preceded by a string. This can happen in things like paths.
@@ -278,7 +289,7 @@ def tokenize_line(line, begidx, endidx):
if prev_space_index == 0 or prev_space_index == begidx - 1:
tokens.append('')
- return tokens, unclosed_quote
+ return tokens
# noinspection PyUnusedLocal
@@ -293,7 +304,7 @@ def basic_complete(text, line, begidx, endidx, match_against):
:return: List[str] - a list of possible tab completions
"""
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -326,7 +337,7 @@ def flag_based_complete(text, line, begidx, endidx, flag_dict, all_else=None):
"""
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -374,7 +385,7 @@ def index_based_complete(text, line, begidx, endidx, index_dict, all_else=None):
"""
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -418,7 +429,7 @@ def path_complete(text, line, begidx, endidx, dir_exe_only=False, dir_only=False
"""
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -444,6 +455,9 @@ def path_complete(text, line, begidx, endidx, dir_exe_only=False, dir_only=False
# If the user only entered a '~', then complete it with a slash
if completion_token == '~':
+ # This is a directory, so don't add a space or quote
+ set_allow_appended_space(False)
+ set_allow_closing_quote(False)
return [os.path.sep]
elif completion_token.startswith('~'):
@@ -1478,14 +1492,25 @@ class Cmd(cmd.Cmd):
if begidx > 0:
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx, preserve_quotes=True)
+
+ # Check if the token being complete has an unclosed quote
+ token_being_completed = tokens[-1]
+ unclosed_quote = ''
+
+ if len(token_being_completed) > 1:
+ first_char = token_being_completed[0]
+ last_char = token_being_completed[-1]
+ quotes = ['"', "'"]
+
+ if first_char in quotes and first_char != last_char:
+ unclosed_quote = first_char
- # If the cursor is right after a closed quote, then insert a space
- quotes = ['"', "'"]
- prior_char = line[begidx - 1]
- if not unclosed_quote and prior_char in quotes:
- self.completion_matches = [' ']
- return self.completion_matches[state]
+ # If the cursor is right after a closed quote, then insert a space
+ prior_char = line[begidx - 1]
+ if not unclosed_quote and prior_char in quotes:
+ self.completion_matches = [' ']
+ return self.completion_matches[state]
# Parse the command line
command, args, expanded_line = self.parseline(line)
@@ -1582,7 +1607,7 @@ class Cmd(cmd.Cmd):
subcmd_index = 2
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -2425,7 +2450,7 @@ Usage: Usage: unalias [-a] name [name ...]
shell_cmd_index = 1
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []
@@ -2498,7 +2523,7 @@ Usage: Usage: unalias [-a] name [name ...]
subcmd_index = 1
# Get all tokens through the one being completed
- tokens, unclosed_quote = tokenize_line(line, begidx, endidx)
+ tokens = tokens_for_completion(line, begidx, endidx)
if len(tokens) == 0:
return []