python-mode improvements; context

--HG--
branch : pmacs2
This commit is contained in:
moculus 2008-05-11 23:40:06 +00:00
parent 5c9608d300
commit e2e6e28c07
6 changed files with 311 additions and 204 deletions

View File

@ -161,17 +161,17 @@ class Buffer(object):
def _region_add(self, p1, p2, lines, act): def _region_add(self, p1, p2, lines, act):
move = DelMove(self, p1, p2) move = DelMove(self, p1, p2)
self.add_to_stack(move, act) self.add_to_stack(move, act)
for w in self.windows:
w.region_added(p1, lines)
for name in self.highlights: for name in self.highlights:
self.highlights[name].relex_add(self.lines, p1.y, p1.x, lines) self.highlights[name].relex_add(self.lines, p1.y, p1.x, lines)
for w in self.windows:
w.region_added(p1, lines)
def _region_del(self, p1, p2, lines, act): def _region_del(self, p1, p2, lines, act):
move = AddMove(self, p1, lines) move = AddMove(self, p1, lines)
self.add_to_stack(move, act) self.add_to_stack(move, act)
for w in self.windows:
w.region_removed(p1, p2)
for name in self.highlights: for name in self.highlights:
self.highlights[name].relex_del(self.lines, p1.y, p1.x, p2.y, p2.x) self.highlights[name].relex_del(self.lines, p1.y, p1.x, p2.y, p2.x)
for w in self.windows:
w.region_removed(p1, p2)
# internal validation # internal validation
def _validate_point(self, p): def _validate_point(self, p):

78
context.py Normal file
View File

@ -0,0 +1,78 @@
class Context(object):
"""This object stores and updates contextual metadata about the buffer."""
def __init__(self, mode):
self.mode = mode
self.names = None
self.namelines = None
def region_added(self, p, newlines):
self.adjust_name_map(p, len(newlines) - 1)
self.rebuild_name_map(p.y, p.y + len(newlines))
def region_removed(self, p1, p2):
self.adjust_name_map(p2, p1.y - p2.y)
self.rebuild_name_map(p1.y, p1.y + 1)
def adjust_name_map(self, p, delta):
if delta == 0:
return
elif delta > 0:
self.namelines.extend([None] * delta)
for i in reversed(range(p.y + 1, len(self.mode.window.buffer.lines))):
self.namelines[i] = self.namelines[i - delta]
for i in range(p.y, p.y + delta):
self.namelines[i] = None
else:
for i in range(p.y + 1, len(self.mode.window.buffer.lines)):
self.namelines[i + delta] = self.namelines[i]
def _init_name_map(self):
self.names = {}
self.namelines = [None] * len(self.mode.window.buffer.lines)
def build_name_map(self):
self._init_name_map()
self._build_name_map(0, len(self.mode.window.buffer.lines), None, None, [])
def rebuild_name_map(self, y1, y2):
for y in range(y1, y2):
name = self.namelines[y]
if name:
if name in self.names:
del self.names[name]
if name in self.classes:
del self.classes[name]
if name in self.functions:
del self.functions[name]
self.namelines[y] = None
if y1 == 0:
self._build_name_map(y1, y2, None, None, [])
else:
last, curr, stack = None, self.namelines[y1 - 1], []
count = 0
if curr:
for part in curr.split('.'):
stack.append([count, part])
count += self.mode.tabwidth
self._build_name_map(y1, y2, last, curr, stack)
def _build_name_map(self, y1, y2, last, curr, stack):
raise Exception, 'unimplemented'
def get_line_name(self, y):
if self.namelines is None:
self.build_name_map()
if y < len(self.namelines):
return self.namelines[y]
else:
return None
def get_names(self):
if self.names is None:
self.build_name_map()
return self.names
def get_name_list(self):
return self._ordered_dict(self.get_names())
def _ordered_dict(self, d):
pairs = [[d[key], key] for key in d]
pairs.sort()
return [x[1] for x in pairs]

104
edb.py
View File

