Preserve subsequent Markdown cells in .livemd ()

* Add import/export tests

* Implement live markdown annotation for breaking Markdown cells
This commit is contained in:
Jonatan Kłosko 2021-07-26 13:35:37 +02:00 committed by GitHub
parent 049cf3b5a0
commit 7dd80489a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 4 deletions
lib/livebook
test/livebook/live_markdown

View file

@ -27,9 +27,18 @@ defmodule Livebook.LiveMarkdown do
# using Markdown, in such case the JSON contains a "livebook_object" field
#
# * a metadata that may appear anywhere and applies to the element
# it directly precedes, for instance `<!-- livebook:{"force_markdown":true} -->`
# forces the next Markdown block to be treated as part of Markdown cell
# (even if it's Elixir code block)
# it directly precedes, recognised metadatas are:
#
# - `{"force_markdown":true}` - an annotation forcing the next Markdown
# block to be treated as part of Markdown cell (relevant for Elixir code
# blocks, which otherwise are interpreted as Elixir cells)
#
# - `{"break_markdown":true}` - an annotation splitting the markdown content
# into separate Markdown cells
#
# - section metadata, recognised keys `branch_parent_index`
#
# - cell metadata, recognised keys: `disable_formatting`
#
# ## Example
#

View file

@ -24,7 +24,20 @@ defmodule Livebook.LiveMarkdown.Export do
defp render_section(section, notebook) do
name = "## #{section.name}"
cells = Enum.map(section.cells, &render_cell/1)
{cells, _} =
Enum.map_reduce(section.cells, nil, fn cell, prev_cell ->
separator =
if is_struct(cell, Cell.Markdown) and is_struct(prev_cell, Cell.Markdown) do
[~s/<!-- livebook:{"break_markdown":true} -->\n\n/]
else
[]
end
rendered = separator ++ [render_cell(cell)]
{rendered, cell}
end)
metadata = section_metadata(section, notebook)
[name | cells]

View file

@ -141,6 +141,13 @@ defmodule Livebook.LiveMarkdown.Import do
group_elements(ast, [{:cell, :markdown, [ast_node]} | elems])
end
defp group_elements(
[{:comment, _, [~s/livebook:{"break_markdown":true}/], %{comment: true}} | ast],
elems
) do
group_elements(ast, [{:cell, :markdown, []} | elems])
end
defp group_elements(
[{:comment, _, ["livebook:" <> json], %{comment: true}} | ast],
elems

View file

@ -504,4 +504,51 @@ defmodule Livebook.LiveMarkdown.ExportTest do
assert expected_document == document
end
test "separates consecutive markdown cells by a break annotation" do
notebook = %{
Notebook.new()
| name: "My Notebook",
metadata: %{},
sections: [
%{
Notebook.Section.new()
| name: "Section 1",
metadata: %{},
cells: [
%{
Notebook.Cell.new(:markdown)
| metadata: %{},
source: """
Cell 1\
"""
},
%{
Notebook.Cell.new(:markdown)
| metadata: %{},
source: """
Cell 2\
"""
}
]
}
]
}
expected_document = """
# My Notebook
## Section 1
Cell 1
<!-- livebook:{"break_markdown":true} -->
Cell 2
"""
document = Export.notebook_to_markdown(notebook)
assert expected_document == document
end
end

View file

@ -502,4 +502,41 @@ defmodule Livebook.LiveMarkdown.ImportTest do
]
} = notebook
end
test "imports markdown content into separate cells when a break annotation is encountered" do
markdown = """
# My Notebook
## Section 1
Cell 1
<!-- livebook:{"break_markdown":true} -->
Cell 2
"""
{notebook, []} = Import.notebook_from_markdown(markdown)
assert %Notebook{
name: "My Notebook",
sections: [
%Notebook.Section{
name: "Section 1",
cells: [
%Cell.Markdown{
source: """
Cell 1\
"""
},
%Cell.Markdown{
source: """
Cell 2\
"""
}
]
}
]
} = notebook
end
end