inda19plusplus / logik

Logisim clone
MIT License
2 stars 5 forks source link

Undo #9

Closed NogginBops closed 4 years ago

NogginBops commented 4 years ago

How do we support an undo feature?

NogginBops commented 4 years ago

One way of doing undo is to have the concept on a "transaction", that represents one interaction (e.g. placing one component). And then all transactions get appended to a list of transactions done. Then when undoing you pop the latest transaction and undo the interaction the transaction represented.

This means that the transaction system needs to define a set of actions that are represent-able in a transaction. This means that the transaction needs to have a lossless inverse.

What also needs to be considered is that multiple transactions might reference the same data. Say a user creates a wire to then delete it later. In this case there would be two transactions appended; one for creating the wire, and then one for deleting it. Then if the user decides to undo the deletion the inverse of the deletion will be done, which is creating the component again. But this might not be enough for a lossless inverse. because now the first transaction that created the component might not refer to the newly created component.

Here is an example of how the timeline might look:

Add Transaction CreateComp (Type.And, Pos(0, 0), Comp*);
// More Transactions....
Add Transaction DeleteComp (Comp*);

Undo Transaction DeleteComp (Comp*);
// This will create a new component to emulate the inverse
// but this is not enough because new the pointer stored in 
// the CreateComp transaction doesn't reference the one
// created during the undo, instead it references the old
// destroyed component.
// We would also expect the actual deletion to free the
// Comp* pointer so we would actually not be able to 
// use that pointer here...

Undo Transaction CreateComp (Type.And, Pos(0, 0), Comp*); // ERROR!! Pointer to freed memory or some thing

A way to solve that particular problem is by using an ID instead of a pointer:

Add Transaction CreateComp (Type.And, Pos(0, 0), #234);
// More Transactions....
Add Transaction DeleteComp (#234);

Undo Transaction DeleteComp(#234);
// Here the undo transaction can create a lossless inverse because it 
// can create a new component with the same name.
// So the following CreateComp can lookup that component by id.
// I this case it's ok to create a component with the specified ID
// because no other component could use that ID while that specific
// component was created.

Undo Transaction CreateComp(Type.And, Pos(0, 0), #234);
// Here we know how to undo this because we can just delete the component
// with the ID #234. But the question is if this suffices a lossless inverse.
// But from what I can tell this is enough for a full inverse.
NogginBops commented 4 years ago

This is now for the most part working. Closing this issue. If any issue arises we can open a new issue for that bug.