Skip to content

Commit 70efe4b

Browse files
add string literal completion
1 parent fc74d87 commit 70efe4b

File tree

3 files changed

+35
-4
lines changed

3 files changed

+35
-4
lines changed

bpython/autocomplete.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ def get_completer(cursor_offset, current_line, locals_, argspec, full_code, mode
6969
'mode':mode, 'complete_magic_methods':complete_magic_methods}
7070

7171
# mutually exclusive matchers: if one returns [], don't go on
72-
for completer in [ImportCompletion, FilenameCompletion,
73-
MagicMethodCompletion, GlobalCompletion]:
72+
for completer in [StringLiteralAttrCompletion, ImportCompletion,
73+
FilenameCompletion, MagicMethodCompletion, GlobalCompletion]:
7474
matches = completer.matches(cursor_offset, current_line, **kwargs)
7575
if matches is not None:
7676
return sorted(set(matches)), completer
@@ -315,6 +315,20 @@ def matches(cls, cursor_offset, line, argspec, **kwargs):
315315
return matches
316316
locate = staticmethod(lineparts.current_word)
317317

318+
class StringLiteralAttrCompletion(BaseCompletionType):
319+
locate = staticmethod(lineparts.current_string_literal_attr)
320+
@classmethod
321+
def matches(cls, cursor_offset, line, **kwargs):
322+
r = cls.locate(cursor_offset, line)
323+
if r is None:
324+
return None
325+
start, end, word = r
326+
attrs = dir('')
327+
matches = [att for att in attrs if att.startswith(word)]
328+
if not word.startswith('_'):
329+
return [match for match in matches if not match.startswith('_')]
330+
return matches
331+
318332
class SafeEvalFailed(Exception):
319333
"""If this object is returned, safe_eval failed"""
320334
# Because every normal Python value is a possible return value of safe_eval

bpython/line.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,11 @@ def current_dotted_attribute(cursor_offset, line):
149149
start, end, word = match
150150
if '.' in word[1:]:
151151
return start, end, word
152+
153+
def current_string_literal_attr(cursor_offset, line):
154+
"""The attribute following a string literal"""
155+
matches = re.finditer("('''" + r'''|"""|'|")((?:(?=([^"'\\]+|\\.|(?!\1)["']))\3)*)\1[.]([a-zA-Z_]?[\w]*)''', line)
156+
for m in matches:
157+
if (m.start(4) <= cursor_offset and m.end(4) >= cursor_offset):
158+
return m.start(4), m.end(4), m.group(4)
152159
return None

bpython/test/test_line_properties.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import unittest
22
import re
33

4-
from bpython.line import current_word, current_dict_key, current_dict, current_string, current_object, current_object_attribute, current_from_import_from, current_from_import_import, current_import
5-
from bpython.line import current_word, current_dict_key, current_dict, current_string, current_object, current_object_attribute, current_from_import_from, current_from_import_import, current_import, current_method_definition_name, current_single_word
4+
from bpython.line import current_word, current_dict_key, current_dict, current_string, current_object, current_object_attribute, current_from_import_from, current_from_import_import, current_import, current_method_definition_name, current_single_word, current_string_literal_attr
65

76

87
def cursor(s):
@@ -254,5 +253,16 @@ def test_simple(self):
254253
self.assertAccess('.foo|')
255254
self.assertAccess(' <foo|>')
256255

256+
class TestCurrentStringLiteral(LineTestCase):
257+
def setUp(self):
258+
self.func = current_string_literal_attr
259+
def test_simple(self):
260+
self.assertAccess('"hey".<a|>')
261+
self.assertAccess('"hey"|')
262+
self.assertAccess('"hey"|.a')
263+
self.assertAccess('"hey".<a|b>')
264+
self.assertAccess('"hey".asdf d|')
265+
self.assertAccess('"hey".<|>')
266+
257267
if __name__ == '__main__':
258268
unittest.main()

0 commit comments

Comments
 (0)