@ -1,66 +1,59 @@
import bdb, os, re, sys, time, traceback #! /usr/bin/env python
class Edb(bdb.Bdb): """Another Python debugger."""
run_ = 0
def interaction(self, frame, t): import os, pdb, sys, time, traceback
__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",
"post_mortem", "help"]
class Edb(pdb.Pdb):
def cmdloop(self, intro=None):
stop = False
while not stop:
sys.stdout.write(self.prompt)
sys.stdout.flush()
line = sys.stdin.readline()
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
def columnize(self, list, displaywidth=80):
pass
def do_help(self, arg):
pass
def print_topics(self, header, cmds, cmdlen, maxcol):
pass pass
def user_call(self, frame, args):
name = frame.f_code.co_name or "<unknown>"
print "call", name, args
sys.stdin.readline()
self.set_continue() # continue
def user_line(self, frame): # Simplified interface
if self.run_: def run(statement, globals=None, locals=None):
self.run_ = 0 Edb().run(statement, globals, locals)
self.set_trace() # start tracing def runeval(expression, globals=None, locals=None):
else: return Edb().runeval(expression, globals, locals)
# arrived at breakpoint def runctx(statement, globals, locals):
name = frame.f_code.co_name or "<unknown>" run(statement, globals, locals)
filename = self.canonic(frame.f_code.co_filename) def runcall(*args, **kwds):
print "break at", filename, frame.f_lineno, "in", name return Edb().runcall(*args, **kwds)
print "continue..." def set_trace():
sys.stdin.readline() Edb().set_trace(sys._getframe().f_back)
self.set_continue() # continue to next breakpoint
def user_return(self, frame, value): # Post-Mortem interface
name = frame.f_code.co_name or "<unknown>" def post_mortem(t):
print "return from", name, value p = Edb()
print "continue..." p.reset()
sys.stdin.readline() while t.tb_next is not None:
self.set_continue() # continue t = t.tb_next
p.interaction(t.tb_frame, t)
def user_exception(self, frame, exception): def pm():
name = frame.f_code.co_name or "<unknown>" post_mortem(sys.last_traceback)
print "exception in", name, exception
print "continue..."
sys.stdin.readline()
self.set_continue() # continue
def _runscript(self, filename):
# Start with fresh empty copy of globals and locals and tell the script
# that it's being run as __main__ to avoid scripts being able to access
# the pdb.py namespace.
globals_ = {"__name__" : "__main__"}
locals_ = globals_
# When bdb sets tracing, a number of call and line events happens
# BEFORE debugger even reaches user's code (and the exact sequence of
# events depends on python version). So we take special measures to
# avoid stopping before we reach the main script (see user_line and
# user_call for details).
self._wait_for_mainpyfile = 1
self.mainpyfile = self.canonic(filename)
self._user_requested_quit = 0
statement = 'execfile( "%s")' % filename
self.run(statement, globals=globals_, locals=locals_)
def main(): def main():
if not sys.argv[1:]: if not sys.argv[1:]:
print "usage: edb.py scriptfile [arg] ..." print "usage: edb.py scriptfile [arg] ..."
sys.exit(2) sys.exit(2)
mainpyfile = sys.argv[1] # Get script filename mainpyfile = sys.argv[1]
if not os.path.exists(mainpyfile): if not os.path.exists(mainpyfile):
print 'Error:', mainpyfile, 'does not exist' print 'Error:', mainpyfile, 'does not exist'
sys.exit(1) sys.exit(1)
@ -79,18 +72,17 @@ def main():
print "The program finished and will be restarted" print "The program finished and will be restarted"
except SystemExit: except SystemExit:
# In most cases SystemExit does not warrant a post-mortem session. # In most cases SystemExit does not warrant a post-mortem session.
print "The program exited via sys.exit(). Exit status:", print "The program exited via sys.exit(). Exit status: ",
print sys.exc_info()[1] print sys.exc_info()[1]
except: except:
traceback.print_exc() traceback.print_exc()
print "Uncaught exception. Entering post mortem debugging" print "Uncaught exception. Entering post mortem debugging"
#print "Running 'cont' or 'step' will restart the program" print "Running 'cont' or 'step' will restart the program"
t = sys.exc_info()[2] t = sys.exc_info()[2]
while t.tb_next is not None: while t.tb_next is not None:
t = t.tb_next t = t.tb_next
edb.interaction(t.tb_frame,t) edb.interaction(t.tb_frame,t)
print "Post mortem debugger finished. The "+mainpyfile+" will be restarted" print "Post mortem debugger finished. The "+mainpyfile+" will be restarted"
# When invoked as main program, invoke the debugger on a script
if __name__=='__main__': if __name__=='__main__':
main() main()

