-module(gliff@internal@merge).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gliff/internal/merge.gleam").
-export([merge3/3]).
-export_type([change_hunk/0]).
-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).
-type change_hunk() :: {change_hunk, integer(), integer(), list(binary())}.
-file("src/gliff/internal/merge.gleam", 42).
?DOC(false).
-spec collect_change(
list(gliff@types:raw_edit()),
integer(),
list(binary()),
list(binary())
) -> {change_hunk(), list(gliff@types:raw_edit()), integer()}.
collect_change(Edits, Pos, Deleted, Inserted) ->
case Edits of
[{raw_delete, V} | Rest] ->
collect_change(Rest, Pos + 1, [V | Deleted], Inserted);
[{raw_insert, V@1} | Rest@1] ->
collect_change(Rest@1, Pos, Deleted, [V@1 | Inserted]);
_ ->
Base_start = Pos - erlang:length(Deleted),
Hunk = {change_hunk, Base_start, Pos, lists:reverse(Inserted)},
{Hunk, Edits, Pos}
end.
-file("src/gliff/internal/merge.gleam", 27).
?DOC(false).
-spec edits_to_change_hunks(
list(gliff@types:raw_edit()),
integer(),
list(change_hunk())
) -> list(change_hunk()).
edits_to_change_hunks(Edits, Pos, Acc) ->
case Edits of
[] ->
lists:reverse(Acc);
[{raw_equal, _} | Rest] ->
edits_to_change_hunks(Rest, Pos + 1, Acc);
_ ->
{Hunk, Remaining, New_pos} = collect_change(Edits, Pos, [], []),
edits_to_change_hunks(Remaining, New_pos, [Hunk | Acc])
end.
-file("src/gliff/internal/merge.gleam", 208).
?DOC(false).
-spec hunks_overlap(change_hunk(), change_hunk()) -> boolean().
hunks_overlap(A, B) ->
(erlang:element(2, A) < erlang:element(3, B)) andalso (erlang:element(2, B)
< erlang:element(3, A)).
-file("src/gliff/internal/merge.gleam", 212).
?DOC(false).
-spec slice_lines(list(binary()), integer(), integer()) -> list(binary()).
slice_lines(Lines, From, To) ->
_pipe = Lines,
_pipe@1 = gleam@list:drop(_pipe, From),
gleam@list:take(_pipe@1, To - From).
-file("src/gliff/internal/merge.gleam", 218).
?DOC(false).
-spec split_lines(binary()) -> list(binary()).
split_lines(Text) ->
case Text of
<<""/utf8>> ->
[];
_ ->
gleam@string:split(Text, <<"\n"/utf8>>)
end.
-file("src/gliff/internal/merge.gleam", 225).
?DOC(false).
-spec max(integer(), integer()) -> integer().
max(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/gliff/internal/merge.gleam", 232).
?DOC(false).
-spec min(integer(), integer()) -> integer().
min(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/gliff/internal/merge.gleam", 66).
?DOC(false).
-spec merge_hunks(
list(binary()),
list(change_hunk()),
list(change_hunk()),
integer(),
list(binary()),
list(gliff@types:conflict())
) -> gliff@types:merge_result().
merge_hunks(Base_lines, Ours, Theirs, Pos, Output, Conflicts) ->
case {Ours, Theirs} of
{[], []} ->
Remaining = gleam@list:drop(Base_lines, Pos),
Final_output = lists:append(lists:reverse(Output), Remaining),
Merged = gleam@string:join(Final_output, <<"\n"/utf8>>),
case Conflicts of
[] ->
{merge_ok, Merged};
_ ->
{merge_conflict, Merged, lists:reverse(Conflicts)}
end;
{[O | Rest_ours], []} ->
Context = slice_lines(Base_lines, Pos, erlang:element(2, O)),
New_output = lists:append(
lists:reverse(erlang:element(4, O)),
lists:append(lists:reverse(Context), Output)
),
merge_hunks(
Base_lines,
Rest_ours,
[],
erlang:element(3, O),
New_output,
Conflicts
);
{[], [T | Rest_theirs]} ->
Context@1 = slice_lines(Base_lines, Pos, erlang:element(2, T)),
New_output@1 = lists:append(
lists:reverse(erlang:element(4, T)),
lists:append(lists:reverse(Context@1), Output)
),
merge_hunks(
Base_lines,
[],
Rest_theirs,
erlang:element(3, T),
New_output@1,
Conflicts
);
{[O@1 | Rest_ours@1], [T@1 | Rest_theirs@1]} ->
case hunks_overlap(O@1, T@1) of
false ->
case erlang:element(2, O@1) =< erlang:element(2, T@1) of
true ->
Context@2 = slice_lines(
Base_lines,
Pos,
erlang:element(2, O@1)
),
New_output@2 = lists:append(
lists:reverse(erlang:element(4, O@1)),
lists:append(lists:reverse(Context@2), Output)
),
merge_hunks(
Base_lines,
Rest_ours@1,
[T@1 | Rest_theirs@1],
erlang:element(3, O@1),
New_output@2,
Conflicts
);
false ->
Context@3 = slice_lines(
Base_lines,
Pos,
erlang:element(2, T@1)
),
New_output@3 = lists:append(
lists:reverse(erlang:element(4, T@1)),
lists:append(lists:reverse(Context@3), Output)
),
merge_hunks(
Base_lines,
[O@1 | Rest_ours@1],
Rest_theirs@1,
erlang:element(3, T@1),
New_output@3,
Conflicts
)
end;
true ->
case erlang:element(4, O@1) =:= erlang:element(4, T@1) of
true ->
Context@4 = slice_lines(
Base_lines,
Pos,
erlang:element(2, O@1)
),
Merged_end = max(
erlang:element(3, O@1),
erlang:element(3, T@1)
),
New_output@4 = lists:append(
lists:reverse(erlang:element(4, O@1)),
lists:append(lists:reverse(Context@4), Output)
),
merge_hunks(
Base_lines,
Rest_ours@1,
Rest_theirs@1,
Merged_end,
New_output@4,
Conflicts
);
false ->
Merged_start = min(
erlang:element(2, O@1),
erlang:element(2, T@1)
),
Merged_end@1 = max(
erlang:element(3, O@1),
erlang:element(3, T@1)
),
Context@5 = slice_lines(
Base_lines,
Pos,
Merged_start
),
Base_conflict_lines = slice_lines(
Base_lines,
Merged_start,
Merged_end@1
),
Conflict_markers = [<<">>>>>>> theirs"/utf8>> |
lists:append(
lists:reverse(erlang:element(4, T@1)),
[<<"======="/utf8>> |
lists:append(
lists:reverse(
erlang:element(4, O@1)
),
[<<"<<<<<<< ours"/utf8>>]
)]
)],
New_output@5 = lists:append(
Conflict_markers,
lists:append(lists:reverse(Context@5), Output)
),
Conflict = {conflict,
Base_conflict_lines,
erlang:element(4, O@1),
erlang:element(4, T@1)},
merge_hunks(
Base_lines,
Rest_ours@1,
Rest_theirs@1,
Merged_end@1,
New_output@5,
[Conflict | Conflicts]
)
end
end
end.
-file("src/gliff/internal/merge.gleam", 9).
?DOC(false).
-spec merge3(binary(), binary(), binary()) -> gliff@types:merge_result().
merge3(Base, Ours, Theirs) ->
Base_lines = split_lines(Base),
Ours_lines = split_lines(Ours),
Theirs_lines = split_lines(Theirs),
Ours_edits = gliff@internal@myers:diff(Base_lines, Ours_lines),
Theirs_edits = gliff@internal@myers:diff(Base_lines, Theirs_lines),
Ours_hunks = edits_to_change_hunks(Ours_edits, 0, []),
Theirs_hunks = edits_to_change_hunks(Theirs_edits, 0, []),
merge_hunks(Base_lines, Ours_hunks, Theirs_hunks, 0, [], []).