Skip to main content

src/etui@undo.erl

-module(etui@undo).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/undo.gleam").
-export([undo_new/2, current/1, can_undo/1, can_redo/1, undo_depth/1, push/2, undo/1, redo/1, reset/2]).
-export_type([undo_stack/1]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

-type undo_stack(FCD) :: {undo_stack, list(FCD), FCD, list(FCD), integer()}.

-file("src/etui/undo.gleam", 42).
?DOC(
    " Create a new stack with an initial `present` value.\n"
    " `max_size` limits how many past entries are retained (0 = unlimited).\n"
).
-spec undo_new(FCE, integer()) -> undo_stack(FCE).
undo_new(Initial, Max_size) ->
    {undo_stack, [], Initial, [], Max_size}.

-file("src/etui/undo.gleam", 50).
?DOC(" The current value.\n").
-spec current(undo_stack(FCG)) -> FCG.
current(Stack) ->
    erlang:element(3, Stack).

-file("src/etui/undo.gleam", 55).
?DOC(" `True` if there is at least one past state to undo to.\n").
-spec can_undo(undo_stack(any())) -> boolean().
can_undo(Stack) ->
    not gleam@list:is_empty(erlang:element(2, Stack)).

-file("src/etui/undo.gleam", 60).
?DOC(" `True` if there is at least one future state to redo to.\n").
-spec can_redo(undo_stack(any())) -> boolean().
can_redo(Stack) ->
    not gleam@list:is_empty(erlang:element(4, Stack)).

-file("src/etui/undo.gleam", 65).
?DOC(" Number of past entries available to undo.\n").
-spec undo_depth(undo_stack(any())) -> integer().
undo_depth(Stack) ->
    erlang:length(erlang:element(2, Stack)).

-file("src/etui/undo.gleam", 74).
?DOC(
    " Record `new_value` as the new present, moving the old present into past.\n"
    " Clears the future (redo history) since the branch diverged.\n"
).
-spec push(undo_stack(FCO), FCO) -> undo_stack(FCO).
push(Stack, New_value) ->
    Past = [erlang:element(3, Stack) | erlang:element(2, Stack)],
    Trimmed = case (erlang:element(5, Stack) > 0) andalso (erlang:length(Past) > erlang:element(
        5,
        Stack
    )) of
        true ->
            gleam@list:take(Past, erlang:element(5, Stack));

        false ->
            Past
    end,
    {undo_stack, Trimmed, New_value, [], erlang:element(5, Stack)}.

-file("src/etui/undo.gleam", 90).
?DOC(
    " Undo: move present to future, restore the most-recent past as present.\n"
    " No-op if there is nothing to undo.\n"
).
-spec undo(undo_stack(FCR)) -> undo_stack(FCR).
undo(Stack) ->
    case erlang:element(2, Stack) of
        [] ->
            Stack;

        [Prev | Rest] ->
            {undo_stack,
                Rest,
                Prev,
                [erlang:element(3, Stack) | erlang:element(4, Stack)],
                erlang:element(5, Stack)}
    end.

-file("src/etui/undo.gleam", 105).
?DOC(
    " Redo: move present to past, restore the most-recent future as present.\n"
    " No-op if there is nothing to redo.\n"
).
-spec redo(undo_stack(FCU)) -> undo_stack(FCU).
redo(Stack) ->
    case erlang:element(4, Stack) of
        [] ->
            Stack;

        [Next | Rest] ->
            {undo_stack,
                [erlang:element(3, Stack) | erlang:element(2, Stack)],
                Next,
                Rest,
                erlang:element(5, Stack)}
    end.

-file("src/etui/undo.gleam", 119).
?DOC(" Reset to initial state, clearing all history.\n").
-spec reset(undo_stack(FCX), FCX) -> undo_stack(FCX).
reset(Stack, Initial) ->
    {undo_stack, [], Initial, [], erlang:element(5, Stack)}.