mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-01-01 12:41:43 +08:00
Improve errors formatting (#936)
* Use monospaced font and wrapping for errors * Improve function clause error format
This commit is contained in:
parent
6b19f1d71b
commit
00c2cfb31a
4 changed files with 65 additions and 23 deletions
|
@ -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] ++
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"""
|
||||
<div class="flex flex-col space-y-4">
|
||||
<%= render_error_message_output(@formatted) %>
|
||||
<%= render_error(@formatted) %>
|
||||
<%= if @is_standalone do %>
|
||||
<div>
|
||||
<button class="button-base button-gray" phx-click="restart_runtime">
|
||||
|
@ -112,7 +114,7 @@ defmodule LivebookWeb.Output do
|
|||
end
|
||||
|
||||
defp render_output({:error, formatted, _type}, %{}) do
|
||||
render_error_message_output(formatted)
|
||||
render_error(formatted)
|
||||
end
|
||||
|
||||
# TODO: remove on Livebook v0.7
|
||||
|
@ -123,24 +125,32 @@ defmodule LivebookWeb.Output do
|
|||
:table_dynamic,
|
||||
:frame_dynamic
|
||||
] do
|
||||
render_error_message_output("""
|
||||
render_error_message("""
|
||||
Legacy output format: #{inspect(output)}. Please update Kino to
|
||||
the latest version.
|
||||
""")
|
||||
end
|
||||
|
||||
defp render_output(output, %{}) do
|
||||
render_error_message_output("""
|
||||
render_error_message("""
|
||||
Unknown output format: #{inspect(output)}. If you're using Kino,
|
||||
you may want to update Kino and Livebook to the latest version.
|
||||
""")
|
||||
end
|
||||
|
||||
defp render_error_message_output(message) do
|
||||
defp render_error(message) do
|
||||
assigns = %{message: message}
|
||||
|
||||
~H"""
|
||||
<div class="overflow-auto whitespace-pre text-red-600 tiny-scrollbar"><%= @message %></div>
|
||||
<div class="whitespace-pre-wrap font-editor text-gray-500"><%= ansi_string_to_html(@message) %></div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp render_error_message(message) do
|
||||
assigns = %{message: message}
|
||||
|
||||
~H"""
|
||||
<div class="whitespace-pre-wrap font-editor text-red-600"><%= @message %></div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -94,8 +94,8 @@ defmodule Livebook.EvaluatorTest do
|
|||
Evaluator.evaluate_code(evaluator, self(), code, :code_1)
|
||||
|
||||
assert_receive {:evaluation_response, :code_1,
|
||||
{:error, :error, %FunctionClauseError{},
|
||||
[{List, :first, _arity, _location}]}, metadata()}
|
||||
{:error, :error, :function_clause, [{List, :first, _arity, _location}]},
|
||||
metadata()}
|
||||
end
|
||||
|
||||
test "in case of an error returns only the relevant part of stacktrace",
|
||||
|
|
Loading…
Reference in a new issue