www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Undo in D

reply DigitalDesigns <DigitalDesigns gmail.com> writes:
Is there any idiomatic undo designs in D that give a more natural 
implementation than the standard techniques?
Jun 22 2018
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Saturday, 23 June 2018 at 01:58:31 UTC, DigitalDesigns wrote:
 Is there any idiomatic undo designs in D that give a more 
 natural implementation than the standard techniques?
- The "stuff to undo" can be a forward range ("save" primitive, + assignable from a stored state) - The manager can be an output range of the "stuff to undo" ``` struct UndoManager(Undoable, Storage) if (isForwardRange!Undoable && isOutputRange!(Storage, Undoable)) { Storage storage; Undoable undoable; size_t position; this(Storage storage, Undoable undoable) {} void push(){ storage.put(undoable.save()); length++;} /* etc */ } ``` So that the UndoMgr is reusable with any Undoable and Storage that implement the right primitives. But well, you can do that with any PL that have basic generics, interface. IRL it's probably more complicated than my example. Storage must probably be a RandomAccessRange or have more specific primitives. Now, **and this is D specific / D idiomatic**, to this can be applied the "design by introspection". UndoManager can allow more or less advanced features, depending on the primitives implemented by Storage and Undoable. Technically this is done at compile-time using __traits(hasMember), typically.
Jun 23 2018
parent Basile B. <b2.temp gmx.com> writes:
On Saturday, 23 June 2018 at 14:06:08 UTC, Basile B. wrote:
 On Saturday, 23 June 2018 at 01:58:31 UTC, DigitalDesigns wrote:
 Is there any idiomatic undo designs in D that give a more 
 natural implementation than the standard techniques?
- The "stuff to undo" can be a forward range ("save" primitive, + assignable from a stored state) - The manager can be an output range of the "stuff to undo" ``` struct UndoManager(Undoable, Storage) if (isForwardRange!Undoable && isOutputRange!(Storage, Undoable)) { Storage storage; Undoable undoable; size_t position; this(Storage storage, Undoable undoable) {} void push(){ storage.put(undoable.save()); length++;} /* etc */ } ``` So that the UndoMgr is reusable with any Undoable and Storage that implement the right primitives. But well, you can do that with any PL that have basic generics, interface. IRL it's probably more complicated than my example. Storage must probably be a RandomAccessRange or have more specific primitives. Now, **and this is D specific / D idiomatic**, to this can be applied the "design by introspection". UndoManager can allow more or less advanced features, depending on the primitives implemented by Storage and Undoable. Technically this is done at compile-time using __traits(hasMember), typically.
Actually i did one once for an hex editor and i think this couldn't be applied. Typically a position, a command and a data are the member of a change, not the whole stuff. The generic abstraction should be changed to handle such a thing: struct UndoItem(Position, Command) { Position position; // e.g offset in doc or coordinate, etc Command command; // e.g an enum ubyte[] data; } About the Storage, i think that a linked list is better. No need to templatize it then. From this there's no thing possible that's very idiomatic in the design. Somrething like this old shitty stuff : https://github.com/BBasile/LLClasses/blob/master/LLClasses.d#L5701 but with a better absssstraction (linked code is more or less the translation of what i did in Delphi years before). So finally we come back to a simple generic stuff: struct UndoMgr(Position, Command) { List!(UndoItem!(Position, Command)) items; /* stupid method there - insert - undo - redo - compact - clear */ } UndoRedo is a simple thing, too much abstraction could make the thing abstruse. But maybe this is what you look for, something utterly complex to fill your emptiness.
Jun 23 2018
prev sibling parent bauss <jj_1337 live.dk> writes:
On Saturday, 23 June 2018 at 01:58:31 UTC, DigitalDesigns wrote:
 Is there any idiomatic undo designs in D that give a more 
 natural implementation than the standard techniques?
There is the solution above, but there I've implemented something similar in Diamond. It's a little bit different concept since it's based on "snapshot" types which basically is a type that keeps snapshots of its values and thus you can change the state of it. They're used for transactions within Diamond, which could show-case how to use them. http://diamondmvc.org/docs/data/#transactions But a quick example would be from the docs: ``` import diamond.data; auto value = new Snapshot!int; value = 100; value = 200; value = 300; value = 400; import std.stdio : writefln; writefln("%d %d %d %d %d", value[0], value[1], value[2], value[3], value); // Prints: 100 200 300 400 400 ``` For more information also see: http://diamond.dpldocs.info/diamond.data.snapshot.Snapshot.html
Jun 23 2018