A single user action. Like replace all, is really many edits, but it should undo as one step. The Composite pattern solves this: a Macro is a command made of other commands, and it is a command itself, so the editor treats it exactly like any other.
Macro(commands): execute(doc) runs each sub-command in order; undo(doc) undoes them in reverse order (so overlapping edits unwind correctly)class Document:
def __init__(self, text=""):
self.text = text
class Command:
def execute(self, doc):
raise NotImplementedError
def undo(self, doc):
raise NotImplementedError
class Insert(Command):
def __init__(self, pos, text):
self.pos = pos
self.text = text
def execute(self, doc):
doc.text = doc.text[:self.pos] + self.text + doc.text[self.pos:]
def undo(self, doc):
doc.text = doc.text[:self.pos] + doc.text[self.pos + len(self.text):]
class Delete(Command):
def __init__(self, pos, length):
self.pos = pos
self.length = length
self._removed = ""
def execute(self, doc):
self._removed = doc.text[self.pos:self.pos + self.length]
doc.text = doc.text[:self.pos] + doc.text[self.pos + self.length:]
def undo(self, doc):
doc.text = doc.text[:self.pos] + self._removed + doc.text[self.pos:]
class Macro(Command):
def __init__(self, commands):
self.commands = commands
def execute(self, doc):
"""Run every sub-command in order."""
# TODO: for c in self.commands: c.execute(doc)
pass
def undo(self, doc):
"""Undo every sub-command in reverse order."""
# TODO: for c in reversed(self.commands): c.undo(doc)
pass
class Editor:
def __init__(self, text=""):
self.doc = Document(text)
self._undo_stack = []
self._redo_stack = []
def do(self, command):
command.execute(self.doc)
self._undo_stack.append(command)
self._redo_stack.clear()
def undo(self):
if self._undo_stack:
command = self._undo_stack.pop()
command.undo(self.doc)
self._redo_stack.append(command)
def redo(self):
if self._redo_stack:
command = self._redo_stack.pop()
command.execute(self.doc)
self._undo_stack.append(command)
@property
def text(self):
return self.doc.text
The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.
A single user action. Like replace all, is really many edits, but it should undo as one step. The Composite pattern solves this: a Macro is a command made of other commands, and it is a command itself, so the editor treats it exactly like any other.
Macro(commands): execute(doc) runs each sub-command in order; undo(doc) undoes them in reverse order (so overlapping edits unwind correctly)Press Run Tests or ββ© to check your solution.