@@ -1599,6 +1599,35 @@ def path_complete(self, text, line, begidx, endidx, dir_exe_only=False, dir_only
15991599 :param dir_only: bool - only return directories
16001600 :return: List[str] - a list of possible tab completions
16011601 """
1602+
1603+ # Used to complete ~ and ~user strings with a list of users that have existing home dirs
1604+ def complete_users ():
1605+ # Only works on Unix systems
1606+ try :
1607+ import pwd
1608+ except ImportError :
1609+ return []
1610+
1611+ # Get a list of users from password database
1612+ users = []
1613+ for cur_pw in pwd .getpwall ():
1614+
1615+ # Check if the user has an existing home dir
1616+ if os .path .isdir (cur_pw .pw_dir ):
1617+
1618+ # Add a ~ to the user to match against text
1619+ cur_user = '~' + cur_pw .pw_name
1620+ if cur_user .startswith (text ):
1621+ if add_trailing_sep_if_dir :
1622+ cur_user += os .path .sep
1623+ users .append (cur_user )
1624+
1625+ # These are directories, so don't add a space or quote
1626+ self .allow_appended_space = False
1627+ self .allow_closing_quote = False
1628+
1629+ return users
1630+
16021631 # Determine if a trailing separator should be appended to directory completions
16031632 add_trailing_sep_if_dir = False
16041633 if endidx == len (line ) or (endidx < len (line ) and line [endidx ] != os .path .sep ):
@@ -1608,9 +1637,9 @@ def path_complete(self, text, line, begidx, endidx, dir_exe_only=False, dir_only
16081637 cwd = os .getcwd ()
16091638 cwd_added = False
16101639
1611- # Used to replace ~ in the final results
1612- user_path = os . path . expanduser ( '~' )
1613- tilde_expanded = False
1640+ # Used to replace expanded user path in final result
1641+ orig_tilde_path = ''
1642+ expanded_tilde_path = ''
16141643
16151644 # If the search text is blank, then search in the CWD for *
16161645 if not text :
@@ -1623,35 +1652,30 @@ def path_complete(self, text, line, begidx, endidx, dir_exe_only=False, dir_only
16231652 if wildcard in text :
16241653 return []
16251654
1626- # Used if we need to prepend a directory to the search string
1627- dirname = ' '
1655+ # Start the search string
1656+ search_str = text + '* '
16281657
1629- # If the user only entered a '~', then complete it with a slash
1630- if text == '~' :
1631- # This is a directory, so don't add a space or quote
1632- self .allow_appended_space = False
1633- self .allow_closing_quote = False
1634- return [text + os .path .sep ]
1658+ # Handle tilde expansion and completion
1659+ if text .startswith ('~' ):
1660+ sep_index = text .find (os .path .sep , 1 )
16351661
1636- elif text .startswith ('~' ):
1637- # Tilde without separator between path is invalid
1638- if not text .startswith ('~' + os .path .sep ):
1639- return []
1662+ # If there is no slash, then the user is still completing the user after the tilde
1663+ if sep_index == - 1 :
1664+ return complete_users ()
16401665
1641- # Mark that we are expanding a tilde
1642- tilde_expanded = True
1666+ # Otherwise expand the user dir
1667+ else :
1668+ search_str = os .path .expanduser (search_str )
1669+
1670+ # Get what we need to restore the original tilde path later
1671+ orig_tilde_path = text [:sep_index ]
1672+ expanded_tilde_path = os .path .expanduser (orig_tilde_path )
16431673
16441674 # If the search text does not have a directory, then use the cwd
16451675 elif not os .path .dirname (text ):
1646- dirname = os .getcwd ()
1676+ search_str = os .path . join ( os . getcwd (), search_str )
16471677 cwd_added = True
16481678
1649- # Build the search string
1650- search_str = os .path .join (dirname , text + '*' )
1651-
1652- # Expand "~" to the real user directory
1653- search_str = os .path .expanduser (search_str )
1654-
16551679 # Find all matching path completions
16561680 matches = glob .glob (search_str )
16571681
@@ -1677,13 +1701,13 @@ def path_complete(self, text, line, begidx, endidx, dir_exe_only=False, dir_only
16771701 matches [index ] += os .path .sep
16781702 self .display_matches [index ] += os .path .sep
16791703
1680- # Remove cwd if it was added
1704+ # Remove cwd if it was added to match the text readline expects
16811705 if cwd_added :
16821706 matches = [cur_path .replace (cwd + os .path .sep , '' , 1 ) for cur_path in matches ]
16831707
1684- # Restore a tilde if we expanded one
1685- if tilde_expanded :
1686- matches = [cur_path .replace (user_path , '~' , 1 ) for cur_path in matches ]
1708+ # Restore the tilde string if we expanded one to match the text readline expects
1709+ if expanded_tilde_path :
1710+ matches = [cur_path .replace (expanded_tilde_path , orig_tilde_path , 1 ) for cur_path in matches ]
16871711
16881712 return matches
16891713
@@ -1732,7 +1756,7 @@ def shell_cmd_complete(self, text, line, begidx, endidx, complete_blank=False):
17321756 return []
17331757
17341758 # If there are no path characters in the search text, then do shell command completion in the user's path
1735- if os .path .sep not in text :
1759+ if not text . startswith ( '~' ) and os .path .sep not in text :
17361760 return self .get_exes_in_path (text )
17371761
17381762 # Otherwise look for executables in the given path
0 commit comments