Packages

dbg_inspect provides an extended version of `IO.inspect/1` function for the debug purposes.

Current section

Files

Jump to
dbg_inspect lib dbg.ex
Raw

lib/dbg.ex

defmodule Dbg do
@moduledoc """
Dbg module provides an extended version of `IO.inspect/1` function for the debug purposes.
"""
@doc """
`Dbg.inspect/2` prints the first argument like regular IO.inspect/1,
but including filename and line of code,
where it was called, and a representation of the expression
passed as the first argument, provides colored output to simplify it detecting
and ability to include values of variables, used it this expression, in this output.
It's worth noting that `Dbg.inspect/2` prints its output to :stderr stream.
And also doesn't add any performance penalty in `production` enviroment,
b/c when `Mix.env == :prod` it doesn't change an original AST at all.
## Options
The accepted options are:
* `show_vars` - if true prints also values of variables which were used in the first argument
## Examples
> x = 7
> x |> Dbg.inspect()
./dbg.ex:16
x # => 7
> {x, y} = {5, 7}
> Dbg.inspect(x+y)
./dbg.ex:22
x + y # => 12
See more examples in tests.
"""
defmacro inspect(ast, opts \\ [show_vars: false]) do
inspect(Mix.env(), ast, opts)
end
defp inspect(:prod, ast, _opts), do: ast
defp inspect(_env, ast, opts) do
value_representation = generate_value_representation(ast)
vars = get_vars(ast, opts)
quote do
result = unquote(ast)
IO.puts(:stderr, [
"\e[41m\e[1m\x1B[K\n",
Dbg.short_file_name(__ENV__.file),
":",
to_string(__ENV__.line)
])
unquote(vars) |> Dbg.print_vars()
IO.puts(:stderr, [
" ",
unquote(value_representation),
" #=> ",
Kernel.inspect(result),
"\x1B[K\n\e[0m"
])
result
end
end
def print_vars(vars) do
vars
|> Enum.each(fn {var_name, var_value} ->
IO.puts(:stderr, [" #{var_name} = ", Kernel.inspect(var_value), "\x1B[K"])
end)
end
def short_file_name(file_name) do
String.replace(file_name, File.cwd!(), ".")
end
defp generate_value_representation(ast) do
ast
|> Macro.to_string()
|> Code.format_string!(line_length: 60)
|> Enum.join()
|> String.replace("\n", "\n ")
end
defp get_vars(ast, show_vars: true) do
ast
|> find_vars([])
|> Enum.map(&{elem(&1, 0), &1})
end
defp get_vars(_, _), do: []
defp find_vars({_, _, [inner_ast]}, vars) do
find_vars(inner_ast, vars)
end
defp find_vars({_, _, [inner_ast | additional_args]}, vars) do
new_vars = find_vars(List.flatten(additional_args), [])
find_vars(inner_ast, vars ++ new_vars)
end
defp find_vars([var_ast, args], vars) when is_tuple(var_ast) and is_list(args) do
find_vars([var_ast | args], vars)
end
defp find_vars(args, vars) when is_list(args) do
new_vars = args |> Enum.map(&only_vars/1) |> Enum.filter(& &1)
vars ++ new_vars
end
defp find_vars({var_name, _, nil} = var_ast, vars) when is_atom(var_name) do
[var_ast | vars]
end
defp find_vars(_, vars), do: vars
defp only_vars({var_name, _, nil} = var_ast) when is_atom(var_name), do: var_ast
defp only_vars([{label, var_ast}]) when is_atom(label), do: only_vars(var_ast)
defp only_vars({label, var_ast}) when is_atom(label), do: only_vars(var_ast)
defp only_vars(_), do: false
end