X Tutup
"""Extracting and changing portions of the current line All functions take cursor offset from the beginning of the line and the line of Python code, and return None, or a tuple of the start index, end index, and the word.""" from __future__ import unicode_literals from itertools import chain from collections import namedtuple from bpython.lazyre import LazyReCompile current_word_re = LazyReCompile(r'[\w_][\w0-9._]*[(]?') LinePart = namedtuple('LinePart', ['start', 'stop', 'word']) def current_word(cursor_offset, line): """the object.attribute.attribute just before or under the cursor""" pos = cursor_offset matches = current_word_re.finditer(line) start = pos end = pos word = None for m in matches: if m.start() < pos and m.end() >= pos: start = m.start() end = m.end() word = m.group() if word is None: return None return LinePart(start, end, word) current_dict_key_re = LazyReCompile(r'''[\w_][\w0-9._]*\[([\w0-9._(), '"]*)''') def current_dict_key(cursor_offset, line): """If in dictionary completion, return the current key""" matches = current_dict_key_re.finditer(line) for m in matches: if m.start(1) <= cursor_offset and m.end(1) >= cursor_offset: return LinePart(m.start(1), m.end(1), m.group(1)) return None current_dict_re = LazyReCompile(r'''([\w_][\w0-9._]*)\[([\w0-9._(), '"]*)''') def current_dict(cursor_offset, line): """If in dictionary completion, return the dict that should be used""" matches = current_dict_re.finditer(line) for m in matches: if m.start(2) <= cursor_offset and m.end(2) >= cursor_offset: return LinePart(m.start(1), m.end(1), m.group(1)) return None current_string_re = LazyReCompile( '''(?P(?:""")|"|(?:''\')|')(?:((?P.+?)(?P=open))|''' '''(?P.+))''') def current_string(cursor_offset, line): """If inside a string of nonzero length, return the string (excluding quotes) Weaker than bpython.Repl's current_string, because that checks that a string is a string based on previous lines in the buffer.""" for m in current_string_re.finditer(line): i = 3 if m.group(3) else 4 if m.start(i) <= cursor_offset and m.end(i) >= cursor_offset: return LinePart(m.start(i), m.end(i), m.group(i)) return None current_object_re = LazyReCompile(r'([\w_][\w0-9_]*)[.]') def current_object(cursor_offset, line): """If in attribute completion, the object on which attribute should be looked up.""" match = current_word(cursor_offset, line) if match is None: return None start, end, word = match matches = current_object_re.finditer(word) s = '' for m in matches: if m.end(1) + start < cursor_offset: if s: s += '.' s += m.group(1) if not s: return None return LinePart(start, start + len(s), s) current_object_attribute_re = LazyReCompile(r'([\w_][\w0-9_]*)[.]?') def current_object_attribute(cursor_offset, line): """If in attribute completion, the attribute being completed""" # TODO replace with more general current_expression_attribute match = current_word(cursor_offset, line) if match is None: return None start, end, word = match matches = current_object_attribute_re.finditer(word) next(matches) for m in matches: if (m.start(1) + start <= cursor_offset and m.end(1) + start >= cursor_offset): return LinePart(m.start(1) + start, m.end(1) + start, m.group(1)) return None current_from_import_from_re = LazyReCompile( r'from ([\w0-9_.]*)(?:\s+import\s+([\w0-9_]+[,]?\s*)+)*') def current_from_import_from(cursor_offset, line): """If in from import completion, the word after from returns None if cursor not in or just after one of the two interesting parts of an import: from (module) import (name1, name2) """ # TODO allow for as's tokens = line.split() if not ('from' in tokens or 'import' in tokens): return None matches = current_from_import_from_re.finditer(line) for m in matches: if ((m.start(1) < cursor_offset and m.end(1) >= cursor_offset) or (m.start(2) < cursor_offset and m.end(2) >= cursor_offset)): return LinePart(m.start(1), m.end(1), m.group(1)) return None current_from_import_import_re_1 = LazyReCompile(r'from\s([\w0-9_.]*)\s+import') current_from_import_import_re_2 = LazyReCompile(r'([\w0-9_]+)') current_from_import_import_re_3 = LazyReCompile(r'[,][ ]([\w0-9_]*)') def current_from_import_import(cursor_offset, line): """If in from import completion, the word after import being completed returns None if cursor not in or just after one of these words """ baseline = current_from_import_import_re_1.search(line) if baseline is None: return None match1 = current_from_import_import_re_2.search(line[baseline.end():]) if match1 is None: return None matches = current_from_import_import_re_3.finditer(line[baseline.end():]) for m in chain((match1, ), matches): start = baseline.end() + m.start(1) end = baseline.end() + m.end(1) if start < cursor_offset and end >= cursor_offset: return LinePart(start, end, m.group(1)) return None current_import_re_1 = LazyReCompile(r'import') current_import_re_2 = LazyReCompile(r'([\w0-9_.]+)') current_import_re_3 = LazyReCompile(r'[,][ ]([\w0-9_.]*)') def current_import(cursor_offset, line): # TODO allow for multiple as's baseline = current_import_re_1.search(line) if baseline is None: return None match1 = current_import_re_2.search(line[baseline.end():]) if match1 is None: return None matches = current_import_re_3.finditer(line[baseline.end():]) for m in chain((match1, ), matches): start = baseline.end() + m.start(1) end = baseline.end() + m.end(1) if start < cursor_offset and end >= cursor_offset: return LinePart(start, end, m.group(1)) current_method_definition_name_re = LazyReCompile("def\s+([a-zA-Z_][\w]*)") def current_method_definition_name(cursor_offset, line): """The name of a method being defined""" matches = current_method_definition_name_re.finditer(line) for m in matches: if (m.start(1) <= cursor_offset and m.end(1) >= cursor_offset): return LinePart(m.start(1), m.end(1), m.group(1)) return None current_single_word_re = LazyReCompile(r"(?= cursor_offset: return LinePart(m.start(1), m.end(1), m.group(1)) return None def current_dotted_attribute(cursor_offset, line): """The dotted attribute-object pair before the cursor""" match = current_word(cursor_offset, line) if match is None: return None start, end, word = match if '.' in word[1:]: return LinePart(start, end, word) current_expression_attribute_re = LazyReCompile( r'[.]\s*((?:[\w_][\w0-9_]*)|(?:))') def current_expression_attribute(cursor_offset, line): """If after a dot, the attribute being completed""" # TODO replace with more general current_expression_attribute matches = current_expression_attribute_re.finditer(line) for m in matches: if (m.start(1) <= cursor_offset and m.end(1) >= cursor_offset): return LinePart(m.start(1), m.end(1), m.group(1)) return None
X Tutup