-module(gliff@internal@cleanup).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gliff/internal/cleanup.gleam").
-export([semantic/1, semantic_lossless/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/cleanup.gleam", 221).
?DOC(false).
-spec is_blank_line(binary()) -> boolean().
is_blank_line(S) ->
gleam@string:trim(S) =:= <<""/utf8>>.
-file("src/gliff/internal/cleanup.gleam", 225).
?DOC(false).
-spec is_sentence_end(binary()) -> boolean().
is_sentence_end(C) ->
((C =:= <<"."/utf8>>) orelse (C =:= <<"!"/utf8>>)) orelse (C =:= <<"?"/utf8>>).
-file("src/gliff/internal/cleanup.gleam", 229).
?DOC(false).
-spec is_whitespace(binary()) -> boolean().
is_whitespace(C) ->
(((C =:= <<" "/utf8>>) orelse (C =:= <<"\t"/utf8>>)) orelse (C =:= <<"\n"/utf8>>))
orelse (C =:= <<"\r"/utf8>>).
-file("src/gliff/internal/cleanup.gleam", 240).
?DOC(false).
-spec is_alpha_or_digit(binary()) -> boolean().
is_alpha_or_digit(C) ->
gleam_stdlib:contains_string(
<<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"/utf8>>,
C
).
-file("src/gliff/internal/cleanup.gleam", 233).
?DOC(false).
-spec is_non_alnum(binary()) -> boolean().
is_non_alnum(C) ->
case C of
<<" "/utf8>> ->
false;
<<"\t"/utf8>> ->
false;
<<"\n"/utf8>> ->
false;
<<"\r"/utf8>> ->
false;
_ ->
not is_alpha_or_digit(C)
end.
-file("src/gliff/internal/cleanup.gleam", 185).
?DOC(false).
-spec score_between(binary(), binary()) -> integer().
score_between(Left, Right) ->
L_end = gleam@string:last(Left),
R_start = gleam@string:first(Right),
case {L_end, R_start} of
{{ok, Lc}, {ok, Rc}} ->
L_blank = is_blank_line(Left),
R_blank = is_blank_line(Right),
case L_blank andalso R_blank of
true ->
6;
false ->
case L_blank orelse R_blank of
true ->
5;
false ->
case (Lc =:= <<"\n"/utf8>>) orelse (Rc =:= <<"\n"/utf8>>) of
true ->
5;
false ->
case is_sentence_end(Lc) andalso (Rc =:= <<" "/utf8>>) of
true ->
4;
false ->
case is_whitespace(Lc) orelse is_whitespace(
Rc
) of
true ->
3;
false ->
case is_non_alnum(Lc) orelse is_non_alnum(
Rc
) of
true ->
2;
false ->
1
end
end
end
end
end
end;
{_, _} ->
5
end.
-file("src/gliff/internal/cleanup.gleam", 178).
?DOC(false).
-spec score_pair({ok, binary()} | {error, nil}, {ok, binary()} | {error, nil}) -> integer().
score_pair(Left, Right) ->
case {Left, Right} of
{{ok, L}, {ok, R}} ->
score_between(L, R);
{_, _} ->
5
end.
-file("src/gliff/internal/cleanup.gleam", 166).
?DOC(false).
-spec score_boundary(list(binary()), list(binary())) -> integer().
score_boundary(Edit_end, After) ->
Last_of_edit = gleam@list:last(Edit_end),
First_of_after = gleam@list:first(After),
score_pair(Last_of_edit, First_of_after).
-file("src/gliff/internal/cleanup.gleam", 172).
?DOC(false).
-spec score_boundary_left(list(binary()), list(binary())) -> integer().
score_boundary_left(Edit_start, Before) ->
Last_of_before = gleam@list:last(Before),
First_of_edit = gleam@list:first(Edit_start),
score_pair(Last_of_before, First_of_edit).
-file("src/gliff/internal/cleanup.gleam", 254).
?DOC(false).
-spec merge_loop(list(gliff@types:edit()), list(gliff@types:edit())) -> list(gliff@types:edit()).
merge_loop(Edits, Acc) ->
case Edits of
[] ->
Acc;
[{equal, L1}, {equal, L2} | Rest] ->
merge_loop([{equal, lists:append(L1, L2)} | Rest], Acc);
[{insert, L1@1}, {insert, L2@1} | Rest@1] ->
merge_loop([{insert, lists:append(L1@1, L2@1)} | Rest@1], Acc);
[{delete, L1@2}, {delete, L2@2} | Rest@2] ->
merge_loop([{delete, lists:append(L1@2, L2@2)} | Rest@2], Acc);
[Edit | Rest@3] ->
merge_loop(Rest@3, [Edit | Acc])
end.
-file("src/gliff/internal/cleanup.gleam", 249).
?DOC(false).
-spec merge_adjacent(list(gliff@types:edit())) -> list(gliff@types:edit()).
merge_adjacent(Edits) ->
_pipe = merge_loop(Edits, []),
lists:reverse(_pipe).
-file("src/gliff/internal/cleanup.gleam", 269).
?DOC(false).
-spec count_chars(list(binary())) -> integer().
count_chars(Lines) ->
gleam@list:fold(Lines, 0, fun(Acc, Line) -> Acc + string:length(Line) end).
-file("src/gliff/internal/cleanup.gleam", 24).
?DOC(false).
-spec eliminate_loop(list(gliff@types:edit()), list(gliff@types:edit())) -> list(gliff@types:edit()).
eliminate_loop(Edits, Acc) ->
case Edits of
[] ->
Acc;
[{delete, D}, {equal, Eq}, {insert, I} | Rest] ->
Eq_len = count_chars(Eq),
D_len = count_chars(D),
I_len = count_chars(I),
case ((Eq_len * 2) < D_len) orelse ((Eq_len * 2) < I_len) of
true ->
eliminate_loop(
Rest,
[{insert, lists:append([Eq, I])},
{delete, lists:append([D, Eq])} |
Acc]
);
false ->
eliminate_loop(
[{equal, Eq}, {insert, I} | Rest],
[{delete, D} | Acc]
)
end;
[{insert, I@1}, {equal, Eq@1}, {delete, D@1} | Rest@1] ->
Eq_len@1 = count_chars(Eq@1),
D_len@1 = count_chars(D@1),
I_len@1 = count_chars(I@1),
case ((Eq_len@1 * 2) < D_len@1) orelse ((Eq_len@1 * 2) < I_len@1) of
true ->
eliminate_loop(
Rest@1,
[{delete, lists:append([Eq@1, D@1])},
{insert, lists:append([I@1, Eq@1])} |
Acc]
);
false ->
eliminate_loop(
[{equal, Eq@1}, {delete, D@1} | Rest@1],
[{insert, I@1} | Acc]
)
end;
[Edit | Rest@2] ->
eliminate_loop(Rest@2, [Edit | Acc])
end.
-file("src/gliff/internal/cleanup.gleam", 19).
?DOC(false).
-spec eliminate_trivial_equalities(list(gliff@types:edit())) -> list(gliff@types:edit()).
eliminate_trivial_equalities(Edits) ->
_pipe = eliminate_loop(Edits, []),
lists:reverse(_pipe).
-file("src/gliff/internal/cleanup.gleam", 5).
?DOC(false).
-spec semantic(list(gliff@types:edit())) -> list(gliff@types:edit()).
semantic(Edits) ->
_pipe = Edits,
_pipe@1 = eliminate_trivial_equalities(_pipe),
merge_adjacent(_pipe@1).
-file("src/gliff/internal/cleanup.gleam", 285).
?DOC(false).
-spec common_prefix_strings_inner(
list(binary()),
list(binary()),
list(binary())
) -> list(binary()).
common_prefix_strings_inner(A, B, Acc) ->
case {A, B} of
{[Ah | At], [Bh | Bt]} when Ah =:= Bh ->
common_prefix_strings_inner(At, Bt, [Ah | Acc]);
{_, _} ->
Acc
end.
-file("src/gliff/internal/cleanup.gleam", 273).
?DOC(false).
-spec common_suffix_strings(list(binary()), list(binary())) -> list(binary()).
common_suffix_strings(A, B) ->
A_rev = lists:reverse(A),
B_start = B,
_pipe = common_prefix_strings_inner(A_rev, B_start, []),
lists:reverse(_pipe).
-file("src/gliff/internal/cleanup.gleam", 280).
?DOC(false).
-spec common_prefix_strings(list(binary()), list(binary())) -> list(binary()).
common_prefix_strings(A, B) ->
_pipe = common_prefix_strings_inner(A, B, []),
lists:reverse(_pipe).
-file("src/gliff/internal/cleanup.gleam", 297).
?DOC(false).
-spec take_last(list(EJR), integer()) -> list(EJR).
take_last(Items, N) ->
Len = erlang:length(Items),
gleam@list:drop(Items, case Len > N of
true ->
Len - N;
false ->
0
end).
-file("src/gliff/internal/cleanup.gleam", 121).
?DOC(false).
-spec find_best_shift(list(binary()), list(binary()), list(binary())) -> {list(binary()),
list(binary()),
list(binary())}.
find_best_shift(Before, Edit, After) ->
Suffix_common = common_suffix_strings(Edit, After),
Prefix_common = common_prefix_strings(
Edit,
begin
_pipe = Before,
lists:reverse(_pipe)
end
),
Suffix_len = erlang:length(Suffix_common),
Prefix_len = erlang:length(Prefix_common),
case (Suffix_len > 0) andalso (score_boundary(Suffix_common, After) > score_boundary(
Edit,
After
)) of
true ->
Shifted_edit = lists:append(
take_last(Before, Suffix_len),
gleam@list:take(Edit, erlang:length(Edit) - Suffix_len)
),
New_before = gleam@list:take(
Before,
erlang:length(Before) - Suffix_len
),
New_after = lists:append(gleam@list:take(Edit, Suffix_len), After),
{New_before, Shifted_edit, New_after};
false ->
case (Prefix_len > 0) andalso (score_boundary_left(
Prefix_common,
Before
)
> score_boundary_left(Edit, Before)) of
true ->
Shifted_edit@1 = lists:append(
gleam@list:drop(Edit, Prefix_len),
take_last(Before, Prefix_len)
),
New_before@1 = lists:append(
Before,
gleam@list:take(Edit, Prefix_len)
),
{New_before@1, Shifted_edit@1, After};
false ->
{Before, Edit, After}
end
end.
-file("src/gliff/internal/cleanup.gleam", 63).
?DOC(false).
-spec shift_edits_to_boundaries(
list(gliff@types:edit()),
list(gliff@types:edit())
) -> list(gliff@types:edit()).
shift_edits_to_boundaries(Edits, Acc) ->
case Edits of
[] ->
Acc;
[{equal, Eq1}, {delete, D}, {equal, Eq2} | Rest] ->
{New_eq1, New_d, New_eq2} = find_best_shift(Eq1, D, Eq2),
case New_eq1 of
[] ->
shift_edits_to_boundaries(
[{equal, New_eq2} | Rest],
[{delete, New_d} | Acc]
);
_ ->
shift_edits_to_boundaries(
[{equal, New_eq2} | Rest],
[{delete, New_d}, {equal, New_eq1} | Acc]
)
end;
[{equal, Eq1@1}, {insert, I}, {equal, Eq2@1} | Rest@1] ->
{New_eq1@1, New_i, New_eq2@1} = find_best_shift(Eq1@1, I, Eq2@1),
case New_eq1@1 of
[] ->
shift_edits_to_boundaries(
[{equal, New_eq2@1} | Rest@1],
[{insert, New_i} | Acc]
);
_ ->
shift_edits_to_boundaries(
[{equal, New_eq2@1} | Rest@1],
[{insert, New_i}, {equal, New_eq1@1} | Acc]
)
end;
[{equal, Eq1@2}, {delete, D@1}, {insert, I@1}, {equal, Eq2@2} | Rest@2] ->
{New_eq1@2, New_d@1, _} = find_best_shift(Eq1@2, D@1, Eq2@2),
{_, New_i@1, New_eq2@2} = find_best_shift(Eq1@2, I@1, Eq2@2),
case New_eq1@2 of
[] ->
shift_edits_to_boundaries(
[{equal, New_eq2@2} | Rest@2],
[{insert, New_i@1}, {delete, New_d@1} | Acc]
);
_ ->
shift_edits_to_boundaries(
[{equal, New_eq2@2} | Rest@2],
[{insert, New_i@1},
{delete, New_d@1},
{equal, New_eq1@2} |
Acc]
)
end;
[Edit | Rest@3] ->
shift_edits_to_boundaries(Rest@3, [Edit | Acc])
end.
-file("src/gliff/internal/cleanup.gleam", 11).
?DOC(false).
-spec semantic_lossless(list(gliff@types:edit())) -> list(gliff@types:edit()).
semantic_lossless(Edits) ->
_pipe = shift_edits_to_boundaries(Edits, []),
_pipe@1 = lists:reverse(_pipe),
merge_adjacent(_pipe@1).