diff --git a/lib/asciinema/recordings/snapshot.ex b/lib/asciinema/recordings/snapshot.ex index 1a437e80f..972e4a782 100644 --- a/lib/asciinema/recordings/snapshot.ex +++ b/lib/asciinema/recordings/snapshot.ex @@ -93,20 +93,30 @@ defmodule Asciinema.Recordings.Snapshot do defp group_line_segments([]), do: [] - defp group_line_segments([first_segment | segments]) do + defp group_line_segments(cells) do {segments, last_segment} = - Enum.reduce(segments, {[], first_segment}, fn {char, attrs, char_width}, - {segments, - {prev_char, prev_attrs, prev_char_width}} -> - if attrs == prev_attrs && char_width == prev_char_width && char_width == 1 && - !special_char(char) do - {segments, {prev_char <> char, attrs, char_width}} + Enum.reduce(cells, {[], nil}, fn {cur_char, cur_attrs, cur_char_width} = current, + {segments, prev} -> + if cur_char_width > 1 || special_char(cur_char) do + {[current, prev | segments], nil} else - {[{prev_char, prev_attrs, prev_char_width} | segments], {char, attrs, char_width}} + case prev do + {prev_chars, prev_attrs, prev_char_width} -> + if cur_attrs == prev_attrs && cur_char_width == prev_char_width do + {segments, {prev_chars <> cur_char, prev_attrs, prev_char_width}} + else + {[prev | segments], current} + end + + nil -> + {segments, current} + end end end) - Enum.reverse([last_segment | segments]) + [last_segment | segments] + |> Enum.filter(& &1) + |> Enum.reverse() end @box_drawing_range Range.new(0x2500, 0x257F) diff --git a/test/asciinema/recordings/snapshot_test.exs b/test/asciinema/recordings/snapshot_test.exs index 852bf4832..ccc0601a5 100644 --- a/test/asciinema/recordings/snapshot_test.exs +++ b/test/asciinema/recordings/snapshot_test.exs @@ -9,6 +9,91 @@ defmodule Asciinema.Recordings.SnapshotTest do |> Map.get(:lines) end + describe "regroup/2" do + test "splits into individual chars" do + lines = [[[" foo bar ", %{fg: 1}, 1]]] + + lines = + lines + |> Snapshot.new(:segments) + |> Snapshot.regroup(:cells) + |> Map.get(:lines) + + assert lines == [ + [ + {" ", %{fg: 1}, 1}, + {"f", %{fg: 1}, 1}, + {"o", %{fg: 1}, 1}, + {"o", %{fg: 1}, 1}, + {" ", %{fg: 1}, 1}, + {"b", %{fg: 1}, 1}, + {"a", %{fg: 1}, 1}, + {"r", %{fg: 1}, 1}, + {" ", %{fg: 1}, 1} + ] + ] + end + + test "groups into multi-char segments" do + lines = [ + [ + [" ", %{fg: 1}, 1], + ["f", %{fg: 1}, 1], + ["o", %{fg: 2}, 1], + ["o", %{fg: 2}, 1], + [" ", %{fg: 1}, 1], + ["b", %{fg: 1}, 1], + ["a", %{fg: 1}, 1], + ["r", %{fg: 1}, 1], + ["连", %{fg: 1}, 2] + ] + ] + + lines = + lines + |> Snapshot.new(:cells) + |> Snapshot.regroup(:segments) + |> Map.get(:lines) + + assert lines == [[ + {" f", %{fg: 1}, 1}, + {"oo", %{fg: 2}, 1}, + {" bar", %{fg: 1}, 1}, + {"连", %{fg: 1}, 2}, + ]] + end + + test "keeps special chars in their own segments" do + lines = [ + [ + ["▚", %{}, 1], + ["f", %{}, 1], + ["o", %{}, 1], + ["o", %{}, 1], + ["▚", %{}, 1], + ["b", %{}, 1], + ["a", %{}, 1], + ["r", %{}, 1], + ["▚", %{}, 1] + ] + ] + + lines = + lines + |> Snapshot.new(:cells) + |> Snapshot.regroup(:segments) + |> Map.get(:lines) + + assert lines == [[ + {"▚", %{}, 1}, + {"foo", %{}, 1}, + {"▚", %{}, 1}, + {"bar", %{}, 1}, + {"▚", %{}, 1}, + ]] + end + end + describe "crop/3" do test "blank taller terminal" do assert crop(