View File

@ -86,6 +86,7 @@ class Fundamental(Handler):
grammar = None grammar = None
lexer = None lexer = None
tabber = None tabber = None
context = None
colors = {} colors = {}
config = {} config = {}
actions = [] actions = []
@ -322,9 +323,7 @@ class Fundamental(Handler):
self.window.set_error(err) self.window.set_error(err)
def region_added(self, p, newlines): def region_added(self, p, newlines):
if self.tabber is not None: if self.lexer is not None:
self.tabber.region_added(p, newlines)
if self.lexer:
ydelta = len(newlines) - 1 ydelta = len(newlines) - 1
xdelta = len(newlines[-1]) xdelta = len(newlines[-1])
ghist = {} ghist = {}
@ -345,10 +344,13 @@ class Fundamental(Handler):
ghist.setdefault(name, {}) ghist.setdefault(name, {})
ghist[name][newp] = self.ghist[name][gp] ghist[name][newp] = self.ghist[name][gp]
self.ghist = ghist self.ghist = ghist
def region_removed(self, p1, p2):
if self.tabber is not None: if self.tabber is not None:
self.tabber.region_removed(p1, p2) self.tabber.region_added(p, newlines)
if self.lexer: if self.context is not None:
self.context.region_added(p, newlines)
def region_removed(self, p1, p2):
if self.lexer is not None:
ydelta = p2.y - p1.y ydelta = p2.y - p1.y
xdelta = p2.x - p1.x xdelta = p2.x - p1.x
ghist = {} ghist = {}
@ -371,4 +373,9 @@ class Fundamental(Handler):
ghist.setdefault(name, {}) ghist.setdefault(name, {})
ghist[name][newp] = self.ghist[name][gp] ghist[name][newp] = self.ghist[name][gp]
self.ghist = ghist self.ghist = ghist
if self.tabber is not None:
self.tabber.region_removed(p1, p2)
if self.context is not None:
self.context.region_removed(p1, p2)
install = Fundamental.install install = Fundamental.install

View File

