defmodule EIO do
def inspect(input, opts \\ []) do
{when_fn, opts} = Keyword.pop(opts, :when)
if when_fn && not is_function(when_fn, 1) do
raise ArgumentError, ":when must be a function accepting a single argument"
end
when_result =
try do
if when_fn do
when_fn.(input)
else
true
end
rescue
_ -> true
end
if when_result do
{highlight_subject, opts} = Keyword.pop(opts, :highlight)
opts =
opts
|> Keyword.put(:inspect_fun, highlighter(highlight_subject))
|> Keyword.put(:syntax_colors, highlight: :green_background)
IO.inspect(input, opts)
else
input
end
end
defp highlighter(nil) do
&Inspect.inspect/2
end
defp highlighter(subject_fn) when is_function(subject_fn) do
fn input, opts ->
try do
if subject_fn.(input) do
highlight(input, opts)
else
Inspect.inspect(input, opts)
end
rescue
_ ->
Inspect.inspect(input, opts)
end
end
end
defp highlighter(subject) do
fn input, opts ->
cond do
input == subject ->
highlight(input, opts)
struct_module?(subject) and is_struct(input, subject) ->
highlight(input, opts)
is_tuple(subject) and is_struct(input) ->
with {module, fields} <- subject,
true <- struct_module?(module),
true <- is_map(fields) or is_list(fields),
true <- module == input.__struct__,
true <- Enum.all?(fields, fn {k, v} -> Map.fetch!(input, k) == v end) do
highlight(input, opts)
else
_ -> Inspect.inspect(input, opts)
end
is_map(subject) and is_map(input) and
Enum.all?(subject, fn {k, v} ->
Map.has_key?(input, k) and
Map.fetch!(input, k) == v
end) ->
highlight(input, opts)
true ->
Inspect.inspect(input, opts)
end
end
end
defp highlight(subject, opts) do
subject
|> Inspect.inspect(opts)
|> Inspect.Algebra.color(:highlight, opts)
end
defp struct_module?(name) do
is_atom(name) and function_exported?(name, :__struct__, 1)
end
end
EIO.inspect(%{a: :b, c: :b}, highlight: :b, when: &(&1.c == :b))
EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: DateTime)
EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: {DateTime, %{year: 2023}})
EIO.inspect(%{a: %{b: DateTime.utc_now()}}, highlight: {DateTime, year: 2023})
EIO.inspect(%{a: %{b: DateTime.utc_now()}},
highlight: %{year: 2023},
when: &(&1.a.b.year == 2023)
)
EIO.inspect(%{a: %{b: %{c: 1, d: 8}}}, highlight: %{d: 8})
EIO.inspect([:a, :b, :c], highlight: :c, when: &(:c in &1))
EIO.inspect([DateTime.utc_now(), :b, :c], highlight: &(&1.year == 2023))
EIO.inspect(Enum.map(1..100, fn _ -> Enum.random(1..100) end),
limit: :infinity,
highlight: &(rem(&1, 7) == 0)
)