10a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport string
20a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Tkinter import *
30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
40a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom idlelib.Delegator import Delegator
50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
60a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<redo>>
70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-y>
80a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Alt-z>
90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<undo>>
110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-z>
120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Control-z>
130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<dump-undo-state>>
150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-backslash>
160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Control-backslash>
170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass UndoDelegator(Delegator):
200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    max_undo = 1000
220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self):
240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        Delegator.__init__(self)
250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.reset_undo()
260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def setdelegate(self, delegate):
280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.delegate is not None:
290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.unbind("<<undo>>")
300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.unbind("<<redo>>")
310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.unbind("<<dump-undo-state>>")
320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        Delegator.setdelegate(self, delegate)
330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if delegate is not None:
340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bind("<<undo>>", self.undo_event)
350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bind("<<redo>>", self.redo_event)
360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bind("<<dump-undo-state>>", self.dump_event)
370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def dump_event(self, event):
390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        from pprint import pprint
400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pprint(self.undolist[:self.pointer])
410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        print "pointer:", self.pointer,
420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        print "saved:", self.saved,
430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        print "can_merge:", self.can_merge,
440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        print "get_saved():", self.get_saved()
450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pprint(self.undolist[self.pointer:])
460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return "break"
470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def reset_undo(self):
490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.was_saved = -1
500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.pointer = 0
510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.undolist = []
520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.undoblock = 0  # or a CommandSequence instance
530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.set_saved(1)
540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def set_saved(self, flag):
560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if flag:
570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.saved = self.pointer
580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.saved = -1
600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.can_merge = False
610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.check_saved()
620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def get_saved(self):
640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.saved == self.pointer
650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    saved_change_hook = None
670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def set_saved_change_hook(self, hook):
690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.saved_change_hook = hook
700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    was_saved = -1
720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def check_saved(self):
740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        is_saved = self.get_saved()
750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if is_saved != self.was_saved:
760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.was_saved = is_saved
770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if self.saved_change_hook:
780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.saved_change_hook()
790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def insert(self, index, chars, tags=None):
810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.addcmd(InsertCommand(index, chars, tags))
820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def delete(self, index1, index2=None):
840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.addcmd(DeleteCommand(index1, index2))
850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Clients should call undo_block_start() and undo_block_stop()
870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # around a sequence of editing cmds to be treated as a unit by
880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # undo & redo.  Nested matching calls are OK, and the inner calls
890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # then act like nops.  OK too if no editing cmds, or only one
900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # editing cmd, is issued in between:  if no cmds, the whole
910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # sequence has no effect; and if only one cmd, that cmd is entered
920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # directly into the undo list, as if undo_block_xxx hadn't been
930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # called.  The intent of all that is to make this scheme easy
940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # to use:  all the client has to worry about is making sure each
950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # _start() call is matched by a _stop() call.
960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo_block_start(self):
980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.undoblock == 0:
990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.undoblock = CommandSequence()
1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.undoblock.bump_depth()
1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo_block_stop(self):
1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.undoblock.bump_depth(-1) == 0:
1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            cmd = self.undoblock
1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.undoblock = 0
1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if len(cmd) > 0:
1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                if len(cmd) == 1:
1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    # no need to wrap a single cmd
1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                    cmd = cmd.getcmd(0)
1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                # this blk of cmds, or single cmd, has already
1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                # been done, so don't execute it again
1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.addcmd(cmd, 0)
1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def addcmd(self, cmd, execute=True):
1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if execute:
1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            cmd.do(self.delegate)
1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.undoblock != 0:
1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.undoblock.append(cmd)
1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return
1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.can_merge and self.pointer > 0:
1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            lastcmd = self.undolist[self.pointer-1]
1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if lastcmd.merge(cmd):
1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                return
1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.undolist[self.pointer:] = [cmd]
1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.saved > self.pointer:
1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.saved = -1
1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.pointer = self.pointer + 1
1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if len(self.undolist) > self.max_undo:
1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            ##print "truncating undo list"
1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            del self.undolist[0]
1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.pointer = self.pointer - 1
1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if self.saved >= 0:
1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self.saved = self.saved - 1
1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.can_merge = True
1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.check_saved()
1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo_event(self, event):
1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.pointer == 0:
1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bell()
1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return "break"
1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmd = self.undolist[self.pointer - 1]
1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmd.undo(self.delegate)
1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.pointer = self.pointer - 1
1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.can_merge = False
1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.check_saved()
1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return "break"
1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def redo_event(self, event):
1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.pointer >= len(self.undolist):
1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.bell()
1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return "break"
1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmd = self.undolist[self.pointer]
1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmd.redo(self.delegate)
1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.pointer = self.pointer + 1
1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.can_merge = False
1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.check_saved()
1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return "break"
1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass Command:
1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Base class for Undoable commands
1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    tags = None
1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, index1, index2, chars, tags=None):
1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_before = {}
1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_after = {}
1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index1 = index1
1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index2 = index2
1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.chars = chars
1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if tags:
1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.tags = tags
1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __repr__(self):
1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        s = self.__class__.__name__
1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        t = (self.index1, self.index2, self.chars, self.tags)
1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.tags is None:
1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            t = t[:-1]
1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return s + repr(t)
1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def do(self, text):
1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pass
1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def redo(self, text):
1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pass
1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo(self, text):
1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pass
1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def merge(self, cmd):
1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return 0
1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def save_marks(self, text):
1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        marks = {}
1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for name in text.mark_names():
1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if name != "insert" and name != "current":
1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                marks[name] = text.index(name)
1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return marks
2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def set_marks(self, text, marks):
2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for name, index in marks.items():
2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            text.mark_set(name, index)
2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass InsertCommand(Command):
2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Undoable insert command
2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, index1, chars, tags=None):
2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        Command.__init__(self, index1, None, chars, tags)
2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def do(self, text):
2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_before = self.save_marks(text)
2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index1 = text.index(self.index1)
2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if text.compare(self.index1, ">", "end-1c"):
2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # Insert before the final newline
2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.index1 = text.index("end-1c")
2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.insert(self.index1, self.chars, self.tags)
2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_after = self.save_marks(text)
2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("do: %s\n" % self)
2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def redo(self, text):
2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.mark_set('insert', self.index1)
2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.insert(self.index1, self.chars, self.tags)
2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.set_marks(text, self.marks_after)
2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.see('insert')
2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("redo: %s\n" % self)
2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo(self, text):
2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.mark_set('insert', self.index1)
2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.delete(self.index1, self.index2)
2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.set_marks(text, self.marks_before)
2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.see('insert')
2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("undo: %s\n" % self)
2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def merge(self, cmd):
2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.__class__ is not cmd.__class__:
2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return False
2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.index2 != cmd.index1:
2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return False
2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.tags != cmd.tags:
2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return False
2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if len(cmd.chars) != 1:
2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return False
2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.chars and \
2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao           self.classify(self.chars[-1]) != self.classify(cmd.chars):
2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return False
2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index2 = cmd.index2
2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.chars = self.chars + cmd.chars
2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return True
2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    alphanumeric = string.ascii_letters + string.digits + "_"
2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def classify(self, c):
2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if c in self.alphanumeric:
2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return "alphanumeric"
2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if c == "\n":
2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return "newline"
2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return "punctuation"
2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass DeleteCommand(Command):
2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Undoable delete command
2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, index1, index2=None):
2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        Command.__init__(self, index1, index2, None, None)
2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def do(self, text):
2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_before = self.save_marks(text)
2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.index1 = text.index(self.index1)
2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self.index2:
2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.index2 = text.index(self.index2)
2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.index2 = text.index(self.index1 + " +1c")
2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if text.compare(self.index2, ">", "end-1c"):
2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # Don't delete the final newline
2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.index2 = text.index("end-1c")
2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.chars = text.get(self.index1, self.index2)
2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.delete(self.index1, self.index2)
2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.marks_after = self.save_marks(text)
2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("do: %s\n" % self)
2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def redo(self, text):
2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.mark_set('insert', self.index1)
2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.delete(self.index1, self.index2)
2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.set_marks(text, self.marks_after)
2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.see('insert')
2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("redo: %s\n" % self)
2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo(self, text):
2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.mark_set('insert', self.index1)
2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.insert(self.index1, self.chars)
2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.set_marks(text, self.marks_before)
2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        text.see('insert')
2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        ##sys.__stderr__.write("undo: %s\n" % self)
2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass CommandSequence(Command):
3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # Wrapper for a sequence of undoable cmds to be undone/redone
3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # as a unit
3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self):
3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.cmds = []
3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.depth = 0
3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __repr__(self):
3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        s = self.__class__.__name__
3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        strs = []
3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for cmd in self.cmds:
3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            strs.append("    %r" % (cmd,))
3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return s + "(\n" + ",\n".join(strs) + "\n)"
3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __len__(self):
3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return len(self.cmds)
3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def append(self, cmd):
3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.cmds.append(cmd)
3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def getcmd(self, i):
3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.cmds[i]
3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def redo(self, text):
3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for cmd in self.cmds:
3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            cmd.redo(text)
3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def undo(self, text):
3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmds = self.cmds[:]
3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        cmds.reverse()
3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for cmd in cmds:
3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            cmd.undo(text)
3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def bump_depth(self, incr=1):
3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.depth = self.depth + incr
3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.depth
3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef main():
3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    from idlelib.Percolator import Percolator
3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root = Tk()
3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root.wm_protocol("WM_DELETE_WINDOW", root.quit)
3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    text = Text()
3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    text.pack()
3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    text.focus_set()
3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    p = Percolator(text)
3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    d = UndoDelegator()
3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    p.insertfilter(d)
3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root.mainloop()
3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == "__main__":
3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    main()
353