@ -165,20 +165,20 @@ class Dir(mode.Fundamental):
modename = 'Dir' modename = 'Dir'
grammar = DirGrammar() grammar = DirGrammar()
colors = { colors = {
'dir_blk.start': ('cyan', 'default', 'bold'), 'dir_blk.start': ('cyan', 'default', 'bold'),
'dir_blk.name': ('cyan', 'default', 'bold'), 'dir_blk.dir_name': ('cyan', 'default', 'bold'),
'dir_chr.start': ('yellow', 'default', 'bold'), 'dir_chr.start': ('yellow', 'default', 'bold'),
'dir_chr.name': ('yellow', 'default', 'bold'), 'dir_chr.dir_name': ('yellow', 'default', 'bold'),
'dir_dir.start': ('blue', 'default', 'bold'), 'dir_dir.start': ('blue', 'default', 'bold'),
'dir_dir.dir_name': ('blue', 'default', 'bold'), 'dir_dir.dir_name': ('blue', 'default', 'bold'),
'dir_lnk.start': ('green', 'default', 'bold'), 'dir_lnk.start': ('green', 'default', 'bold'),
'dir_lnk.name': ('green', 'default', 'bold'), 'dir_lnk.dir_name': ('green', 'default', 'bold'),
'dir_fifo.start': ('red', 'default', 'bold'), 'dir_fifo.start': ('red', 'default', 'bold'),
'dir_fifo.name': ('red', 'default', 'bold'), 'dir_fifo.dir_name': ('red', 'default', 'bold'),
'dir_sock.start': ('red', 'default', 'bold'), 'dir_sock.start': ('red', 'default', 'bold'),
'dir_sock.name': ('red', 'default', 'bold'), 'dir_sock.dir_name': ('red', 'default', 'bold'),
'dir_unk.start': ('magenta', 'default', 'bold'), 'dir_unk.start': ('magenta', 'default', 'bold'),
'dir_unk.name': ('magenta', 'default', 'bold'), 'dir_unk.dir_name': ('magenta', 'default', 'bold'),
'dir_perm.perm_setid': ('yellow', 'default', 'bold'), 'dir_perm.perm_setid': ('yellow', 'default', 'bold'),
'dir_perm.perm_sticky': ('yellow', 'default', 'bold'), 'dir_perm.perm_sticky': ('yellow', 'default', 'bold'),

View File

@ -1,5 +1,5 @@
import commands, os.path, sets, string, sys, traceback import commands, os.path, sets, string, sys, traceback
import color, completer, default, mode, method, regex, tab import color, completer, context, default, mode, method, regex, tab
from point import Point from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule from lex import Grammar, PatternRule, RegionRule, OverridePatternRule
@ -173,53 +173,6 @@ class PythonTabber(tab.StackTabber):
self._append(token.string, currlvl + w) self._append(token.string, currlvl + w)
return currlvl return currlvl
class PythonInitNames(method.Method):
'''Jump to a function defined in this module'''
def _execute(self, w, **vargs):
w.mode.build_name_map()
w.application.set_error("Initialized name maps")
class PythonGotoName(method.Method):
'''Jump to a class or function defined in this module'''
args = [method.Argument("name", type(""), "pythonname", "Goto Name: ")]
def _execute(self, w, **vargs):
name = vargs['name']
d = {}
d.update(w.mode.get_classes())
d.update(w.mode.get_functions())
if name in d:
w.goto(Point(0, d[name]))
else:
w.application.set_error("Function %r was not found" % name)
class PythonGotoFunction(method.Method):
'''Jump to a function defined in this module'''
args = [method.Argument("name", type(""), "pythonfunction", "Goto Function: ")]
def _execute(self, w, **vargs):
name = vargs['name']
functions = w.mode.get_functions()
if name in functions:
w.goto(Point(0, functions[name]))
else:
w.application.set_error("Function %r was not found" % name)
class PythonGotoClass(method.Method):
'''Jump to a class defined in this module'''
args = [method.Argument("name", type(""), "pythonclass", "Goto Class: ")]
def _execute(self, w, **vargs):
name = vargs['name']
classes = w.mode.get_classes()
if name in classes:
w.goto(Point(0, classes[name]))
else:
w.application.set_error("Class %r was not found" % name)
class PythonListNames(method.Method):
'''Show the user all functions defined in this module'''
def _execute(self, w, **vargs):
names = w.mode.get_function_names()
names.extend(w.mode.get_class_names())
output = '\n'.join(sorted(names)) + "\n"
w.application.data_buffer("*Python-List-Names*", output, switch_to=True)
class PythonCheckSyntax(method.Method): class PythonCheckSyntax(method.Method):
'''Check the syntax of the current python file''' '''Check the syntax of the current python file'''
def _execute(self, w, **vargs): def _execute(self, w, **vargs):
@ -318,35 +271,151 @@ class PythonDictCleanup(method.Method):
class PythonInsertTripleSquotes(method.Method): class PythonInsertTripleSquotes(method.Method):
'''Insert a triple-quoted string using single-quotes''' '''Insert a triple-quoted string using single-quotes'''
_q = "'''"
def _execute(self, w, **vargs): def _execute(self, w, **vargs):
w.insert_string_at_cursor("''''''") w.insert_string_at_cursor('%s%s' % (_q, _q))
for i in range(0, 3): for i in range(0, 3):
w.backward() w.backward()
class PythonInsertTripleDquotes(PythonInsertTripleSquotes):
class PythonInsertTripleDquotes(method.Method):
'''Insert a triple-quoted string using double-quotes''' '''Insert a triple-quoted string using double-quotes'''
def _execute(self, w, **vargs): _q = '"""'
w.insert_string_at_cursor('""""""')
for i in range(0, 3): class PythonInitNames(method.Method):
w.backward() '''Jump to a function defined in this module'''
def _execute(self, w, **vargs):
w.mode.context.build_name_map()
w.application.set_error("Initialized name maps")
class PythonGotoName(method.Method):
'''Jump to a class or function defined in this module'''
args = [method.Argument("name", type(""), "pythonname", "Goto Name: ")]
title = 'Name'
def _get_dict(self, w):
return w.mode.context.get_names()
def _execute(self, w, **vargs):
name = vargs['name']
d = self._get_dict(w)
if name in d:
w.goto(Point(0, d[name]))
else:
w.application.set_error("%r %r was not found" % (title, name))
class PythonGotoFunction(PythonGotoName):
'''Jump to a function defined in this module'''
args = [method.Argument("name", type(""), "pythonfunction", "Goto Function: ")]
title = 'Function'
def _get_dict(self, w):
return w.mode.context.get_functions()
class PythonGotoClass(method.Method):
'''Jump to a class defined in this module'''
args = [method.Argument("name", type(""), "pythonclass", "Goto Class: ")]
title = 'Class'
def _get_dict(self, w):
return w.mode.context.get_classes()
class PythonListNames(method.Method):
'''Show the user all functions defined in this module'''
def _execute(self, w, **vargs):
names = w.mode.context.get_names()
output = '\n'.join(sorted(names)) + "\n"
w.application.data_buffer("*Python-List-Names*", output, switch_to=True)
class PythonFunctionCompleter(completer.Completer):
def get_candidates(self, s, w=None):
old_window = w.buffer.method.old_window
functions = old_window.mode.get_functions()
return [n for n in functions if n.startswith(s)]
class PythonClassCompleter(completer.Completer):
def get_candidates(self, s, w=None):
old_window = w.buffer.method.old_window
classes = old_window.mode.get_classes()
return [n for n in classes if n.startswith(s)]
class PythonNameCompleter(completer.Completer): class PythonNameCompleter(completer.Completer):
def _get_dict(self, w):
return w.buffer.method.old_window.mode.context.get_names()
def get_candidates(self, s, w=None): def get_candidates(self, s, w=None):
old_window = w.buffer.method.old_window return [n for n in self._get_dict(w) if n.startswith(s)]
names = [] class PythonFunctionCompleter(PythonNameCompleter):
names.extend(old_window.mode.get_classes()) def _get_dict(self, w):
names.extend(old_window.mode.get_functions()) return w.buffer.method.old_window.mode.context.get_functions()
return [n for n in names if n.startswith(s)] class PythonClassCompleter(completer.Completer):
def _get_dict(self, w):
return w.buffer.method.old_window.mode.context.get_classes()
class PythonContext(context.Context):
def __init__(self, mode):
self.mode = mode
self.names = None
self.namelines = None
self.classes = None
self.functions = None
# new object methods
def get_functions(self):
if self.functions is None:
self.build_name_map()
return self.functions
def get_classes(self):
if self.classes is None:
self.build_name_map()
return self.classes
def get_function_list(self):
return self._ordered_dict(self.get_functions())
def get_class_list(self):
return self._ordered_dict(self.get_classes())
# overridden object methods
def _init_name_map(self):
self.names = {}
self.classes = {}
self.functions = {}
self.namelines = [None] * len(self.mode.window.buffer.lines)
def _build_name_map(self, y1, y2, last, curr, stack):
blen = len(self.mode.window.buffer.lines)
highlights = self.mode.window.get_highlighter()
i = y1
while i < y2:
g = highlights.tokens[i]
if (len(g) == 1 and g[0].name == 'eol' or
len(g) == 2 and g[0].name == 'null' and g[1].name == 'eol'):
if last is None:
last = i
i += 1
if i == y2 and y2 < blen:
y2 += 1
continue
if g[0].name == 'null':
j, lvl = 1, len(g[0].string)
else:
j, lvl = 0, 0
while stack and lvl <= stack[-1][0]:
stack.pop(-1)
if last is not None:
curr = '.'.join([x[1] for x in stack])
if curr:
for k in range(last, i):
self.namelines[k] = curr
last = None
if len(g[j:]) > 3:
d, found = None, False
if g[j].name == 'python_keyword' and g[j].string == 'class':
d, found = self.classes, True
elif g[j].name == 'python_keyword' and g[j].string == 'def':
d, found = self.functions, True
if found:
stack.append([lvl, g[j+2].string])
curr = '.'.join([x[1] for x in stack])
d[curr] = i
self.names[curr] = i
else:
curr = '.'.join([x[1] for x in stack])
if i == y2 - 1 and curr != self.namelines[i] and y2 < blen:
y2 += 1
if curr:
self.namelines[i] = curr
i += 1
if last is not None and y2 < len(self.namelines):
if self.namelines[y2]:
n = len(self.namelines[y2].split('.'))
curr = '.'.join([x[1] for x in stack[:n]])
if curr:
for k in range(last, y2):
self.namelines[k] = curr
class Python(mode.Fundamental): class Python(mode.Fundamental):
modename = 'Python' modename = 'Python'
@ -382,6 +451,20 @@ class Python(mode.Fundamental):
"pythonfunction": PythonFunctionCompleter(), "pythonfunction": PythonFunctionCompleter(),
"pythonclass": PythonClassCompleter(), "pythonclass": PythonClassCompleter(),
} }
format = "%(flag)s %(bname)-18s (%(mname)s) %(cursor)s/%(mark)s %(perc)s [%(name)s]"
def get_status_names(self):
w = self.window
c = w.logical_cursor()
names = {
'bname': w.buffer.name(),
'mname': self.name(),
'flag': self._get_flag(),
'perc': self._get_perc(),
'cursor': '(%d,%d)' % (c.y + 1, c.x + 1),
'mark': self._get_mark(),
'name': self.context.get_line_name(c.y),
}
return names
def __init__(self, w): def __init__(self, w):
mode.Fundamental.__init__(self, w) mode.Fundamental.__init__(self, w)
self.add_bindings('close-paren', (')',)) self.add_bindings('close-paren', (')',))
@ -394,59 +477,6 @@ class Python(mode.Fundamental):
self.add_bindings('python-dict-cleanup', ('C-c h',)) self.add_bindings('python-dict-cleanup', ('C-c h',))
self.add_bindings('python-insert-triple-squotes', ('C-c M-\'',)) self.add_bindings('python-insert-triple-squotes', ('C-c M-\'',))
self.add_bindings('python-insert-triple-dquotes', ('C-c M-"',)) self.add_bindings('python-insert-triple-dquotes', ('C-c M-"',))
self.context = PythonContext(self)
self.classes = None
self.functions = None
def build_name_map(self):
b = self.window.buffer
scope_stack = []
self.classes = {}
self.functions = {}
for i in range(0, len(b.lines)):
if regex.whitespace.match(b.lines[i]):
continue
m = regex.python_indent.match(b.lines[i])
assert m
lvl = len(m.group(1))
while scope_stack:
if lvl <= scope_stack[-1][0]:
scope_stack.pop(-1)
else:
break
m = regex.python_scope.match(b.lines[i])
if m:
(ws, typ, name) = m.groups()
lvl = len(ws)
if typ == 'class':
#raise Exception, repr(m.groups())
d = self.classes
else:
d = self.functions
if scope_stack:
prefix = '.'.join([x[1] for x in scope_stack])
d['%s.%s' % (prefix, name)] = i
else:
d[name] = i
scope_stack.append((len(ws), name))
def get_functions(self):
if self.functions is None:
self.build_name_map()
return self.functions
def get_function_names(self):
functions = self.get_functions()
pairs = [[functions[key], key] for key in functions]
pairs.sort()
names = [x[1] for x in pairs]
return names
def get_classes(self):
if self.classes is None:
self.build_name_map()
return self.classes
def get_class_names(self):
classes = self.get_classes()
pairs = [[classes[key], key] for key in classes]
pairs.sort()
names = [x[1] for x in pairs]
return names
install = Python.install install = Python.install