src/gliff@internal@inline.erl

-module(gliff@internal@inline).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gliff/internal/inline.gleam").
-export([highlight/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.

?MODULEDOC(false).

-file("src/gliff/internal/inline.gleam", 105).
?DOC(false).
-spec classify_edit(gliff@types:raw_edit(), boolean()) -> {binary(), boolean()}.
classify_edit(Edit, For_delete) ->
    case Edit of
        {raw_equal, V} ->
            {V, false};

        {raw_delete, V@1} ->
            case For_delete of
                true ->
                    {V@1, true};

                false ->
                    {<<""/utf8>>, false}
            end;

        {raw_insert, V@2} ->
            case For_delete of
                true ->
                    {<<""/utf8>>, false};

                false ->
                    {V@2, true}
            end
    end.

-file("src/gliff/internal/inline.gleam", 57).
?DOC(false).
-spec collect_spans(
    list(gliff@types:raw_edit()),
    boolean(),
    binary(),
    boolean(),
    list(gliff@types:span())
) -> list(gliff@types:span()).
collect_spans(Edits, For_delete, Current, Current_is_changed, Acc) ->
    case Edits of
        [] ->
            case Current of
                <<""/utf8>> ->
                    Acc;

                _ ->
                    case Current_is_changed of
                        true ->
                            [{changed, Current} | Acc];

                        false ->
                            [{unchanged, Current} | Acc]
                    end
            end;

        [Edit | Rest] ->
            {Char, Is_changed} = classify_edit(Edit, For_delete),
            case Char of
                <<""/utf8>> ->
                    collect_spans(
                        Rest,
                        For_delete,
                        Current,
                        Current_is_changed,
                        Acc
                    );

                _ ->
                    case Is_changed =:= Current_is_changed of
                        true ->
                            collect_spans(
                                Rest,
                                For_delete,
                                <<Current/binary, Char/binary>>,
                                Current_is_changed,
                                Acc
                            );

                        false ->
                            New_acc = case Current of
                                <<""/utf8>> ->
                                    Acc;

                                _ ->
                                    case Current_is_changed of
                                        true ->
                                            [{changed, Current} | Acc];

                                        false ->
                                            [{unchanged, Current} | Acc]
                                    end
                            end,
                            collect_spans(
                                Rest,
                                For_delete,
                                Char,
                                Is_changed,
                                New_acc
                            )
                    end
            end
    end.

-file("src/gliff/internal/inline.gleam", 52).
?DOC(false).
-spec raw_edits_to_spans(list(gliff@types:raw_edit()), boolean()) -> list(gliff@types:span()).
raw_edits_to_spans(Raw_edits, For_delete) ->
    _pipe = collect_spans(Raw_edits, For_delete, <<""/utf8>>, false, []),
    lists:reverse(_pipe).

-file("src/gliff/internal/inline.gleam", 39).
?DOC(false).
-spec diff_line_pair(list(binary()), list(binary()), boolean()) -> list(gliff@types:span()).
diff_line_pair(Del_lines, Ins_lines, For_delete) ->
    Del_text = gleam@string:join(Del_lines, <<"\n"/utf8>>),
    Ins_text = gleam@string:join(Ins_lines, <<"\n"/utf8>>),
    Del_chars = gleam@string:to_graphemes(Del_text),
    Ins_chars = gleam@string:to_graphemes(Ins_text),
    Raw_edits = gliff@internal@myers:diff(Del_chars, Ins_chars),
    raw_edits_to_spans(Raw_edits, For_delete).

-file("src/gliff/internal/inline.gleam", 15).
?DOC(false).
-spec highlight_loop(list(gliff@types:edit()), list(gliff@types:inline_edit())) -> list(gliff@types:inline_edit()).
highlight_loop(Edits, Acc) ->
    case Edits of
        [] ->
            Acc;

        [{equal, Lines} | Rest] ->
            highlight_loop(Rest, [{inline_equal, Lines} | Acc]);

        [{delete, Del_lines}, {insert, Ins_lines} | Rest@1] ->
            Del_spans = diff_line_pair(Del_lines, Ins_lines, true),
            Ins_spans = diff_line_pair(Del_lines, Ins_lines, false),
            highlight_loop(
                Rest@1,
                [{inline_insert, Ins_spans}, {inline_delete, Del_spans} | Acc]
            );

        [{delete, Lines@1} | Rest@2] ->
            Spans = gleam@list:map(Lines@1, fun(L) -> {changed, L} end),
            highlight_loop(Rest@2, [{inline_delete, Spans} | Acc]);

        [{insert, Lines@2} | Rest@3] ->
            Spans@1 = gleam@list:map(Lines@2, fun(L@1) -> {changed, L@1} end),
            highlight_loop(Rest@3, [{inline_insert, Spans@1} | Acc])
    end.

-file("src/gliff/internal/inline.gleam", 10).
?DOC(false).
-spec highlight(list(gliff@types:edit())) -> list(gliff@types:inline_edit()).
highlight(Edits) ->
    _pipe = highlight_loop(Edits, []),
    lists:reverse(_pipe).