Skip to content

Commit 4d9767b

Browse files
Merge branch 'array-item-autocomplete'
This implementation modifies the interpreter locals dictionary, which was required to reuse more code.
2 parents 9f45c32 + ce1eb1a commit 4d9767b

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

bpython/autocomplete.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,44 @@ def list_attributes(self, obj):
335335
return dir(obj)
336336

337337

338+
class ArrayItemMembersCompletion(BaseCompletionType):
339+
340+
def __init__(self, shown_before_tab=True, mode=SIMPLE):
341+
self._shown_before_tab = shown_before_tab
342+
self.completer = AttrCompletion(mode=mode)
343+
344+
def matches(self, cursor_offset, line, **kwargs):
345+
if 'locals_' not in kwargs:
346+
return None
347+
locals_ = kwargs['locals_']
348+
349+
r = self.locate(cursor_offset, line)
350+
if r is None:
351+
return None
352+
member_part = r[2]
353+
_, _, dexpr = lineparts.current_array_with_indexer(cursor_offset, line)
354+
try:
355+
locals_['temp_val_from_array'] = safe_eval(dexpr, locals_)
356+
except (EvaluationError, IndexError):
357+
return set()
358+
359+
temp_line = line.replace(member_part, 'temp_val_from_array.')
360+
361+
matches = self.completer.matches(len(temp_line), temp_line, **kwargs)
362+
matches_with_correct_name = \
363+
set(match.replace('temp_val_from_array.', member_part) for match in matches)
364+
365+
del locals_['temp_val_from_array']
366+
367+
return matches_with_correct_name
368+
369+
def locate(self, current_offset, line):
370+
return lineparts.current_array_item_member_name(current_offset, line)
371+
372+
def format(self, match):
373+
return after_last_dot(match)
374+
375+
338376
class DictKeyCompletion(BaseCompletionType):
339377

340378
def matches(self, cursor_offset, line, **kwargs):
@@ -565,6 +603,7 @@ def get_default_completer(mode=SIMPLE):
565603
MagicMethodCompletion(mode=mode),
566604
MultilineJediCompletion(mode=mode),
567605
GlobalCompletion(mode=mode),
606+
ArrayItemMembersCompletion(mode=mode),
568607
CumulativeCompleter((AttrCompletion(mode=mode),
569608
ParameterNameCompletion(mode=mode)),
570609
mode=mode)

bpython/line.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,27 @@ def current_string_literal_attr(cursor_offset, line):
226226
if m.start(4) <= cursor_offset and m.end(4) >= cursor_offset:
227227
return LinePart(m.start(4), m.end(4), m.group(4))
228228
return None
229+
230+
231+
current_array_with_indexer_re = LazyReCompile(
232+
r'''([\w_][\w0-9._]*(?:\[[a-zA-Z0-9_"']+\])+)\.(.*)''')
233+
234+
235+
def current_array_with_indexer(cursor_offset, line):
236+
"""an array and indexer, e.g. foo[1]"""
237+
matches = current_array_with_indexer_re.finditer(line)
238+
for m in matches:
239+
if m.start(1) <= cursor_offset and m.end(1) <= cursor_offset:
240+
return LinePart(m.start(1), m.end(1), m.group(1))
241+
242+
243+
current_array_item_member_name_re = LazyReCompile(
244+
r'''([\w_][\w0-9._]*(?:\[[a-zA-Z0-9_"']+\])+\.)(.*)''')
245+
246+
247+
def current_array_item_member_name(cursor_offset, line):
248+
"""the member name after an array indexer, e.g. foo[1].bar"""
249+
matches = current_array_item_member_name_re.finditer(line)
250+
for m in matches:
251+
if m.start(2) <= cursor_offset and m.end(2) >= cursor_offset:
252+
return LinePart(m.start(1), m.end(2), m.group(1))

bpython/test/test_autocomplete.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,22 @@ def __getattr__(self, attr):
263263
self.com.matches(4, 'a.__', locals_=locals_))
264264

265265

266+
class TestArrayItemCompletion(unittest.TestCase):
267+
@classmethod
268+
def setUpClass(cls):
269+
cls.com = autocomplete.ArrayItemMembersCompletion()
270+
271+
def test_att_matches_found_on_instance(self):
272+
self.assertSetEqual(self.com.matches(5, 'a[0].', locals_={'a': [Foo()]}),
273+
set(['a[0].method', 'a[0].a', 'a[0].b']))
274+
275+
@skip_old_style
276+
def test_att_matches_found_on_old_style_instance(self):
277+
self.assertSetEqual(self.com.matches(5, 'a[0].',
278+
locals_={'a': [OldStyleFoo()]}),
279+
set(['a[0].method', 'a[0].a', 'a[0].b']))
280+
281+
266282
class TestMagicMethodCompletion(unittest.TestCase):
267283

268284
def test_magic_methods_complete_after_double_underscores(self):

0 commit comments

Comments
 (0)