@@ -283,6 +283,35 @@ def next_token_inside_string(s, inside_string):
283283 inside_string = False
284284 return inside_string
285285
286+
287+ class MatchesIterator (object ):
288+ def __init__ (self , current_word = '' , matches = []):
289+ self .current_word = current_word
290+ self .matches = list (matches )
291+ self .index = - 1
292+
293+ def __nonzero__ (self ):
294+ return self .index != - 1
295+
296+ def __iter__ (self ):
297+ return self
298+
299+ def current (self ):
300+ if self .index == - 1 :
301+ raise ValueError ('No current match.' )
302+ return self .matches [self .index ]
303+
304+ def next (self ):
305+ self .index = (self .index + 1 ) % len (self .matches )
306+ return self .matches [self .index ]
307+
308+ def update (self , current_word = '' , matches = []):
309+ if current_word != self .current_word :
310+ self .current_word = current_word
311+ self .matches = list (matches )
312+ self .index = - 1
313+
314+
286315class Interpreter (code .InteractiveInterpreter ):
287316
288317 def __init__ (self , locals = None , encoding = sys .getdefaultencoding ()):
@@ -476,6 +505,7 @@ def __init__(self, scr, interp, statusbar=None, idle=None):
476505 self .idle = idle
477506 self .f_string = ''
478507 self .matches = []
508+ self .matches_iter = MatchesIterator ()
479509 self .argspec = None
480510 self .s = ''
481511 self .inside_string = False
@@ -722,14 +752,17 @@ def _complete(self, tab=False):
722752 cw = self .cw ()
723753 cs = self .current_string ()
724754 if not (cw or cs or self .argspec ):
755+ self .matches_iter .update ()
725756 self .scr .redrawwin ()
726757 self .scr .refresh ()
727758 return False
728759
729760 if not cw :
730761 self .matches = []
762+ self .matches_iter .update ()
731763
732764 if cs and tab :
765+ # Filename completion
733766 self .matches = list ()
734767 user_dir = os .path .expanduser ('~' )
735768 for filename in glob (os .path .expanduser (cs + '*' )):
@@ -738,13 +771,15 @@ def _complete(self, tab=False):
738771 if cs .startswith ('~' ):
739772 filename = '~' + filename [len (user_dir ):]
740773 self .matches .append (filename )
774+ self .matches_iter .update (cs , self .matches )
741775 return True
742776
743777 # Check for import completion
744778 e = False
745779 matches = importcompletion .complete (self .s , cw )
746780 if matches is not None and not matches :
747781 self .matches = []
782+ self .matches_iter .update ()
748783 self .scr .redrawwin ()
749784 return False
750785
@@ -765,6 +800,7 @@ def _complete(self, tab=False):
765800
766801 if e or not matches :
767802 self .matches = []
803+ self .matches_iter .update ()
768804 if not self .argspec :
769805 self .scr .redrawwin ()
770806 return False
@@ -782,6 +818,8 @@ def _complete(self, tab=False):
782818 self .tab ()
783819 return False
784820
821+ self .matches_iter .update (cw , self .matches )
822+
785823 try :
786824 self .show_list (self .matches , self .argspec )
787825 except curses .error :
@@ -792,7 +830,7 @@ def _complete(self, tab=False):
792830 return False
793831 return True
794832
795- def show_list (self , items , topline = None ):
833+ def show_list (self , items , topline = None , current_item = None ):
796834 shared = Struct ()
797835 shared .cols = 0
798836 shared .rows = 0
@@ -808,6 +846,8 @@ def show_list(self, items, topline=None):
808846 self .list_win .erase ()
809847 if items and '.' in items [0 ]:
810848 items = [x .rsplit ('.' )[- 1 ] for x in items ]
849+ if current_item :
850+ current_item = current_item .rsplit ('.' )[- 1 ]
811851
812852 if topline :
813853 height_offset = self .mkargspec (topline , down ) + 1
@@ -891,7 +931,11 @@ def lsize():
891931
892932 for ix , i in enumerate (v_items ):
893933 padding = (wl - len (i )) * ' '
894- self .list_win .addstr (i + padding , get_colpair ('main' ))
934+ if i == current_item :
935+ color = get_colpair ('operator' )
936+ else :
937+ color = get_colpair ('main' )
938+ self .list_win .addstr (i + padding , color )
895939 if ((cols == 1 or (ix and not (ix + 1 ) % cols ))
896940 and ix + 1 < len (v_items )):
897941 self .list_win .addstr ('\n ' )
@@ -1641,20 +1685,44 @@ def tab(self):
16411685 self .print_line (self .s )
16421686 return True
16431687
1644- self .complete (tab = True )
1645- if not OPTS .auto_display_list and not self .list_win_visible :
1646- return True
1688+ if not self .matches_iter :
1689+ self .complete (tab = True )
1690+ if not OPTS .auto_display_list and not self .list_win_visible :
1691+ return True
16471692
1648- cw = self .current_string () or self .cw ()
1649- if not cw :
1650- return True
1693+ cw = self .current_string () or self .cw ()
1694+ if not cw :
1695+ return True
1696+ else :
1697+ cw = self .matches_iter .current_word
16511698
16521699 b = os .path .commonprefix (self .matches )
16531700 if b :
16541701 self .s += b [len (cw ):]
1702+ expanded = bool (b [len (cw ):])
16551703 self .print_line (self .s )
16561704 if len (self .matches ) == 1 and OPTS .auto_display_list :
16571705 self .scr .touchwin ()
1706+ if expanded :
1707+ self .matches_iter .update (b , self .matches )
1708+ else :
1709+ expanded = False
1710+
1711+ if not expanded and self .matches :
1712+ if self .matches_iter :
1713+ self .s = self .s [:- len (self .matches_iter .current ())] + cw
1714+ current_match = self .matches_iter .next ()
1715+ if current_match :
1716+ try :
1717+ self .show_list (self .matches , self .argspec , current_match )
1718+ except curses .error :
1719+ # XXX: This is a massive hack, it will go away when I get
1720+ # cusswords into a good enough state that we can start
1721+ # using it.
1722+ self .list_win .border ()
1723+ self .list_win .refresh ()
1724+ self .s += current_match [len (cw ):]
1725+ self .print_line (self .s , True )
16581726 return True
16591727
16601728 def atbol (self ):
0 commit comments