pmacs3/method/__init__.py

856 lines
28 KiB
Python

import os, commands, re, sets, tempfile
from subprocess import Popen, PIPE, STDOUT
import buffer, default, dirutil, regex, util, window
from point import Point
DATATYPES = {
"path": None,
"buffer": None,
"method": None,
"command": None,
"shell": None,
"shellcommand": None,
}
class MethodError(Exception):
pass
class Argument(object):
def __init__(self, name, type=type(""), datatype=None, prompt=None, help="",
default=default.none, load_default=False):
self.name = name
self.type = type
self.datatype = datatype
if prompt is None:
self.prompt = "%s: " % (name)
else:
self.prompt = prompt
self.help = help
self.load_default = load_default
self.default = default
def coerce_to_type(self, value):
if self.type == type(0):
try:
return int(value, 0)
except:
raise Exception, "expected int; got %s" % (repr(value))
else:
return value
def ask_for_value(self, method, w, **vargs):
app = w.application
assert app.mini_buffer_is_open() is False, "Recursive minibuffer antics"
vargs2 = vargs.copy()
assert callable(self.default), "default value func must be callable"
if self.load_default:
d = None
starting_value = self.default(w)
else:
d = self.default(w)
starting_value = None
def return_value(v):
if d is not None and v == "":
v = d
vargs2[self.name] = self.coerce_to_type(v)
app.close_mini_buffer()
method.execute(w, **vargs2)
tabber = DATATYPES.get(self.datatype, None)
if d is not None:
p = self.prompt + "(%s) " % (d)
else:
p = self.prompt
app.open_mini_buffer(p, return_value, method, tabber)
if starting_value:
app.mini_buffer.set_data(starting_value)
class Method(object):
_is_method = True
args = []
def __init__(self):
self.name = self._name()
self.help = self.__doc__
def _name(cls):
s = cls.__name__
s2 = s[0].lower()
for c in s[1:]:
if c.isupper():
s2 += '-' + c.lower()
elif c == '_':
s2 += '-'
else:
s2 += c
return s2
_name = classmethod(_name)
def _pre_execute(self, w, **vargs):
pass
def execute(self, w, **vargs):
try:
self._pre_execute(w, **vargs)
except MethodError, e:
w.set_error(str(e))
return
for arg in self.args:
if arg.name not in vargs:
self.old_window = w
arg.ask_for_value(self, w, **vargs)
return
self._execute(w, **vargs)
def _execute(self, w, **vargs):
raise Exception, "Unimplemented Method: %s %r" % (self.name, vargs)
class RelexBuffer(Method):
'''Relex the buffer; this resets syntax highlighting'''
def _execute(self, w, **vargs):
h = w.get_highlighter()
if h is None:
w.set_error("No lexer for buffer.")
else:
h.highlight(w.buffer.lines)
w.set_error("Buffer relexed.")
class ToggleWindow(Method):
'''Move between visible windows'''
def _execute(self, w, **vargs):
w.application.toggle_window()
# complex text maniuplation
class TransposeWords(Method):
'''Switch the place of the two words nearest the cursor'''
pass
# you wanna quit right?
class Exit(Method):
'''Exit the program, unless there are unsaved changes'''
def _execute(self, w, **vargs):
a = w.application
assert a.mini_buffer_is_open() is False, "Recursive minibuffer antics"
changed = False
for b in w.application.bufferlist.buffers:
changed = b.changed()
if changed:
break
if not changed:
w.application.exit()
return
else:
self._old_window = w
self._prompt = "There are buffers with unsaved changes; exit anyway? "
a.open_mini_buffer(self._prompt, self._callback)
def _callback(self, v):
a = self._old_window.application
if v == 'yes':
a.exit()
a.close_mini_buffer()
if v == 'no':
return
a.open_mini_buffer(self._prompt, self._callback)
a.set_error('Please type "yes" or "no"')
# insert text
class InsertString(Method):
_is_method = False
def __init__(self, s):
self.name = "insert-string-%s" % s
self.args = []
self.help = "Insert %r into the current buffer." % s
self.string = s
def _execute(self, w, **vargs):
w.insert_string_at_cursor(self.string)
class OverwriteChar(Method):
_is_method = False
def __init__(self, c):
self.name = 'overwrite-char-%s' % c
self.args = []
self.help = "Overwrite %r into the current buffer." % c
self.char = c
def _execute(self, w, **vargs):
w.overwrite_char_at_cursor(self.char)
# killing/copying/etc.
class Kill(Method):
'''Kill the contents of the current line'''
def _execute(self, w, **vargs):
w.kill_line()
class KillRegion(Method):
'''Kill the region between the mark and the cursor'''
def _execute(self, w, **vargs):
w.kill_region()
w.set_error("Region killed by %s" % self.name)
class Copy(Method):
'''Copy the contents of the current line'''
def _execute(self, w, **vargs):
w.copy_line()
class CopyRegion(Method):
'''Copy the region between the mark and the cursor'''
def _execute(self, w, **vargs):
w.copy_region()
w.set_active_point(w.mark)
w.set_error("Region copied")
class Yank(Method):
'''Paste the top item in the kill ring into the buffer'''
def _execute(self, w, **vargs):
if w.application.has_kill():
w.yank()
else:
w.set_error("Kill ring is empty")
class ShowKill(Method):
'''Display the top item in the kill ring'''
def _execute(self, w, **vargs):
if w.application.has_kill():
s = w.application.get_kill()
x = w.application.x
if len(s) > x - 40:
s = s[:x - 40] + "..."
w.set_error("Kill ring contains %r" % s)
else:
w.set_error("Kill ring is empty")
class PopKill(Method):
'''Pop the top item in the kill ring off'''
def _execute(self, w, **vargs):
if w.application.has_kill():
s = w.pop_kill()
x = w.application.x
if len(s) > x - 40:
s = s[:x - 40] + "..."
w.set_error("Removed %r from Kill ring" % s)
else:
w.set_error("Kill ring is empty")
# delete
class DeleteLeft(Method):
'''Delete the character to the left of the cursor'''
def _execute(self, w, **vargs):
(x, y) = w.logical_cursor().xy()
line = w.buffer.lines[y]
tabwidth = w.mode.tabwidth
if x >= tabwidth and x % tabwidth == 0 and line[0:x].isspace():
w.kill(Point(x - tabwidth, y), Point(x, y))
else:
w.left_delete()
class DeleteRight(Method):
'''Delete the character under the cursor'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
line = w.buffer.lines[cursor.y]
if len(line[cursor.x:]) >= 4 and line[:cursor.x + 4].isspace():
w.kill(Point(cursor.x, cursor.y),
Point(cursor.x + 4, cursor.y))
else:
w.right_delete()
class DeleteLeftWord(Method):
'''Delete the from the cursor left to the end of the word'''
def _execute(self, w, **vargs):
w.kill_left_word()
class DeleteRightWord(Method):
'''Delete the from under cursor right to the end of the word'''
def _execute(self, w, **vargs):
w.kill_right_word()
class DeleteLeftWhitespace(Method):
'''Delete all contiguous of whitespace left of the cursor'''
def _execute(self, w, **vargs):
c = w.logical_cursor()
p = c
l = w.point_left(p)
if l is None:
return
while l is not None and w.point_char(l) in (' ', '\n'):
p = l
l = w.point_left(p)
if p < c:
w.kill(p, c)
class DeleteRightWhitespace(Method):
'''Delete all contiguous of whitespace under and right of the cursor'''
def _execute(self, w, **vargs):
c = w.logical_cursor()
p = c
while w.point_char(p) in (' ', '\n'):
r = w.point_right(p)
if r is None:
break
p = r
if p > c:
w.kill(c, p)
class DeleteLeftSpace(Method):
'''Delete all contiguous spaces left of the cursor'''
def _execute(self, w, **vargs):
c = w.logical_cursor()
p = c
l = w.point_left(p)
if l is None:
return
while l is not None and w.point_char(l) == ' ':
p = l
l = w.point_left(p)
if p < c:
w.kill(p, c)
class DeleteRightSpace(Method):
'''Delete all contiguous spaces under and right of the cursor'''
def _execute(self, w, **vargs):
c = w.logical_cursor()
p = c
while w.point_char(p) == ' ':
r = w.point_right(p)
if r is None:
break
p = r
if p > c:
w.kill(c, p)
class MetaX(Method):
'''Invoke commands by name'''
args = [Argument('method', datatype="method", prompt="M-x ")]
def _execute(self, w, **vargs):
name = vargs['method']
if name in w.application.methods:
w.application.methods[name].execute(w)
else:
w.set_error('no method named %r found' % name)
class ToggleMargins(Method):
'''Show or hide column margins'''
def _execute(self, w, **vargs):
w.margins_visible = not w.margins_visible
class CenterView(Method):
'''Move view to center on cursor'''
def _execute(self, w, **vargs):
w.center_view()
class SetMark(Method):
'''Set the mark to the current cursor location'''
def _execute(self, w, **vargs):
w.set_mark()
class SwitchMark(Method):
'''Switch the mark and the cursor locations'''
def _execute(self, w, **vargs):
w.switch_mark()
# insertion methods
class InsertNewline(Method):
'''Insert newline into buffer at the cursor'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor('\n')
class InsertSpace(Method):
'''Insert space into buffer at the cursor'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor(' ')
class InsertSquotes(Method):
'''Insert a pair of single-quotes into the buffer'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor("''")
w.backward()
class InsertDquotes(Method):
'''Insert a pair of double-quotes into the buffer'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor('""')
w.backward()
class InsertEscapedSquote(Method):
'''Insert an escaped single-quote'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor("\\'")
class InsertEscapedDquote(Method):
'''Insert an escaped double-quote'''
def _execute(self, w, **vargs):
w.insert_string_at_cursor('\\"')
class InsertTab(Method):
'''Insert tab into buffer, or tabbify line, depending on mode'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if w.mode.tabber:
i = w.mode.tabber.get_level(cursor.y)
else:
i = None
if i is None:
w.insert_string_at_cursor(' ' * w.mode.tabwidth)
else:
j = w.buffer.count_leading_whitespace(cursor.y)
if i != j:
KillWhitespace().execute(w)
w.insert_string(Point(0, cursor.y), ' ' * i)
else:
w.goto(Point(j, cursor.y))
class KillWhitespace(Method):
'''Delete leading whitespace on current line'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
i = w.buffer.count_leading_whitespace(cursor.y)
if i > 0:
w.kill(Point(0, cursor.y), Point(i, cursor.y))
# tabification
class TabBuffer(Method):
'''Tabbify every line in the current buffer'''
def _execute(self, w, **vargs):
y = w.logical_cursor().y
it = InsertTab()
for i in range(0, len(w.buffer.lines)):
w.goto_line(i)
it.execute(w)
w.goto_line(y)
class GetIndentionLevel(Method):
'''Calculate the indention level for this line'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if not w.mode.tabber:
w.set_error('No tabber available')
return
else:
i = w.mode.tabber.get_level(cursor.y)
w.set_error('Indention level: %r' % i)
# commenting
class CommentRegion(Method):
'''Prepend a comment to every line in the current buffer'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if cursor < w.mark:
p1 = cursor
p2 = w.mark
elif w.mark < cursor:
p1 = w.mark
p2 = cursor
else:
w.input_line = "Empty kill region"
return
for y in range(p1.y, p2.y):
w.buffer.insert_string(Point(0, y), "#")
class UncommentRegion(Method):
'''Remove a comment from every line in the current buffer'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if cursor < w.mark:
p1 = cursor
p2 = w.mark
elif w.mark < cursor:
p1 = w.mark
p2 = cursor
else:
w.input_line = "Empty kill region"
return
for y in range(p1.y, p2.y):
if w.buffer.lines[y].startswith("#"):
w.buffer.delete(Point(0, y), Point(1, y))
# wrapping/justifying/etc
class WrapLine(Method):
limit = 80
space_re = re.compile(' +')
def _token_len(self, tokens):
l = 0
for t in tokens:
l += len(t)
return l
def _find_line_bounds(self, tokens, x, y):
if len(tokens[0]) > self.limit:
i = 1
else:
i = 0
l = self._token_len(tokens[:i+1])
while i < len(tokens) and l <= self.limit:
i += 1
l = self._token_len(tokens[:i+1])
while i > 1 and tokens and tokens[i-1].isspace():
token = tokens.pop(i-1)
l -= len(token)
if x > l:
x -= len(token)
i -= 1
return i, x, y
def _clear_preceeding_spaces(self, tokens, x, y):
while tokens and self.space_re.match(tokens[0]):
if x > 0:
x = max(0, x - len(tokens[0]))
del tokens[0]
return x, y
def _wrap_line(self, line, x, y):
tokens = re.findall('[^ ]+| +', line)
if self._token_len(tokens) <= self.limit:
return None, None, None
lines = []
while tokens and self._token_len(tokens) > self.limit:
i, x, y = self._find_line_bounds(tokens, x, y)
s = ''.join(tokens[:i])
lines.append(s)
if x > len(s):
y += 1
x -= len(s)
del tokens[:i]
x, y = self._clear_preceeding_spaces(tokens, x, y)
if tokens:
lines.append(''.join(tokens) + ' ')
return lines, x, y
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
x, y = cursor.xy()
lines, x, y = self._wrap_line(w.buffer.lines[y], x, y)
if lines is None:
return
p1 = Point(0, cursor.y)
p2 = Point(len(w.buffer.lines[cursor.y]), cursor.y)
w.buffer.delete(p1, p2)
p3 = Point(0, cursor.y)
w.buffer.insert_lines(p3, lines)
w.goto(Point(x, y))
class WrapParagraph(Method):
limit = 80
valid_re = re.compile('^( *)([^ ].*)$')
empty_re = regex.whitespace
prefix_re = None
def _execute(self, w, **vargs):
# we will store the start of our paragaph in p1, and also the original
# cursor position.
p1 = oldc = w.logical_cursor()
cur_offset = 0
m = self.valid_re.match(w.buffer.lines[p1.y])
if not m:
# the line was empty, so return
return
elif m.group(1):
# the line had leading whitespace, so return
return
# see if we are starting in the middle of the paragraph; if so, then
# let's find the actual begining, and update p1 accordingly.
i = p1.y
if i > 1 and w.buffer.lines[i] and not w.buffer.lines[i].startswith(' '):
while i > 1 and w.buffer.lines[i - 1] and not w.buffer.lines[i - 1].startswith(' '):
i -= 1
p1 = Point(0, i)
# get the first line; strip it, and put it in our new lines list.
s1 = w.buffer.lines[p1.y][p1.x:]
s2 = s1.rstrip()
if p1.y <= oldc.y:
cur_offset += len(s1) - len(s2)
lines = [s2]
# ok, so now let's move forward and find the end of the paragraph.
i = p1.y + 1
while i < len(w.buffer.lines) and w.buffer.lines[i] and not w.buffer.lines[i].startswith(' '):
s1 = w.buffer.lines[i]
s2 = s1.rstrip()
if oldc.y == i:
# once we've adjusted all our previous lines, adjust our
# stored cursor to keep it's X and Y in sync (set Y to the line
# the paragraph started on, increase X by the previous lines
# plus added spaces minus removed whitespace.
x = p1.x + oldc.x + sum([len(x) + 1 for x in lines]) - cur_offset
oldc = Point(x, p1.y)
elif i < oldc.y:
cur_offset += len(s1) - len(s2)
lines.append(s2)
i += 1
# stringify our paragraph
s = " ".join(lines)
# ok, so now we need to find the line breaks
newlines = []
while s:
# if we have less than the limit left, add it and we're done!
if len(s) < self.limit:
newlines.append(s)
break
# look for the rightmost space within our bounds
j = s.rfind(' ', 0, self.limit)
# if we failed to find one, look for the leftmost space
if j == -1:
j = s.find(' ')
# if we failed to find any, use the whole rest of the paragraph
if j == -1:
j = len(s)
# add the next chunk we found and adjust the paragraph
newlines.append(s[:j])
s = s[j + 1:]
# translate our cursor according to the line breaks we just did.
(x, y) = oldc.xy()
k = 0
while k < len(newlines) - 1 and x > len(newlines[k]):
x = x - len(newlines[k]) - 1
y += 1
k += 1
# kill the old paragraph region, insert the new, and goto the new cursor
w.kill(p1, Point(len(w.buffer.lines[i-1]), i-1))
w.insert_lines(p1, newlines)
w.goto(Point(x, y))
class JustifyRight(Method):
'''Justify text with the previous line right from the cursor by whitespace'''
def _execute(self, w, **vargs):
DeleteLeftSpace().execute(w)
cursor = w.logical_cursor()
prev_line = w.buffer.lines[cursor.y-1]
this_line = w.buffer.lines[cursor.y]
if cursor.y <= 0:
return
if cursor.x >= len(prev_line):
return
i = cursor.x
while prev_line[i] != ' ':
i += 1
if i >= len(prev_line):
return
while prev_line[i] == ' ':
i += 1
if i >= len(prev_line):
return
s = ' ' * (i - cursor.x)
w.insert_string_at_cursor(s)
class JustifyLeft(Method):
'''Justify text with the previous line left from the cursor by whitespace'''
def _execute(self, w, **vargs):
DeleteRightWhitespace().execute(w)
cursor = w.logical_cursor()
prev_line = w.buffer.lines[cursor.y-1]
this_line = w.buffer.lines[cursor.y]
if cursor.y <= 0:
return
if cursor.x <= 0:
return
i = cursor.x
while i >= len(prev_line):
i -= 1
if i <= 0:
return
if this_line[i] != ' ':
return
while prev_line[i] != ' ':
i -= 1
if i <= 0:
return
if this_line[i] != ' ':
return
while prev_line[i] == ' ':
i -= 1
if i >= len(prev_line):
return
if this_line[i] != ' ':
return
w.buffer.delete(Point(i, cursor.y), cursor)
# undo/redo
class Undo(Method):
'''Undo last action'''
def _execute(self, w, **vargs):
try:
w.undo()
except Exception, e:
w.set_error("%s" % (e))
class Redo(Method):
'''Redo last undone action'''
def _execute(self, w, **vargs):
try:
w.redo()
except Exception, e:
w.set_error("%s" % (e))
class UnindentBlock(Method):
'''Prepend 4 spaces to each line in region'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if cursor < w.mark:
p1 = cursor
p2 = w.mark
elif w.mark < cursor:
p1 = w.mark
p2 = cursor
else:
w.input_line = "Empty kill region"
return
lines = w.buffer.lines[p1.y:p2.y]
for i in range(0, len(lines)):
if lines[i].startswith(' '):
lines[i] = lines[i][4:]
w.buffer.delete(Point(0, p1.y), Point(0, p2.y))
w.buffer.insert_string(Point(0, p1.y), '\n'.join(lines) + '\n')
class IndentBlock(Method):
'''Add 4 spaces to each line in region'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if cursor < w.mark:
p1 = cursor
p2 = w.mark
elif w.mark < cursor:
p1 = w.mark
p2 = cursor
else:
w.input_line = "Empty kill region"
return
lines = w.buffer.lines[p1.y:p2.y]
tstr = ' ' * w.mode.tabwidth
for i in range(0, len(lines)):
lines[i] = tstr + lines[i]
w.buffer.delete(Point(0, p1.y), Point(0, p2.y))
w.buffer.insert_string(Point(0, p1.y), '\n'.join(lines) + '\n')
class FileDiff(Method):
'''diff the buffer's contents with the given file'''
args = [Argument("path", type=type(""), prompt="Filename: ", datatype='path')]
def _execute(self, w, **vargs):
cmd = ("/usr/bin/diff", '-u', '-', vargs['path'])
pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
pid = pipe.pid
indata = w.buffer.make_string()
pipe.stdin.write(indata)
pipe.stdin.close()
outdata = pipe.stdout.read()
errdata = pipe.stderr.read()
status = pipe.wait() >> 8
if status == 0:
w.set_error("No difference found")
elif status == 1:
w.application.data_buffer("*Diff*", outdata, switch_to=True, modename='diff')
w.set_error("Differences were found")
else:
w.application.data_buffer("*Diff*", errdata, switch_to=True)
w.set_error("There was an error: %d exited with status %s" % (pid, status))
class SetMode(Method):
'''Set the mode of the current buffer'''
args = [Argument('mode', datatype='mode', prompt="Enter new mode: ")]
def _execute(self, w, **vargs):
mode_name = vargs['mode']
m = w.application.modes[mode_name](w)
w.set_mode(m)
w.set_error('Set mode to %r' % (mode_name))
class Cancel(Method):
'''Cancel command in-progress, and return to the main buffer'''
def execute(self, w, **vargs):
w.application.close_mini_buffer()
w.set_error('Cancel')
class SplitWindow(Method):
'''Split the main window horizontally into upper and lower windows'''
def execute(self, w, **vargs):
a = w.application
a.add_slot()
if not w.cursor_is_visible():
p = w.first
w.goto(p)
n = len(a.bufferlist.slots)
a.set_error('Window has been split into %d windows!' % n)
class UnsplitWindow(Method):
'''Maximize the current window to fill the screen'''
def execute(self, w, **vargs):
w.application.single_slot()
w.set_error('Window has been unsplit back to one window!')
class CloseTag(Method):
mytag = ')'
def _execute(self, w, **vargs):
# first, de-reference some variables and actually do the insertion
# NOTE: we derence the cursor *before* inserting the character, so it is
# expecected that the cursor variable should be the point the new
# character is on.
(x, y) = w.logical_cursor().xy()
w.insert_string_at_cursor(self.mytag)
app = w.application
buffer = w.buffer
highlighter = buffer.highlights[w.mode.name()]
tokens = highlighter.tokens
# REFACTOR: we have methods in window to do this now
i = 0
while i < len(tokens[y]):
token = tokens[y][i]
if token.x == x and token.string == self.mytag:
break
elif token.x <= x and token.end_x() > x:
return
i += 1
if i >= len(tokens[y]):
return
tag_stack = []
while y >= 0:
while i >= 0 and i < len(tokens[y]):
token = tokens[y][i]
n = token.name
s = token.string
if n in w.mode.closetokens and s in w.mode.closetags:
tag_stack.append(s)
elif n in w.mode.opentokens and s in w.mode.opentags:
if tag_stack[-1] == w.mode.opentags[s]:
del tag_stack[-1]
else:
app.set_error("tag mismatch; got %r expected %r" %
(s, w.mode.closetags[tag_stack[-1]]))
return
if len(tag_stack) == 0:
p = Point(token.x, y)
s = w.buffer.lines[p.y][:p.x+1]
if len(s) > 60:
s = "..." + s[-60:]
msg = 'matches %r' % s
w.set_active_point(p, msg)
return
i -= 1
y -= 1
i = len(tokens[y]) - 1
class CloseParen(CloseTag):
mytag = ')'
class CloseBrace(CloseTag):
mytag = '}'
class CloseBracket(CloseTag):
mytag = ']'
class RegisterSave(Method):
MAX_TXT = 30
MAX_REG = 20
'''Save the top item of the kill stack into the named register'''
args = [Argument('name', datatype="str", prompt="Register name: ")]
def _pre_execute(self, w, **vargs):
if not w.has_kill():
raise MethodError, "No text on the kill stack"
def _execute(self, w, **vargs):
name = vargs['name']
text = w.get_kill()
w.application.registers[name] = text
if len(name) > self.MAX_REG:
name = name[:self.MAX_REG] + '...'
if len(text) > self.MAX_TXT:
text = text[:self.MAX_TXT] + '...'
w.set_error('Saved %r into register %r' % (text, name))
class RegisterRestore(Method):
MAX_TXT = 30
MAX_REG = 18
'''Push the value saved in the named register onto the kill stack'''
args = [Argument('name', datatype="str", prompt="Register name: ")]
def _execute(self, w, **vargs):
name = vargs['name']
if name not in w.application.registers:
w.set_error('Register %r does not exist' % name)
return
text = app.registers[name]
w.push_kill(text)
if len(text) > self.MAX_TXT:
text = text[0:self.MAX_TXT] + '...'
if len(name) > self.MAX_REG:
name = name[0:self.MAX_REG] + '...'
w.set_error('Restored %r from register %r' % (text2, name2))