diff --git a/lib/livebook/live_markdown/export.ex b/lib/livebook/live_markdown/export.ex index 83be376a3..0ffa19f44 100644 --- a/lib/livebook/live_markdown/export.ex +++ b/lib/livebook/live_markdown/export.ex @@ -238,12 +238,11 @@ defmodule Livebook.LiveMarkdown.Export do end defp render_output(%{type: :terminal_text, text: text}, _ctx) do - text = String.replace_suffix(text, "\n", "") - delimiter = MarkdownHelpers.code_block_delimiter(text) - text = strip_ansi(text) + render_text_output(text) + end - [delimiter, "\n", text, "\n", delimiter] - |> prepend_metadata(%{output: true}) + defp render_output(%{type: :error, message: message}, _ctx) do + render_text_output(message) end defp render_output(%{type: :js, js_view: %{ref: ref}}, ctx) do @@ -279,6 +278,14 @@ defmodule Livebook.LiveMarkdown.Export do defp render_output(_output, _ctx), do: :ignored + defp render_text_output(text) do + text = text |> strip_ansi() |> String.replace_suffix("\n", "") + delimiter = MarkdownHelpers.code_block_delimiter(text) + + [delimiter, "\n", text, "\n", delimiter] + |> prepend_metadata(%{output: true}) + end + defp encode_js_data(data) when is_binary(data), do: {:ok, data} defp encode_js_data(data) do @@ -349,7 +356,7 @@ defmodule Livebook.LiveMarkdown.Export do string |> Livebook.Utils.ANSI.parse_ansi_string() |> elem(0) - |> Enum.map(fn {_modifiers, string} -> string end) + |> Enum.map_join(fn {_modifiers, string} -> string end) end defp render_notebook_footer(_notebook, _notebook_source, _include_stamp? = false), do: {[], []} diff --git a/test/livebook/live_markdown/export_test.exs b/test/livebook/live_markdown/export_test.exs index 4cae590dd..b81a9c0f3 100644 --- a/test/livebook/live_markdown/export_test.exs +++ b/test/livebook/live_markdown/export_test.exs @@ -947,6 +947,57 @@ defmodule Livebook.LiveMarkdown.ExportTest do assert expected_document == document end + test "includes error outputs" do + notebook = %{ + Notebook.new() + | name: "My Notebook", + sections: [ + %{ + Notebook.Section.new() + | name: "Section 1", + cells: [ + %{ + Notebook.Cell.new(:code) + | source: """ + raise "hello"\ + """, + outputs: [ + {0, + %{ + type: :error, + message: + "\e[31m** (RuntimeError) hello\e[0m\n\e[31m #cell:tlbdimkdsfldvwge:1: (file)\n\e[0m", + context: nil + }} + ] + } + ] + } + ] + } + + expected_document = """ + # My Notebook + + ## Section 1 + + ```elixir + raise "hello" + ``` + + + + ``` + ** (RuntimeError) hello + #cell:tlbdimkdsfldvwge:1: (file) + ``` + """ + + {document, []} = Export.notebook_to_livemd(notebook, include_outputs: true) + + assert expected_document == document + end + test "includes outputs when notebook has :persist_outputs set" do notebook = %{ Notebook.new()