Thread overview
Undo in D
Jun 23, 2018
DigitalDesigns
Jun 23, 2018
Basile B.
Jun 23, 2018
Basile B.
Jun 23, 2018
bauss
June 23, 2018
Is there any idiomatic undo designs in D that give a more natural implementation than the standard techniques?
June 23, 2018
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, e.g C# or FreePascal. The key is to have a nice generic 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.
June 23, 2018
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, e.g C# or FreePascal. The key is to have a nice generic 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
    */
}

nothing D specific, you can do that in C# or in FreePascal. 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.
June 23, 2018
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