diff --git a/lib/livebook/live_markdown/export.ex b/lib/livebook/live_markdown/export.ex index 317a89ac8..be2a2a25a 100644 --- a/lib/livebook/live_markdown/export.ex +++ b/lib/livebook/live_markdown/export.ex @@ -39,11 +39,12 @@ defmodule Livebook.LiveMarkdown.Export do defp render_cell(%Cell.Elixir{} = cell) do code = get_elixir_cell_code(cell) + delimiter = code_block_delimiter(code) """ - ```elixir + #{delimiter}elixir #{code} - ```\ + #{delimiter}\ """ |> prepend_metadata(cell.metadata) end @@ -117,12 +118,23 @@ defmodule Livebook.LiveMarkdown.Export do defp format_code(code) do try do - Code.format_string!(code) + code + |> Code.format_string!() + |> IO.iodata_to_binary() rescue _ -> code end end + defp code_block_delimiter(code) do + max_streak = + Regex.scan(~r/`{3,}/, code) + |> Enum.map(fn [string] -> byte_size(string) end) + |> Enum.max(&>=/2, fn -> 2 end) + + String.duplicate("`", max_streak + 1) + end + defp put_truthy(map, entries) do Enum.reduce(entries, map, fn {key, value}, map -> if value do diff --git a/lib/livebook/notebook/explore/intro_to_kino.livemd b/lib/livebook/notebook/explore/intro_to_kino.livemd index a18174711..e326cd7b2 100644 --- a/lib/livebook/notebook/explore/intro_to_kino.livemd +++ b/lib/livebook/notebook/explore/intro_to_kino.livemd @@ -154,7 +154,7 @@ most busy processes! Sometimes you may want to render arbitrary content as rich-text, that's when `Kino.Markdown.new/1` comes into play: -```elixir +````elixir """ # Example @@ -174,7 +174,7 @@ A regular Markdown file. | 2 | Erlang | https://www.erlang.org | """ |> Kino.Markdown.new() -``` +```` ## Kino.render/1 diff --git a/test/livebook/live_markdown/export_test.exs b/test/livebook/live_markdown/export_test.exs index ce194388b..8fa42e5a6 100644 --- a/test/livebook/live_markdown/export_test.exs +++ b/test/livebook/live_markdown/export_test.exs @@ -418,4 +418,57 @@ defmodule Livebook.LiveMarkdown.ExportTest do assert expected_document == document end + + test "handles backticks in code cell" do + notebook = %{ + Notebook.new() + | name: "My Notebook", + metadata: %{}, + sections: [ + %{ + Notebook.Section.new() + | name: "Section 1", + metadata: %{}, + cells: [ + %{ + Notebook.Cell.new(:elixir) + | source: """ + \"\"\" + ```elixir + x = 1 + ``` + + ````markdown + # Heading + ```` + \"\"\"\ + """ + } + ] + } + ] + } + + expected_document = """ + # My Notebook + + ## Section 1 + + `````elixir + \"\"\" + ```elixir + x = 1 + ``` + + ````markdown + # Heading + ```` + \"\"\" + ````` + """ + + document = Export.notebook_to_markdown(notebook) + + assert expected_document == document + end end