X Tutup
Skip to content

Commit 8f659de

Browse files
committed
Implement tabable completion list.
1 parent f586f54 commit 8f659de

File tree

1 file changed

+76
-8
lines changed

1 file changed

+76
-8
lines changed

bpython/cli.py

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
286315
class 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

Comments
 (0)
X Tutup