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):
move = DelMove(self, p1, p2)
self.add_to_stack(move, act)
for w in self.windows:
w.region_added(p1, lines)
for name in self.highlights:
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):
move = AddMove(self, p1, lines)
self.add_to_stack(move, act)
for w in self.windows:
w.region_removed(p1, p2)
for name in self.highlights:
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
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):
run_ = 0
def interaction(self, frame, t):
"""Another Python debugger."""
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
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):
if self.run_:
self.run_ = 0
self.set_trace() # start tracing
else:
# arrived at breakpoint
name = frame.f_code.co_name or "<unknown>"
filename = self.canonic(frame.f_code.co_filename)
print "break at", filename, frame.f_lineno, "in", name
print "continue..."
sys.stdin.readline()
self.set_continue() # continue to next breakpoint
# Simplified interface
def run(statement, globals=None, locals=None):
Edb().run(statement, globals, locals)
def runeval(expression, globals=None, locals=None):
return Edb().runeval(expression, globals, locals)
def runctx(statement, globals, locals):
run(statement, globals, locals)
def runcall(*args, **kwds):
return Edb().runcall(*args, **kwds)
def set_trace():
Edb().set_trace(sys._getframe().f_back)
def user_return(self, frame, value):
name = frame.f_code.co_name or "<unknown>"
print "return from", name, value
print "continue..."
sys.stdin.readline()
self.set_continue() # continue
def user_exception(self, frame, exception):
name = frame.f_code.co_name or "<unknown>"
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_)
# Post-Mortem interface
def post_mortem(t):
p = Edb()
p.reset()
while t.tb_next is not None:
t = t.tb_next
p.interaction(t.tb_frame, t)
def pm():
post_mortem(sys.last_traceback)
def main():
if not sys.argv[1:]:
print "usage: edb.py scriptfile [arg] ..."
sys.exit(2)
mainpyfile = sys.argv[1] # Get script filename
mainpyfile = sys.argv[1]
if not os.path.exists(mainpyfile):
print 'Error:', mainpyfile, 'does not exist'
sys.exit(1)
@ -79,18 +72,17 @@ def main():
print "The program finished and will be restarted"
except SystemExit:
# 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]
except:
traceback.print_exc()
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]
while t.tb_next is not None:
t = t.tb_next
edb.interaction(t.tb_frame,t)
print "Post mortem debugger finished. The "+mainpyfile+" will be restarted"
# When invoked as main program, invoke the debugger on a script
if __name__=='__main__':
main()

View File

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

View File

@ -165,20 +165,20 @@ class Dir(mode.Fundamental):
modename = 'Dir'
grammar = DirGrammar()
colors = {
'dir_blk.start': ('cyan', 'default', 'bold'),
'dir_blk.name': ('cyan', 'default', 'bold'),
'dir_chr.start': ('yellow', 'default', 'bold'),
'dir_chr.name': ('yellow', 'default', 'bold'),
'dir_dir.start': ('blue', 'default', 'bold'),
'dir_dir.dir_name': ('blue', 'default', 'bold'),
'dir_lnk.start': ('green', 'default', 'bold'),
'dir_lnk.name': ('green', 'default', 'bold'),
'dir_fifo.start': ('red', 'default', 'bold'),
'dir_fifo.name': ('red', 'default', 'bold'),
'dir_sock.start': ('red', 'default', 'bold'),
'dir_sock.name': ('red', 'default', 'bold'),
'dir_unk.start': ('magenta', 'default', 'bold'),
'dir_unk.name': ('magenta', 'default', 'bold'),
'dir_blk.start': ('cyan', 'default', 'bold'),
'dir_blk.dir_name': ('cyan', 'default', 'bold'),
'dir_chr.start': ('yellow', 'default', 'bold'),
'dir_chr.dir_name': ('yellow', 'default', 'bold'),
'dir_dir.start': ('blue', 'default', 'bold'),
'dir_dir.dir_name': ('blue', 'default', 'bold'),
'dir_lnk.start': ('green', 'default', 'bold'),
'dir_lnk.dir_name': ('green', 'default', 'bold'),
'dir_fifo.start': ('red', 'default', 'bold'),
'dir_fifo.dir_name': ('red', 'default', 'bold'),
'dir_sock.start': ('red', 'default', 'bold'),
'dir_sock.dir_name': ('red', 'default', 'bold'),
'dir_unk.start': ('magenta', 'default', 'bold'),
'dir_unk.dir_name': ('magenta', 'default', 'bold'),
'dir_perm.perm_setid': ('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 color, completer, default, mode, method, regex, tab
import color, completer, context, default, mode, method, regex, tab
from point import Point
from lex import Grammar, PatternRule, RegionRule, OverridePatternRule
@ -173,53 +173,6 @@ class PythonTabber(tab.StackTabber):
self._append(token.string, currlvl + w)
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):
'''Check the syntax of the current python file'''
def _execute(self, w, **vargs):
@ -318,35 +271,151 @@ class PythonDictCleanup(method.Method):
class PythonInsertTripleSquotes(method.Method):
'''Insert a triple-quoted string using single-quotes'''
_q = "'''"
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):
w.backward()
class PythonInsertTripleDquotes(method.Method):
class PythonInsertTripleDquotes(PythonInsertTripleSquotes):
'''Insert a triple-quoted string using double-quotes'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor('""""""')
for i in range(0, 3):
w.backward()
_q = '"""'
class PythonInitNames(method.Method):
'''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):
def _get_dict(self, w):
return w.buffer.method.old_window.mode.context.get_names()
def get_candidates(self, s, w=None):
old_window = w.buffer.method.old_window
names = []
names.extend(old_window.mode.get_classes())
names.extend(old_window.mode.get_functions())
return [n for n in names if n.startswith(s)]
return [n for n in self._get_dict(w) if n.startswith(s)]
class PythonFunctionCompleter(PythonNameCompleter):
def _get_dict(self, w):
return w.buffer.method.old_window.mode.context.get_functions()
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):
modename = 'Python'
@ -382,6 +451,20 @@ class Python(mode.Fundamental):
"pythonfunction": PythonFunctionCompleter(),
"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):
mode.Fundamental.__init__(self, w)
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-insert-triple-squotes', ('C-c M-\'',))
self.add_bindings('python-insert-triple-dquotes', ('C-c M-"',))
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
self.context = PythonContext(self)
install = Python.install