Skip to content

Instantly share code, notes, and snippets.

@prophile
Created April 3, 2012 15:17
from collections import namedtuple
import functools
from copy import copy
ListInsert = namedtuple('ListInsert', ['index'])
ListDelete = namedtuple('ListDelete', ['index'])
TextReplace = namedtuple('TextReplace', ['index', 'old', 'new'])
Compound = namedtuple('Compound', ['elements'])
inverses = {}
def inverts(ty):
def wrapper(fn):
inverses[ty] = fn
return fn
return wrapper
evaluators = {}
def evaluates(ty):
def wrapper(fn):
evaluators[ty] = fn
return fn
return wrapper
def inverse(value):
return inverses[type(value)](**value._asdict())
def evaluate(value, list):
return evaluators[type(value)](copy(list), **value._asdict())
# Inverses
@inverts(ListInsert)
def list_insert_inverse(index):
return ListDelete(index=index)
@inverts(ListDelete)
def list_delete_inverse(index):
return ListInsert(index=index)
@inverts(TextReplace)
def text_replace_inverse(index, old, new):
return TextReplace(index=index,
old=new,
new=old)
@inverts(Compound)
def compound_inverse(elements):
return Compound(elements=list(reversed([inverse(el) for el in elements])))
# Evaluators
@evaluates(ListInsert)
def list_insert_eval(list, index):
assert index <= len(list)
list.insert(index, '')
return list
@evaluates(ListDelete)
def list_delete_eval(list, index):
assert index < len(list)
assert list[index] == ''
list.pop(index)
return list
@evaluates(TextReplace)
def text_replace_eval(list, index, old, new):
assert index < len(list)
assert list[index] == old
list[index] = new
return list
@evaluates(Compound)
def compound_eval(list, elements):
for el in elements:
list = evaluate(el, list)
return list
class ElementList(object):
def __init__(self):
self.list = []
self.undo_stack = []
self.redo_stack = []
def append(self, value):
index = len(self.list)
self.apply(Compound(elements=[ListInsert(index=index),
TextReplace(index=index,
old='',
new=value)]))
def delete(self, index):
self.apply(Compound(elements=[TextReplace(index=index,
old=self.list[index],
new=''),
ListDelete(index=index)]))
def edit(self, index, new_value):
self.apply(TextReplace(index=index,
old=self.list[index],
new=new_value))
def apply(self, action):
self.list = evaluate(action, self.list)
self.undo_stack.append(action)
self.redo_stack = []
def undo(self):
action = self.undo_stack.pop()
self.redo_stack.append(action)
self.list = evaluate(inverse(action), self.list)
def redo(self):
action = self.redo_stack.pop()
self.undo_stack.append(action)
self.list = evaluate(action, self.list)
commands = {}
def handler(command):
def wrapper(fn):
commands[command] = fn
return fn
return wrapper
shared_list = ElementList()
@handler('A')
def cmd_add():
text = raw_input('Enter text to add: ')
shared_list.append(text)
@handler('E')
def cmd_edit():
index = int(raw_input('Enter index to edit: '))
text = raw_input('Enter text to edit: ')
shared_list.edit(index, text)
@handler('D')
def cmd_delete():
index = int(raw_input('Enter index to delete: '))
shared_list.delete(index)
@handler('U')
def cmd_undo():
shared_list.undo()
@handler('R')
def cmd_redo():
shared_list.redo()
try:
while True:
command = raw_input("Enter command ('A'dd, 'E'dit, 'D'elete, 'U'ndo, 'R'edo): ").upper()
if command in commands:
commands[command]()
print '\n'.join(shared_list.list)
else:
print 'Unknown command'
except EOFError:
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment