Skip to content

Instantly share code, notes, and snippets.

@rmoorman
Forked from tony612/diff.ex
Created November 29, 2018 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmoorman/ee0624da64cf425736342fc97be1c919 to your computer and use it in GitHub Desktop.
Save rmoorman/ee0624da64cf425736342fc97be1c919 to your computer and use it in GitHub Desktop.
elixir diff(extracted from ExUnit code)
defmodule Diff do
def diff(left, right, %{diff_type: :cli} = config) do
# %{enabled: true}
Diff.Cli.diff(left, right, config)
end
def format_sides(left, right, formatter) do
case format_diff(left, right, formatter) do
{left, right} ->
{IO.iodata_to_binary(left), IO.iodata_to_binary(right)}
nil ->
{left, right}
end
end
defp format_diff(left, right, formatter) do
if left && right && formatter.(:diff_enabled?, false) do
if script = edit_script(left, right) do
colorize_diff(script, formatter, {[], []})
end
end
end
defp colorize_diff(script, formatter, acc) when is_list(script) do
Enum.reduce(script, acc, &colorize_diff(&1, formatter, &2))
end
defp colorize_diff({:eq, content}, _formatter, {left, right}) do
{[left | content], [right | content]}
end
defp colorize_diff({:del, content}, formatter, {left, right}) do
format = colorize_format(content, :diff_delete, :diff_delete_whitespace)
{[left | formatter.(format, content)], right}
end
defp colorize_diff({:ins, content}, formatter, {left, right}) do
format = colorize_format(content, :diff_insert, :diff_insert_whitespace)
{left, [right | formatter.(format, content)]}
end
defp colorize_format(content, normal, whitespace) do
if String.trim_leading(content) == "", do: whitespace, else: normal
end
defp edit_script(left, right) do
task = Task.async(ExUnit.Diff, :script, [left, right])
case Task.yield(task, 1500) || Task.shutdown(task, :brutal_kill) do
{:ok, script} -> script
nil -> nil
end
end
end
defmodule Diff.Cli do
def diff(left, right, config) do
formatter = &formatter(&1, &2, config)
Diff.format_sides(left, right, formatter)
end
defp formatter(:diff_enabled?, _, %{enabled: enabled}), do: enabled
defp formatter(:diff_delete, msg, config), do: colorize(:red, msg, config)
defp formatter(:diff_delete_whitespace, msg, config),
do: colorize(IO.ANSI.color_background(2, 0, 0), msg, config)
defp formatter(:diff_insert, msg, config), do: colorize(:green, msg, config)
defp formatter(:diff_insert_whitespace, msg, config),
do: colorize(IO.ANSI.color_background(0, 2, 0), msg, config)
defp formatter(_, msg, _config), do: msg
defp colorize(escape, string, %{enabled: enabled}) do
if enabled do
[escape, string, :reset]
|> IO.ANSI.format_fragment(true)
|> IO.iodata_to_binary()
else
string
end
end
end
m1 = %{a: %{b: 1}}
m2 = %{a: %{b: 2}}
Diff.diff(m1, m2, %{diff_type: :cli, enabled: true})
{"%{a: %{b: \e[31m1\e[0m}}", "%{a: %{b: \e[32m2\e[0m}}"}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment