diff --git a/mode/consolemini.py b/mode/consolemini.py index 4f872b3..e143811 100644 --- a/mode/consolemini.py +++ b/mode/consolemini.py @@ -1,10 +1,12 @@ -import code, string, StringIO, sys, traceback -import color, completer, method, mode +import code, re, string, StringIO, sys, traceback +import color, completer, lex, method, mode from lex import Grammar, PatternRule +from mode.python import PythonGrammar from point import Point class ConsoleMini(mode.Fundamental): modename = 'ConsoleMini' + grammar = PythonGrammar def __init__(self, w): mode.Fundamental.__init__(self, w) self.bindings = {} @@ -46,7 +48,7 @@ class ConsoleMini(mode.Fundamental): self.add_action_and_bindings(ConsoleCancel(), ('C-]',)) self.add_action_and_bindings(ConsoleHistoryPrev(), ('C-p',)) self.add_action_and_bindings(ConsoleHistoryNext(), ('C-n',)) - #self.add_action_and_bindings(ConsoleTab(), ('TAB',)) + self.add_action_and_bindings(ConsoleTab(), ('TAB',)) for c in string.letters + string.digits + string.punctuation: self.add_binding('insert-string-%s' % c, c) @@ -134,50 +136,69 @@ class ConsoleHistoryNext(method.Method): w.mode.hindex += 1 w.buffer.set_data(w.mode.history[w.mode.hindex]) -#class ConsoleTab(method.Method): -# def execute(self, w, **vargs): -# a = w.application -# s = w.buffer.make_string() -# -# if '"' in s or "'" in s or "(" in s or ")" in s or "[" in s or "]" in s: -# return -# -# parts = s.split(".") -# if len(parts) == 0: -# return -# -# v = a.globals() -# v.update(a.locals()) -# obj = None -# for part in parts[:-1]: -# if obj is None: -# if part in v: -# obj = v[part] -# else: -# return -# else: -# if hasattr(obj, part): -# obj = getattr(obj, part) -# else: -# return -# -# if obj is None: -# pool = v.keys() -# else: -# pool = dir(obj) -# candidates = [x for x in pool if x.startswith(parts[-1])] -# -# if len(candidates) == 0: -# return -# -# common = completer.find_common_string(candidates) -# s2 = '.'.join(parts[:-1]) + '.' + common -# -# w.buffer.set_data(s2) -# -# if len(candidates) > 1: -# if not a.has_buffer_name('*Console*'): -# a.add_buffer(buffer.ConsoleBuffer()) -# b = a.bufferlist.get_buffer_by_name('*Console*') -# b.insert_string(b.get_buffer_end(), repr(candidates) + '\n', force=True) +class ConsoleTab(method.Method): + def execute(self, w, **vargs): + a = w.application + s = w.buffer.make_string() + l = lex.Lexer(w.mode, PythonGrammar) + + tokens = list(l.lex([s])) + x = w.logical_cursor().x + #raise Exception, repr(x) + + curr_t = None + curr_i = None + for i in range(0, len(tokens)): + t = tokens[i] + if t.x < x and t.end_x() >= x: + curr_i = i + curr_t = t + if curr_t is None: + #raise Exception, 'not found: %r %r' % (x, tokens) + return + + first_t = curr_t + j = curr_i + + names = [curr_t.string] + name_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') + while j >= 1: + j -= 1 + t = tokens[j] + if name_re.match(t.string): + names.insert(0, t.string) + elif t.string == '.': + pass + else: + break + + obj = None + g = globals() + i = 0 + name = None + while i < len(names): + name = names[i] + if obj is None: + if name in w.mode.locals: + obj = w.mode.locals[name] + elif name in w.mode.globals: + obj = w.mode.globals[name] + else: + break + else: + if hasattr(obj, name): + obj = getattr(obj, name) + else: + break + i += 1 + if obj is not None and i == len(names) - 1: + newnames = dir(obj) + candidates = [x for x in newnames if x.startswith(name)] + if len(candidates) > 1: + s = completer.find_common_string(candidates)[len(name):] + w.insert_string_at_cursor(s) + elif len(candidates) == 1: + s = candidates[0][len(name):] + w.insert_string_at_cursor(s) + install = ConsoleMini.install