diff --git a/lib/livebook/evaluator.ex b/lib/livebook/evaluator.ex index fab800151..4ba065bc7 100644 --- a/lib/livebook/evaluator.ex +++ b/lib/livebook/evaluator.ex @@ -349,24 +349,18 @@ defmodule Livebook.Evaluator do defp eval(code, binding, env) do try do - quoted = Code.string_to_quoted!(code) + quoted = Code.string_to_quoted!(code, file: env.file) # TODO: Use Code.eval_quoted_with_env/3 on Elixir v1.14 {result, binding, env} = :elixir.eval_quoted(quoted, binding, env) {:ok, result, binding, env} catch kind, error -> - {kind, error, stacktrace} = prepare_error(kind, error, __STACKTRACE__) + stacktrace = prune_stacktrace(__STACKTRACE__) {:error, kind, error, stacktrace} end end - defp prepare_error(kind, error, stacktrace) do - {error, stacktrace} = Exception.blame(kind, error, stacktrace) - stacktrace = prune_stacktrace(stacktrace) - {kind, error, stacktrace} - end - # Adapted from https://github.com/elixir-lang/elixir/blob/1c1654c88adfdbef38ff07fc30f6fbd34a542c07/lib/iex/lib/iex/evaluator.ex#L355-L372 @elixir_internals [:elixir, :elixir_expand, :elixir_compiler, :elixir_module] ++ diff --git a/lib/livebook/evaluator/default_formatter.ex b/lib/livebook/evaluator/default_formatter.ex index dc7932ed0..8f9a9c022 100644 --- a/lib/livebook/evaluator/default_formatter.ex +++ b/lib/livebook/evaluator/default_formatter.ex @@ -26,7 +26,7 @@ defmodule Livebook.Evaluator.DefaultFormatter do end def format_response({:error, kind, error, stacktrace}) do - formatted = Exception.format(kind, error, stacktrace) + formatted = format_error(kind, error, stacktrace) {:error, formatted, error_type(error)} end @@ -61,12 +61,7 @@ defmodule Livebook.Evaluator.DefaultFormatter do end end - defp format_error(kind, error, stacktrace) do - {error, stacktrace} = Exception.blame(kind, error, stacktrace) - Exception.format(kind, error, stacktrace) - end - - defp inspect_opts(opts) do + defp inspect_opts(opts \\ []) do default_opts = [pretty: true, width: 100, syntax_colors: syntax_colors()] Keyword.merge(default_opts, opts) end @@ -103,4 +98,47 @@ defmodule Livebook.Evaluator.DefaultFormatter do Exception.message(exception) =~ "Mix.install/2 can only be called with the same dependencies" end + + defp format_error(kind, error, stacktrace) do + {blamed, stacktrace} = Exception.blame(kind, error, stacktrace) + + banner = + case blamed do + %FunctionClauseError{} -> + banner = Exception.format_banner(kind, error, stacktrace) + blame = FunctionClauseError.blame(blamed, &inspect(&1, inspect_opts()), &blame_match/1) + [error_color(banner), pad(blame)] + + _ -> + banner = Exception.format_banner(kind, blamed, stacktrace) + error_color(banner) + end + + message = + if stacktrace == [] do + banner + else + stacktrace = Exception.format_stacktrace(stacktrace) + [banner, "\n", error_color(stacktrace)] + end + + IO.iodata_to_binary(message) + end + + defp blame_match(%{match?: true, node: node}), do: Macro.to_string(node) + + defp blame_match(%{match?: false, node: node}) do + node + |> Macro.to_string() + |> error_color() + |> IO.iodata_to_binary() + end + + defp pad(string) do + " " <> String.replace(string, "\n", "\n ") + end + + defp error_color(string) do + IO.ANSI.format([:red, string], true) + end end diff --git a/lib/livebook_web/live/output.ex b/lib/livebook_web/live/output.ex index 371bf1e74..31b1763bc 100644 --- a/lib/livebook_web/live/output.ex +++ b/lib/livebook_web/live/output.ex @@ -1,6 +1,8 @@ defmodule LivebookWeb.Output do use Phoenix.Component + import LivebookWeb.Helpers + alias LivebookWeb.Output @doc """ @@ -93,7 +95,7 @@ defmodule LivebookWeb.Output do ~H"""
- <%= render_error_message_output(@formatted) %> + <%= render_error(@formatted) %> <%= if @is_standalone do %>