diff --git a/lib/livebook/live_markdown.ex b/lib/livebook/live_markdown.ex index 946b9a84c..fa982ab2c 100644 --- a/lib/livebook/live_markdown.ex +++ b/lib/livebook/live_markdown.ex @@ -36,6 +36,8 @@ defmodule Livebook.LiveMarkdown do # - `{"break_markdown":true}` - an annotation splitting the markdown content # into separate Markdown cells # + # - `{"output":true}` - an annotation marking a code snippet as cell output + # # - section metadata, recognised keys `branch_parent_index` # # - cell metadata, recognised keys: `disable_formatting` diff --git a/lib/livebook/live_markdown/export.ex b/lib/livebook/live_markdown/export.ex index 9a920cdb6..53d9e0559 100644 --- a/lib/livebook/live_markdown/export.ex +++ b/lib/livebook/live_markdown/export.ex @@ -161,13 +161,17 @@ defmodule Livebook.LiveMarkdown.Export do text = String.replace_suffix(text, "\n", "") delimiter = MarkdownHelpers.code_block_delimiter(text) text = strip_ansi(text) - [delimiter, "output\n", text, "\n", delimiter] + + [delimiter, "\n", text, "\n", delimiter] + |> prepend_metadata(%{output: true}) end defp render_output({:text, text}, _ctx) do delimiter = MarkdownHelpers.code_block_delimiter(text) text = strip_ansi(text) - [delimiter, "output\n", text, "\n", delimiter] + + [delimiter, "\n", text, "\n", delimiter] + |> prepend_metadata(%{output: true}) end defp render_output( @@ -180,7 +184,10 @@ defmodule Livebook.LiveMarkdown.Export do case encode_js_data(payload) do {:ok, binary} -> - ["```", info_string, "\n", binary, "\n", "```"] + delimiter = MarkdownHelpers.code_block_delimiter(binary) + + [delimiter, info_string, "\n", binary, "\n", delimiter] + |> prepend_metadata(%{output: true}) _ -> :ignored diff --git a/lib/livebook/live_markdown/import.ex b/lib/livebook/live_markdown/import.ex index 21f36b19b..c4a96c4f7 100644 --- a/lib/livebook/live_markdown/import.ex +++ b/lib/livebook/live_markdown/import.ex @@ -180,6 +180,7 @@ defmodule Livebook.LiveMarkdown.Import do end end + # Import ```output snippets for backward compatibility defp take_outputs( [{"pre", _, [{"code", [{"class", "output"}], [output], %{}}], %{}} | ast], outputs @@ -187,6 +188,29 @@ defmodule Livebook.LiveMarkdown.Import do take_outputs(ast, [{:text, output} | outputs]) end + defp take_outputs( + [ + {:comment, _, [~s/livebook:{"output":true}/], %{comment: true}}, + {"pre", _, [{"code", [], [output], %{}}], %{}} + | ast + ], + outputs + ) do + take_outputs(ast, [{:text, output} | outputs]) + end + + # Ignore other exported outputs + defp take_outputs( + [ + {:comment, _, [~s/livebook:{"output":true}/], %{comment: true}}, + {"pre", _, [{"code", [{"class", _info_string}], [_output], %{}}], %{}} + | ast + ], + outputs + ) do + take_outputs(ast, outputs) + end + defp take_outputs(ast, outputs), do: {outputs, ast} # Builds a notebook from the list of elements obtained in the previous step. diff --git a/test/livebook/live_markdown/export_test.exs b/test/livebook/live_markdown/export_test.exs index 7c0de1a4b..6c7969046 100644 --- a/test/livebook/live_markdown/export_test.exs +++ b/test/livebook/live_markdown/export_test.exs @@ -584,7 +584,9 @@ defmodule Livebook.LiveMarkdown.ExportTest do IO.puts("hey") ``` - ```output + + + ``` hey ``` """ @@ -624,11 +626,15 @@ defmodule Livebook.LiveMarkdown.ExportTest do IO.puts("hey") ``` - ```output + + + ``` hey ``` - ```output + + + ``` :ok ``` """ @@ -754,6 +760,8 @@ defmodule Livebook.LiveMarkdown.ExportTest do :ok ``` + + ```mermaid graph TD; A-->B; @@ -802,6 +810,8 @@ defmodule Livebook.LiveMarkdown.ExportTest do :ok ``` + + ```box {"height":50,"width":50} ``` @@ -853,6 +863,8 @@ defmodule Livebook.LiveMarkdown.ExportTest do :ok ``` + + ```vega-lite {"height":50,"width":50} ``` @@ -897,7 +909,9 @@ defmodule Livebook.LiveMarkdown.ExportTest do IO.puts("hey") ``` - ```output + + + ``` hey ``` """ diff --git a/test/livebook/live_markdown/import_test.exs b/test/livebook/live_markdown/import_test.exs index b46ef8238..847d3635d 100644 --- a/test/livebook/live_markdown/import_test.exs +++ b/test/livebook/live_markdown/import_test.exs @@ -340,6 +340,8 @@ defmodule Livebook.LiveMarkdown.ImportTest do Enum.to_list(1..10) ``` + + ```erlang spawn_link(fun() -> io:format("Hiya") end). ``` @@ -566,11 +568,15 @@ defmodule Livebook.LiveMarkdown.ImportTest do IO.puts("hey") ``` - ```output + + + ``` hey ``` - ```output + + + ``` :ok ``` """ @@ -596,6 +602,63 @@ defmodule Livebook.LiveMarkdown.ImportTest do } = notebook end + test "discards other output snippets" do + markdown = """ + # My Notebook + + ## Section 1 + + ```elixir + IO.puts("hey") + ``` + + ```elixir + plot() + ``` + + + + ```vega-lite + {} + ``` + + ```elixir + :ok + ``` + """ + + {notebook, []} = Import.notebook_from_markdown(markdown) + + assert %Notebook{ + name: "My Notebook", + sections: [ + %Notebook.Section{ + name: "Section 1", + cells: [ + %Cell.Elixir{ + source: """ + IO.puts("hey")\ + """, + outputs: [] + }, + %Cell.Elixir{ + source: """ + plot()\ + """, + outputs: [] + }, + %Cell.Elixir{ + source: """ + :ok\ + """, + outputs: [] + } + ] + } + ] + } = notebook + end + test "imports notebook :persist_outputs attribute" do markdown = """ @@ -654,5 +717,49 @@ defmodule Livebook.LiveMarkdown.ImportTest do " Also, to make the input reactive you can use an automatically reevaluating cell" ] == messages end + + test "imports snippets with output info string" do + # We now explicitly mark every output sinppet with + # and use empty snippets for textual outputs, however previously + # we supported ```output too, so let's ensure they still work + + markdown = """ + # My Notebook + + ## Section 1 + + ```elixir + IO.puts("hey") + ``` + + ```output + hey + ``` + + ```output + :ok + ``` + """ + + {notebook, []} = Import.notebook_from_markdown(markdown) + + assert %Notebook{ + name: "My Notebook", + sections: [ + %Notebook.Section{ + name: "Section 1", + cells: [ + %Cell.Elixir{ + source: """ + IO.puts("hey")\ + """, + outputs: [{0, {:text, ":ok"}}, {1, {:text, "hey"}}] + } + ] + } + ], + output_counter: 2 + } = notebook + end end end diff --git a/test/livebook_web/controllers/session_controller_test.exs b/test/livebook_web/controllers/session_controller_test.exs index ca388bdf7..eeff70fed 100644 --- a/test/livebook_web/controllers/session_controller_test.exs +++ b/test/livebook_web/controllers/session_controller_test.exs @@ -114,7 +114,9 @@ defmodule LivebookWeb.SessionControllerTest do IO.puts("hey") ``` - ```output + + + ``` hey ``` """