@@ -165,7 +165,7 @@ def set_use_arg_list(val):
165165
166166def flag_based_complete (text , line , begidx , endidx , flag_dict , default_completer = None ):
167167 """
168- Tab completes based on a particular flag preceding the text being completed
168+ Tab completes based on a particular flag preceding the token being completed
169169 :param text: str - the string prefix we are attempting to match (all returned matches must begin with it)
170170 :param line: str - the current input line with leading whitespace removed
171171 :param begidx: int - the beginning index of the prefix text
@@ -181,15 +181,14 @@ def flag_based_complete(text, line, begidx, endidx, flag_dict, default_completer
181181 :return: List[str] - a list of possible tab completions
182182 """
183183
184- # Get all tokens prior to text being completed
184+ # Get all tokens prior to token being completed
185185 try :
186186 prev_space_index = max (line .rfind (' ' , 0 , begidx ), 0 )
187187 tokens = shlex .split (line [:prev_space_index ], posix = POSIX_SHLEX )
188188 except ValueError :
189189 # Invalid syntax for shlex (Probably due to missing closing quote)
190190 return []
191191
192- # Nothing to do
193192 if len (tokens ) == 0 :
194193 return []
195194
@@ -199,7 +198,7 @@ def flag_based_complete(text, line, begidx, endidx, flag_dict, default_completer
199198 # Must have at least the command and one argument for a flag to be present
200199 if len (tokens ) > 1 :
201200
202- # Get the argument that precedes the text being completed
201+ # Get the argument that precedes the token being completed
203202 flag = tokens [- 1 ]
204203
205204 # Check if the flag is in the dictionary
@@ -242,12 +241,12 @@ def index_based_complete(text, line, begidx, endidx, index_dict, default_complet
242241 values - there are two types of values
243242 1. iterable list of strings to match against (dictionaries, lists, etc.)
244243 2. function that performs tab completion (ex: path_complete)
245- :param default_completer: callable - an optional completer to use if the text being completed is not at
244+ :param default_completer: callable - an optional completer to use if the token being completed is not at
246245 any index in index_dict
247246 :return: List[str] - a list of possible tab completions
248247 """
249248
250- # Get all tokens prior to text being completed
249+ # Get all tokens prior to token being completed
251250 try :
252251 prev_space_index = max (line .rfind (' ' , 0 , begidx ), 0 )
253252 tokens = shlex .split (line [:prev_space_index ], posix = POSIX_SHLEX )
@@ -260,7 +259,7 @@ def index_based_complete(text, line, begidx, endidx, index_dict, default_complet
260259 # Must have at least the command
261260 if len (tokens ) > 0 :
262261
263- # Get the index of the text being completed
262+ # Get the index of the token being completed
264263 index = len (tokens )
265264
266265 # Check if the index is in the dictionary
@@ -300,20 +299,32 @@ def path_complete(text, line, begidx, endidx, dir_exe_only=False, dir_only=False
300299 :return: List[str] - a list of possible tab completions
301300 """
302301
302+ # Get all tokens prior to token being completed
303+ try :
304+ prev_space_index = max (line .rfind (' ' , 0 , begidx ), 0 )
305+ tokens = shlex .split (line [:prev_space_index ], posix = POSIX_SHLEX )
306+ except ValueError :
307+ # Invalid syntax for shlex (Probably due to missing closing quote)
308+ return []
309+
310+ if len (tokens ) == 0 :
311+ return []
312+
303313 # Determine if a trailing separator should be appended to directory completions
304314 add_trailing_sep_if_dir = False
305315 if endidx == len (line ) or (endidx < len (line ) and line [endidx ] != os .path .sep ):
306316 add_trailing_sep_if_dir = True
307317
308318 add_sep_after_tilde = False
309- # If no path and no search text has been entered, then search in the CWD for *
310- if not text and line [begidx - 1 ] == ' ' and (begidx >= len (line ) or line [begidx ] == ' ' ):
319+
320+ # Readline places begidx after ~ and path separators (/) so we need to extract any directory
321+ # path that appears before the search text
322+ dirname = line [prev_space_index + 1 :begidx ]
323+
324+ # If no directory path and no search text has been entered, then search in the CWD for *
325+ if not dirname and not text :
311326 search_str = os .path .join (os .getcwd (), '*' )
312327 else :
313- # Parse out the path being searched
314- prev_space_index = line .rfind (' ' , 0 , begidx )
315- dirname = line [prev_space_index + 1 :begidx ]
316-
317328 # Purposely don't match any path containing wildcards - what we are doing is complicated enough!
318329 wildcards = ['*' , '?' ]
319330 for wildcard in wildcards :
@@ -354,7 +365,7 @@ def path_complete(text, line, begidx, endidx, dir_exe_only=False, dir_only=False
354365
355366 # If there is a single completion
356367 if len (completions ) == 1 :
357- # If it is a file and we are at the end of the line, then add a space for convenience
368+ # If it is a file and we are at the end of the line, then add a space
358369 if os .path .isfile (path_completions [0 ]) and endidx == len (line ):
359370 completions [0 ] += ' '
360371 # If tilde was expanded without a separator, prepend one
@@ -1340,7 +1351,7 @@ def complete_help(self, text, line, begidx, endidx):
13401351 Override of parent class method to handle tab completing subcommands
13411352 """
13421353
1343- # Get all tokens prior to text being completed
1354+ # Get all tokens prior to token being completed
13441355 try :
13451356 prev_space_index = max (line .rfind (' ' , 0 , begidx ), 0 )
13461357 tokens = shlex .split (line [:prev_space_index ], posix = POSIX_SHLEX )
@@ -2030,22 +2041,20 @@ def do_shell(self, command):
20302041 proc = subprocess .Popen (command , stdout = self .stdout , shell = True )
20312042 proc .communicate ()
20322043
2033- # noinspection PyUnusedLocal
20342044 @staticmethod
2035- def _shell_command_complete (text , line , begidx , endidx ):
2036- """Method called to complete an input line by environment PATH executable completion.
2045+ def _get_exes_in_path (starts_with , at_eol ):
2046+ """
2047+ Called by complete_shell to get names of executables in a user's path
20372048
2038- :param text: str - the string prefix we are attempting to match (all returned matches must begin with it)
2039- :param line: str - the current input line with leading whitespace removed
2040- :param begidx: int - the beginning index of the prefix text
2041- :param endidx: int - the ending index of the prefix text
2049+ :param starts_with: str - what the exes should start with
2050+ :param at_eol: bool - tells if the user's cursor is at the end of the command line
20422051 :return: List[str] - a list of possible tab completions
20432052 """
20442053
20452054 # Purposely don't match any executable containing wildcards
20462055 wildcards = ['*' , '?' ]
20472056 for wildcard in wildcards :
2048- if wildcard in text :
2057+ if wildcard in starts_with :
20492058 return []
20502059
20512060 # Get a list of every directory in the PATH environment variable and ignore symbolic links
@@ -2054,9 +2063,9 @@ def _shell_command_complete(text, line, begidx, endidx):
20542063 # Use a set to store exe names since there can be duplicates
20552064 exes = set ()
20562065
2057- # Find every executable file in the PATH that matches the pattern
2066+ # Find every executable file in the user's path that matches the pattern
20582067 for path in paths :
2059- full_path = os .path .join (path , text )
2068+ full_path = os .path .join (path , starts_with )
20602069 matches = [f for f in glob .glob (full_path + '*' ) if os .path .isfile (f ) and os .access (f , os .X_OK )]
20612070
20622071 for match in matches :
@@ -2067,13 +2076,13 @@ def _shell_command_complete(text, line, begidx, endidx):
20672076 results .sort ()
20682077
20692078 # If there is a single completion and we are at end of the line, then add a space at the end for convenience
2070- if len (results ) == 1 and endidx == len ( line ) :
2079+ if len (results ) == 1 and at_eol :
20712080 results [0 ] += ' '
20722081
20732082 return results
20742083
20752084 def complete_shell (self , text , line , begidx , endidx ):
2076- """Handles tab completion of executable commands and local file system paths.
2085+ """Handles tab completion of executable commands and local file system paths for the shell command
20772086
20782087 :param text: str - the string prefix we are attempting to match (all returned matches must begin with it)
20792088 :param line: str - the current input line with leading whitespace removed
@@ -2082,15 +2091,14 @@ def complete_shell(self, text, line, begidx, endidx):
20822091 :return: List[str] - a list of possible tab completions
20832092 """
20842093
2085- # Get all tokens prior to text being completed
2094+ # Get all tokens prior to token being completed
20862095 try :
20872096 prev_space_index = max (line .rfind (' ' , 0 , begidx ), 0 )
20882097 tokens = shlex .split (line [:prev_space_index ], posix = POSIX_SHLEX )
20892098 except ValueError :
20902099 # Invalid syntax for shlex (Probably due to missing closing quote)
20912100 return []
20922101
2093- # Nothing to do
20942102 if len (tokens ) == 0 :
20952103 return []
20962104
@@ -2106,19 +2114,19 @@ def complete_shell(self, text, line, begidx, endidx):
21062114 if len (cmd_token ) == 0 :
21072115 return []
21082116
2117+ # Look for path characters in the token
21092118 if not (cmd_token .startswith ('~' ) or os .path .sep in cmd_token ):
21102119 # No path characters are in this token, it is OK to try shell command completion.
2111- command_completions = self ._shell_command_complete (text , line , begidx , endidx )
2120+ command_completions = self ._get_exes_in_path (text , endidx == len ( line ) )
21122121
21132122 if command_completions :
21142123 return command_completions
21152124
21162125 # If we have no results, try path completion to find the shell commands
21172126 return path_complete (text , line , begidx , endidx , dir_exe_only = True )
21182127
2119- # Shell command has been completed
2128+ # We are past the shell command, so do path completion
21202129 else :
2121- # Do path completion
21222130 return path_complete (text , line , begidx , endidx )
21232131
21242132 # noinspection PyBroadException
0 commit comments